/*
* 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 <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 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 */
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)
{
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;
old->end = new.end;
} else {
__release_resource(old);
*old = new;
conflict = __request_resource(root, old);
BUG_ON(conflict);
}
out:
write_unlock(&resource_lock);
return err;
}
/**
* allocate_resource - allocate empty slot in the resource tree given range & alignment.
* The resource will be reallocated with a new size if it was already allocated
* @root: root resource descriptor
* @new: resource descriptor desired by caller
* @size: requested resource region size
* @min: minimum boundary to allocate
* @max: maximum boundary to allocate
* @align: alignment requested, in bytes
* @alignf: alignment function, optional, called if not NULL
* @alignf_data: arbitrary data to pass to the @alignf function
*/
int allocate_resource(struct resource *root, struct resource *new,
resource_size_t size, resource_size_t min,
resource_size_t max, resource_size_t align,
resource_size_t (*alignf)(void *,
const struct resource *,
resource_size_t,
resource_size_t),
void *alignf_data)
{
int err;
struct resource_constraint constraint;
if (!alignf)
alignf = simple_align_resource;
constraint.min = min;
constraint.max = max;
constraint.align = align;
constraint.alignf = alignf;
constraint.alignf_data = alignf_data;
if ( new->parent ) {
/* resource is already allocated, try reallocating with
the new constraints */
return reallocate_resource(root, new, size, &constraint);
}
write_lock(&resource_lock);
err = find_resource(root, new, size, &constraint);
if (err >= 0 && __request_resource(root, new))
err = -EBUSY;
write_unlock(&resource_lock);
return err;
}
EXPORT_SYMBOL(allocate_resource);
/**
* lookup_resource - find an existing resource by a resource start address
* @root: root resource descriptor
* @start: resource start address
*
* Returns a pointer to the resource if found, NULL otherwise
*/
struct resource *lookup_resource(struct resource *root, resource_size_t start)
{
struct resource *res;
read_lock(&resource_lock);
for (res = root->child; res; res = res->sibling) {
if (res->start == start)
break;
}
read_unlock(&resource_lock);
return res;
}
/*
* Insert a resource into the resource tree. If successful, return NULL,
* otherwise return the conflicting resource (compare to __request_resource())
*/
static struct resource * __insert_resource(struct resource *parent, struct resource *new)
{
struct resource *first, *next;
for (;; parent = first) {
first = __request_resource(parent, new);
if (!first)
return first;
if (first == parent)
return first;
if (WARN_ON(first == new)) /* duplicated insertion */
return first;
if ((first->start > new->start) || (first->end < new->end))
break;
if ((first->start == new->start) && (first->end == new->end))
break;
}
for (next = first; ; next = next->sibling) {
/* Partial overlap? Bad, and unfixable */
if (next->start < new->start || next->end > new->end)
return next;
if (!next->sibling)
break;
if (next->sibling->start > new->end)
break;
}
new->parent = parent;
new->sibling = next->sibling;
new->child = first;
next->sibling = NULL;
for (next = first; next; next = next->sibling)
next->parent = new;
if (parent->child == first) {
parent->child = new;
} else {
next = parent->child;
while (next->sibling != first)
next = next->sibling;
next->sibling = new;
}
return NULL;
}
/**
* insert_resource_conflict - Inserts resource in the resource tree
* @parent: parent of the new resource
* @new: new resource to insert
*
* Returns 0 on success, conflict resource if the resource can't be inserted.
*
* This function is equivalent to request_resource_conflict when no conflict
* happens. If a conflict happens, and the conflicting resources
* entirely fit within the range of the new resource, then the new
* resource is inserted and the conflicting resources become children of
* the new resource.
*/
struct resource *insert_resource_conflict(struct resource *parent, struct resource *new)
{
struct resource *conflict;
write_lock(&resource_lock);
conflict = __insert_resource(parent, new);
write_unlock(&resource_lock);
return conflict;
}
/**
* insert_resource - Inserts a resource in the resource tree
* @parent: parent of the new resource
* @new: new resource to insert
*
* Returns 0 on success, -EBUSY if the resource can't be inserted.
*/
int insert_resource(struct resource *parent, struct resource *new)
{
struct resource *conflict;
conflict = insert_resource_conflict(parent, new);
return conflict ? -EBUSY : 0;
}
/**
* insert_resource_expand_to_fit - Insert a resource into the resource tree
* @root: root resource descriptor
* @new: new resource to insert
*
* Insert a resource into the resource tree, possibly expanding it in order
* to make it encompass any conflicting resources.
*/
void insert_resource_expand_to_fit(struct resource *root, struct resource *new)
{
if (new->parent)
return;
write_lock(&resource_lock);
for (;;) {
struct resource *conflict;
conflict = __insert_resource(root, new);
if (!conflict)
break;
if (conflict == root)
break;
/* Ok, expand resource to cover the conflict, then try again .. */
if (conflict->start < new->start)
new->start = conflict->start;
if (conflict->end > new->end)
new->end = conflict->end;
printk("Expanded resource %s due to conflict with %s\n", new->name, conflict->name);
}
write_unlock(&resource_lock);
}
static int __adjust_resource(struct resource *res, resource_size_t start,
resource_size_t size)
{
struct resource *tmp, *parent = res->parent;
resource_size_t end = start + size - 1;
int result = -EBUSY;
if (!parent)
goto skip;
if ((start < parent->start) || (end > parent->end))
goto out;
if (res->sibling && (res->sibling->start <= end))
goto out;
tmp = parent->child;
if (tmp != res) {
while (tmp->sibling != res)
tmp = tmp->sibling;
if (start <= tmp->end)
goto out;
}
skip:
for (tmp = res->child; tmp; tmp = tmp->sibling)
if ((tmp->start < start) || (tmp->end > end))
goto out;
res->start = start;