diff options
author | Jeff Dike <jdike@addtoit.com> | 2006-03-31 10:30:08 (GMT) |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-03-31 20:18:50 (GMT) |
commit | 02dea0875b0f9b331a65fd6097dfd6115ca4ef24 (patch) | |
tree | 100126d72e889296a5d8d8714dd681ab041710cb /arch/um/drivers | |
parent | 85b6bce3658a823aa169586fe71ffba0f12ccc71 (diff) | |
download | linux-02dea0875b0f9b331a65fd6097dfd6115ca4ef24.tar.xz |
[PATCH] UML: Hotplug memory, take 2
Changes since first version
added check for MADV_REMOVE support on the host
fixed error return botch
shrunk sprintf array by one character
This adds hotplug memory support to UML. The mconsole syntax is
config mem=[+-]n[KMG]
In other words, add or subtract some number of kilobytes, megabytes, or
gigabytes.
Unplugged pages are allocated and then madvise(MADV_TRUNCATE), which is a
currently experimental madvise extension. These pages are tracked so they
can be plugged back in later if the admin decides to give them back. The
first page to be unplugged is used to keep track of about 4M of other
pages. A list_head is the first thing on this page. The rest is filled
with addresses of other unplugged pages. This first page is not madvised,
obviously.
When this page is filled, the next page is used in a similar way and linked
onto a list with the first page. Etc. This whole process reverses when
pages are plugged back in. When a tracking page no longer tracks any
unplugged pages, then it is next in line for plugging, which is done by
freeing pages back to the kernel.
Signed-off-by: Jeff Dike <jdike@addtoit.com>
Cc: Paolo 'Blaisorblade' Giarrusso <blaisorblade@yahoo.it>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/um/drivers')
-rw-r--r-- | arch/um/drivers/mconsole_kern.c | 138 |
1 files changed, 138 insertions, 0 deletions
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c index 1488816..d060fce 100644 --- a/arch/um/drivers/mconsole_kern.c +++ b/arch/um/drivers/mconsole_kern.c @@ -20,6 +20,8 @@ #include "linux/namei.h" #include "linux/proc_fs.h" #include "linux/syscalls.h" +#include "linux/list.h" +#include "linux/mm.h" #include "linux/console.h" #include "asm/irq.h" #include "asm/uaccess.h" @@ -347,6 +349,142 @@ static struct mc_device *mconsole_find_dev(char *name) return(NULL); } +#define UNPLUGGED_PER_PAGE \ + ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long)) + +struct unplugged_pages { + struct list_head list; + void *pages[UNPLUGGED_PER_PAGE]; +}; + +static unsigned long long unplugged_pages_count = 0; +static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages); +static int unplug_index = UNPLUGGED_PER_PAGE; + +static int mem_config(char *str) +{ + unsigned long long diff; + int err = -EINVAL, i, add; + char *ret; + + if(str[0] != '=') + goto out; + + str++; + if(str[0] == '-') + add = 0; + else if(str[0] == '+'){ + add = 1; + } + else goto out; + + str++; + diff = memparse(str, &ret); + if(*ret != '\0') + goto out; + + diff /= PAGE_SIZE; + + for(i = 0; i < diff; i++){ + struct unplugged_pages *unplugged; + void *addr; + + if(add){ + if(list_empty(&unplugged_pages)) + break; + + unplugged = list_entry(unplugged_pages.next, + struct unplugged_pages, list); + if(unplug_index > 0) + addr = unplugged->pages[--unplug_index]; + else { + list_del(&unplugged->list); + addr = unplugged; + unplug_index = UNPLUGGED_PER_PAGE; + } + + free_page((unsigned long) addr); + unplugged_pages_count--; + } + else { + struct page *page; + + page = alloc_page(GFP_ATOMIC); + if(page == NULL) + break; + + unplugged = page_address(page); + if(unplug_index == UNPLUGGED_PER_PAGE){ + INIT_LIST_HEAD(&unplugged->list); + list_add(&unplugged->list, &unplugged_pages); + unplug_index = 0; + } + else { + struct list_head *entry = unplugged_pages.next; + addr = unplugged; + + unplugged = list_entry(entry, + struct unplugged_pages, + list); + unplugged->pages[unplug_index++] = addr; + err = os_drop_memory(addr, PAGE_SIZE); + if(err) + printk("Failed to release memory - " + "errno = %d\n", err); + } + + unplugged_pages_count++; + } + } + + err = 0; +out: + return err; +} + +static int mem_get_config(char *name, char *str, int size, char **error_out) +{ + char buf[sizeof("18446744073709551615")]; + int len = 0; + + sprintf(buf, "%ld", uml_physmem); + CONFIG_CHUNK(str, size, len, buf, 1); + + return len; +} + +static int mem_id(char **str, int *start_out, int *end_out) +{ + *start_out = 0; + *end_out = 0; + + return 0; +} + +static int mem_remove(int n) +{ + return -EBUSY; +} + +static struct mc_device mem_mc = { + .name = "mem", + .config = mem_config, + .get_config = mem_get_config, + .id = mem_id, + .remove = mem_remove, +}; + +static int mem_mc_init(void) +{ + if(can_drop_memory()) + mconsole_register_dev(&mem_mc); + else printk("Can't release memory to the host - memory hotplug won't " + "be supported\n"); + return 0; +} + +__initcall(mem_mc_init); + #define CONFIG_BUF_SIZE 64 static void mconsole_get_config(int (*get_config)(char *, char *, int, |