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 19 #include <asm/pgalloc.h> 20 #include <asm/uaccess.h> 21 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)"); 26 27 #include "../../../drivers/s390/net/smsgiucv.h" 28 29 #define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2) 30 31 struct cmm_page_array { 32 struct cmm_page_array *next; 33 unsigned long index; 34 unsigned long pages[CMM_NR_PAGES]; 35 }; 36 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; 43 44 static struct cmm_page_array *cmm_page_list = NULL; 45 static struct cmm_page_array *cmm_timed_page_list = NULL; 46 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; 51 52 static void cmm_timer_fn(unsigned long); 53 static void cmm_set_timer(void); 54 55 static long 56 cmm_strtoul(const char *cp, char **endp) 57 { 58 unsigned int base = 10; 59 60 if (*cp == '0') { 61 base = 8; 62 cp++; 63 if ((*cp == 'x' || *cp == 'X') && isxdigit(cp[1])) { 64 base = 16; 65 cp++; 66 } 67 } 68 return simple_strtoul(cp, endp, base); 69 } 70 71 static long 72 cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list) 73 { 74 struct cmm_page_array *pa; 75 unsigned long page; 76 77 pa = *list; 78 while (pages) { 79 page = __get_free_page(GFP_NOIO); 80 if (!page) 81 break; 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); 86 if (!pa) { 87 free_page(page); 88 break; 89 } 90 pa->next = *list; 91 pa->index = 0; 92 *list = pa; 93 } 94 diag10(page); 95 pa->pages[pa->index++] = page; 96 (*counter)++; 97 pages--; 98 } 99 return pages; 100 } 101 102 static void 103 cmm_free_pages(long pages, long *counter, struct cmm_page_array **list) 104 { 105 struct cmm_page_array *pa; 106 unsigned long page; 107 108 pa = *list; 109 while (pages) { 110 if (!pa || pa->index <= 0) 111 break; 112 page = pa->pages[--pa->index]; 113 if (pa->index == 0) { 114 pa = pa->next; 115 free_page((unsigned long) *list); 116 *list = pa; 117 } 118 free_page(page); 119 (*counter)--; 120 pages--; 121 } 122 } 123 124 static int 125 cmm_thread(void *dummy) 126 { 127 int rc; 128 129 daemonize("cmmthread"); 130 while (1) { 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; 139 break; 140 } 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); 146 } 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); 154 } 155 if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer)) 156 cmm_set_timer(); 157 } 158 return 0; 159 } 160 161 static void 162 cmm_start_thread(void) 163 { 164 kernel_thread(cmm_thread, 0, 0); 165 } 166 167 static void 168 cmm_kick_thread(void) 169 { 170 if (!test_and_set_bit(0, &cmm_thread_active)) 171 schedule_work(&cmm_thread_starter); 172 wake_up(&cmm_thread_wait); 173 } 174 175 static void 176 cmm_set_timer(void) 177 { 178 if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) { 179 if (timer_pending(&cmm_timer)) 180 del_timer(&cmm_timer); 181 return; 182 } 183 if (timer_pending(&cmm_timer)) { 184 if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ)) 185 return; 186 } 187 cmm_timer.function = cmm_timer_fn; 188 cmm_timer.data = 0; 189 cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ; 190 add_timer(&cmm_timer); 191 } 192 193 static void 194 cmm_timer_fn(unsigned long ignored) 195 { 196 long pages; 197 198 pages = cmm_timed_pages_target - cmm_timeout_pages; 199 if (pages < 0) 200 cmm_timed_pages_target = 0; 201 else 202 cmm_timed_pages_target = pages; 203 cmm_kick_thread(); 204 cmm_set_timer(); 205 } 206 207 void 208 cmm_set_pages(long pages) 209 { 210 cmm_pages_target = pages; 211 cmm_kick_thread(); 212 } 213 214 long 215 cmm_get_pages(void) 216 { 217 return cmm_pages; 218 } 219 220 void 221 cmm_add_timed_pages(long pages) 222 { 223 cmm_timed_pages_target += pages; 224 cmm_kick_thread(); 225 } 226 227 long 228 cmm_get_timed_pages(void) 229 { 230 return cmm_timed_pages; 231 } 232 233 void 234 cmm_set_timeout(long pages, long seconds) 235 { 236 cmm_timeout_pages = pages; 237 cmm_timeout_seconds = seconds; 238 cmm_set_timer(); 239 } 240 241 static inline int 242 cmm_skip_blanks(char *cp, char **endp) 243 { 244 char *str; 245 246 for (str = cp; *str == ' ' || *str == '\t'; str++); 247 *endp = str; 248 return str != cp; 249 } 250 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 256 257 static struct ctl_table cmm_table[]; 258 259 static int 260 cmm_pages_handler(ctl_table *ctl, int write, struct file *filp, 261 void __user *buffer, size_t *lenp, loff_t *ppos) 262 { 263 char buf[16], *p; 264 long pages; 265 int len; 266 267 if (!*lenp || (*ppos && !write)) { 268 *lenp = 0; 269 return 0; 270 } 271 272 if (write) { 273 len = *lenp; 274 if (copy_from_user(buf, buffer, 275 len > sizeof(buf) ? sizeof(buf) : len)) 276 return -EFAULT; 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); 282 else 283 cmm_add_timed_pages(pages); 284 } else { 285 if (ctl == &cmm_table[0]) 286 pages = cmm_get_pages(); 287 else 288 pages = cmm_get_timed_pages(); 289 len = sprintf(buf, "%ld\n", pages); 290 if (len > *lenp) 291 len = *lenp; 292 if (copy_to_user(buffer, buf, len)) 293 return -EFAULT; 294 } 295 *lenp = len; 296 *ppos += len; 297 return 0; 298 } 299 300 static int 301 cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp, 302 void __user *buffer, size_t *lenp, loff_t *ppos) 303 { 304 char buf[64], *p; 305 long pages, seconds; 306 int len; 307 308 if (!*lenp || (*ppos && !write)) { 309 *lenp = 0; 310 return 0; 311 } 312 313 if (write) { 314 len = *lenp; 315 if (copy_from_user(buf, buffer, 316 len > sizeof(buf) ? sizeof(buf) : len)) 317 return -EFAULT; 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); 324 } else { 325 len = sprintf(buf, "%ld %ld\n", 326 cmm_timeout_pages, cmm_timeout_seconds); 327 if (len > *lenp) 328 len = *lenp; 329 if (copy_to_user(buffer, buf, len)) 330 return -EFAULT; 331 } 332 *lenp = len; 333 *ppos += len; 334 return 0; 335 } 336 337 static struct ctl_table cmm_table[] = { 338 { 339 .ctl_name = VM_CMM_PAGES, 340 .procname = "cmm_pages", 341 .mode = 0644, 342 .proc_handler = &cmm_pages_handler, 343 }, 344 { 345 .ctl_name = VM_CMM_TIMED_PAGES, 346 .procname = "cmm_timed_pages", 347 .mode = 0644, 348 .proc_handler = &cmm_pages_handler, 349 }, 350 { 351 .ctl_name = VM_CMM_TIMEOUT, 352 .procname = "cmm_timeout", 353 .mode = 0644, 354 .proc_handler = &cmm_timeout_handler, 355 }, 356 { .ctl_name = 0 } 357 }; 358 359 static struct ctl_table cmm_dir_table[] = { 360 { 361 .ctl_name = CTL_VM, 362 .procname = "vm", 363 .maxlen = 0, 364 .mode = 0555, 365 .child = cmm_table, 366 }, 367 { .ctl_name = 0 } 368 }; 369 #endif 370 371 #ifdef CONFIG_CMM_IUCV 372 #define SMSG_PREFIX "CMM" 373 static void 374 cmm_smsg_target(char *from, char *msg) 375 { 376 long pages, seconds; 377 378 if (strlen(sender) > 0 && strcmp(from, sender) != 0) 379 return; 380 if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg)) 381 return; 382 if (strncmp(msg, "SHRINK", 6) == 0) { 383 if (!cmm_skip_blanks(msg + 6, &msg)) 384 return; 385 pages = cmm_strtoul(msg, &msg); 386 cmm_skip_blanks(msg, &msg); 387 if (*msg == '\0') 388 cmm_set_pages(pages); 389 } else if (strncmp(msg, "RELEASE", 7) == 0) { 390 if (!cmm_skip_blanks(msg + 7, &msg)) 391 return; 392 pages = cmm_strtoul(msg, &msg); 393 cmm_skip_blanks(msg, &msg); 394 if (*msg == '\0') 395 cmm_add_timed_pages(pages); 396 } else if (strncmp(msg, "REUSE", 5) == 0) { 397 if (!cmm_skip_blanks(msg + 5, &msg)) 398 return; 399 pages = cmm_strtoul(msg, &msg); 400 if (!cmm_skip_blanks(msg, &msg)) 401 return; 402 seconds = cmm_strtoul(msg, &msg); 403 cmm_skip_blanks(msg, &msg); 404 if (*msg == '\0') 405 cmm_set_timeout(pages, seconds); 406 } 407 } 408 #endif 409 410 struct ctl_table_header *cmm_sysctl_header; 411 412 static int 413 cmm_init (void) 414 { 415 #ifdef CONFIG_CMM_PROC 416 cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1); 417 #endif 418 #ifdef CONFIG_CMM_IUCV 419 smsg_register_callback(SMSG_PREFIX, cmm_smsg_target); 420 #endif 421 INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, NULL); 422 init_waitqueue_head(&cmm_thread_wait); 423 init_timer(&cmm_timer); 424 return 0; 425 } 426 427 static void 428 cmm_exit(void) 429 { 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); 434 #endif 435 #ifdef CONFIG_CMM_IUCV 436 smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target); 437 #endif 438 } 439 440 module_init(cmm_init); 441 module_exit(cmm_exit); 442 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); 448 449 MODULE_LICENSE("GPL"); 450