summaryrefslogtreecommitdiffstats
path: root/kernel/resource.c
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>1999-10-09 00:00:47 +0000
commitd6434e1042f3b0a6dfe1b1f615af369486f9b1fa (patch)
treee2be02f33984c48ec019c654051d27964e42c441 /kernel/resource.c
parent609d1e803baf519487233b765eb487f9ec227a18 (diff)
Merge with 2.3.19.
Diffstat (limited to 'kernel/resource.c')
-rw-r--r--kernel/resource.c405
1 files changed, 233 insertions, 172 deletions
diff --git a/kernel/resource.c b/kernel/resource.c
index 4c672c6b0..26ee5e29d 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1,232 +1,293 @@
/*
* linux/kernel/resource.c
*
- * Copyright (C) 1995, 1999 Linus Torvalds
- * David Hinds
+ * Copyright (C) 1999 Linus Torvalds
+ * Copyright (C) 1999 Martin Mares <mj@ucw.cz>
*
- * Kernel resource management
- *
- * We now distinguish between claiming space for devices (using the
- * 'occupy' and 'vacate' calls), and associating a resource with a
- * device driver (with the 'request', 'release', and 'check' calls).
- * A resource can be claimed even if there is no associated driver
- * (by occupying with name=NULL). Vacating a resource makes it
- * available for other dynamically configured devices.
+ * Arbitrary resource management.
*/
#include <linux/sched.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/init.h>
+#include <linux/malloc.h>
+#include <linux/spinlock.h>
-#define RSRC_TABLE_SIZE 128
-
-struct resource_entry {
- u_long from, num;
- const char *name;
- struct resource_entry *next;
-};
+struct resource ioport_resource = { "PCI IO", 0x0000, 0xFFFF, IORESOURCE_IO };
+struct resource iomem_resource = { "PCI mem", 0x00000000, 0xFFFFFFFF, IORESOURCE_MEM };
-struct resource_entry res_list[] = {
- { 0, 0, NULL, NULL }, /* IO */
- { 0, 0, NULL, NULL } /* mem */
-};
-
-static struct resource_entry rsrc_table[RSRC_TABLE_SIZE];
+static rwlock_t resource_lock = RW_LOCK_UNLOCKED;
/*
* This generates reports for /proc/ioports and /proc/memory
*/
-int get_resource_list(int class, char *buf)
+static char * do_resource_list(struct resource *entry, const char *fmt, int offset, char *buf, char *end)
{
- struct resource_entry *root = &res_list[class];
- struct resource_entry *p;
- int len = 0;
- char *fmt = (class == RES_IO) ?
- "%04lx-%04lx : %s\n" : "%08lx-%08lx : %s\n";
-
- for (p = root->next; (p) && (len < 4000); p = p->next)
- len += sprintf(buf+len, fmt, p->from, p->from+p->num-1,
- (p->name ? p->name : "occupied"));
- if (p)
- len += sprintf(buf+len, "4K limit reached!\n");
- return len;
+ if (offset < 0)
+ offset = 0;
+
+ while (entry) {
+ const char *name = entry->name;
+ unsigned long from, to;
+
+ if ((int) (end-buf) < 80)
+ return buf;
+
+ from = entry->start;
+ to = entry->end;
+ if (!name)
+ name = "<BAD>";
+
+ buf += sprintf(buf, fmt + offset, from, to, name);
+ if (entry->child)
+ buf = do_resource_list(entry->child, fmt, offset-2, buf, end);
+ entry = entry->sibling;
+ }
+
+ return buf;
}
-/*
- * Basics: find a matching resource entry, or find an insertion point
- */
-static struct resource_entry *
-find_match(struct resource_entry *root, u_long from, u_long num)
+int get_resource_list(struct resource *root, char *buf, int size)
{
- struct resource_entry *p;
- for (p = root; p; p = p->next)
- if ((p->from == from) && (p->num == num))
- return p;
- return NULL;
-}
+ char *fmt;
+ int retval;
-static struct resource_entry *
-find_gap(struct resource_entry *root, u_long from, u_long num)
+ fmt = " %08lx-%08lx : %s\n";
+ if (root == &ioport_resource)
+ fmt = " %04lx-%04lx : %s\n";
+ read_lock(&resource_lock);
+ retval = do_resource_list(root->child, fmt, 8, buf, buf + size) - buf;
+ read_unlock(&resource_lock);
+ return retval;
+}
+
+/* Return the conflict entry if you can't request it */
+static struct resource * __request_resource(struct resource *root, struct resource *new)
{
- struct resource_entry *p;
- if (from > from+num-1)
- return NULL;
- for (p = root; ; p = p->next) {
- if ((p != root) && (p->from+p->num-1 >= from)) {
- p = NULL;
- break;
+ unsigned long start = new->start;
+ unsigned long 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;
}
- if ((p->next == NULL) || (p->next->from > from+num-1))
- break;
+ p = &tmp->sibling;
+ if (tmp->end < start)
+ continue;
+ return tmp;
}
- return p;
}
-/*
- * Call this from a driver to assert ownership of a resource
- */
-void request_resource(int class, unsigned long from,
- unsigned long num, const char *name)
+int request_resource(struct resource *root, struct resource *new)
{
- struct resource_entry *root = &res_list[class];
- struct resource_entry *p;
- long flags;
- int i;
-
- p = find_match(root, from, num);
- if (p) {
- p->name = name;
- return;
- }
+ struct resource *conflict;
+
+ write_lock(&resource_lock);
+ conflict = __request_resource(root, new);
+ write_unlock(&resource_lock);
+ return conflict ? -EBUSY : 0;
+}
+
+int release_resource(struct resource *old)
+{
+ struct resource *tmp, **p;
- save_flags(flags);
- cli();
- for (i = 0; i < RSRC_TABLE_SIZE; i++)
- if (rsrc_table[i].num == 0)
+ p = &old->parent->child;
+ for (;;) {
+ tmp = *p;
+ if (!tmp)
break;
- if (i == RSRC_TABLE_SIZE)
- printk("warning: resource table is full\n");
- else {
- p = find_gap(root, from, num);
- if (p == NULL) {
- restore_flags(flags);
- return;
+ if (tmp == old) {
+ *p = tmp->sibling;
+ old->parent = NULL;
+ return 0;
}
- rsrc_table[i].name = name;
- rsrc_table[i].from = from;
- rsrc_table[i].num = num;
- rsrc_table[i].next = p->next;
- p->next = &rsrc_table[i];
+ p = &tmp->sibling;
}
- restore_flags(flags);
+ return -EINVAL;
}
-/*
- * Call these when a driver is unloaded but the device remains
+/*
+ * Find empty slot in the resource tree given range and alignment.
*/
-void release_resource(int class, unsigned long from, unsigned long num)
+static int find_resource(struct resource *root, struct resource *new,
+ unsigned long size,
+ unsigned long min, unsigned long max,
+ unsigned long align)
{
- struct resource_entry *root = &res_list[class];
- struct resource_entry *p;
- p = find_match(root, from, num);
- if (p) p->name = NULL;
+ struct resource *this = root->child;
+ unsigned long start, end;
+
+ start = root->start;
+ for(;;) {
+ if (this)
+ end = this->start;
+ else
+ end = root->end;
+ if (start < min)
+ start = min;
+ if (end > max)
+ end = max;
+ start = (start + align - 1) & ~(align - 1);
+ if (start < end && end - start + 1 >= size) {
+ new->start = start;
+ new->end = start + size - 1;
+ return 0;
+ }
+ if (!this)
+ break;
+ start = this->end + 1;
+ this = this->sibling;
+ }
+ return -EBUSY;
}
/*
- * Call these to check a region for conflicts before probing
+ * Allocate empty slot in the resource tree given range and alignment.
*/
-int check_resource(int class, unsigned long from, unsigned long num)
+int allocate_resource(struct resource *root, struct resource *new,
+ unsigned long size,
+ unsigned long min, unsigned long max,
+ unsigned long align)
{
- struct resource_entry *root = &res_list[class];
- struct resource_entry *p;
- p = find_match(root, from, num);
- if (p != NULL)
- return (p->name != NULL) ? -EBUSY : 0;
- return (find_gap(root, from, num) == NULL) ? -EBUSY : 0;
+ int err;
+
+ write_lock(&resource_lock);
+ err = find_resource(root, new, size, min, max, align);
+ if (err >= 0 && __request_resource(root, new))
+ err = -EBUSY;
+ write_unlock(&resource_lock);
+ return err;
}
/*
- * Call this to claim a resource for a piece of hardware
+ * This is compatibility stuff for IO resources.
+ *
+ * Note how this, unlike the above, knows about
+ * the IO flag meanings (busy etc).
+ *
+ * Request-region creates a new busy region.
+ *
+ * Check-region returns non-zero if the area is already busy
+ *
+ * Release-region releases a matching busy region.
*/
-unsigned long occupy_resource(int class, unsigned long base,
- unsigned long end, unsigned long num,
- unsigned long align, const char *name)
+struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name)
{
- struct resource_entry *root = &res_list[class];
- unsigned long from = 0, till;
- unsigned long flags;
- int i;
- struct resource_entry *p, *q;
+ struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
- if ((base > end-1) || (num > end - base))
- return 0;
+ if (res) {
+ memset(res, 0, sizeof(*res));
+ res->name = name;
+ res->start = start;
+ res->end = start + n - 1;
+ res->flags = IORESOURCE_BUSY;
- for (i = 0; i < RSRC_TABLE_SIZE; i++)
- if (rsrc_table[i].num == 0)
- break;
- if (i == RSRC_TABLE_SIZE)
- return 0;
-
- save_flags(flags);
- cli();
- /* printk("occupy: search in %08lx[%08lx] ", base, end - base); */
- for (p = root; p != NULL; p = q) {
- q = p->next;
- /* Find window in list */
- from = (p->from+p->num + align-1) & ~(align-1);
- till = (q == NULL) ? (0 - align) : q->from;
- /* printk(" %08lx:%08lx", from, till); */
- /* Clip window with base and end */
- if (from < base) from = base;
- if (till > end) till = end;
- /* See if result is large enougth */
- if ((from < till) && (from + num < till))
+ write_lock(&resource_lock);
+
+ for (;;) {
+ struct resource *conflict;
+
+ conflict = __request_resource(parent, res);
+ if (!conflict)
+ break;
+ if (conflict != parent) {
+ parent = conflict;
+ if (!(conflict->flags & IORESOURCE_BUSY))
+ continue;
+ }
+
+ /* Uhhuh, that didn't work out.. */
+ kfree(res);
+ res = NULL;
break;
+ }
+ write_unlock(&resource_lock);
}
- /* printk("\r\n"); */
- restore_flags(flags);
-
- if (p == NULL)
- return 0;
-
- rsrc_table[i].name = name;
- rsrc_table[i].from = from;
- rsrc_table[i].num = num;
- rsrc_table[i].next = p->next;
- p->next = &rsrc_table[i];
- return from;
+ return res;
}
-/*
- * Call this when a resource becomes available for other hardware
- */
-void vacate_resource(int class, unsigned long from, unsigned long num)
+int __check_region(struct resource *parent, unsigned long start, unsigned long n)
{
- struct resource_entry *root = &res_list[class];
- struct resource_entry *p, *q;
- long flags;
-
- save_flags(flags);
- cli();
- for (p = root; ; p = q) {
- q = p->next;
- if (q == NULL)
- break;
- if ((q->from == from) && (q->num == num)) {
- q->num = 0;
- p->next = q->next;
+ struct resource * res;
+
+ res = __request_region(parent, start, n, "check-region");
+ if (!res)
+ return -EBUSY;
+
+ release_resource(res);
+ kfree(res);
+ return 0;
+}
+
+void __release_region(struct resource *parent, unsigned long start, unsigned long n)
+{
+ struct resource **p;
+ unsigned long end;
+
+ p = &parent->child;
+ end = start + n - 1;
+
+ for (;;) {
+ struct resource *res = *p;
+
+ if (!res)
break;
+ if (res->start <= start && res->end >= end) {
+ if (!(res->flags & IORESOURCE_BUSY)) {
+ p = &res->child;
+ continue;
+ }
+ if (res->start != start || res->end != end)
+ break;
+ *p = res->sibling;
+ kfree(res);
+ return;
}
+ p = &res->sibling;
}
- restore_flags(flags);
+ printk("Trying to free nonexistent resource <%04lx-%04lx>\n", start, end);
}
-/* Called from init/main.c to reserve IO ports. */
-void __init reserve_setup(char *str, int *ints)
+/*
+ * Called from init/main.c to reserve IO ports.
+ */
+#define MAXRESERVE 4
+static int __init reserve_setup(char *str)
{
- int i;
+ int opt = 2, io_start, io_num;
+ static int reserved = 0;
+ static struct resource reserve[MAXRESERVE];
+
+ while (opt==2) {
+ int x = reserved;
- for (i = 1; i < ints[0]; i += 2)
- request_region(ints[i], ints[i+1], "reserved");
+ if (get_option (&str, &io_start) != 2) break;
+ if (get_option (&str, &io_num) == 0) break;
+ if (x < MAXRESERVE) {
+ struct resource *res = reserve + x;
+ res->name = "reserved";
+ res->start = io_start;
+ res->end = io_start + io_num - 1;
+ res->child = NULL;
+ if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0)
+ reserved = x+1;
+ }
+ }
+ return 1;
}
+
+__setup("reserve=", reserve_setup);