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