aboutsummaryrefslogtreecommitdiffstats
path: root/arch/um/drivers/mconsole_kern.c
diff options
context:
space:
mode:
authorJeff Dike <jdike@addtoit.com>2006-03-31 05:30:08 -0500
committerLinus Torvalds <torvalds@g5.osdl.org>2006-03-31 15:18:50 -0500
commit02dea0875b0f9b331a65fd6097dfd6115ca4ef24 (patch)
tree100126d72e889296a5d8d8714dd681ab041710cb /arch/um/drivers/mconsole_kern.c
parent85b6bce3658a823aa169586fe71ffba0f12ccc71 (diff)
[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/mconsole_kern.c')
-rw-r--r--arch/um/drivers/mconsole_kern.c138
1 files changed, 138 insertions, 0 deletions
diff --git a/arch/um/drivers/mconsole_kern.c b/arch/um/drivers/mconsole_kern.c
index 1488816588ea..d060fce4940e 100644
--- a/arch/um/drivers/mconsole_kern.c
+++ b/arch/um/drivers/mconsole_kern.c
@@ -20,6 +20,8 @@
20#include "linux/namei.h" 20#include "linux/namei.h"
21#include "linux/proc_fs.h" 21#include "linux/proc_fs.h"
22#include "linux/syscalls.h" 22#include "linux/syscalls.h"
23#include "linux/list.h"
24#include "linux/mm.h"
23#include "linux/console.h" 25#include "linux/console.h"
24#include "asm/irq.h" 26#include "asm/irq.h"
25#include "asm/uaccess.h" 27#include "asm/uaccess.h"
@@ -347,6 +349,142 @@ static struct mc_device *mconsole_find_dev(char *name)
347 return(NULL); 349 return(NULL);
348} 350}
349 351
352#define UNPLUGGED_PER_PAGE \
353 ((PAGE_SIZE - sizeof(struct list_head)) / sizeof(unsigned long))
354
355struct unplugged_pages {
356 struct list_head list;
357 void *pages[UNPLUGGED_PER_PAGE];
358};
359
360static unsigned long long unplugged_pages_count = 0;
361static struct list_head unplugged_pages = LIST_HEAD_INIT(unplugged_pages);
362static int unplug_index = UNPLUGGED_PER_PAGE;
363
364static int mem_config(char *str)
365{
366 unsigned long long diff;
367 int err = -EINVAL, i, add;
368 char *ret;
369
370 if(str[0] != '=')
371 goto out;
372
373 str++;
374 if(str[0] == '-')
375 add = 0;
376 else if(str[0] == '+'){
377 add = 1;
378 }
379 else goto out;
380
381 str++;
382 diff = memparse(str, &ret);
383 if(*ret != '\0')
384 goto out;
385
386 diff /= PAGE_SIZE;
387
388 for(i = 0; i < diff; i++){
389 struct unplugged_pages *unplugged;
390 void *addr;
391
392 if(add){
393 if(list_empty(&unplugged_pages))
394 break;
395
396 unplugged = list_entry(unplugged_pages.next,
397 struct unplugged_pages, list);
398 if(unplug_index > 0)
399 addr = unplugged->pages[--unplug_index];
400 else {
401 list_del(&unplugged->list);
402 addr = unplugged;
403 unplug_index = UNPLUGGED_PER_PAGE;
404 }
405
406 free_page((unsigned long) addr);
407 unplugged_pages_count--;
408 }
409 else {
410 struct page *page;
411
412 page = alloc_page(GFP_ATOMIC);
413 if(page == NULL)
414 break;
415
416 unplugged = page_address(page);
417 if(unplug_index == UNPLUGGED_PER_PAGE){
418 INIT_LIST_HEAD(&unplugged->list);
419 list_add(&unplugged->list, &unplugged_pages);
420 unplug_index = 0;
421 }
422 else {
423 struct list_head *entry = unplugged_pages.next;
424 addr = unplugged;
425
426 unplugged = list_entry(entry,
427 struct unplugged_pages,
428 list);
429 unplugged->pages[unplug_index++] = addr;
430 err = os_drop_memory(addr, PAGE_SIZE);
431 if(err)
432 printk("Failed to release memory - "
433 "errno = %d\n", err);
434 }
435
436 unplugged_pages_count++;
437 }
438 }
439
440 err = 0;
441out:
442 return err;
443}
444
445static int mem_get_config(char *name, char *str, int size, char **error_out)
446{
447 char buf[sizeof("18446744073709551615")];
448 int len = 0;
449
450 sprintf(buf, "%ld", uml_physmem);
451 CONFIG_CHUNK(str, size, len, buf, 1);
452
453 return len;
454}
455
456static int mem_id(char **str, int *start_out, int *end_out)
457{
458 *start_out = 0;
459 *end_out = 0;
460
461 return 0;
462}
463
464static int mem_remove(int n)
465{
466 return -EBUSY;
467}
468
469static struct mc_device mem_mc = {
470 .name = "mem",
471 .config = mem_config,
472 .get_config = mem_get_config,
473 .id = mem_id,
474 .remove = mem_remove,
475};
476
477static int mem_mc_init(void)
478{
479 if(can_drop_memory())
480 mconsole_register_dev(&mem_mc);
481 else printk("Can't release memory to the host - memory hotplug won't "
482 "be supported\n");
483 return 0;
484}
485
486__initcall(mem_mc_init);
487
350#define CONFIG_BUF_SIZE 64 488#define CONFIG_BUF_SIZE 64
351 489
352static void mconsole_get_config(int (*get_config)(char *, char *, int, 490static void mconsole_get_config(int (*get_config)(char *, char *, int,