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