5 * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
6 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
8 * Collaborative memory management interface.
11 #include <linux/errno.h>
13 #include <linux/init.h>
14 #include <linux/module.h>
15 #include <linux/sched.h>
16 #include <linux/sysctl.h>
17 #include <linux/ctype.h>
19 #include <asm/pgalloc.h>
20 #include <asm/uaccess.h>
22 static char *sender = "VMRMSVM";
23 module_param(sender, charp, 0400);
24 MODULE_PARM_DESC(sender,
25 "Guest name that may send SMSG messages (default VMRMSVM)");
27 #include "../../../drivers/s390/net/smsgiucv.h"
29 #define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2)
31 struct cmm_page_array {
32 struct cmm_page_array *next;
34 unsigned long pages[CMM_NR_PAGES];
37 static long cmm_pages = 0;
38 static long cmm_timed_pages = 0;
39 static volatile long cmm_pages_target = 0;
40 static volatile long cmm_timed_pages_target = 0;
41 static long cmm_timeout_pages = 0;
42 static long cmm_timeout_seconds = 0;
44 static struct cmm_page_array *cmm_page_list = NULL;
45 static struct cmm_page_array *cmm_timed_page_list = NULL;
47 static unsigned long cmm_thread_active = 0;
48 static struct work_struct cmm_thread_starter;
49 static wait_queue_head_t cmm_thread_wait;
50 static struct timer_list cmm_timer;
52 static void cmm_timer_fn(unsigned long);
53 static void cmm_set_timer(void);
56 cmm_strtoul(const char *cp, char **endp)
58 unsigned int base = 10;
63 if ((*cp == 'x' || *cp == 'X') && isxdigit(cp[1])) {
68 return simple_strtoul(cp, endp, base);
72 cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list)
74 struct cmm_page_array *pa;
79 page = __get_free_page(GFP_NOIO);
82 if (!pa || pa->index >= CMM_NR_PAGES) {
83 /* Need a new page for the page list. */
84 pa = (struct cmm_page_array *)
85 __get_free_page(GFP_NOIO);
95 pa->pages[pa->index++] = page;
103 cmm_free_pages(long pages, long *counter, struct cmm_page_array **list)
105 struct cmm_page_array *pa;
110 if (!pa || pa->index <= 0)
112 page = pa->pages[--pa->index];
113 if (pa->index == 0) {
115 free_page((unsigned long) *list);
125 cmm_thread(void *dummy)
129 daemonize("cmmthread");
131 rc = wait_event_interruptible(cmm_thread_wait,
132 (cmm_pages != cmm_pages_target ||
133 cmm_timed_pages != cmm_timed_pages_target));
134 if (rc == -ERESTARTSYS) {
135 /* Got kill signal. End thread. */
136 clear_bit(0, &cmm_thread_active);
137 cmm_pages_target = cmm_pages;
138 cmm_timed_pages_target = cmm_timed_pages;
141 if (cmm_pages_target > cmm_pages) {
142 if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list))
143 cmm_pages_target = cmm_pages;
144 } else if (cmm_pages_target < cmm_pages) {
145 cmm_free_pages(1, &cmm_pages, &cmm_page_list);
147 if (cmm_timed_pages_target > cmm_timed_pages) {
148 if (cmm_alloc_pages(1, &cmm_timed_pages,
149 &cmm_timed_page_list))
150 cmm_timed_pages_target = cmm_timed_pages;
151 } else if (cmm_timed_pages_target < cmm_timed_pages) {
152 cmm_free_pages(1, &cmm_timed_pages,
153 &cmm_timed_page_list);
155 if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer))
162 cmm_start_thread(void)
164 kernel_thread(cmm_thread, NULL, 0);
168 cmm_kick_thread(void)
170 if (!test_and_set_bit(0, &cmm_thread_active))
171 schedule_work(&cmm_thread_starter);
172 wake_up(&cmm_thread_wait);
178 if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) {
179 if (timer_pending(&cmm_timer))
180 del_timer(&cmm_timer);
183 if (timer_pending(&cmm_timer)) {
184 if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ))
187 cmm_timer.function = cmm_timer_fn;
189 cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ;
190 add_timer(&cmm_timer);
194 cmm_timer_fn(unsigned long ignored)
198 pages = cmm_timed_pages_target - cmm_timeout_pages;
200 cmm_timed_pages_target = 0;
202 cmm_timed_pages_target = pages;
208 cmm_set_pages(long pages)
210 cmm_pages_target = pages;
221 cmm_add_timed_pages(long pages)
223 cmm_timed_pages_target += pages;
228 cmm_get_timed_pages(void)
230 return cmm_timed_pages;
234 cmm_set_timeout(long pages, long seconds)
236 cmm_timeout_pages = pages;
237 cmm_timeout_seconds = seconds;
242 cmm_skip_blanks(char *cp, char **endp)
246 for (str = cp; *str == ' ' || *str == '\t'; str++);
251 #ifdef CONFIG_CMM_PROC
252 /* These will someday get removed. */
253 #define VM_CMM_PAGES 1111
254 #define VM_CMM_TIMED_PAGES 1112
255 #define VM_CMM_TIMEOUT 1113
257 static struct ctl_table cmm_table[];
260 cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
261 void __user *buffer, size_t *lenp, loff_t *ppos)
267 if (!*lenp || (*ppos && !write)) {
274 if (copy_from_user(buf, buffer,
275 len > sizeof(buf) ? sizeof(buf) : len))
277 buf[sizeof(buf) - 1] = '\0';
278 cmm_skip_blanks(buf, &p);
279 pages = cmm_strtoul(p, &p);
280 if (ctl == &cmm_table[0])
281 cmm_set_pages(pages);
283 cmm_add_timed_pages(pages);
285 if (ctl == &cmm_table[0])
286 pages = cmm_get_pages();
288 pages = cmm_get_timed_pages();
289 len = sprintf(buf, "%ld\n", pages);
292 if (copy_to_user(buffer, buf, len))
301 cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
302 void __user *buffer, size_t *lenp, loff_t *ppos)
308 if (!*lenp || (*ppos && !write)) {
315 if (copy_from_user(buf, buffer,
316 len > sizeof(buf) ? sizeof(buf) : len))
318 buf[sizeof(buf) - 1] = '\0';
319 cmm_skip_blanks(buf, &p);
320 pages = cmm_strtoul(p, &p);
321 cmm_skip_blanks(p, &p);
322 seconds = cmm_strtoul(p, &p);
323 cmm_set_timeout(pages, seconds);
325 len = sprintf(buf, "%ld %ld\n",
326 cmm_timeout_pages, cmm_timeout_seconds);
329 if (copy_to_user(buffer, buf, len))
337 static struct ctl_table cmm_table[] = {
339 .ctl_name = VM_CMM_PAGES,
340 .procname = "cmm_pages",
342 .proc_handler = &cmm_pages_handler,
345 .ctl_name = VM_CMM_TIMED_PAGES,
346 .procname = "cmm_timed_pages",
348 .proc_handler = &cmm_pages_handler,
351 .ctl_name = VM_CMM_TIMEOUT,
352 .procname = "cmm_timeout",
354 .proc_handler = &cmm_timeout_handler,
359 static struct ctl_table cmm_dir_table[] = {
371 #ifdef CONFIG_CMM_IUCV
372 #define SMSG_PREFIX "CMM"
374 cmm_smsg_target(char *from, char *msg)
378 if (strlen(sender) > 0 && strcmp(from, sender) != 0)
380 if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg))
382 if (strncmp(msg, "SHRINK", 6) == 0) {
383 if (!cmm_skip_blanks(msg + 6, &msg))
385 pages = cmm_strtoul(msg, &msg);
386 cmm_skip_blanks(msg, &msg);
388 cmm_set_pages(pages);
389 } else if (strncmp(msg, "RELEASE", 7) == 0) {
390 if (!cmm_skip_blanks(msg + 7, &msg))
392 pages = cmm_strtoul(msg, &msg);
393 cmm_skip_blanks(msg, &msg);
395 cmm_add_timed_pages(pages);
396 } else if (strncmp(msg, "REUSE", 5) == 0) {
397 if (!cmm_skip_blanks(msg + 5, &msg))
399 pages = cmm_strtoul(msg, &msg);
400 if (!cmm_skip_blanks(msg, &msg))
402 seconds = cmm_strtoul(msg, &msg);
403 cmm_skip_blanks(msg, &msg);
405 cmm_set_timeout(pages, seconds);
410 struct ctl_table_header *cmm_sysctl_header;
415 #ifdef CONFIG_CMM_PROC
416 cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
418 #ifdef CONFIG_CMM_IUCV
419 smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
421 INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, NULL);
422 init_waitqueue_head(&cmm_thread_wait);
423 init_timer(&cmm_timer);
430 cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
431 cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
432 #ifdef CONFIG_CMM_PROC
433 unregister_sysctl_table(cmm_sysctl_header);
435 #ifdef CONFIG_CMM_IUCV
436 smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
440 module_init(cmm_init);
441 module_exit(cmm_exit);
443 EXPORT_SYMBOL(cmm_set_pages);
444 EXPORT_SYMBOL(cmm_get_pages);
445 EXPORT_SYMBOL(cmm_add_timed_pages);
446 EXPORT_SYMBOL(cmm_get_timed_pages);
447 EXPORT_SYMBOL(cmm_set_timeout);
449 MODULE_LICENSE("GPL");