aboutsummaryrefslogtreecommitdiffstats
path: root/arch/s390/mm/cmm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/s390/mm/cmm.c')
-rw-r--r--arch/s390/mm/cmm.c205
1 files changed, 128 insertions, 77 deletions
diff --git a/arch/s390/mm/cmm.c b/arch/s390/mm/cmm.c
index 786a44dba5bf..607f50ead1fd 100644
--- a/arch/s390/mm/cmm.c
+++ b/arch/s390/mm/cmm.c
@@ -15,6 +15,8 @@
15#include <linux/sched.h> 15#include <linux/sched.h>
16#include <linux/sysctl.h> 16#include <linux/sysctl.h>
17#include <linux/ctype.h> 17#include <linux/ctype.h>
18#include <linux/swap.h>
19#include <linux/kthread.h>
18 20
19#include <asm/pgalloc.h> 21#include <asm/pgalloc.h>
20#include <asm/uaccess.h> 22#include <asm/uaccess.h>
@@ -34,18 +36,18 @@ struct cmm_page_array {
34 unsigned long pages[CMM_NR_PAGES]; 36 unsigned long pages[CMM_NR_PAGES];
35}; 37};
36 38
37static long cmm_pages = 0; 39static long cmm_pages;
38static long cmm_timed_pages = 0; 40static long cmm_timed_pages;
39static volatile long cmm_pages_target = 0; 41static volatile long cmm_pages_target;
40static volatile long cmm_timed_pages_target = 0; 42static volatile long cmm_timed_pages_target;
41static long cmm_timeout_pages = 0; 43static long cmm_timeout_pages;
42static long cmm_timeout_seconds = 0; 44static long cmm_timeout_seconds;
43 45
44static struct cmm_page_array *cmm_page_list = NULL; 46static struct cmm_page_array *cmm_page_list;
45static struct cmm_page_array *cmm_timed_page_list = NULL; 47static struct cmm_page_array *cmm_timed_page_list;
48static DEFINE_SPINLOCK(cmm_lock);
46 49
47static unsigned long cmm_thread_active = 0; 50static struct task_struct *cmm_thread_ptr;
48static struct work_struct cmm_thread_starter;
49static wait_queue_head_t cmm_thread_wait; 51static wait_queue_head_t cmm_thread_wait;
50static struct timer_list cmm_timer; 52static struct timer_list cmm_timer;
51 53
@@ -53,71 +55,100 @@ static void cmm_timer_fn(unsigned long);
53static void cmm_set_timer(void); 55static void cmm_set_timer(void);
54 56
55static long 57static long
56cmm_alloc_pages(long pages, long *counter, struct cmm_page_array **list) 58cmm_alloc_pages(long nr, long *counter, struct cmm_page_array **list)
57{ 59{
58 struct cmm_page_array *pa; 60 struct cmm_page_array *pa, *npa;
59 unsigned long page; 61 unsigned long addr;
60 62
61 pa = *list; 63 while (nr) {
62 while (pages) { 64 addr = __get_free_page(GFP_NOIO);
63 page = __get_free_page(GFP_NOIO); 65 if (!addr)
64 if (!page)
65 break; 66 break;
67 spin_lock(&cmm_lock);
68 pa = *list;
66 if (!pa || pa->index >= CMM_NR_PAGES) { 69 if (!pa || pa->index >= CMM_NR_PAGES) {
67 /* Need a new page for the page list. */ 70 /* Need a new page for the page list. */
68 pa = (struct cmm_page_array *) 71 spin_unlock(&cmm_lock);
72 npa = (struct cmm_page_array *)
69 __get_free_page(GFP_NOIO); 73 __get_free_page(GFP_NOIO);
70 if (!pa) { 74 if (!npa) {
71 free_page(page); 75 free_page(addr);
72 break; 76 break;
73 } 77 }
74 pa->next = *list; 78 spin_lock(&cmm_lock);
75 pa->index = 0; 79 pa = *list;
76 *list = pa; 80 if (!pa || pa->index >= CMM_NR_PAGES) {
81 npa->next = pa;
82 npa->index = 0;
83 pa = npa;
84 *list = pa;
85 } else
86 free_page((unsigned long) npa);
77 } 87 }
78 diag10(page); 88 diag10(addr);
79 pa->pages[pa->index++] = page; 89 pa->pages[pa->index++] = addr;
80 (*counter)++; 90 (*counter)++;
81 pages--; 91 spin_unlock(&cmm_lock);
92 nr--;
82 } 93 }
83 return pages; 94 return nr;
84} 95}
85 96
86static void 97static long
87cmm_free_pages(long pages, long *counter, struct cmm_page_array **list) 98cmm_free_pages(long nr, long *counter, struct cmm_page_array **list)
88{ 99{
89 struct cmm_page_array *pa; 100 struct cmm_page_array *pa;
90 unsigned long page; 101 unsigned long addr;
91 102
103 spin_lock(&cmm_lock);
92 pa = *list; 104 pa = *list;
93 while (pages) { 105 while (nr) {
94 if (!pa || pa->index <= 0) 106 if (!pa || pa->index <= 0)
95 break; 107 break;
96 page = pa->pages[--pa->index]; 108 addr = pa->pages[--pa->index];
97 if (pa->index == 0) { 109 if (pa->index == 0) {
98 pa = pa->next; 110 pa = pa->next;
99 free_page((unsigned long) *list); 111 free_page((unsigned long) *list);
100 *list = pa; 112 *list = pa;
101 } 113 }
102 free_page(page); 114 free_page(addr);
103 (*counter)--; 115 (*counter)--;
104 pages--; 116 nr--;
105 } 117 }
118 spin_unlock(&cmm_lock);
119 return nr;
106} 120}
107 121
122static int cmm_oom_notify(struct notifier_block *self,
123 unsigned long dummy, void *parm)
124{
125 unsigned long *freed = parm;
126 long nr = 256;
127
128 nr = cmm_free_pages(nr, &cmm_timed_pages, &cmm_timed_page_list);
129 if (nr > 0)
130 nr = cmm_free_pages(nr, &cmm_pages, &cmm_page_list);
131 cmm_pages_target = cmm_pages;
132 cmm_timed_pages_target = cmm_timed_pages;
133 *freed += 256 - nr;
134 return NOTIFY_OK;
135}
136
137static struct notifier_block cmm_oom_nb = {
138 .notifier_call = cmm_oom_notify
139};
140
108static int 141static int
109cmm_thread(void *dummy) 142cmm_thread(void *dummy)
110{ 143{
111 int rc; 144 int rc;
112 145
113 daemonize("cmmthread");
114 while (1) { 146 while (1) {
115 rc = wait_event_interruptible(cmm_thread_wait, 147 rc = wait_event_interruptible(cmm_thread_wait,
116 (cmm_pages != cmm_pages_target || 148 (cmm_pages != cmm_pages_target ||
117 cmm_timed_pages != cmm_timed_pages_target)); 149 cmm_timed_pages != cmm_timed_pages_target ||
118 if (rc == -ERESTARTSYS) { 150 kthread_should_stop()));
119 /* Got kill signal. End thread. */ 151 if (kthread_should_stop() || rc == -ERESTARTSYS) {
120 clear_bit(0, &cmm_thread_active);
121 cmm_pages_target = cmm_pages; 152 cmm_pages_target = cmm_pages;
122 cmm_timed_pages_target = cmm_timed_pages; 153 cmm_timed_pages_target = cmm_timed_pages;
123 break; 154 break;
@@ -143,16 +174,8 @@ cmm_thread(void *dummy)
143} 174}
144 175
145static void 176static void
146cmm_start_thread(void)
147{
148 kernel_thread(cmm_thread, NULL, 0);
149}
150
151static void
152cmm_kick_thread(void) 177cmm_kick_thread(void)
153{ 178{
154 if (!test_and_set_bit(0, &cmm_thread_active))
155 schedule_work(&cmm_thread_starter);
156 wake_up(&cmm_thread_wait); 179 wake_up(&cmm_thread_wait);
157} 180}
158 181
@@ -177,21 +200,21 @@ cmm_set_timer(void)
177static void 200static void
178cmm_timer_fn(unsigned long ignored) 201cmm_timer_fn(unsigned long ignored)
179{ 202{
180 long pages; 203 long nr;
181 204
182 pages = cmm_timed_pages_target - cmm_timeout_pages; 205 nr = cmm_timed_pages_target - cmm_timeout_pages;
183 if (pages < 0) 206 if (nr < 0)
184 cmm_timed_pages_target = 0; 207 cmm_timed_pages_target = 0;
185 else 208 else
186 cmm_timed_pages_target = pages; 209 cmm_timed_pages_target = nr;
187 cmm_kick_thread(); 210 cmm_kick_thread();
188 cmm_set_timer(); 211 cmm_set_timer();
189} 212}
190 213
191void 214void
192cmm_set_pages(long pages) 215cmm_set_pages(long nr)
193{ 216{
194 cmm_pages_target = pages; 217 cmm_pages_target = nr;
195 cmm_kick_thread(); 218 cmm_kick_thread();
196} 219}
197 220
@@ -202,9 +225,9 @@ cmm_get_pages(void)
202} 225}
203 226
204void 227void
205cmm_add_timed_pages(long pages) 228cmm_add_timed_pages(long nr)
206{ 229{
207 cmm_timed_pages_target += pages; 230 cmm_timed_pages_target += nr;
208 cmm_kick_thread(); 231 cmm_kick_thread();
209} 232}
210 233
@@ -215,9 +238,9 @@ cmm_get_timed_pages(void)
215} 238}
216 239
217void 240void
218cmm_set_timeout(long pages, long seconds) 241cmm_set_timeout(long nr, long seconds)
219{ 242{
220 cmm_timeout_pages = pages; 243 cmm_timeout_pages = nr;
221 cmm_timeout_seconds = seconds; 244 cmm_timeout_seconds = seconds;
222 cmm_set_timer(); 245 cmm_set_timer();
223} 246}
@@ -245,7 +268,7 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
245 void __user *buffer, size_t *lenp, loff_t *ppos) 268 void __user *buffer, size_t *lenp, loff_t *ppos)
246{ 269{
247 char buf[16], *p; 270 char buf[16], *p;
248 long pages; 271 long nr;
249 int len; 272 int len;
250 273
251 if (!*lenp || (*ppos && !write)) { 274 if (!*lenp || (*ppos && !write)) {
@@ -260,17 +283,17 @@ cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
260 return -EFAULT; 283 return -EFAULT;
261 buf[sizeof(buf) - 1] = '\0'; 284 buf[sizeof(buf) - 1] = '\0';
262 cmm_skip_blanks(buf, &p); 285 cmm_skip_blanks(buf, &p);
263 pages = simple_strtoul(p, &p, 0); 286 nr = simple_strtoul(p, &p, 0);
264 if (ctl == &cmm_table[0]) 287 if (ctl == &cmm_table[0])
265 cmm_set_pages(pages); 288 cmm_set_pages(nr);
266 else 289 else
267 cmm_add_timed_pages(pages); 290 cmm_add_timed_pages(nr);
268 } else { 291 } else {
269 if (ctl == &cmm_table[0]) 292 if (ctl == &cmm_table[0])
270 pages = cmm_get_pages(); 293 nr = cmm_get_pages();
271 else 294 else
272 pages = cmm_get_timed_pages(); 295 nr = cmm_get_timed_pages();
273 len = sprintf(buf, "%ld\n", pages); 296 len = sprintf(buf, "%ld\n", nr);
274 if (len > *lenp) 297 if (len > *lenp)
275 len = *lenp; 298 len = *lenp;
276 if (copy_to_user(buffer, buf, len)) 299 if (copy_to_user(buffer, buf, len))
@@ -286,7 +309,7 @@ cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
286 void __user *buffer, size_t *lenp, loff_t *ppos) 309 void __user *buffer, size_t *lenp, loff_t *ppos)
287{ 310{
288 char buf[64], *p; 311 char buf[64], *p;
289 long pages, seconds; 312 long nr, seconds;
290 int len; 313 int len;
291 314
292 if (!*lenp || (*ppos && !write)) { 315 if (!*lenp || (*ppos && !write)) {
@@ -301,10 +324,10 @@ cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
301 return -EFAULT; 324 return -EFAULT;
302 buf[sizeof(buf) - 1] = '\0'; 325 buf[sizeof(buf) - 1] = '\0';
303 cmm_skip_blanks(buf, &p); 326 cmm_skip_blanks(buf, &p);
304 pages = simple_strtoul(p, &p, 0); 327 nr = simple_strtoul(p, &p, 0);
305 cmm_skip_blanks(p, &p); 328 cmm_skip_blanks(p, &p);
306 seconds = simple_strtoul(p, &p, 0); 329 seconds = simple_strtoul(p, &p, 0);
307 cmm_set_timeout(pages, seconds); 330 cmm_set_timeout(nr, seconds);
308 } else { 331 } else {
309 len = sprintf(buf, "%ld %ld\n", 332 len = sprintf(buf, "%ld %ld\n",
310 cmm_timeout_pages, cmm_timeout_seconds); 333 cmm_timeout_pages, cmm_timeout_seconds);
@@ -357,7 +380,7 @@ static struct ctl_table cmm_dir_table[] = {
357static void 380static void
358cmm_smsg_target(char *from, char *msg) 381cmm_smsg_target(char *from, char *msg)
359{ 382{
360 long pages, seconds; 383 long nr, seconds;
361 384
362 if (strlen(sender) > 0 && strcmp(from, sender) != 0) 385 if (strlen(sender) > 0 && strcmp(from, sender) != 0)
363 return; 386 return;
@@ -366,27 +389,27 @@ cmm_smsg_target(char *from, char *msg)
366 if (strncmp(msg, "SHRINK", 6) == 0) { 389 if (strncmp(msg, "SHRINK", 6) == 0) {
367 if (!cmm_skip_blanks(msg + 6, &msg)) 390 if (!cmm_skip_blanks(msg + 6, &msg))
368 return; 391 return;
369 pages = simple_strtoul(msg, &msg, 0); 392 nr = simple_strtoul(msg, &msg, 0);
370 cmm_skip_blanks(msg, &msg); 393 cmm_skip_blanks(msg, &msg);
371 if (*msg == '\0') 394 if (*msg == '\0')
372 cmm_set_pages(pages); 395 cmm_set_pages(nr);
373 } else if (strncmp(msg, "RELEASE", 7) == 0) { 396 } else if (strncmp(msg, "RELEASE", 7) == 0) {
374 if (!cmm_skip_blanks(msg + 7, &msg)) 397 if (!cmm_skip_blanks(msg + 7, &msg))
375 return; 398 return;
376 pages = simple_strtoul(msg, &msg, 0); 399 nr = simple_strtoul(msg, &msg, 0);
377 cmm_skip_blanks(msg, &msg); 400 cmm_skip_blanks(msg, &msg);
378 if (*msg == '\0') 401 if (*msg == '\0')
379 cmm_add_timed_pages(pages); 402 cmm_add_timed_pages(nr);
380 } else if (strncmp(msg, "REUSE", 5) == 0) { 403 } else if (strncmp(msg, "REUSE", 5) == 0) {
381 if (!cmm_skip_blanks(msg + 5, &msg)) 404 if (!cmm_skip_blanks(msg + 5, &msg))
382 return; 405 return;
383 pages = simple_strtoul(msg, &msg, 0); 406 nr = simple_strtoul(msg, &msg, 0);
384 if (!cmm_skip_blanks(msg, &msg)) 407 if (!cmm_skip_blanks(msg, &msg))
385 return; 408 return;
386 seconds = simple_strtoul(msg, &msg, 0); 409 seconds = simple_strtoul(msg, &msg, 0);
387 cmm_skip_blanks(msg, &msg); 410 cmm_skip_blanks(msg, &msg);
388 if (*msg == '\0') 411 if (*msg == '\0')
389 cmm_set_timeout(pages, seconds); 412 cmm_set_timeout(nr, seconds);
390 } 413 }
391} 414}
392#endif 415#endif
@@ -396,21 +419,49 @@ struct ctl_table_header *cmm_sysctl_header;
396static int 419static int
397cmm_init (void) 420cmm_init (void)
398{ 421{
422 int rc = -ENOMEM;
423
399#ifdef CONFIG_CMM_PROC 424#ifdef CONFIG_CMM_PROC
400 cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1); 425 cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
426 if (!cmm_sysctl_header)
427 goto out;
401#endif 428#endif
402#ifdef CONFIG_CMM_IUCV 429#ifdef CONFIG_CMM_IUCV
403 smsg_register_callback(SMSG_PREFIX, cmm_smsg_target); 430 rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
431 if (rc < 0)
432 goto out_smsg;
404#endif 433#endif
405 INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, NULL); 434 rc = register_oom_notifier(&cmm_oom_nb);
435 if (rc < 0)
436 goto out_oom_notify;
406 init_waitqueue_head(&cmm_thread_wait); 437 init_waitqueue_head(&cmm_thread_wait);
407 init_timer(&cmm_timer); 438 init_timer(&cmm_timer);
408 return 0; 439 cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
440 rc = IS_ERR(cmm_thread_ptr) ? PTR_ERR(cmm_thread_ptr) : 0;
441 if (!rc)
442 goto out;
443 /*
444 * kthread_create failed. undo all the stuff from above again.
445 */
446 unregister_oom_notifier(&cmm_oom_nb);
447
448out_oom_notify:
449#ifdef CONFIG_CMM_IUCV
450 smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
451out_smsg:
452#endif
453#ifdef CONFIG_CMM_PROC
454 unregister_sysctl_table(cmm_sysctl_header);
455#endif
456out:
457 return rc;
409} 458}
410 459
411static void 460static void
412cmm_exit(void) 461cmm_exit(void)
413{ 462{
463 kthread_stop(cmm_thread_ptr);
464 unregister_oom_notifier(&cmm_oom_nb);
414 cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list); 465 cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
415 cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list); 466 cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
416#ifdef CONFIG_CMM_PROC 467#ifdef CONFIG_CMM_PROC