[PATCH] Make UML use ptrace-abi.h
[sfrench/cifs-2.6.git] / arch / s390 / mm / cmm.c
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
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;
39 static long cmm_timed_pages;
40 static volatile long cmm_pages_target;
41 static volatile long cmm_timed_pages_target;
42 static long cmm_timeout_pages;
43 static long cmm_timeout_seconds;
44
45 static struct cmm_page_array *cmm_page_list;
46 static struct cmm_page_array *cmm_timed_page_list;
47 static DEFINE_SPINLOCK(cmm_lock);
48
49 static unsigned long cmm_thread_active;
50 static struct work_struct cmm_thread_starter;
51 static wait_queue_head_t cmm_thread_wait;
52 static struct timer_list cmm_timer;
53
54 static void cmm_timer_fn(unsigned long);
55 static void cmm_set_timer(void);
56
57 static long
58 cmm_alloc_pages(long nr, long *counter, struct cmm_page_array **list)
59 {
60         struct cmm_page_array *pa, *npa;
61         unsigned long addr;
62
63         while (nr) {
64                 addr = __get_free_page(GFP_NOIO);
65                 if (!addr)
66                         break;
67                 spin_lock(&cmm_lock);
68                 pa = *list;
69                 if (!pa || pa->index >= CMM_NR_PAGES) {
70                         /* Need a new page for the page list. */
71                         spin_unlock(&cmm_lock);
72                         npa = (struct cmm_page_array *)
73                                 __get_free_page(GFP_NOIO);
74                         if (!npa) {
75                                 free_page(addr);
76                                 break;
77                         }
78                         spin_lock(&cmm_lock);
79                         pa = *list;
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);
87                 }
88                 diag10(addr);
89                 pa->pages[pa->index++] = addr;
90                 (*counter)++;
91                 spin_unlock(&cmm_lock);
92                 nr--;
93         }
94         return nr;
95 }
96
97 static long
98 cmm_free_pages(long nr, long *counter, struct cmm_page_array **list)
99 {
100         struct cmm_page_array *pa;
101         unsigned long addr;
102
103         spin_lock(&cmm_lock);
104         pa = *list;
105         while (nr) {
106                 if (!pa || pa->index <= 0)
107                         break;
108                 addr = pa->pages[--pa->index];
109                 if (pa->index == 0) {
110                         pa = pa->next;
111                         free_page((unsigned long) *list);
112                         *list = pa;
113                 }
114                 free_page(addr);
115                 (*counter)--;
116                 nr--;
117         }
118         spin_unlock(&cmm_lock);
119         return nr;
120 }
121
122 static 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
137 static struct notifier_block cmm_oom_nb = {
138         .notifier_call = cmm_oom_notify
139 };
140
141 static int
142 cmm_thread(void *dummy)
143 {
144         int rc;
145
146         daemonize("cmmthread");
147         while (1) {
148                 rc = wait_event_interruptible(cmm_thread_wait,
149                         (cmm_pages != cmm_pages_target ||
150                          cmm_timed_pages != cmm_timed_pages_target));
151                 if (rc == -ERESTARTSYS) {
152                         /* Got kill signal. End thread. */
153                         clear_bit(0, &cmm_thread_active);
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_start_thread(void)
180 {
181         kernel_thread(cmm_thread, NULL, 0);
182 }
183
184 static void
185 cmm_kick_thread(void)
186 {
187         if (!test_and_set_bit(0, &cmm_thread_active))
188                 schedule_work(&cmm_thread_starter);
189         wake_up(&cmm_thread_wait);
190 }
191
192 static void
193 cmm_set_timer(void)
194 {
195         if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) {
196                 if (timer_pending(&cmm_timer))
197                         del_timer(&cmm_timer);
198                 return;
199         }
200         if (timer_pending(&cmm_timer)) {
201                 if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ))
202                         return;
203         }
204         cmm_timer.function = cmm_timer_fn;
205         cmm_timer.data = 0;
206         cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ;
207         add_timer(&cmm_timer);
208 }
209
210 static void
211 cmm_timer_fn(unsigned long ignored)
212 {
213         long nr;
214
215         nr = cmm_timed_pages_target - cmm_timeout_pages;
216         if (nr < 0)
217                 cmm_timed_pages_target = 0;
218         else
219                 cmm_timed_pages_target = nr;
220         cmm_kick_thread();
221         cmm_set_timer();
222 }
223
224 void
225 cmm_set_pages(long nr)
226 {
227         cmm_pages_target = nr;
228         cmm_kick_thread();
229 }
230
231 long
232 cmm_get_pages(void)
233 {
234         return cmm_pages;
235 }
236
237 void
238 cmm_add_timed_pages(long nr)
239 {
240         cmm_timed_pages_target += nr;
241         cmm_kick_thread();
242 }
243
244 long
245 cmm_get_timed_pages(void)
246 {
247         return cmm_timed_pages;
248 }
249
250 void
251 cmm_set_timeout(long nr, long seconds)
252 {
253         cmm_timeout_pages = nr;
254         cmm_timeout_seconds = seconds;
255         cmm_set_timer();
256 }
257
258 static inline int
259 cmm_skip_blanks(char *cp, char **endp)
260 {
261         char *str;
262
263         for (str = cp; *str == ' ' || *str == '\t'; str++);
264         *endp = str;
265         return str != cp;
266 }
267
268 #ifdef CONFIG_CMM_PROC
269 /* These will someday get removed. */
270 #define VM_CMM_PAGES            1111
271 #define VM_CMM_TIMED_PAGES      1112
272 #define VM_CMM_TIMEOUT          1113
273
274 static struct ctl_table cmm_table[];
275
276 static int
277 cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
278                   void __user *buffer, size_t *lenp, loff_t *ppos)
279 {
280         char buf[16], *p;
281         long nr;
282         int len;
283
284         if (!*lenp || (*ppos && !write)) {
285                 *lenp = 0;
286                 return 0;
287         }
288
289         if (write) {
290                 len = *lenp;
291                 if (copy_from_user(buf, buffer,
292                                    len > sizeof(buf) ? sizeof(buf) : len))
293                         return -EFAULT;
294                 buf[sizeof(buf) - 1] = '\0';
295                 cmm_skip_blanks(buf, &p);
296                 nr = simple_strtoul(p, &p, 0);
297                 if (ctl == &cmm_table[0])
298                         cmm_set_pages(nr);
299                 else
300                         cmm_add_timed_pages(nr);
301         } else {
302                 if (ctl == &cmm_table[0])
303                         nr = cmm_get_pages();
304                 else
305                         nr = cmm_get_timed_pages();
306                 len = sprintf(buf, "%ld\n", nr);
307                 if (len > *lenp)
308                         len = *lenp;
309                 if (copy_to_user(buffer, buf, len))
310                         return -EFAULT;
311         }
312         *lenp = len;
313         *ppos += len;
314         return 0;
315 }
316
317 static int
318 cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
319                     void __user *buffer, size_t *lenp, loff_t *ppos)
320 {
321         char buf[64], *p;
322         long nr, seconds;
323         int len;
324
325         if (!*lenp || (*ppos && !write)) {
326                 *lenp = 0;
327                 return 0;
328         }
329
330         if (write) {
331                 len = *lenp;
332                 if (copy_from_user(buf, buffer,
333                                    len > sizeof(buf) ? sizeof(buf) : len))
334                         return -EFAULT;
335                 buf[sizeof(buf) - 1] = '\0';
336                 cmm_skip_blanks(buf, &p);
337                 nr = simple_strtoul(p, &p, 0);
338                 cmm_skip_blanks(p, &p);
339                 seconds = simple_strtoul(p, &p, 0);
340                 cmm_set_timeout(nr, seconds);
341         } else {
342                 len = sprintf(buf, "%ld %ld\n",
343                               cmm_timeout_pages, cmm_timeout_seconds);
344                 if (len > *lenp)
345                         len = *lenp;
346                 if (copy_to_user(buffer, buf, len))
347                         return -EFAULT;
348         }
349         *lenp = len;
350         *ppos += len;
351         return 0;
352 }
353
354 static struct ctl_table cmm_table[] = {
355         {
356                 .ctl_name       = VM_CMM_PAGES,
357                 .procname       = "cmm_pages",
358                 .mode           = 0644,
359                 .proc_handler   = &cmm_pages_handler,
360         },
361         {
362                 .ctl_name       = VM_CMM_TIMED_PAGES,
363                 .procname       = "cmm_timed_pages",
364                 .mode           = 0644,
365                 .proc_handler   = &cmm_pages_handler,
366         },
367         {
368                 .ctl_name       = VM_CMM_TIMEOUT,
369                 .procname       = "cmm_timeout",
370                 .mode           = 0644,
371                 .proc_handler   = &cmm_timeout_handler,
372         },
373         { .ctl_name = 0 }
374 };
375
376 static struct ctl_table cmm_dir_table[] = {
377         {
378                 .ctl_name       = CTL_VM,
379                 .procname       = "vm",
380                 .maxlen         = 0,
381                 .mode           = 0555,
382                 .child          = cmm_table,
383         },
384         { .ctl_name = 0 }
385 };
386 #endif
387
388 #ifdef CONFIG_CMM_IUCV
389 #define SMSG_PREFIX "CMM"
390 static void
391 cmm_smsg_target(char *from, char *msg)
392 {
393         long nr, seconds;
394
395         if (strlen(sender) > 0 && strcmp(from, sender) != 0)
396                 return;
397         if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg))
398                 return;
399         if (strncmp(msg, "SHRINK", 6) == 0) {
400                 if (!cmm_skip_blanks(msg + 6, &msg))
401                         return;
402                 nr = simple_strtoul(msg, &msg, 0);
403                 cmm_skip_blanks(msg, &msg);
404                 if (*msg == '\0')
405                         cmm_set_pages(nr);
406         } else if (strncmp(msg, "RELEASE", 7) == 0) {
407                 if (!cmm_skip_blanks(msg + 7, &msg))
408                         return;
409                 nr = simple_strtoul(msg, &msg, 0);
410                 cmm_skip_blanks(msg, &msg);
411                 if (*msg == '\0')
412                         cmm_add_timed_pages(nr);
413         } else if (strncmp(msg, "REUSE", 5) == 0) {
414                 if (!cmm_skip_blanks(msg + 5, &msg))
415                         return;
416                 nr = simple_strtoul(msg, &msg, 0);
417                 if (!cmm_skip_blanks(msg, &msg))
418                         return;
419                 seconds = simple_strtoul(msg, &msg, 0);
420                 cmm_skip_blanks(msg, &msg);
421                 if (*msg == '\0')
422                         cmm_set_timeout(nr, seconds);
423         }
424 }
425 #endif
426
427 struct ctl_table_header *cmm_sysctl_header;
428
429 static int
430 cmm_init (void)
431 {
432 #ifdef CONFIG_CMM_PROC
433         cmm_sysctl_header = register_sysctl_table(cmm_dir_table, 1);
434 #endif
435 #ifdef CONFIG_CMM_IUCV
436         smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
437 #endif
438         register_oom_notifier(&cmm_oom_nb);
439         INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, NULL);
440         init_waitqueue_head(&cmm_thread_wait);
441         init_timer(&cmm_timer);
442         return 0;
443 }
444
445 static void
446 cmm_exit(void)
447 {
448         unregister_oom_notifier(&cmm_oom_nb);
449         cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
450         cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
451 #ifdef CONFIG_CMM_PROC
452         unregister_sysctl_table(cmm_sysctl_header);
453 #endif
454 #ifdef CONFIG_CMM_IUCV
455         smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
456 #endif
457 }
458
459 module_init(cmm_init);
460 module_exit(cmm_exit);
461
462 EXPORT_SYMBOL(cmm_set_pages);
463 EXPORT_SYMBOL(cmm_get_pages);
464 EXPORT_SYMBOL(cmm_add_timed_pages);
465 EXPORT_SYMBOL(cmm_get_timed_pages);
466 EXPORT_SYMBOL(cmm_set_timeout);
467
468 MODULE_LICENSE("GPL");