/* * linux/kernel/resource.c * * Copyright (C) 1999 Linus Torvalds * Copyright (C) 1999 Martin Mares * * Arbitrary resource management. */ #include #include #include #include #include #include #include #include #include #include #include struct resource ioport_resource = { "PCI IO", 0x0000, IO_SPACE_LIMIT, IORESOURCE_IO }; struct resource iomem_resource = { "PCI mem", 0UL, ~0UL, IORESOURCE_MEM }; static rwlock_t resource_lock = RW_LOCK_UNLOCKED; #ifdef CONFIG_PROC_FS #define MAX_IORES_LEVEL 5 /* * do_resource_list(): * for reports of /proc/ioports and /proc/iomem; * do current entry, then children, then siblings; */ static int do_resource_list(struct seq_file *m, struct resource *res, const char *fmt, int level) { while (res) { const char *name; name = res->name ? res->name : ""; if (level > MAX_IORES_LEVEL) level = MAX_IORES_LEVEL; seq_printf (m, fmt + 2 * MAX_IORES_LEVEL - 2 * level, res->start, res->end, name); if (res->child) do_resource_list(m, res->child, fmt, level + 1); res = res->sibling; } return 0; } static int ioresources_show(struct seq_file *m, void *v) { struct resource *root = m->private; char *fmt; int retval; fmt = root->end < 0x10000 ? " %04lx-%04lx : %s\n" : " %08lx-%08lx : %s\n"; read_lock(&resource_lock); retval = do_resource_list(m, root->child, fmt, 0); read_unlock(&resource_lock); return retval; } static int ioresources_open(struct file *file, struct resource *root) { char *buf = kmalloc(PAGE_SIZE, GFP_KERNEL); struct seq_file *m; int res; if (!buf) return -ENOMEM; res = single_open(file, ioresources_show, root); if (!res) { m = file->private_data; m->buf = buf; m->size = PAGE_SIZE; } else kfree(buf); return res; } static int ioports_open(struct inode *inode, struct file *file) { return ioresources_open(file, &ioport_resource); } static struct file_operations proc_ioports_operations = { .open = ioports_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int iomem_open(struct inode *inode, struct file *file) { return ioresources_open(file, &iomem_resource); } static struct file_operations proc_iomem_operations = { .open = iomem_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init ioresources_init(void) { struct proc_dir_entry *entry; entry = create_proc_entry("ioports", 0, NULL); if (entry) entry->proc_fops = &proc_ioports_operations; entry = create_proc_entry("iomem", 0, NULL); if (entry) entry->proc_fops = &proc_iomem_operations; return 0; } __initcall(ioresources_init); #endif /* CONFIG_PROC_FS */ /* 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 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; } 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) { int retval; write_lock(&resource_lock); retval = __release_resource(old); write_unlock(&resource_lock); return retval; } /* * Find empty slot in the resource tree given range and alignment. */ 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, unsigned long), void *alignf_data) { struct resource *this = root->child; new->start = root->start; for(;;) { if (this) new->end = this->start; else new->end = root->end; if (new->start < min) new->start = min; if (new->end > max) new->end = max; new->start = (new->start + align - 1) & ~(align - 1); if (alignf) alignf(alignf_data, new, size, align); if (new->start < new->end && new->end - new->start + 1 >= size) { new->end = new->start + size - 1; return 0; } if (!this) break; new->start = this->end + 1; this = this->sibling; } return -EBUSY; } /* * Allocate empty slot in the resource tree given range and alignment. */ 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, unsigned long), void *alignf_data) { int err; write_lock(&resource_lock); err = find_resource(root, new, size, min, max, align, alignf, alignf_data); if (err >= 0 && __request_resource(root, new)) err = -EBUSY; write_unlock(&resource_lock); 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. */ struct resource * __request_region(struct resource *parent, unsigned long start, unsigned long n, const char *name) { struct resource *res = kmalloc(sizeof(*res), GFP_KERNEL); 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); 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; } int __deprecated __check_region(struct resource *parent, unsigned long start, unsigned long n) { 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; } printk(KERN_WARNING "Trying to free nonexistent resource <%08lx-%08lx>\n", start, end); } /* * Called from init/main.c to reserve IO ports. */ #define MAXRESERVE 4 static int __init reserve_setup(char *str) { static int reserved = 0; static struct resource reserve[MAXRESERVE]; for (;;) { int io_start, io_num; 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 = io_start; res->end = io_start + io_num - 1; res->flags = IORESOURCE_BUSY; res->child = NULL; if (request_resource(res->start >= 0x10000 ? &iomem_resource : &ioport_resource, res) == 0) reserved = x+1; } } return 1; } __setup("reserve=", reserve_setup);