/* * linux/kernel/resource.c * * Copyright (C) 1999 Linus Torvalds * Copyright (C) 1999 Martin Mares <mj@ucw.cz> * * 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> #include <asm/io.h> struct resource ioport_resource = { "PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO }; struct resource iomem_resource = { "PCI mem", 0x00000000, 0xffffffff, IORESOURCE_MEM }; static rwlock_t resource_lock = RW_LOCK_UNLOCKED; /* * This generates reports for /proc/ioports and /proc/iomem */ 26 static char * do_resource_list(struct resource *entry, const char *fmt, int offset, char *buf, char *end) { 28 if (offset < 0) offset = 0; 31 while (entry) { const char *name = entry->name; unsigned long from, to; 35 if ((int) (end-buf) < 80) 36 return buf; from = entry->start; to = entry->end; 40 if (!name) name = "<BAD>"; buf += sprintf(buf, fmt + offset, from, to, name); 44 if (entry->child) buf = do_resource_list(entry->child, fmt, offset-2, buf, end); entry = entry->sibling; } 49 return buf; } 52 int get_resource_list(struct resource *root, char *buf, int size) { char *fmt; int retval; fmt = " %08lx-%08lx : %s\n"; 58 if (root->end < 0x10000) fmt = " %04lx-%04lx : %s\n"; read_lock(&resource_lock); retval = do_resource_list(root->child, fmt, 8, buf, buf + size) - buf; 62 read_unlock(&resource_lock); 63 return retval; } /* Return the conflict entry if you can't request it */ 67 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; 73 if (end < start) 74 return root; 75 if (start < root->start) 76 return root; 77 if (end > root->end) 78 return root; p = &root->child; 80 for (;;) { tmp = *p; 82 if (!tmp || tmp->start > end) { new->sibling = tmp; *p = new; new->parent = root; 86 return NULL; } p = &tmp->sibling; 89 if (tmp->end < start) 90 continue; 91 return tmp; } } 95 static int __release_resource(struct resource *old) { struct resource *tmp, **p; p = &old->parent->child; 100 for (;;) { tmp = *p; 102 if (!tmp) 103 break; 104 if (tmp == old) { *p = tmp->sibling; old->parent = NULL; 107 return 0; } p = &tmp->sibling; } 111 return -EINVAL; } 114 int request_resource(struct resource *root, struct resource *new) { struct resource *conflict; write_lock(&resource_lock); conflict = __request_resource(root, new); 120 write_unlock(&resource_lock); 121 return conflict ? -EBUSY : 0; } 124 int release_resource(struct resource *old) { int retval; write_lock(&resource_lock); retval = __release_resource(old); 130 write_unlock(&resource_lock); 131 return retval; } 134 int check_resource(struct resource *root, unsigned long start, unsigned long len) { struct resource *conflict, tmp; tmp.start = start; tmp.end = start + len - 1; write_lock(&resource_lock); conflict = __request_resource(root, &tmp); 142 if (!conflict) __release_resource(&tmp); 144 write_unlock(&resource_lock); 145 return conflict ? -EBUSY : 0; } /* * Find empty slot in the resource tree given range and alignment. */ 151 static int find_resource(struct resource *root, struct resource *new, unsigned long size, unsigned long min, unsigned long max, unsigned long align, void (*alignf)(void *, struct resource *, unsigned long), void *alignf_data) { struct resource *this = root->child; new->start = root->start; 161 for(;;) { 162 if (this) new->end = this->start; 164 else new->end = root->end; 166 if (new->start < min) new->start = min; 168 if (new->end > max) new->end = max; new->start = (new->start + align - 1) & ~(align - 1); 171 if (alignf) alignf(alignf_data, new, size); 173 if (new->start < new->end && new->end - new->start + 1 >= size) { new->end = new->start + size - 1; 175 return 0; } 177 if (!this) 178 break; new->start = this->end + 1; this = this->sibling; } 182 return -EBUSY; } /* * Allocate empty slot in the resource tree given range and alignment. */ 188 int allocate_resource(struct resource *root, struct resource *new, unsigned long size, unsigned long min, unsigned long max, unsigned long align, void (*alignf)(void *, struct resource *, unsigned long), void *alignf_data) { int err; write_lock(&resource_lock); err = find_resource(root, new, size, min, max, align, alignf, alignf_data); 199 if (err >= 0 && __request_resource(root, new)) err = -EBUSY; 201 write_unlock(&resource_lock); 202 return err; } /* * 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. */ 217 struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name) { struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL); 221 if (res) { memset(res, 0, sizeof(*res)); res->name = name; res->start = start; res->end = start + n - 1; res->flags = IORESOURCE_BUSY; write_lock(&resource_lock); 230 for (;;) { struct resource *conflict; conflict = __request_resource(parent, res); 234 if (!conflict) 235 break; 236 if (conflict != parent) { parent = conflict; 238 if (!(conflict->flags & IORESOURCE_BUSY)) 239 continue; } /* Uhhuh, that didn't work out.. */ kfree(res); res = NULL; 245 break; } 247 write_unlock(&resource_lock); } 249 return res; } 252 int __check_region(struct resource *parent, unsigned long start, unsigned long n) { struct resource * res; res = __request_region(parent, start, n, "check-region"); 257 if (!res) 258 return -EBUSY; release_resource(res); kfree(res); 262 return 0; } 265 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; 273 for (;;) { struct resource *res = *p; 276 if (!res) 277 break; 278 if (res->start <= start && res->end >= end) { 279 if (!(res->flags & IORESOURCE_BUSY)) { p = &res->child; 281 continue; } 283 if (res->start != start || res->end != end) 284 break; *p = res->sibling; kfree(res); 287 return; } p = &res->sibling; } printk("Trying to free nonexistent resource <%08lx-%08lx>\n", start, end); } /* * Called from init/main.c to reserve IO ports. */ #define MAXRESERVE 4 298 static int __init reserve_setup(char *str) { int opt = 2, io_start, io_num; static int reserved = 0; static struct resource reserve[MAXRESERVE]; 304 while (opt==2) { int x = reserved; 307 if (get_option (&str, &io_start) != 2) break; 308 if (get_option (&str, &io_num) == 0) break; 309 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; 315 if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0) reserved = x+1; } } 319 return 1; } __setup("reserve=", reserve_setup);