[S390] Fix sparse warnings.
[deliverable/linux.git] / arch / s390 / mm / cmm.c
CommitLineData
1da177e4
LT
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
1da177e4
LT
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
15439d74 22static char *sender = "VMRMSVM";
447570cf 23module_param(sender, charp, 0400);
15439d74
MS
24MODULE_PARM_DESC(sender,
25 "Guest name that may send SMSG messages (default VMRMSVM)");
26
1da177e4
LT
27#include "../../../drivers/s390/net/smsgiucv.h"
28
29#define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2)
30
31struct cmm_page_array {
32 struct cmm_page_array *next;
33 unsigned long index;
34 unsigned long pages[CMM_NR_PAGES];
35};
36
37static long cmm_pages = 0;
38static long cmm_timed_pages = 0;
39static volatile long cmm_pages_target = 0;
40static volatile long cmm_timed_pages_target = 0;
41static long cmm_timeout_pages = 0;
42static long cmm_timeout_seconds = 0;
43
aaedd944
AV
44static struct cmm_page_array *cmm_page_list = NULL;
45static struct cmm_page_array *cmm_timed_page_list = NULL;
1da177e4
LT
46
47static unsigned long cmm_thread_active = 0;
48static struct work_struct cmm_thread_starter;
49static wait_queue_head_t cmm_thread_wait;
50static struct timer_list cmm_timer;
51
52static void cmm_timer_fn(unsigned long);
53static void cmm_set_timer(void);
54
55static long
56cmm_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
71static long
72cmm_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
102static void
103cmm_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
124static int
125cmm_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
161static void
162cmm_start_thread(void)
163{
d2c993d8 164 kernel_thread(cmm_thread, NULL, 0);
1da177e4
LT
165}
166
167static void
168cmm_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
175static void
176cmm_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
193static void
194cmm_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
207void
208cmm_set_pages(long pages)
209{
210 cmm_pages_target = pages;
211 cmm_kick_thread();
212}
213
214long
215cmm_get_pages(void)
216{
217 return cmm_pages;
218}
219
220void
221cmm_add_timed_pages(long pages)
222{
223 cmm_timed_pages_target += pages;
224 cmm_kick_thread();
225}
226
227long
228cmm_get_timed_pages(void)
229{
230 return cmm_timed_pages;
231}
232
233void
234cmm_set_timeout(long pages, long seconds)
235{
236 cmm_timeout_pages = pages;
237 cmm_timeout_seconds = seconds;
238 cmm_set_timer();
239}
240
241static inline int
242cmm_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
257static struct ctl_table cmm_table[];
258
259static int
260cmm_pages_handler(ctl_table *ctl, int write, struct file *filp,
aaedd944 261 void __user *buffer, size_t *lenp, loff_t *ppos)
1da177e4
LT
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
300static int
301cmm_timeout_handler(ctl_table *ctl, int write, struct file *filp,
aaedd944 302 void __user *buffer, size_t *lenp, loff_t *ppos)
1da177e4
LT
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
337static struct ctl_table cmm_table[] = {
338 {
339 .ctl_name = VM_CMM_PAGES,
340 .procname = "cmm_pages",
5e8b1c40 341 .mode = 0644,
1da177e4
LT
342 .proc_handler = &cmm_pages_handler,
343 },
344 {
345 .ctl_name = VM_CMM_TIMED_PAGES,
346 .procname = "cmm_timed_pages",
5e8b1c40 347 .mode = 0644,
1da177e4
LT
348 .proc_handler = &cmm_pages_handler,
349 },
350 {
351 .ctl_name = VM_CMM_TIMEOUT,
352 .procname = "cmm_timeout",
5e8b1c40 353 .mode = 0644,
1da177e4
LT
354 .proc_handler = &cmm_timeout_handler,
355 },
356 { .ctl_name = 0 }
357};
358
359static 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"
373static void
15439d74 374cmm_smsg_target(char *from, char *msg)
1da177e4
LT
375{
376 long pages, seconds;
377
15439d74
MS
378 if (strlen(sender) > 0 && strcmp(from, sender) != 0)
379 return;
1da177e4
LT
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
410struct ctl_table_header *cmm_sysctl_header;
411
412static int
413cmm_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
aaedd944 421 INIT_WORK(&cmm_thread_starter, (void *) cmm_start_thread, NULL);
1da177e4
LT
422 init_waitqueue_head(&cmm_thread_wait);
423 init_timer(&cmm_timer);
424 return 0;
425}
426
427static void
428cmm_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
440module_init(cmm_init);
441module_exit(cmm_exit);
442
443EXPORT_SYMBOL(cmm_set_pages);
444EXPORT_SYMBOL(cmm_get_pages);
445EXPORT_SYMBOL(cmm_add_timed_pages);
446EXPORT_SYMBOL(cmm_get_timed_pages);
447EXPORT_SYMBOL(cmm_set_timeout);
448
449MODULE_LICENSE("GPL");
This page took 0.158741 seconds and 5 git commands to generate.