cmm.c 9.3 KB

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