mdesc.c

来自「linux 内核源代码」· C语言 代码 · 共 913 行 · 第 1/2 页

C
913
字号
/* mdesc.c: Sun4V machine description handling. * * Copyright (C) 2007 David S. Miller <davem@davemloft.net> */#include <linux/kernel.h>#include <linux/types.h>#include <linux/bootmem.h>#include <linux/log2.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/mm.h>#include <linux/miscdevice.h>#include <asm/hypervisor.h>#include <asm/mdesc.h>#include <asm/prom.h>#include <asm/oplib.h>#include <asm/smp.h>/* Unlike the OBP device tree, the machine description is a full-on * DAG.  An arbitrary number of ARCs are possible from one * node to other nodes and thus we can't use the OBP device_node * data structure to represent these nodes inside of the kernel. * * Actually, it isn't even a DAG, because there are back pointers * which create cycles in the graph. * * mdesc_hdr and mdesc_elem describe the layout of the data structure * we get from the Hypervisor. */struct mdesc_hdr {	u32	version; /* Transport version */	u32	node_sz; /* node block size */	u32	name_sz; /* name block size */	u32	data_sz; /* data block size */} __attribute__((aligned(16)));struct mdesc_elem {	u8	tag;#define MD_LIST_END	0x00#define MD_NODE		0x4e#define MD_NODE_END	0x45#define MD_NOOP		0x20#define MD_PROP_ARC	0x61#define MD_PROP_VAL	0x76#define MD_PROP_STR	0x73#define MD_PROP_DATA	0x64	u8	name_len;	u16	resv;	u32	name_offset;	union {		struct {			u32	data_len;			u32	data_offset;		} data;		u64	val;	} d;};struct mdesc_mem_ops {	struct mdesc_handle *(*alloc)(unsigned int mdesc_size);	void (*free)(struct mdesc_handle *handle);};struct mdesc_handle {	struct list_head	list;	struct mdesc_mem_ops	*mops;	void			*self_base;	atomic_t		refcnt;	unsigned int		handle_size;	struct mdesc_hdr	mdesc;};static void mdesc_handle_init(struct mdesc_handle *hp,			      unsigned int handle_size,			      void *base){	BUG_ON(((unsigned long)&hp->mdesc) & (16UL - 1));	memset(hp, 0, handle_size);	INIT_LIST_HEAD(&hp->list);	hp->self_base = base;	atomic_set(&hp->refcnt, 1);	hp->handle_size = handle_size;}static struct mdesc_handle * __init mdesc_bootmem_alloc(unsigned int mdesc_size){	struct mdesc_handle *hp;	unsigned int handle_size, alloc_size;	handle_size = (sizeof(struct mdesc_handle) -		       sizeof(struct mdesc_hdr) +		       mdesc_size);	alloc_size = PAGE_ALIGN(handle_size);	hp = __alloc_bootmem(alloc_size, PAGE_SIZE, 0UL);	if (hp)		mdesc_handle_init(hp, handle_size, hp);	return hp;}static void mdesc_bootmem_free(struct mdesc_handle *hp){	unsigned int alloc_size, handle_size = hp->handle_size;	unsigned long start, end;	BUG_ON(atomic_read(&hp->refcnt) != 0);	BUG_ON(!list_empty(&hp->list));	alloc_size = PAGE_ALIGN(handle_size);	start = (unsigned long) hp;	end = start + alloc_size;	while (start < end) {		struct page *p;		p = virt_to_page(start);		ClearPageReserved(p);		__free_page(p);		start += PAGE_SIZE;	}}static struct mdesc_mem_ops bootmem_mdesc_ops = {	.alloc = mdesc_bootmem_alloc,	.free  = mdesc_bootmem_free,};static struct mdesc_handle *mdesc_kmalloc(unsigned int mdesc_size){	unsigned int handle_size;	void *base;	handle_size = (sizeof(struct mdesc_handle) -		       sizeof(struct mdesc_hdr) +		       mdesc_size);	base = kmalloc(handle_size + 15, GFP_KERNEL | __GFP_NOFAIL);	if (base) {		struct mdesc_handle *hp;		unsigned long addr;		addr = (unsigned long)base;		addr = (addr + 15UL) & ~15UL;		hp = (struct mdesc_handle *) addr;		mdesc_handle_init(hp, handle_size, base);		return hp;	}	return NULL;}static void mdesc_kfree(struct mdesc_handle *hp){	BUG_ON(atomic_read(&hp->refcnt) != 0);	BUG_ON(!list_empty(&hp->list));	kfree(hp->self_base);}static struct mdesc_mem_ops kmalloc_mdesc_memops = {	.alloc = mdesc_kmalloc,	.free  = mdesc_kfree,};static struct mdesc_handle *mdesc_alloc(unsigned int mdesc_size,					struct mdesc_mem_ops *mops){	struct mdesc_handle *hp = mops->alloc(mdesc_size);	if (hp)		hp->mops = mops;	return hp;}static void mdesc_free(struct mdesc_handle *hp){	hp->mops->free(hp);}static struct mdesc_handle *cur_mdesc;static LIST_HEAD(mdesc_zombie_list);static DEFINE_SPINLOCK(mdesc_lock);struct mdesc_handle *mdesc_grab(void){	struct mdesc_handle *hp;	unsigned long flags;	spin_lock_irqsave(&mdesc_lock, flags);	hp = cur_mdesc;	if (hp)		atomic_inc(&hp->refcnt);	spin_unlock_irqrestore(&mdesc_lock, flags);	return hp;}EXPORT_SYMBOL(mdesc_grab);void mdesc_release(struct mdesc_handle *hp){	unsigned long flags;	spin_lock_irqsave(&mdesc_lock, flags);	if (atomic_dec_and_test(&hp->refcnt)) {		list_del_init(&hp->list);		hp->mops->free(hp);	}	spin_unlock_irqrestore(&mdesc_lock, flags);}EXPORT_SYMBOL(mdesc_release);static DEFINE_MUTEX(mdesc_mutex);static struct mdesc_notifier_client *client_list;void mdesc_register_notifier(struct mdesc_notifier_client *client){	u64 node;	mutex_lock(&mdesc_mutex);	client->next = client_list;	client_list = client;	mdesc_for_each_node_by_name(cur_mdesc, node, client->node_name)		client->add(cur_mdesc, node);	mutex_unlock(&mdesc_mutex);}static const u64 *parent_cfg_handle(struct mdesc_handle *hp, u64 node){	const u64 *id;	u64 a;	id = NULL;	mdesc_for_each_arc(a, hp, node, MDESC_ARC_TYPE_BACK) {		u64 target;		target = mdesc_arc_target(hp, a);		id = mdesc_get_property(hp, target,					"cfg-handle", NULL);		if (id)			break;	}	return id;}/* Run 'func' on nodes which are in A but not in B.  */static void invoke_on_missing(const char *name,			      struct mdesc_handle *a,			      struct mdesc_handle *b,			      void (*func)(struct mdesc_handle *, u64)){	u64 node;	mdesc_for_each_node_by_name(a, node, name) {		int found = 0, is_vdc_port = 0;		const char *name_prop;		const u64 *id;		u64 fnode;		name_prop = mdesc_get_property(a, node, "name", NULL);		if (name_prop && !strcmp(name_prop, "vdc-port")) {			is_vdc_port = 1;			id = parent_cfg_handle(a, node);		} else			id = mdesc_get_property(a, node, "id", NULL);		if (!id) {			printk(KERN_ERR "MD: Cannot find ID for %s node.\n",			       (name_prop ? name_prop : name));			continue;		}		mdesc_for_each_node_by_name(b, fnode, name) {			const u64 *fid;			if (is_vdc_port) {				name_prop = mdesc_get_property(b, fnode,							       "name", NULL);				if (!name_prop ||				    strcmp(name_prop, "vdc-port"))					continue;				fid = parent_cfg_handle(b, fnode);				if (!fid) {					printk(KERN_ERR "MD: Cannot find ID "					       "for vdc-port node.\n");					continue;				}			} else				fid = mdesc_get_property(b, fnode,							 "id", NULL);			if (*id == *fid) {				found = 1;				break;			}		}		if (!found)			func(a, node);	}}static void notify_one(struct mdesc_notifier_client *p,		       struct mdesc_handle *old_hp,		       struct mdesc_handle *new_hp){	invoke_on_missing(p->node_name, old_hp, new_hp, p->remove);	invoke_on_missing(p->node_name, new_hp, old_hp, p->add);}static void mdesc_notify_clients(struct mdesc_handle *old_hp,				 struct mdesc_handle *new_hp){	struct mdesc_notifier_client *p = client_list;	while (p) {		notify_one(p, old_hp, new_hp);		p = p->next;	}}void mdesc_update(void){	unsigned long len, real_len, status;	struct mdesc_handle *hp, *orig_hp;	unsigned long flags;	mutex_lock(&mdesc_mutex);	(void) sun4v_mach_desc(0UL, 0UL, &len);	hp = mdesc_alloc(len, &kmalloc_mdesc_memops);	if (!hp) {		printk(KERN_ERR "MD: mdesc alloc fails\n");		goto out;	}	status = sun4v_mach_desc(__pa(&hp->mdesc), len, &real_len);	if (status != HV_EOK || real_len > len) {		printk(KERN_ERR "MD: mdesc reread fails with %lu\n",		       status);		atomic_dec(&hp->refcnt);		mdesc_free(hp);		goto out;	}	spin_lock_irqsave(&mdesc_lock, flags);	orig_hp = cur_mdesc;	cur_mdesc = hp;	spin_unlock_irqrestore(&mdesc_lock, flags);	mdesc_notify_clients(orig_hp, hp);	spin_lock_irqsave(&mdesc_lock, flags);	if (atomic_dec_and_test(&orig_hp->refcnt))		mdesc_free(orig_hp);	else		list_add(&orig_hp->list, &mdesc_zombie_list);	spin_unlock_irqrestore(&mdesc_lock, flags);out:	mutex_unlock(&mdesc_mutex);}static struct mdesc_elem *node_block(struct mdesc_hdr *mdesc){	return (struct mdesc_elem *) (mdesc + 1);}static void *name_block(struct mdesc_hdr *mdesc){	return ((void *) node_block(mdesc)) + mdesc->node_sz;}static void *data_block(struct mdesc_hdr *mdesc){	return ((void *) name_block(mdesc)) + mdesc->name_sz;}u64 mdesc_node_by_name(struct mdesc_handle *hp,		       u64 from_node, const char *name){	struct mdesc_elem *ep = node_block(&hp->mdesc);	const char *names = name_block(&hp->mdesc);	u64 last_node = hp->mdesc.node_sz / 16;	u64 ret;	if (from_node == MDESC_NODE_NULL) {		ret = from_node = 0;	} else if (from_node >= last_node) {		return MDESC_NODE_NULL;	} else {		ret = ep[from_node].d.val;	}	while (ret < last_node) {		if (ep[ret].tag != MD_NODE)			return MDESC_NODE_NULL;		if (!strcmp(names + ep[ret].name_offset, name))			break;		ret = ep[ret].d.val;	}	if (ret >= last_node)		ret = MDESC_NODE_NULL;	return ret;}EXPORT_SYMBOL(mdesc_node_by_name);const void *mdesc_get_property(struct mdesc_handle *hp, u64 node,			       const char *name, int *lenp){	const char *names = name_block(&hp->mdesc);	u64 last_node = hp->mdesc.node_sz / 16;	void *data = data_block(&hp->mdesc);	struct mdesc_elem *ep;	if (node == MDESC_NODE_NULL || node >= last_node)		return NULL;	ep = node_block(&hp->mdesc) + node;	ep++;	for (; ep->tag != MD_NODE_END; ep++) {		void *val = NULL;		int len = 0;		switch (ep->tag) {		case MD_PROP_VAL:			val = &ep->d.val;			len = 8;			break;		case MD_PROP_STR:		case MD_PROP_DATA:			val = data + ep->d.data.data_offset;			len = ep->d.data.data_len;			break;		default:			break;		}		if (!val)			continue;		if (!strcmp(names + ep->name_offset, name)) {			if (lenp)				*lenp = len;			return val;		}	}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?