1785 lines · 45.4 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/sched.h>#include <linux/seq_file.h>#include <linux/device.h>#include <linux/pfn.h>#include <linux/mm.h>#include <linux/resource_ext.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); /* * For memory hotplug, there is no way to free resource entries allocated * by boot mem after the system is up. So for reusing the resource entry * we need to remember the resource. */static struct resource *bootmem_resource_free;static DEFINE_SPINLOCK(bootmem_resource_lock); static struct resource *next_resource(struct resource *p, bool sibling_only){	/* Caller wants to traverse through siblings only */	if (sibling_only)		return p->sibling; 	if (p->child)		return p->child;	while (!p->sibling && p->parent)		p = p->parent;	return p->sibling;} static void *r_next(struct seq_file *m, void *v, loff_t *pos){	struct resource *p = v;	(*pos)++;	return (void *)next_resource(p, false);} #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 = PDE_DATA(file_inode(m->file));	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 = 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 (!res)		return; 	if (!PageSlab(virt_to_head_page(res))) {		spin_lock(&bootmem_resource_lock);		res->sibling = bootmem_resource_free;		bootmem_resource_free = res;		spin_unlock(&bootmem_resource_lock);	} else {		kfree(res);	}} static struct resource *alloc_resource(gfp_t flags){	struct resource *res = NULL; 	spin_lock(&bootmem_resource_lock);	if (bootmem_resource_free) {		res = bootmem_resource_free;		bootmem_resource_free = res->sibling;	}	spin_unlock(&bootmem_resource_lock); 	if (res)		memset(res, 0, sizeof(struct resource));	else		res = kzalloc(sizeof(struct resource), flags); 	return res;} /* 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); /** * Finds the lowest iomem resource that covers part of [@start..@end].  The * caller must specify @start, @end, @flags, and @desc (which may be * IORES_DESC_NONE). * * 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. * * This function walks the whole tree and not just first level children * unless @first_lvl is true. * * @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 * @first_lvl:	walk only the first level children, if set * @res:	return ptr, if resource found */static int find_next_iomem_res(resource_size_t start, resource_size_t end,			       unsigned long flags, unsigned long desc,			       bool first_lvl, struct resource *res){	bool siblings_only = true;	struct resource *p; 	if (!res)		return -EINVAL; 	if (start >= end)		return -EINVAL; 	read_lock(&resource_lock); 	for (p = iomem_resource.child; p; p = next_resource(p, siblings_only)) {		/* 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; 		/*		 * Now that we found a range that matches what we look for,		 * check the flags and the descriptor. If we were not asked to		 * use only the first level, start looking at children as well.		 */		siblings_only = first_lvl; 		if ((p->flags & flags) != flags)			continue;		if ((desc != IORES_DESC_NONE) && (desc != p->desc))			continue; 		/* Found a match, break */		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 __walk_iomem_res_desc(resource_size_t start, resource_size_t end,				 unsigned long flags, unsigned long desc,				 bool first_lvl, void *arg,				 int (*func)(struct resource *, void *)){	struct resource res;	int ret = -EINVAL; 	while (start < end &&	       !find_next_iomem_res(start, end, flags, desc, first_lvl, &res)) {		ret = (*func)(&res, arg);		if (ret)			break; 		start = res.end + 1;	} 	return ret;} /** * Walks through iomem resources and calls func() with matching resource * ranges. This walks through whole tree and not just first level children. * All the memory ranges which overlap start,end and also match flags and * desc are valid candidates. * * @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 * * 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, false, arg, func);}EXPORT_SYMBOL_GPL(walk_iomem_res_desc); /* * 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, true,				     arg, func);} /* * This function calls the @func callback against all memory ranges, which * are ranges marked as IORESOURCE_MEM and IORESOUCE_BUSY. */int walk_mem_res(u64 start, u64 end, void *arg,		 int (*func)(struct resource *, void *)){	unsigned long flags = IORESOURCE_MEM | IORESOURCE_BUSY; 	return __walk_iomem_res_desc(start, end, flags, IORES_DESC_NONE, true,				     arg, func);} /* * This function calls the @func callback against all memory ranges of type * System RAM which are marked as IORESOURCE_SYSTEM_RAM and IORESOUCE_BUSY. * It is to be used only for System RAM. * * This will find System RAM ranges that are children of top-level resources * in addition to top-level System RAM resources. */int walk_system_ram_range(unsigned long start_pfn, unsigned long nr_pages,			  void *arg, int (*func)(unsigned long, unsigned long, void *)){	resource_size_t start, end;	unsigned long flags;	struct resource res;	unsigned long pfn, end_pfn;	int ret = -EINVAL; 	start = (u64) start_pfn << PAGE_SHIFT;	end = ((u64)(start_pfn + nr_pages) << PAGE_SHIFT) - 1;	flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;	while (start < end &&	       !find_next_iomem_res(start, end, flags, IORES_DESC_NONE,				    false, &res)) {		pfn = PFN_UP(res.start);		end_pfn = PFN_DOWN(res.end + 1);		if (end_pfn > pfn)			ret = (*func)(pfn, end_pfn - pfn, arg);		if (ret)			break;		start = res.end + 1;	}

Lines 1–500 of 1785.