More general resource allocation scheme: a patch to look at

David Hinds (dhinds@lahmed.Stanford.EDU)
Fri, 18 Jun 1999 12:02:04 -0700


Let me know what you think of this patch. I obviously can't test all
drivers for weird interactions, but it seems to do the right things on
my system, with normal drivers and with tweaked PCMCIA drivers. I
figure we should first set up this infrastructure (and see if it
causes any problems), then later, we can update things to populate the
memory map.

The patch should apply against either 2.2.* or 2.3.*.

-- Dave

--- linux/kernel/resource.c.O Thu Nov 12 09:58:32 1998
+++ linux/kernel/resource.c Fri Jun 18 09:12:50 1999
@@ -1,10 +1,17 @@
/*
* linux/kernel/resource.c
*
- * Copyright (C) 1995 Linus Torvalds
- * David Hinds
+ * Copyright (C) 1995, 1999 Linus Torvalds
+ * David Hinds
*
- * Kernel io-region resource management
+ * 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.
*/

#include <linux/sched.h>
@@ -12,47 +19,59 @@
#include <linux/ioport.h>
#include <linux/init.h>

-#define IOTABLE_SIZE 128
+#define RSRC_TABLE_SIZE 128

-typedef struct resource_entry_t {
+struct resource_entry {
u_long from, num;
const char *name;
- struct resource_entry_t *next;
-} resource_entry_t;
+ struct resource_entry *next;
+};

-static resource_entry_t iolist = { 0, 0, "", NULL };
+struct resource_entry res_list[] = {
+ { 0, 0, NULL, NULL }, /* IO */
+ { 0, 0, NULL, NULL } /* mem */
+};

-static resource_entry_t iotable[IOTABLE_SIZE];
+static struct resource_entry rsrc_table[RSRC_TABLE_SIZE];

/*
- * This generates the report for /proc/ioports
+ * This generates reports for /proc/ioports and /proc/memory
*/
-int get_ioport_list(char *buf)
+int get_resource_list(int class, char *buf)
{
- resource_entry_t *p;
+ struct resource_entry *root = &res_list[class];
+ struct resource_entry *p;
int len = 0;
-
- for (p = iolist.next; (p) && (len < 4000); p = p->next)
- len += sprintf(buf+len, "%04lx-%04lx : %s\n",
- p->from, p->from+p->num-1, p->name);
+ 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;
}

/*
- * The workhorse function: find where to put a new entry
+ * Basics: find a matching resource entry, or find an insertion point
*/
-static resource_entry_t *find_gap(resource_entry_t *root,
- u_long from, u_long num)
+static struct resource_entry *
+find_match(struct resource_entry *root, u_long from, u_long num)
{
- unsigned long flags;
- resource_entry_t *p;
-
+ struct resource_entry *p;
+ for (p = root; p; p = p->next)
+ if ((p->from == from) && (p->num == num))
+ return p;
+ return NULL;
+}
+
+static struct resource_entry *
+find_gap(struct resource_entry *root, u_long from, u_long num)
+{
+ struct resource_entry *p;
if (from > from+num-1)
return NULL;
- save_flags(flags);
- cli();
for (p = root; ; p = p->next) {
if ((p != root) && (p->from+p->num-1 >= from)) {
p = NULL;
@@ -61,123 +80,147 @@
if ((p->next == NULL) || (p->next->from > from+num-1))
break;
}
- restore_flags(flags);
return p;
}

/*
- * Call this from the device driver to register the ioport region.
+ * Call this from a driver to assert ownership of a resource
*/
-void request_region(unsigned long from, unsigned long num, const char *name)
+void request_resource(int class, unsigned long from,
+ unsigned long num, const char *name)
{
- resource_entry_t *p;
+ struct resource_entry *root = &res_list[class];
+ struct resource_entry *p;
+ long flags;
int i;

- for (i = 0; i < IOTABLE_SIZE; i++)
- if (iotable[i].num == 0)
+ p = find_match(root, from, num);
+ if (p) {
+ p->name = name;
+ return;
+ }
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < RSRC_TABLE_SIZE; i++)
+ if (rsrc_table[i].num == 0)
break;
- if (i == IOTABLE_SIZE)
- printk("warning: ioport table is full\n");
+ if (i == RSRC_TABLE_SIZE)
+ printk("warning: resource table is full\n");
else {
- p = find_gap(&iolist, from, num);
- if (p == NULL)
+ p = find_gap(root, from, num);
+ if (p == NULL) {
+ restore_flags(flags);
return;
- iotable[i].name = name;
- iotable[i].from = from;
- iotable[i].num = num;
- iotable[i].next = p->next;
- p->next = &iotable[i];
- return;
+ }
+ 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];
}
+ restore_flags(flags);
}

/*
- * Call this when the device driver is unloaded
+ * Call these when a driver is unloaded but the device remains
*/
-void release_region(unsigned long from, unsigned long num)
+void release_resource(int class, unsigned long from, unsigned long num)
{
- resource_entry_t *p, *q;
-
- for (p = &iolist; ; p = q) {
- q = p->next;
- if (q == NULL)
- break;
- if ((q->from == from) && (q->num == num)) {
- q->num = 0;
- p->next = q->next;
- return;
- }
- }
+ struct resource_entry *root = &res_list[class];
+ struct resource_entry *p;
+ p = find_match(root, from, num);
+ if (p) p->name = NULL;
}

/*
- * Call this to check the ioport region before probing
+ * Call these to check a region for conflicts before probing
*/
-int check_region(unsigned long from, unsigned long num)
+int check_resource(int class, unsigned long from, unsigned long num)
{
- return (find_gap(&iolist, from, num) == NULL) ? -EBUSY : 0;
+ 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;
}

-#ifdef __sparc__ /* Why to carry unused code on other architectures? */
/*
- * This is for architectures with MMU-managed ports (sparc).
+ * Call this to claim a resource for a piece of hardware
*/
-unsigned long occupy_region(unsigned long base, unsigned long end,
- unsigned long num, unsigned int align, const char *name)
+unsigned long occupy_resource(int class, unsigned long base,
+ unsigned long end, unsigned long num,
+ unsigned long align, const char *name)
{
+ struct resource_entry *root = &res_list[class];
unsigned long from = 0, till;
unsigned long flags;
int i;
- resource_entry_t *p; /* Scanning ptr */
- resource_entry_t *p1; /* === p->next */
- resource_entry_t *s; /* Found slot */
+ struct resource_entry *p, *q;

- if (base > end-1)
- return 0;
- if (num > end - base)
+ if ((base > end-1) || (num > end - base))
return 0;

- for (i = 0; i < IOTABLE_SIZE; i++)
- if (iotable[i].num == 0)
+ for (i = 0; i < RSRC_TABLE_SIZE; i++)
+ if (rsrc_table[i].num == 0)
break;
- if (i == IOTABLE_SIZE) {
- /* Driver prints a warning typically. */
+ if (i == RSRC_TABLE_SIZE)
return 0;
- }

save_flags(flags);
cli();
/* printk("occupy: search in %08lx[%08lx] ", base, end - base); */
- s = NULL;
- for (p = &iolist; p != NULL; p = p1) {
- p1 = p->next;
+ for (p = root; p != NULL; p = q) {
+ q = p->next;
/* Find window in list */
- from = (p->from+p->num + align-1) & ~((unsigned long)align-1);
- till = (p1 == NULL)? (unsigned long) (0 - (unsigned long)align): p1->from;
+ 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) {
- s = p;
+ if ((from < till) && (from + num < till))
break;
- }
}
/* printk("\r\n"); */
restore_flags(flags);

- if (s == NULL)
+ if (p == NULL)
return 0;

- iotable[i].name = name;
- iotable[i].from = from;
- iotable[i].num = num;
- iotable[i].next = s->next;
- s->next = &iotable[i];
+ 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;
}
-#endif
+
+/*
+ * Call this when a resource becomes available for other hardware
+ */
+void vacate_resource(int class, unsigned long from, unsigned long num)
+{
+ 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;
+ break;
+ }
+ }
+ restore_flags(flags);
+}

/* Called from init/main.c to reserve IO ports. */
void __init reserve_setup(char *str, int *ints)
--- linux/kernel/ksyms.c.O Thu Jun 17 10:32:35 1999
+++ linux/kernel/ksyms.c Fri Jun 18 08:08:19 1999
@@ -302,10 +302,12 @@
EXPORT_SYMBOL(enable_hlt);
#endif

-/* IO port handling */
-EXPORT_SYMBOL(check_region);
-EXPORT_SYMBOL(request_region);
-EXPORT_SYMBOL(release_region);
+/* resource handling */
+EXPORT_SYMBOL(check_resource);
+EXPORT_SYMBOL(request_resource);
+EXPORT_SYMBOL(release_resource);
+EXPORT_SYMBOL(occupy_resource);
+EXPORT_SYMBOL(vacate_resource);

/* process management */
EXPORT_SYMBOL(__wake_up);
--- linux/include/linux/ioport.h.O Wed Oct 22 08:31:55 1997
+++ linux/include/linux/ioport.h Fri Jun 18 08:05:37 1999
@@ -1,14 +1,38 @@
/*
- * portio.h Definitions of routines for detecting, reserving and
+ * ioport.h Definitions of routines for detecting, reserving and
* allocating system resources.
*
- * Version: 0.01 8/30/93
- *
- * Author: Donald Becker (becker@super.org)
+ * Authors: Donald Becker (becker@cesdis.gsfc.nasa.gov)
+ * David Hinds (dhinds@zen.stanford.edu)
*/

-#ifndef _LINUX_PORTIO_H
-#define _LINUX_PORTIO_H
+#ifndef _LINUX_IOPORT_H
+#define _LINUX_IOPORT_H
+
+#define RES_IO 0
+#define RES_MEM 1
+
+extern void reserve_setup(char *str, int *ints);
+
+extern struct resource_entry *iolist, *memlist;
+
+extern int get_resource_list(int class, char *buf);
+extern int check_resource(int class,
+ unsigned long from, unsigned long extent);
+extern void request_resource(int class,
+ unsigned long from, unsigned long extent,
+ const char *name);
+extern void release_resource(int class,
+ unsigned long from, unsigned long extent);
+extern unsigned long occupy_resource(int class,
+ unsigned long base, unsigned long end,
+ unsigned long num, unsigned long align,
+ const char *name);
+extern void vacate_resource(int class,
+ unsigned long from, unsigned long extent);
+
+#define get_ioport_list(buf) get_resource_list(RES_IO, buf)
+#define get_mem_list(buf) get_resource_list(RES_MEM, buf)

#define HAVE_PORTRESERVE
/*
@@ -16,20 +40,21 @@
* Once you have found you hardware, register it with request_region().
* If you unload the driver, use release_region to free ports.
*/
-extern void reserve_setup(char *str, int *ints);
-extern int check_region(unsigned long from, unsigned long extent);
-extern void request_region(unsigned long from, unsigned long extent,const char *name);
-extern void release_region(unsigned long from, unsigned long extent);
-extern int get_ioport_list(char *);
-
-#ifdef __sparc__
-extern unsigned long occupy_region(unsigned long base, unsigned long end,
- unsigned long num, unsigned int align,
- const char *name);
-#endif
+#define check_region(f,e) check_resource(RES_IO,f,e)
+#define request_region(f,e,n) request_resource(RES_IO,f,e,n)
+#define release_region(f,e) release_resource(RES_IO,f,e)
+#define occupy_region(b,e,n,a,s) occupy_resource(RES_IO,b,e,n,a,s)
+#define vacate_region(f,e) vacate_resource(RES_IO,f,e)
+
+#define HAVE_MEMRESERVE
+#define check_mem_region(f,e) check_resource(RES_MEM,f,e)
+#define request_mem_region(f,e,n) request_resource(RES_MEM,f,e,n)
+#define release_mem_region(f,e) release_resource(RES_MEM,f,e)
+#define occupy_mem_region(b,e,n,a,s) occupy_resource(RES_MEM,b,e,n,a,s)
+#define vacate_mem_region(f,e) vacate_resource(RES_MEM,f,e)

#define HAVE_AUTOIRQ
extern void autoirq_setup(int waittime);
extern int autoirq_report(int waittime);

-#endif /* _LINUX_PORTIO_H */
+#endif /* _LINUX_IOPORT_H */
--- linux/fs/proc/root.c.O Thu Jun 17 10:32:34 1999
+++ linux/fs/proc/root.c Fri Jun 18 08:48:46 1999
@@ -621,6 +621,11 @@
S_IFREG | S_IRUGO, 1, 0, 0,
0, &proc_array_inode_operations
};
+static struct proc_dir_entry proc_root_memory = {
+ PROC_MEMORY, 6, "memory",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_array_inode_operations
+};
static struct proc_dir_entry proc_root_cmdline = {
PROC_CMDLINE, 7, "cmdline",
S_IFREG | S_IRUGO, 1, 0, 0,
@@ -709,6 +714,7 @@
proc_register(&proc_root, &proc_root_fs);
proc_register(&proc_root, &proc_root_dma);
proc_register(&proc_root, &proc_root_ioports);
+ proc_register(&proc_root, &proc_root_memory);
proc_register(&proc_root, &proc_root_cmdline);
#ifdef CONFIG_RTC
proc_register(&proc_root, &proc_root_rtc);
--- linux/fs/proc/array.c.O Thu Jun 17 10:30:53 1999
+++ linux/fs/proc/array.c Fri Jun 18 08:48:49 1999
@@ -1326,6 +1326,9 @@

case PROC_IOPORTS:
return get_ioport_list(page);
+
+ case PROC_MEMORY:
+ return get_mem_list(page);
#ifdef CONFIG_BLK_DEV_MD
case PROC_MD:
return get_md_status(page);
--- linux/include/linux/proc_fs.h.O Fri Jun 18 00:39:44 1999
+++ linux/include/linux/proc_fs.h Fri Jun 18 08:48:47 1999
@@ -37,6 +37,7 @@
PROC_KSYMS,
PROC_DMA,
PROC_IOPORTS,
+ PROC_MEMORY,
PROC_PROFILE, /* whether enabled or not */
PROC_CMDLINE,
PROC_SYS,

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.rutgers.edu
Please read the FAQ at http://www.tux.org/lkml/