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