1272 lines · 25.7 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* ds.c: Domain Services driver for Logical Domains * * Copyright (C) 2007, 2008 David S. Miller <davem@davemloft.net> */ #include <linux/kernel.h>#include <linux/module.h>#include <linux/types.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/sched.h>#include <linux/sched/clock.h>#include <linux/delay.h>#include <linux/mutex.h>#include <linux/kthread.h>#include <linux/reboot.h>#include <linux/cpu.h> #include <asm/hypervisor.h>#include <asm/ldc.h>#include <asm/vio.h>#include <asm/mdesc.h>#include <asm/head.h>#include <asm/irq.h> #include "kernel.h" #define DRV_MODULE_NAME		"ds"#define PFX DRV_MODULE_NAME	": "#define DRV_MODULE_VERSION	"1.0"#define DRV_MODULE_RELDATE	"Jul 11, 2007" static char version[] =	DRV_MODULE_NAME ".c:v" DRV_MODULE_VERSION " (" DRV_MODULE_RELDATE ")\n";MODULE_AUTHOR("David S. Miller <davem@davemloft.net>");MODULE_DESCRIPTION("Sun LDOM domain services driver");MODULE_LICENSE("GPL");MODULE_VERSION(DRV_MODULE_VERSION); struct ds_msg_tag {	__u32			type;#define DS_INIT_REQ		0x00#define DS_INIT_ACK		0x01#define DS_INIT_NACK		0x02#define DS_REG_REQ		0x03#define DS_REG_ACK		0x04#define DS_REG_NACK		0x05#define DS_UNREG_REQ		0x06#define DS_UNREG_ACK		0x07#define DS_UNREG_NACK		0x08#define DS_DATA			0x09#define DS_NACK			0x0a 	__u32			len;}; /* Result codes */#define DS_OK			0x00#define DS_REG_VER_NACK		0x01#define DS_REG_DUP		0x02#define DS_INV_HDL		0x03#define DS_TYPE_UNKNOWN		0x04 struct ds_version {	__u16			major;	__u16			minor;}; struct ds_ver_req {	struct ds_msg_tag	tag;	struct ds_version	ver;}; struct ds_ver_ack {	struct ds_msg_tag	tag;	__u16			minor;}; struct ds_ver_nack {	struct ds_msg_tag	tag;	__u16			major;}; struct ds_reg_req {	struct ds_msg_tag	tag;	__u64			handle;	__u16			major;	__u16			minor;	char			svc_id[];}; struct ds_reg_ack {	struct ds_msg_tag	tag;	__u64			handle;	__u16			minor;}; struct ds_reg_nack {	struct ds_msg_tag	tag;	__u64			handle;	__u16			major;}; struct ds_unreg_req {	struct ds_msg_tag	tag;	__u64			handle;}; struct ds_unreg_ack {	struct ds_msg_tag	tag;	__u64			handle;}; struct ds_unreg_nack {	struct ds_msg_tag	tag;	__u64			handle;}; struct ds_data {	struct ds_msg_tag	tag;	__u64			handle;}; struct ds_data_nack {	struct ds_msg_tag	tag;	__u64			handle;	__u64			result;}; struct ds_info;struct ds_cap_state {	__u64			handle; 	void			(*data)(struct ds_info *dp,					struct ds_cap_state *cp,					void *buf, int len); 	const char		*service_id; 	u8			state;#define CAP_STATE_UNKNOWN	0x00#define CAP_STATE_REG_SENT	0x01#define CAP_STATE_REGISTERED	0x02}; static void md_update_data(struct ds_info *dp, struct ds_cap_state *cp,			   void *buf, int len);static void domain_shutdown_data(struct ds_info *dp,				 struct ds_cap_state *cp,				 void *buf, int len);static void domain_panic_data(struct ds_info *dp,			      struct ds_cap_state *cp,			      void *buf, int len);#ifdef CONFIG_HOTPLUG_CPUstatic void dr_cpu_data(struct ds_info *dp,			struct ds_cap_state *cp,			void *buf, int len);#endifstatic void ds_pri_data(struct ds_info *dp,			struct ds_cap_state *cp,			void *buf, int len);static void ds_var_data(struct ds_info *dp,			struct ds_cap_state *cp,			void *buf, int len); static struct ds_cap_state ds_states_template[] = {	{		.service_id	= "md-update",		.data		= md_update_data,	},	{		.service_id	= "domain-shutdown",		.data		= domain_shutdown_data,	},	{		.service_id	= "domain-panic",		.data		= domain_panic_data,	},#ifdef CONFIG_HOTPLUG_CPU	{		.service_id	= "dr-cpu",		.data		= dr_cpu_data,	},#endif	{		.service_id	= "pri",		.data		= ds_pri_data,	},	{		.service_id	= "var-config",		.data		= ds_var_data,	},	{		.service_id	= "var-config-backup",		.data		= ds_var_data,	},}; static DEFINE_SPINLOCK(ds_lock); struct ds_info {	struct ldc_channel	*lp;	u8			hs_state;#define DS_HS_START		0x01#define DS_HS_DONE		0x02 	u64			id; 	void			*rcv_buf;	int			rcv_buf_len; 	struct ds_cap_state	*ds_states;	int			num_ds_states; 	struct ds_info		*next;}; static struct ds_info *ds_info_list; static struct ds_cap_state *find_cap(struct ds_info *dp, u64 handle){	unsigned int index = handle >> 32; 	if (index >= dp->num_ds_states)		return NULL;	return &dp->ds_states[index];} static struct ds_cap_state *find_cap_by_string(struct ds_info *dp,					       const char *name){	int i; 	for (i = 0; i < dp->num_ds_states; i++) {		if (strcmp(dp->ds_states[i].service_id, name))			continue; 		return &dp->ds_states[i];	}	return NULL;} static int __ds_send(struct ldc_channel *lp, void *data, int len){	int err, limit = 1000; 	err = -EINVAL;	while (limit-- > 0) {		err = ldc_write(lp, data, len);		if (!err || (err != -EAGAIN))			break;		udelay(1);	} 	return err;} static int ds_send(struct ldc_channel *lp, void *data, int len){	unsigned long flags;	int err; 	spin_lock_irqsave(&ds_lock, flags);	err = __ds_send(lp, data, len);	spin_unlock_irqrestore(&ds_lock, flags); 	return err;} struct ds_md_update_req {	__u64				req_num;}; struct ds_md_update_res {	__u64				req_num;	__u32				result;}; static void md_update_data(struct ds_info *dp,			   struct ds_cap_state *cp,			   void *buf, int len){	struct ldc_channel *lp = dp->lp;	struct ds_data *dpkt = buf;	struct ds_md_update_req *rp;	struct {		struct ds_data		data;		struct ds_md_update_res	res;	} pkt; 	rp = (struct ds_md_update_req *) (dpkt + 1); 	printk(KERN_INFO "ds-%llu: Machine description update.\n", dp->id); 	mdesc_update(); 	memset(&pkt, 0, sizeof(pkt));	pkt.data.tag.type = DS_DATA;	pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);	pkt.data.handle = cp->handle;	pkt.res.req_num = rp->req_num;	pkt.res.result = DS_OK; 	ds_send(lp, &pkt, sizeof(pkt));} struct ds_shutdown_req {	__u64				req_num;	__u32				ms_delay;}; struct ds_shutdown_res {	__u64				req_num;	__u32				result;	char				reason[1];}; static void domain_shutdown_data(struct ds_info *dp,				 struct ds_cap_state *cp,				 void *buf, int len){	struct ldc_channel *lp = dp->lp;	struct ds_data *dpkt = buf;	struct ds_shutdown_req *rp;	struct {		struct ds_data		data;		struct ds_shutdown_res	res;	} pkt; 	rp = (struct ds_shutdown_req *) (dpkt + 1); 	printk(KERN_ALERT "ds-%llu: Shutdown request from "	       "LDOM manager received.\n", dp->id); 	memset(&pkt, 0, sizeof(pkt));	pkt.data.tag.type = DS_DATA;	pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);	pkt.data.handle = cp->handle;	pkt.res.req_num = rp->req_num;	pkt.res.result = DS_OK;	pkt.res.reason[0] = 0; 	ds_send(lp, &pkt, sizeof(pkt)); 	orderly_poweroff(true);} struct ds_panic_req {	__u64				req_num;}; struct ds_panic_res {	__u64				req_num;	__u32				result;	char				reason[1];}; static void domain_panic_data(struct ds_info *dp,			      struct ds_cap_state *cp,			      void *buf, int len){	struct ldc_channel *lp = dp->lp;	struct ds_data *dpkt = buf;	struct ds_panic_req *rp;	struct {		struct ds_data		data;		struct ds_panic_res	res;	} pkt; 	rp = (struct ds_panic_req *) (dpkt + 1); 	printk(KERN_ALERT "ds-%llu: Panic request from "	       "LDOM manager received.\n", dp->id); 	memset(&pkt, 0, sizeof(pkt));	pkt.data.tag.type = DS_DATA;	pkt.data.tag.len = sizeof(pkt) - sizeof(struct ds_msg_tag);	pkt.data.handle = cp->handle;	pkt.res.req_num = rp->req_num;	pkt.res.result = DS_OK;	pkt.res.reason[0] = 0; 	ds_send(lp, &pkt, sizeof(pkt)); 	panic("PANIC requested by LDOM manager.");} #ifdef CONFIG_HOTPLUG_CPUstruct dr_cpu_tag {	__u64				req_num;	__u32				type;#define DR_CPU_CONFIGURE		0x43#define DR_CPU_UNCONFIGURE		0x55#define DR_CPU_FORCE_UNCONFIGURE	0x46#define DR_CPU_STATUS			0x53 /* Responses */#define DR_CPU_OK			0x6f#define DR_CPU_ERROR			0x65 	__u32				num_records;}; struct dr_cpu_resp_entry {	__u32				cpu;	__u32				result;#define DR_CPU_RES_OK			0x00#define DR_CPU_RES_FAILURE		0x01#define DR_CPU_RES_BLOCKED		0x02#define DR_CPU_RES_CPU_NOT_RESPONDING	0x03#define DR_CPU_RES_NOT_IN_MD		0x04 	__u32				stat;#define DR_CPU_STAT_NOT_PRESENT		0x00#define DR_CPU_STAT_UNCONFIGURED	0x01#define DR_CPU_STAT_CONFIGURED		0x02 	__u32				str_off;}; static void __dr_cpu_send_error(struct ds_info *dp,				struct ds_cap_state *cp,				struct ds_data *data){	struct dr_cpu_tag *tag = (struct dr_cpu_tag *) (data + 1);	struct {		struct ds_data		data;		struct dr_cpu_tag	tag;	} pkt;	int msg_len; 	memset(&pkt, 0, sizeof(pkt));	pkt.data.tag.type = DS_DATA;	pkt.data.handle = cp->handle;	pkt.tag.req_num = tag->req_num;	pkt.tag.type = DR_CPU_ERROR;	pkt.tag.num_records = 0; 	msg_len = (sizeof(struct ds_data) +		   sizeof(struct dr_cpu_tag)); 	pkt.data.tag.len = msg_len - sizeof(struct ds_msg_tag); 	__ds_send(dp->lp, &pkt, msg_len);} static void dr_cpu_send_error(struct ds_info *dp,			      struct ds_cap_state *cp,			      struct ds_data *data){	unsigned long flags; 	spin_lock_irqsave(&ds_lock, flags);	__dr_cpu_send_error(dp, cp, data);	spin_unlock_irqrestore(&ds_lock, flags);} #define CPU_SENTINEL	0xffffffff static void purge_dups(u32 *list, u32 num_ents){	unsigned int i; 	for (i = 0; i < num_ents; i++) {		u32 cpu = list[i];		unsigned int j; 		if (cpu == CPU_SENTINEL)			continue; 		for (j = i + 1; j < num_ents; j++) {			if (list[j] == cpu)				list[j] = CPU_SENTINEL;		}	}} static int dr_cpu_size_response(int ncpus){	return (sizeof(struct ds_data) +		sizeof(struct dr_cpu_tag) +		(sizeof(struct dr_cpu_resp_entry) * ncpus));} static void dr_cpu_init_response(struct ds_data *resp, u64 req_num,				 u64 handle, int resp_len, int ncpus,				 cpumask_t *mask, u32 default_stat){	struct dr_cpu_resp_entry *ent;	struct dr_cpu_tag *tag;	int i, cpu; 	tag = (struct dr_cpu_tag *) (resp + 1);	ent = (struct dr_cpu_resp_entry *) (tag + 1); 	resp->tag.type = DS_DATA;	resp->tag.len = resp_len - sizeof(struct ds_msg_tag);	resp->handle = handle;	tag->req_num = req_num;

Lines 1–500 of 1272.