cregit-Linux how code gets into the kernel

Release 4.15 kernel/resource.c

Directory: kernel
/*
 *      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; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro3050.85%120.00%
Vivek Goyal1627.12%120.00%
Linus Torvalds (pre-git)1220.34%240.00%
Randy Dunlap11.69%120.00%
Total59100.00%5100.00%


static void *r_next(struct seq_file *m, void *v, loff_t *pos) { struct resource *p = v; (*pos)++; return (void *)next_resource(p, false); }

Contributors

PersonTokensPropCommitsCommitProp
Vivek Goyal45100.00%1100.00%
Total45100.00%1100.00%

#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; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro5476.06%120.00%
Linus Torvalds (pre-git)1014.08%240.00%
Linus Torvalds45.63%120.00%
Randy Dunlap34.23%120.00%
Total71100.00%5100.00%


static void r_stop(struct seq_file *m, void *v) __releases(resource_lock) { read_unlock(&resource_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro2184.00%150.00%
Linus Torvalds416.00%150.00%
Total25100.00%2100.00%


static int r_show(struct seq_file *m, void *v) { struct resource *root = m->private; 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; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro7245.00%111.11%
Linus Torvalds4427.50%111.11%
Randy Dunlap2314.38%111.11%
Linus Torvalds (pre-git)2012.50%555.56%
Greg Kroah-Hartman10.62%111.11%
Total160100.00%9100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro3769.81%150.00%
Randy Dunlap1630.19%150.00%
Total53100.00%2100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Al Viro3260.38%133.33%
Randy Dunlap1935.85%133.33%
Linus Torvalds (pre-git)23.77%133.33%
Total53100.00%3100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Randy Dunlap2775.00%125.00%
Denis V. Lunev616.67%125.00%
Linus Torvalds (pre-git)38.33%250.00%
Total36100.00%4100.00%

__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); } }

Contributors

PersonTokensPropCommitsCommitProp
Yasuaki Ishimatsu60100.00%1100.00%
Total60100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Yasuaki Ishimatsu80100.00%1100.00%
Total80100.00%1100.00%

/* 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; } }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)14198.60%480.00%
Greg Kroah-Hartman21.40%120.00%
Total143100.00%5100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)8253.25%150.00%
Toshi Kani7246.75%150.00%
Total154100.00%2100.00%


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; } }

Contributors

PersonTokensPropCommitsCommitProp
Yinghai Lu97100.00%1100.00%
Total97100.00%1100.00%


void release_child_resources(struct resource *r) { write_lock(&resource_lock); __release_child_resources(r); write_unlock(&resource_lock); }

Contributors

PersonTokensPropCommitsCommitProp
Yinghai Lu27100.00%1100.00%
Total27100.00%1100.00%

/** * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)4086.96%150.00%
Björn Helgaas613.04%150.00%
Total46100.00%2100.00%

/** * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Björn Helgaas3081.08%125.00%
Linus Torvalds (pre-git)718.92%375.00%
Total37100.00%4100.00%

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; }

Contributors

PersonTokensPropCommitsCommitProp
Linus Torvalds (pre-git)3594.59%480.00%
Toshi Kani25.41%120.00%
Total37100.00%5100.00%

EXPORT_SYMBOL(release_resource); /* * Finds the lowest iomem resource existing within [res->start.res->end). * The caller must specify res->start, res->end, res->flags, and optionally * desc. If found, returns 0, res is overwritten, if not found, returns -1. * This function walks the whole tree and not just first level children until * and unless first_level_children_only is true. */
static int find_next_iomem_res(struct resource *res, unsigned long desc, bool first_level_children_only) { resource_size_t start, end; struct resource *p; bool sibling_only = false; BUG_ON(!res); start = res->start; end = res->end; BUG_ON(start >= end); if (first_level_children_only) sibling_only = true; read_lock(&resource_lock); for (p = iomem_resource.child; p; p = next_resource(p, sibling_only)) { if ((p->flags & res->flags) != res->flags) continue; if ((desc != IORES_DESC_NONE) && (desc != p->desc)) 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; res->flags = p->flags; res->desc = p->desc; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Kamezawa Hiroyuki16370.26%550.00%
Toshi Kani2711.64%220.00%
Vivek Goyal2611.21%220.00%
Tom Lendacky166.90%110.00%
Total232100.00%10100.00%


static int __walk_iomem_res_desc(struct resource *res, unsigned long desc, bool first_level_children_only, void *arg, int (*func)(struct resource *, void *)) { u64 orig_end = res->end; int ret = -1; while ((res->start < res->end) && !find_next_iomem_res(res, desc, first_level_children_only)) { ret = (*func)(res, arg); if (ret) break; res->start = res->end + 1; res->end = orig_end; } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
Toshi Kani4944.95%125.00%
Vivek Goyal3834.86%125.00%
Tom Lendacky2220.18%250.00%
Total109100.00%4100.00%

/* * 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 * * 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 *)) { struct resource res; res.start = start; res.end = end; res.flags = flags; return __walk_iomem_res_desc(&res, desc, false, arg, func); }

Contributors

PersonTokensPropCommitsCommitProp
Vivek Goyal5067.57%125.00%
Tom Lendacky2331.08%250.00%
Toshi Kani11.35%125.00%
Total74100.00%4100.00%

/* * 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 *)) { struct resource res; res.start = start; res.end = end; res.flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY; return __walk_iomem_res_desc(&res, IORES_DESC_NONE, true, arg, func); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Lendacky5073.53%266.67%
Vivek Goyal1826.47%133.33%
Total68100.00%3100.00%

/* * 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 *)) { struct resource res; res.start = start; res.end = end; res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; return __walk_iomem_res_desc(&res, IORES_DESC_NONE, true, arg, func); }

Contributors

PersonTokensPropCommitsCommitProp
Tom Lendacky68100.00%1100.00%
Total68100.00%1100.00%

#if !defined(CONFIG_ARCH_HAS_WALK_MEMORY) /* * 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. */
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_SYSTEM_RAM | IORESOURCE_BUSY; orig_end = res.end; while ((res.start < res.end) && (find_next_iomem_res(&res, IORES_DESC_NONE, true) >= 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; }

Contributors

PersonTokensPropCommitsCommitProp
Kamezawa Hiroyuki17287.76%228.57%
Fengguang Wu178.67%114.29%
Toshi Kani31.53%228.57%
Yasunori Goto21.02%114.29%
Vivek Goyal21.02%114.29%
Total196100.00%7100.00%

#endif
static int __is_ram(unsigned long pfn, unsigned long nr_pages, void *arg) { return 1; }

Contributors

PersonTokensPropCommitsCommitProp
Fengguang Wu21100.00%1100.00%
Total21100.00%1100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
Fengguang Wu2187.50%150.00%
Andrew Morton312.50%150.00%
Total24100.00%2100.00%

EXPORT_SYMBOL_GPL(page_is_ram); /** * region_intersects() - determine intersection of region with known resources * @start: region start address * @size: size of region * @flags: flags of resource (in iomem_resource) * @desc: descriptor of resource (in iomem_resource) or IORES_DESC_NONE * * Check if the specified region partially overlaps or fully eclipses a * resource identified by @flags and @desc (optional with IORES_DESC_NONE). * Return REGION_DISJOINT if the region does not overlap @flags/@desc, * return REGION_MIXED if the region overlaps @flags/@desc and another * resource, and return REGION_INTERSECTS if the region overlaps @flags/@desc * and no other defined resource. Note that REGION_INTERSECTS is also * returned in the case when the specified region overlaps RAM and undefined * memory holes. * * region_intersect() is used by memory remapping functions to ensure * the user is not remapping RAM and is a vast speed up over walking * through the resource table page by page. */
int region_intersects(resource_size_t start, size_t size, unsigned long flags, unsigned long desc) { resource_size_t end = start + size - 1; int type = 0; int other = 0; struct resource *p; read_lock(&resource_lock); for (p = iomem_resource.child; p ; p = p->sibling) { bool is_type = (((p->flags & flags) == flags) && ((desc == IORES_DESC_NONE) || (desc == p->desc))); if (start >= p->start && start <= p->end) is_type ? type++ : other++; if (end >= p->start && end <= p->end) is_type ? type++ : other++; if (p->start >= start && p->end <= end) is_type ? type++ : other++; } read_unlock(&resource_lock); if (other == 0) return type ? REGION_INTERSECTS : REGION_DISJOINT; if (type) return REGION_MIXED; return REGION_DISJOINT; }

Contributors

PersonTokensPropCommitsCommitProp
Dan J Williams8744.62%120.00%
Mike Travis7638.97%120.00%
Toshi Kani3216.41%360.00%
Total195100.00%5100.00%

EXPORT_SYMBOL_GPL(region_intersects);
void __weak arch_remove_reservations(struct resource *avail) { }

Contributors

PersonTokensPropCommitsCommitProp
Björn Helgaas10100.00%1100.00%
Total10100.00%1100.00%


static resource_size_t simple_align_resource(void *data, const struct resource *avail, resource_size_t size, resource_size_t align) { return avail->start; }

Contributors

PersonTokensPropCommitsCommitProp
Björn Helgaas27100.00%1100.00%
Total27100.00%1100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
Björn Helgaas45100.00%1100.00%
Total45100.00%1100.00%

/* * 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.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.start = ALIGN(tmp.start, constraint->align); avail.end = tmp.end; avail.flags = new->flags & ~IORESOURCE_UNSET; if (avail.start >= tmp