2205 lines · 56.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *	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/pseudo_fs.h>#include <linux/sched.h>#include <linux/seq_file.h>#include <linux/device.h>#include <linux/pfn.h>#include <linux/mm.h>#include <linux/mount.h>#include <linux/resource_ext.h>#include <uapi/linux/magic.h>#include <linux/string.h>#include <linux/vmalloc.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); struct resource soft_reserve_resource = {	.name	= "Soft Reserved",	.start	= 0,	.end	= -1,	.desc	= IORES_DESC_SOFT_RESERVED,	.flags	= IORESOURCE_MEM,}; static DEFINE_RWLOCK(resource_lock); /* * Return the next node of @p in pre-order tree traversal.  If * @skip_children is true, skip the descendant nodes of @p in * traversal.  If @p is a descendant of @subtree_root, only traverse * the subtree under @subtree_root. */static struct resource *next_resource(struct resource *p, bool skip_children,				      struct resource *subtree_root){	if (!skip_children && p->child)		return p->child;	while (!p->sibling && p->parent) {		p = p->parent;		if (p == subtree_root)			return NULL;	}	return p->sibling;} /* * Traverse the resource subtree under @_root in pre-order, excluding * @_root itself. * * NOTE: '__p' is introduced to avoid shadowing '_p' outside of loop. * And it is referenced to avoid unused variable warning. */#define for_each_resource(_root, _p, _skip_children) \	for (typeof(_root) __root = (_root), __p = _p = __root->child;	\	     __p && _p; _p = next_resource(_p, _skip_children, __root)) #ifdef CONFIG_PROC_FS enum { MAX_IORES_LEVEL = 8 }; static void *r_start(struct seq_file *m, loff_t *pos)	__acquires(resource_lock){	struct resource *root = pde_data(file_inode(m->file));	struct resource *p;	loff_t l = *pos; 	read_lock(&resource_lock);	for_each_resource(root, p, false) {		if (l-- == 0)			break;	} 	return p;} static void *r_next(struct seq_file *m, void *v, loff_t *pos){	struct resource *p = v; 	(*pos)++; 	return (void *)next_resource(p, false, NULL);} 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 = pde_data(file_inode(m->file));	struct resource *r = v, *p;	unsigned long long start, end;	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; 	if (file_ns_capable(m->file, &init_user_ns, CAP_SYS_ADMIN)) {		start = r->start;		end = r->end;	} else {		start = end = 0;	} 	seq_printf(m, "%*s%0*llx-%0*llx : %s\n",			depth * 2, "",			width, start,			width, 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 __init ioresources_init(void){	proc_create_seq_data("ioports", 0, NULL, &resource_op,			&ioport_resource);	proc_create_seq_data("iomem", 0, NULL, &resource_op, &iomem_resource);	return 0;}__initcall(ioresources_init); #endif /* CONFIG_PROC_FS */ static void free_resource(struct resource *res){	/**	 * If the resource was allocated using memblock early during boot	 * we'll leak it here: we can only return full pages back to the	 * buddy and trying to be smart and reusing them eventually in	 * alloc_resource() overcomplicates resource handling.	 */	if (res && PageSlab(virt_to_head_page(res)))		kfree(res);} static struct resource *alloc_resource(gfp_t flags){	return kzalloc_obj(struct resource, flags);} /* 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, bool release_child){	struct resource *tmp, **p, *chd; 	p = &old->parent->child;	for (;;) {		tmp = *p;		if (!tmp)			break;		if (tmp == old) {			if (release_child || !(tmp->child)) {				*p = tmp->sibling;			} else {				for (chd = tmp->child;; chd = chd->sibling) {					chd->parent = tmp->parent;					if (!(chd->sibling))						break;				}				*p = tmp->child;				chd->sibling = 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, true);	write_unlock(&resource_lock);	return retval;} EXPORT_SYMBOL(release_resource); static bool is_type_match(struct resource *p, unsigned long flags, unsigned long desc){	return (p->flags & flags) == flags && (desc == IORES_DESC_NONE || desc == p->desc);} /** * find_next_res - Finds the lowest resource that covers part of *		   [@start..@end]. * * If a resource is found, returns 0 and @*res is overwritten with the part * of the resource that's within [@start..@end]; if none is found, returns * -ENODEV.  Returns -EINVAL for invalid parameters. * * @parent:	resource tree root to search * @start:	start address of the resource searched for * @end:	end address of same resource * @flags:	flags which the resource must have * @desc:	descriptor the resource must have * @res:	return ptr, if resource found * * The caller must specify @start, @end, @flags, and @desc * (which may be IORES_DESC_NONE). */static int find_next_res(struct resource *parent, resource_size_t start,			 resource_size_t end, unsigned long flags,			 unsigned long desc, struct resource *res){	/* Skip children until we find a top level range that matches */	bool skip_children = true;	struct resource *p; 	if (!res)		return -EINVAL; 	if (start >= end)		return -EINVAL; 	read_lock(&resource_lock); 	for_each_resource(parent, p, skip_children) {		/* If we passed the resource we are looking for, stop */		if (p->start > end) {			p = NULL;			break;		} 		/* Skip until we find a range that matches what we look for */		if (p->end < start)			continue; 		/*		 * We found a top level range that matches what we are looking		 * for. Time to start checking children too.		 */		skip_children = false; 		/* Found a match, break */		if (is_type_match(p, flags, desc))			break;	} 	if (p) {		/* copy data */		*res = (struct resource) {			.start = max(start, p->start),			.end = min(end, p->end),			.flags = p->flags,			.desc = p->desc,			.parent = p->parent,		};	} 	read_unlock(&resource_lock);	return p ? 0 : -ENODEV;} static int find_next_iomem_res(resource_size_t start, resource_size_t end,			       unsigned long flags, unsigned long desc,			       struct resource *res){	return find_next_res(&iomem_resource, start, end, flags, desc, res);} static int walk_res_desc(struct resource *parent, resource_size_t start,			 resource_size_t end, unsigned long flags,			 unsigned long desc, void *arg,			 int (*func)(struct resource *, void *)){	struct resource res;	int ret = -EINVAL; 	while (start < end &&	       !find_next_res(parent, start, end, flags, desc, &res)) {		ret = (*func)(&res, arg);		if (ret)			break; 		start = res.end + 1;	} 	return ret;} static int __walk_iomem_res_desc(resource_size_t start, resource_size_t end,				 unsigned long flags, unsigned long desc,				 void *arg,				 int (*func)(struct resource *, void *)){	return walk_res_desc(&iomem_resource, start, end, flags, desc, arg, func);}  /** * walk_iomem_res_desc - Walks through iomem resources and calls func() *			 with matching resource ranges. * * * @desc: I/O resource descriptor. Use IORES_DESC_NONE to skip @desc check. * @flags: I/O resource flags * @start: start addr * @end: end addr * @arg: function argument for the callback @func * @func: callback function that is called for each qualifying resource area * * All the memory ranges which overlap start,end and also match flags and * desc are valid candidates. * * NOTE: For a new descriptor search, define a new IORES_DESC in * <linux/ioport.h> and set it in 'desc' of a target resource entry. */int walk_iomem_res_desc(unsigned long desc, unsigned long flags, u64 start,		u64 end, void *arg, int (*func)(struct resource *, void *)){	return __walk_iomem_res_desc(start, end, flags, desc, arg, func);}EXPORT_SYMBOL_GPL(walk_iomem_res_desc); /* * In support of device drivers claiming Soft Reserved resources, walk the Soft * Reserved resource deferral tree. */int walk_soft_reserve_res(u64 start, u64 end, void *arg,			  int (*func)(struct resource *, void *)){	return walk_res_desc(&soft_reserve_resource, start, end, IORESOURCE_MEM,			     IORES_DESC_SOFT_RESERVED, arg, func);}EXPORT_SYMBOL_GPL(walk_soft_reserve_res); /* * This function calls the @func callback against all memory ranges of type * System RAM which are marked as IORESOURCE_SYSTEM_RAM and IORESOUCE_BUSY. * Now, this function is only for System RAM, it deals with full ranges and * not PFNs. If resources are not PFN-aligned, dealing with PFNs can truncate * ranges. */int walk_system_ram_res(u64 start, u64 end, void *arg,			int (*func)(struct resource *, void *)){	unsigned long flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; 	return __walk_iomem_res_desc(start, end, flags, IORES_DESC_NONE, arg,				     func);} /* * This function, being a variant of walk_system_ram_res(), calls the @func * callback against all memory ranges of type System RAM which are marked as * IORESOURCE_SYSTEM_RAM and IORESOUCE_BUSY in reversed order, i.e., from * higher to lower. */int walk_system_ram_res_rev(u64 start, u64 end, void *arg,				int (*func)(struct resource *, void *)){	struct resource res, *rams;	int rams_size = 16, i;

Lines 1–500 of 2205.