1 /* 2 * arch/s390/mm/cmm.c 3 * 4 * S390 version 5 * Copyright (C) 2003 IBM Deutschland Entwicklung GmbH, IBM Corporation 6 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) 7 * 8 * Collaborative memory management interface. 9 */ 10 11 #include <linux/errno.h> 12 #include <linux/fs.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> 18 #include <linux/swap.h> 19 #include <linux/kthread.h> 20 21 #include <asm/pgalloc.h> 22 #include <asm/uaccess.h> 23 #include <asm/diag.h> 24 25 static char *sender = "VMRMSVM"; 26 module_param(sender, charp, 0400); 27 MODULE_PARM_DESC(sender, 28 "Guest name that may send SMSG messages (default VMRMSVM)"); 29 30 #include "../../../drivers/s390/net/smsgiucv.h" 31 32 #define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2) 33 34 struct cmm_page_array { 35 struct cmm_page_array *next; 36 unsigned long index; 37 unsigned long pages[CMM_NR_PAGES]; 38 }; 39 40 static long cmm_pages; 41 static long cmm_timed_pages; 42 static volatile long cmm_pages_target; 43 static volatile long cmm_timed_pages_target; 44 static long cmm_timeout_pages; 45 static long cmm_timeout_seconds; 46 47 static struct cmm_page_array *cmm_page_list; 48 static struct cmm_page_array *cmm_timed_page_list; 49 static DEFINE_SPINLOCK(cmm_lock); 50 51 static struct task_struct *cmm_thread_ptr; 52 static wait_queue_head_t cmm_thread_wait; 53 static struct timer_list cmm_timer; 54 55 static void cmm_timer_fn(unsigned long); 56 static void cmm_set_timer(void); 57 58 static long 59 cmm_alloc_pages(long nr, long *counter, struct cmm_page_array **list) 60 { 61 struct cmm_page_array *pa, *npa; 62 unsigned long addr; 63 64 while (nr) { 65 addr = __get_free_page(GFP_NOIO); 66 if (!addr) 67 break; 68 spin_lock(&cmm_lock); 69 pa = *list; 70 if (!pa || pa->index >= CMM_NR_PAGES) { 71 /* Need a new page for the page list. */ 72 spin_unlock(&cmm_lock); 73 npa = (struct cmm_page_array *) 74 __get_free_page(GFP_NOIO); 75 if (!npa) { 76 free_page(addr); 77 break; 78 } 79 spin_lock(&cmm_lock); 80 pa = *list; 81 if (!pa || pa->index >= CMM_NR_PAGES) { 82 npa->next = pa; 83 npa->index = 0; 84 pa = npa; 85 *list = pa; 86 } else 87 free_page((unsigned long) npa); 88 } 89 diag10(addr); 90 pa->pages[pa->index++] = addr; 91 (*counter)++; 92 spin_unlock(&cmm_lock); 93 nr--; 94 } 95 return nr; 96 } 97 98 static long 99 cmm_free_pages(long nr, long *counter, struct cmm_page_array **list) 100 { 101 struct cmm_page_array *pa; 102 unsigned long addr; 103 104 spin_lock(&cmm_lock); 105 pa = *list; 106 while (nr) { 107 if (!pa || pa->index <= 0) 108 break; 109 addr = pa->pages[--pa->index]; 110 if (pa->index == 0) { 111 pa = pa->next; 112 free_page((unsigned long) *list); 113 *list = pa; 114 } 115 free_page(addr); 116 (*counter)--; 117 nr--; 118 } 119 spin_unlock(&cmm_lock); 120 return nr; 121 } 122 123 static int cmm_oom_notify(struct notifier_block *self, 124 unsigned long dummy, void *parm) 125 { 126 unsigned long *freed = parm; 127 long nr = 256; 128 129 nr = cmm_free_pages(nr, &cmm_timed_pages, &cmm_timed_page_list); 130 if (nr > 0) 131 nr = cmm_free_pages(nr, &cmm_pages, &cmm_page_list); 132 cmm_pages_target = cmm_pages; 133 cmm_timed_pages_target = cmm_timed_pages; 134 *freed += 256 - nr; 135 return NOTIFY_OK; 136 } 137 138 static struct notifier_block cmm_oom_nb = { 139 .notifier_call = cmm_oom_notify 140 }; 141 142 static int 143 cmm_thread(void *dummy) 144 { 145 int rc; 146 147 while (1) { 148 rc = wait_event_interruptible(cmm_thread_wait, 149 (cmm_pages != cmm_pages_target || 150 cmm_timed_pages != cmm_timed_pages_target || 151 kthread_should_stop())); 152 if (kthread_should_stop() || rc == -ERESTARTSYS) { 153 cmm_pages_target = cmm_pages; 154 cmm_timed_pages_target = cmm_timed_pages; 155 break; 156 } 157 if (cmm_pages_target > cmm_pages) { 158 if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list)) 159 cmm_pages_target = cmm_pages; 160 } else if (cmm_pages_target < cmm_pages) { 161 cmm_free_pages(1, &cmm_pages, &cmm_page_list); 162 } 163 if (cmm_timed_pages_target > cmm_timed_pages) { 164 if (cmm_alloc_pages(1, &cmm_timed_pages, 165 &cmm_timed_page_list)) 166 cmm_timed_pages_target = cmm_timed_pages; 167 } else if (cmm_timed_pages_target < cmm_timed_pages) { 168 cmm_free_pages(1, &cmm_timed_pages, 169 &cmm_timed_page_list); 170 } 171 if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer)) 172 cmm_set_timer(); 173 } 174 return 0; 175 } 176 177 static void 178 cmm_kick_thread(void) 179 { 180 wake_up(&cmm_thread_wait); 181 } 182 183 static void 184 cmm_set_timer(void) 185 { 186 if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) { 187 if (timer_pending(&cmm_timer)) 188 del_timer(&cmm_timer); 189 return; 190 } 191 if (timer_pending(&cmm_timer)) { 192 if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ)) 193 return; 194 } 195 cmm_timer.function = cmm_timer_fn; 196 cmm_timer.data = 0; 197 cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ; 198 add_timer(&cmm_timer); 199 } 200 201 static void 202 cmm_timer_fn(unsigned long ignored) 203 { 204 long nr; 205 206 nr = cmm_timed_pages_target - cmm_timeout_pages; 207 if (nr < 0) 208 cmm_timed_pages_target = 0; 209 else 210 cmm_timed_pages_target = nr; 211 cmm_kick_thread(); 212 cmm_set_timer(); 213 } 214 215 void 216 cmm_set_pages(long nr) 217 { 218 cmm_pages_target = nr; 219 cmm_kick_thread(); 220 } 221 222 long 223 cmm_get_pages(void) 224 { 225 return cmm_pages; 226 } 227 228 void 229 cmm_add_timed_pages(long nr) 230 { 231 cmm_timed_pages_target += nr; 232 cmm_kick_thread(); 233 } 234 235 long 236 cmm_get_timed_pages(void) 237 { 238 return cmm_timed_pages; 239 } 240 241 void 242 cmm_set_timeout(long nr, long seconds) 243 { 244 cmm_timeout_pages = nr; 245 cmm_timeout_seconds = seconds; 246 cmm_set_timer(); 247 } 248 249 static int 250 cmm_skip_blanks(char *cp, char **endp) 251 { 252 char *str; 253 254 for (str = cp; *str == ' ' || *str == '\t'; str++); 255 *endp = str; 256 return str != cp; 257 } 258 259 #ifdef CONFIG_CMM_PROC 260 261 static struct ctl_table cmm_table[]; 262 263 static int 264 cmm_pages_handler(ctl_table *ctl, int write, struct file *filp, 265 void __user *buffer, size_t *lenp, loff_t *ppos) 266 { 267 char buf[16], *p; 268 long nr; 269 int len; 270 271 if (!*lenp || (*ppos && !write)) { 272 *lenp = 0; 273 return 0; 274 } 275 276 if (write) { 277 len = *lenp; 278 if (copy_from_user(buf, buffer, 279 len > sizeof(buf) ? sizeof(buf) : len)) 280 return -EFAULT; 281 buf[sizeof(buf) - 1] = '\0'; 282 cmm_skip_blanks(buf, &p); 283 nr = simple_strtoul(p, &p, 0); 284 if (ctl == &cmm_table[0]) 285 cmm_set_pages(nr); 286 else 287 cmm_add_timed_pages(nr); 288 } else { 289 if (ctl == &cmm_table[0]) 290 nr = cmm_get_pages(); 291 else 292 nr = cmm_get_timed_pages(); 293 len = sprintf(buf, "%ld\n", nr); 294 if (len > *lenp) 295 len = *lenp; 296 if (copy_to_user(buffer, buf, len)) 297 return -EFAULT; 298 } 299 *lenp = len; 300 *ppos += len; 301 return 0; 302 } 303 304 static int 305 cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp, 306 void __user *buffer, size_t *lenp, loff_t *ppos) 307 { 308 char buf[64], *p; 309 long nr, seconds; 310 int len; 311 312 if (!*lenp || (*ppos && !write)) { 313 *lenp = 0; 314 return 0; 315 } 316 317 if (write) { 318 len = *lenp; 319 if (copy_from_user(buf, buffer, 320 len > sizeof(buf) ? sizeof(buf) : len)) 321 return -EFAULT; 322 buf[sizeof(buf) - 1] = '\0'; 323 cmm_skip_blanks(buf, &p); 324 nr = simple_strtoul(p, &p, 0); 325 cmm_skip_blanks(p, &p); 326 seconds = simple_strtoul(p, &p, 0); 327 cmm_set_timeout(nr, seconds); 328 } else { 329 len = sprintf(buf, "%ld %ld\n", 330 cmm_timeout_pages, cmm_timeout_seconds); 331 if (len > *lenp) 332 len = *lenp; 333 if (copy_to_user(buffer, buf, len)) 334 return -EFAULT; 335 } 336 *lenp = len; 337 *ppos += len; 338 return 0; 339 } 340 341 static struct ctl_table cmm_table[] = { 342 { 343 .ctl_name = VM_CMM_PAGES, 344 .procname = "cmm_pages", 345 .mode = 0644, 346 .proc_handler = &cmm_pages_handler, 347 }, 348 { 349 .ctl_name = VM_CMM_TIMED_PAGES, 350 .procname = "cmm_timed_pages", 351 .mode = 0644, 352 .proc_handler = &cmm_pages_handler, 353 }, 354 { 355 .ctl_name = VM_CMM_TIMEOUT, 356 .procname = "cmm_timeout", 357 .mode = 0644, 358 .proc_handler = &cmm_timeout_handler, 359 }, 360 { .ctl_name = 0 } 361 }; 362 363 static struct ctl_table cmm_dir_table[] = { 364 { 365 .ctl_name = CTL_VM, 366 .procname = "vm", 367 .maxlen = 0, 368 .mode = 0555, 369 .child = cmm_table, 370 }, 371 { .ctl_name = 0 } 372 }; 373 #endif 374 375 #ifdef CONFIG_CMM_IUCV 376 #define SMSG_PREFIX "CMM" 377 static void 378 cmm_smsg_target(char *from, char *msg) 379 { 380 long nr, seconds; 381 382 if (strlen(sender) > 0 && strcmp(from, sender) != 0) 383 return; 384 if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg)) 385 return; 386 if (strncmp(msg, "SHRINK", 6) == 0) { 387 if (!cmm_skip_blanks(msg + 6, &msg)) 388 return; 389 nr = simple_strtoul(msg, &msg, 0); 390 cmm_skip_blanks(msg, &msg); 391 if (*msg == '\0') 392 cmm_set_pages(nr); 393 } else if (strncmp(msg, "RELEASE", 7) == 0) { 394 if (!cmm_skip_blanks(msg + 7, &msg)) 395 return; 396 nr = simple_strtoul(msg, &msg, 0); 397 cmm_skip_blanks(msg, &msg); 398 if (*msg == '\0') 399 cmm_add_timed_pages(nr); 400 } else if (strncmp(msg, "REUSE", 5) == 0) { 401 if (!cmm_skip_blanks(msg + 5, &msg)) 402 return; 403 nr = simple_strtoul(msg, &msg, 0); 404 if (!cmm_skip_blanks(msg, &msg)) 405 return; 406 seconds = simple_strtoul(msg, &msg, 0); 407 cmm_skip_blanks(msg, &msg); 408 if (*msg == '\0') 409 cmm_set_timeout(nr, seconds); 410 } 411 } 412 #endif 413 414 static struct ctl_table_header *cmm_sysctl_header; 415 416 static int 417 cmm_init (void) 418 { 419 int rc = -ENOMEM; 420 421 #ifdef CONFIG_CMM_PROC 422 cmm_sysctl_header = register_sysctl_table(cmm_dir_table); 423 if (!cmm_sysctl_header) 424 goto out; 425 #endif 426 #ifdef CONFIG_CMM_IUCV 427 rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target); 428 if (rc < 0) 429 goto out_smsg; 430 #endif 431 rc = register_oom_notifier(&cmm_oom_nb); 432 if (rc < 0) 433 goto out_oom_notify; 434 init_waitqueue_head(&cmm_thread_wait); 435 init_timer(&cmm_timer); 436 cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread"); 437 rc = IS_ERR(cmm_thread_ptr) ? PTR_ERR(cmm_thread_ptr) : 0; 438 if (!rc) 439 goto out; 440 /* 441 * kthread_create failed. undo all the stuff from above again. 442 */ 443 unregister_oom_notifier(&cmm_oom_nb); 444 445 out_oom_notify: 446 #ifdef CONFIG_CMM_IUCV 447 smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target); 448 out_smsg: 449 #endif 450 #ifdef CONFIG_CMM_PROC 451 unregister_sysctl_table(cmm_sysctl_header); 452 #endif 453 out: 454 return rc; 455 } 456 457 static void 458 cmm_exit(void) 459 { 460 kthread_stop(cmm_thread_ptr); 461 unregister_oom_notifier(&cmm_oom_nb); 462 cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list); 463 cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list); 464 #ifdef CONFIG_CMM_PROC 465 unregister_sysctl_table(cmm_sysctl_header); 466 #endif 467 #ifdef CONFIG_CMM_IUCV 468 smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target); 469 #endif 470 } 471 472 module_init(cmm_init); 473 module_exit(cmm_exit); 474 475 EXPORT_SYMBOL(cmm_set_pages); 476 EXPORT_SYMBOL(cmm_get_pages); 477 EXPORT_SYMBOL(cmm_add_timed_pages); 478 EXPORT_SYMBOL(cmm_get_timed_pages); 479 EXPORT_SYMBOL(cmm_set_timeout); 480 481 MODULE_LICENSE("GPL"); 482