1208 lines · 27.8 KB · C
RawDownload
/* *	linux/kernel/resource.c * * Copyright (C) 1999	Linus Torvalds * Copyright (C) 1999	Martin Mares <mj@ucw.cz> * * Arbitrary resource management. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/export.h>#include <linux/errno.h>#include <linux/ioport.h>#include <linux/init.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/fs.h>#include <linux/proc_fs.h>#include <linux/sched.h>#include <linux/seq_file.h>#include <linux/device.h>#include <linux/pfn.h>#include <asm/io.h>  struct resource ioport_resource = {	.name	= "PCI IO",	.start	= 0,	.end	= IO_SPACE_LIMIT,	.flags	= IORESOURCE_IO,};EXPORT_SYMBOL(ioport_resource); struct resource iomem_resource = {	.name	= "PCI mem",	.start	= 0,	.end	= -1,	.flags	= IORESOURCE_MEM,};EXPORT_SYMBOL(iomem_resource); /* constraints to be met while allocating resources */struct resource_constraint {	resource_size_t min, max, align;	resource_size_t (*alignf)(void *, const struct resource *,			resource_size_t, resource_size_t);	void *alignf_data;}; static DEFINE_RWLOCK(resource_lock); static void *r_next(struct seq_file *m, void *v, loff_t *pos){	struct resource *p = v;	(*pos)++;	if (p->child)		return p->child;	while (!p->sibling && p->parent)		p = p->parent;	return p->sibling;} #ifdef CONFIG_PROC_FS enum { MAX_IORES_LEVEL = 5 }; static void *r_start(struct seq_file *m, loff_t *pos)	__acquires(resource_lock){	struct resource *p = m->private;	loff_t l = 0;	read_lock(&resource_lock);	for (p = p->child; p && l < *pos; p = r_next(m, p, &l))		;	return p;} static void r_stop(struct seq_file *m, void *v)	__releases(resource_lock){	read_unlock(&resource_lock);} static int r_show(struct seq_file *m, void *v){	struct resource *root = m->private;	struct resource *r = v, *p;	int width = root->end < 0x10000 ? 4 : 8;	int depth; 	for (depth = 0, p = r; depth < MAX_IORES_LEVEL; depth++, p = p->parent)		if (p->parent == root)			break;	seq_printf(m, "%*s%0*llx-%0*llx : %s\n",			depth * 2, "",			width, (unsigned long long) r->start,			width, (unsigned long long) r->end,			r->name ? r->name : "<BAD>");	return 0;} static const struct seq_operations resource_op = {	.start	= r_start,	.next	= r_next,	.stop	= r_stop,	.show	= r_show,}; static int ioports_open(struct inode *inode, struct file *file){	int res = seq_open(file, &resource_op);	if (!res) {		struct seq_file *m = file->private_data;		m->private = &ioport_resource;	}	return res;} static int iomem_open(struct inode *inode, struct file *file){	int res = seq_open(file, &resource_op);	if (!res) {		struct seq_file *m = file->private_data;		m->private = &iomem_resource;	}	return res;} static const struct file_operations proc_ioports_operations = {	.open		= ioports_open,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= seq_release,}; static const struct file_operations proc_iomem_operations = {	.open		= iomem_open,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= seq_release,}; static int __init ioresources_init(void){	proc_create("ioports", 0, NULL, &proc_ioports_operations);	proc_create("iomem", 0, NULL, &proc_iomem_operations);	return 0;}__initcall(ioresources_init); #endif /* CONFIG_PROC_FS */ /* Return the conflict entry if you can't request it */static struct resource * __request_resource(struct resource *root, struct resource *new){	resource_size_t start = new->start;	resource_size_t end = new->end;	struct resource *tmp, **p; 	if (end < start)		return root;	if (start < root->start)		return root;	if (end > root->end)		return root;	p = &root->child;	for (;;) {		tmp = *p;		if (!tmp || tmp->start > end) {			new->sibling = tmp;			*p = new;			new->parent = root;			return NULL;		}		p = &tmp->sibling;		if (tmp->end < start)			continue;		return tmp;	}} static int __release_resource(struct resource *old){	struct resource *tmp, **p; 	p = &old->parent->child;	for (;;) {		tmp = *p;		if (!tmp)			break;		if (tmp == old) {			*p = tmp->sibling;			old->parent = NULL;			return 0;		}		p = &tmp->sibling;	}	return -EINVAL;} static void __release_child_resources(struct resource *r){	struct resource *tmp, *p;	resource_size_t size; 	p = r->child;	r->child = NULL;	while (p) {		tmp = p;		p = p->sibling; 		tmp->parent = NULL;		tmp->sibling = NULL;		__release_child_resources(tmp); 		printk(KERN_DEBUG "release child resource %pR\n", tmp);		/* need to restore size, and keep flags */		size = resource_size(tmp);		tmp->start = 0;		tmp->end = size - 1;	}} void release_child_resources(struct resource *r){	write_lock(&resource_lock);	__release_child_resources(r);	write_unlock(&resource_lock);} /** * request_resource_conflict - request and reserve an I/O or memory resource * @root: root resource descriptor * @new: resource descriptor desired by caller * * Returns 0 for success, conflict resource on error. */struct resource *request_resource_conflict(struct resource *root, struct resource *new){	struct resource *conflict; 	write_lock(&resource_lock);	conflict = __request_resource(root, new);	write_unlock(&resource_lock);	return conflict;} /** * request_resource - request and reserve an I/O or memory resource * @root: root resource descriptor * @new: resource descriptor desired by caller * * Returns 0 for success, negative error code on error. */int request_resource(struct resource *root, struct resource *new){	struct resource *conflict; 	conflict = request_resource_conflict(root, new);	return conflict ? -EBUSY : 0;} EXPORT_SYMBOL(request_resource); /** * release_resource - release a previously reserved resource * @old: resource pointer */int release_resource(struct resource *old){	int retval; 	write_lock(&resource_lock);	retval = __release_resource(old);	write_unlock(&resource_lock);	return retval;} EXPORT_SYMBOL(release_resource); #if !defined(CONFIG_ARCH_HAS_WALK_MEMORY)/* * Finds the lowest memory reosurce exists within [res->start.res->end) * the caller must specify res->start, res->end, res->flags and "name". * If found, returns 0, res is overwritten, if not found, returns -1. */static int find_next_system_ram(struct resource *res, char *name){	resource_size_t start, end;	struct resource *p; 	BUG_ON(!res); 	start = res->start;	end = res->end;	BUG_ON(start >= end); 	read_lock(&resource_lock);	for (p = iomem_resource.child; p ; p = p->sibling) {		/* system ram is just marked as IORESOURCE_MEM */		if (p->flags != res->flags)			continue;		if (name && strcmp(p->name, name))			continue;		if (p->start > end) {			p = NULL;			break;		}		if ((p->end >= start) && (p->start < end))			break;	}	read_unlock(&resource_lock);	if (!p)		return -1;	/* copy data */	if (res->start < p->start)		res->start = p->start;	if (res->end > p->end)		res->end = p->end;	return 0;} /* * This function calls callback against all memory range of "System RAM" * which are marked as IORESOURCE_MEM and IORESOUCE_BUSY. * Now, this function is only for "System RAM". */int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages,		void *arg, int (*func)(unsigned long, unsigned long, void *)){	struct resource res;	unsigned long pfn, end_pfn;	u64 orig_end;	int ret = -1; 	res.start = (u64) start_pfn << PAGE_SHIFT;	res.end = ((u64)(start_pfn + nr_pages) << PAGE_SHIFT) - 1;	res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;	orig_end = res.end;	while ((res.start < res.end) &&		(find_next_system_ram(&res, "System RAM") >= 0)) {		pfn = (res.start + PAGE_SIZE - 1) >> PAGE_SHIFT;		end_pfn = (res.end + 1) >> PAGE_SHIFT;		if (end_pfn > pfn)			ret = (*func)(pfn, end_pfn - pfn, arg);		if (ret)			break;		res.start = res.end + 1;		res.end = orig_end;	}	return ret;} #endif static int __is_ram(unsigned long pfn, unsigned long nr_pages, void *arg){	return 1;}/* * This generic page_is_ram() returns true if specified address is * registered as "System RAM" in iomem_resource list. */int __weak page_is_ram(unsigned long pfn){	return walk_system_ram_range(pfn, 1, NULL, __is_ram) == 1;} void __weak arch_remove_reservations(struct resource *avail){} static resource_size_t simple_align_resource(void *data,					     const struct resource *avail,					     resource_size_t size,					     resource_size_t align){	return avail->start;} static void resource_clip(struct resource *res, resource_size_t min,			  resource_size_t max){	if (res->start < min)		res->start = min;	if (res->end > max)		res->end = max;} static bool resource_contains(struct resource *res1, struct resource *res2){	return res1->start <= res2->start && res1->end >= res2->end;} /* * Find empty slot in the resource tree with the given range and * alignment constraints */static int __find_resource(struct resource *root, struct resource *old,			 struct resource *new,			 resource_size_t  size,			 struct resource_constraint *constraint){	struct resource *this = root->child;	struct resource tmp = *new, avail, alloc; 	tmp.flags = new->flags;	tmp.start = root->start;	/*	 * Skip past an allocated resource that starts at 0, since the assignment	 * of this->start - 1 to tmp->end below would cause an underflow.	 */	if (this && this->start == root->start) {		tmp.start = (this == old) ? old->start : this->end + 1;		this = this->sibling;	}	for(;;) {		if (this)			tmp.end = (this == old) ?  this->end : this->start - 1;		else			tmp.end = root->end; 		if (tmp.end < tmp.start)			goto next; 		resource_clip(&tmp, constraint->min, constraint->max);		arch_remove_reservations(&tmp); 		/* Check for overflow after ALIGN() */		avail = *new;		avail.start = ALIGN(tmp.start, constraint->align);		avail.end = tmp.end;		if (avail.start >= tmp.start) {			alloc.start = constraint->alignf(constraint->alignf_data, &avail,					size, constraint->align);			alloc.end = alloc.start + size - 1;			if (resource_contains(&avail, &alloc)) {				new->start = alloc.start;				new->end = alloc.end;				return 0;			}		} next:		if (!this || this->end == root->end)			break; 		if (this != old)			tmp.start = this->end + 1;		this = this->sibling;	}	return -EBUSY;} /* * Find empty slot in the resource tree given range and alignment. */static int find_resource(struct resource *root, struct resource *new,			resource_size_t size,			struct resource_constraint  *constraint){	return  __find_resource(root, NULL, new, size, constraint);} /** * reallocate_resource - allocate a slot in the resource tree given range & alignment. *	The resource will be relocated if the new size cannot be reallocated in the *	current location. * * @root: root resource descriptor * @old:  resource descriptor desired by caller * @newsize: new size of the resource descriptor * @constraint: the size and alignment constraints to be met. */int reallocate_resource(struct resource *root, struct resource *old,			resource_size_t newsize,			struct resource_constraint  *constraint){	int err=0;	struct resource new = *old;	struct resource *conflict; 	write_lock(&resource_lock); 	if ((err = __find_resource(root, old, &new, newsize, constraint)))		goto out; 	if (resource_contains(&new, old)) {		old->start = new.start;		old->end = new.end;		goto out;	} 	if (old->child) {		err = -EBUSY;		goto out;	} 	if (resource_contains(old, &new)) {		old->start = new.start;

Lines 1–500 of 1208.