patch-2.3.13 linux/kernel/resource.c

Next file: linux/kernel/sched.c
Previous file: linux/kernel/printk.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.3.12/linux/kernel/resource.c linux/kernel/resource.c
@@ -12,9 +12,13 @@
 #include <linux/init.h>
 #include <linux/malloc.h>
 
+#include <asm/spinlock.h>
+
 struct resource ioport_resource = { "PCI IO", 0x0000, 0xFFFF };
 struct resource iomem_resource = { "PCI mem", 0x00000000, 0xFFFFFFFF };
 
+static rwlock_t resource_lock = RW_LOCK_UNLOCKED;
+
 /*
  * This generates reports for /proc/ioports and /proc/memory
  */
@@ -47,25 +51,30 @@
 int get_resource_list(struct resource *root, char *buf, int size)
 {
 	char *fmt;
+	int retval;
 
 	fmt = "        %08lx-%08lx : %s\n";
 	if (root == &ioport_resource)
 		fmt = "        %04lx-%04lx : %s\n";
-	return do_resource_list(root->child, fmt, 8, buf, buf + size) - buf;
+	read_lock(&resource_lock);
+	retval = do_resource_list(root->child, fmt, 8, buf, buf + size) - buf;
+	read_unlock(&resource_lock);
+	return retval;
 }	
 
-int request_resource(struct resource *root, struct resource *new)
+/* Return the conflict entry if you can't request it */
+static struct resource * __request_resource(struct resource *root, struct resource *new)
 {
 	unsigned long start = new->start;
 	unsigned long end = new->end;
 	struct resource *tmp, **p;
 
 	if (end < start)
-		return -EINVAL;
+		return root;
 	if (start < root->start)
-		return -EINVAL;
+		return root;
 	if (end > root->end)
-		return -EINVAL;
+		return root;
 	p = &root->child;
 	for (;;) {
 		tmp = *p;
@@ -73,15 +82,25 @@
 			new->sibling = tmp;
 			*p = new;
 			new->parent = root;
-			return 0;
+			return NULL;
 		}
 		p = &tmp->sibling;
 		if (tmp->end < start)
 			continue;
-		return -EBUSY;
+		return tmp;
 	}
 }
 
+int request_resource(struct resource *root, struct resource *new)
+{
+	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;
@@ -101,6 +120,18 @@
 	return -EINVAL;
 }
 
+/*
+ * 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.
+ */
 struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name)
 {
 	struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
@@ -110,21 +141,33 @@
 		res->name = name;
 		res->start = start;
 		res->end = start + n - 1;
-		if (request_resource(parent, res) != 0) {
+		res->flags = IORESOURCE_BUSY;
+
+		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);
 	}
 	return res;
 }
 
 /*
- * Compatibility cruft.
- *
- * Check-region returns non-zero if something already exists.
- *
- * Release-region releases an anonymous region that matches
- * the IO port range.
  */
 int __check_region(struct resource *parent, unsigned long start, unsigned long n)
 {
@@ -152,35 +195,48 @@
 
 		if (!res)
 			break;
-		if (res->start == start && res->end == end) {
+		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);
-			break;
+			return;
 		}
 		p = &res->sibling;
 	}
+	printk("Trying to free nonexistent resource <%04lx-%04lx>\n", start, end);
 }
 
 /*
  * Called from init/main.c to reserve IO ports.
  */
 #define MAXRESERVE 4
-void __init reserve_setup(char *str, int *ints)
+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];
 
-	for (i = 1; i < ints[0]; i += 2) {
+    while (opt==2) {
 		int x = 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 = ints[i];
-			res->end = res->start + ints[i] - 1;
+			res->start = io_start;
+			res->end = io_start + io_num - 1;
 			res->child = NULL;
 			if (request_resource(&ioport_resource, res) == 0)
 				reserved = x+1;
 		}
 	}
+	return 1;
 }
+
+__setup("reserve=", reserve_setup);

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)