xref: /openbmc/linux/arch/s390/mm/cmm.c (revision a36954f5)
1 /*
2  *  Collaborative memory management interface.
3  *
4  *    Copyright IBM Corp 2003, 2010
5  *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
6  *
7  */
8 
9 #include <linux/errno.h>
10 #include <linux/fs.h>
11 #include <linux/init.h>
12 #include <linux/module.h>
13 #include <linux/moduleparam.h>
14 #include <linux/gfp.h>
15 #include <linux/sched.h>
16 #include <linux/sysctl.h>
17 #include <linux/ctype.h>
18 #include <linux/swap.h>
19 #include <linux/kthread.h>
20 #include <linux/oom.h>
21 #include <linux/suspend.h>
22 #include <linux/uaccess.h>
23 
24 #include <asm/pgalloc.h>
25 #include <asm/diag.h>
26 
27 #ifdef CONFIG_CMM_IUCV
28 static char *cmm_default_sender = "VMRMSVM";
29 #endif
30 static char *sender;
31 module_param(sender, charp, 0400);
32 MODULE_PARM_DESC(sender,
33 		 "Guest name that may send SMSG messages (default VMRMSVM)");
34 
35 #include "../../../drivers/s390/net/smsgiucv.h"
36 
37 #define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2)
38 
39 struct cmm_page_array {
40 	struct cmm_page_array *next;
41 	unsigned long index;
42 	unsigned long pages[CMM_NR_PAGES];
43 };
44 
45 static long cmm_pages;
46 static long cmm_timed_pages;
47 static volatile long cmm_pages_target;
48 static volatile long cmm_timed_pages_target;
49 static long cmm_timeout_pages;
50 static long cmm_timeout_seconds;
51 static int cmm_suspended;
52 
53 static struct cmm_page_array *cmm_page_list;
54 static struct cmm_page_array *cmm_timed_page_list;
55 static DEFINE_SPINLOCK(cmm_lock);
56 
57 static struct task_struct *cmm_thread_ptr;
58 static DECLARE_WAIT_QUEUE_HEAD(cmm_thread_wait);
59 static DEFINE_TIMER(cmm_timer, NULL, 0, 0);
60 
61 static void cmm_timer_fn(unsigned long);
62 static void cmm_set_timer(void);
63 
64 static long cmm_alloc_pages(long nr, long *counter,
65 			    struct cmm_page_array **list)
66 {
67 	struct cmm_page_array *pa, *npa;
68 	unsigned long addr;
69 
70 	while (nr) {
71 		addr = __get_free_page(GFP_NOIO);
72 		if (!addr)
73 			break;
74 		spin_lock(&cmm_lock);
75 		pa = *list;
76 		if (!pa || pa->index >= CMM_NR_PAGES) {
77 			/* Need a new page for the page list. */
78 			spin_unlock(&cmm_lock);
79 			npa = (struct cmm_page_array *)
80 				__get_free_page(GFP_NOIO);
81 			if (!npa) {
82 				free_page(addr);
83 				break;
84 			}
85 			spin_lock(&cmm_lock);
86 			pa = *list;
87 			if (!pa || pa->index >= CMM_NR_PAGES) {
88 				npa->next = pa;
89 				npa->index = 0;
90 				pa = npa;
91 				*list = pa;
92 			} else
93 				free_page((unsigned long) npa);
94 		}
95 		diag10_range(addr >> PAGE_SHIFT, 1);
96 		pa->pages[pa->index++] = addr;
97 		(*counter)++;
98 		spin_unlock(&cmm_lock);
99 		nr--;
100 	}
101 	return nr;
102 }
103 
104 static long cmm_free_pages(long nr, long *counter, struct cmm_page_array **list)
105 {
106 	struct cmm_page_array *pa;
107 	unsigned long addr;
108 
109 	spin_lock(&cmm_lock);
110 	pa = *list;
111 	while (nr) {
112 		if (!pa || pa->index <= 0)
113 			break;
114 		addr = pa->pages[--pa->index];
115 		if (pa->index == 0) {
116 			pa = pa->next;
117 			free_page((unsigned long) *list);
118 			*list = pa;
119 		}
120 		free_page(addr);
121 		(*counter)--;
122 		nr--;
123 	}
124 	spin_unlock(&cmm_lock);
125 	return nr;
126 }
127 
128 static int cmm_oom_notify(struct notifier_block *self,
129 			  unsigned long dummy, void *parm)
130 {
131 	unsigned long *freed = parm;
132 	long nr = 256;
133 
134 	nr = cmm_free_pages(nr, &cmm_timed_pages, &cmm_timed_page_list);
135 	if (nr > 0)
136 		nr = cmm_free_pages(nr, &cmm_pages, &cmm_page_list);
137 	cmm_pages_target = cmm_pages;
138 	cmm_timed_pages_target = cmm_timed_pages;
139 	*freed += 256 - nr;
140 	return NOTIFY_OK;
141 }
142 
143 static struct notifier_block cmm_oom_nb = {
144 	.notifier_call = cmm_oom_notify,
145 };
146 
147 static int cmm_thread(void *dummy)
148 {
149 	int rc;
150 
151 	while (1) {
152 		rc = wait_event_interruptible(cmm_thread_wait,
153 			(!cmm_suspended && (cmm_pages != cmm_pages_target ||
154 			 cmm_timed_pages != cmm_timed_pages_target)) ||
155 			 kthread_should_stop());
156 		if (kthread_should_stop() || rc == -ERESTARTSYS) {
157 			cmm_pages_target = cmm_pages;
158 			cmm_timed_pages_target = cmm_timed_pages;
159 			break;
160 		}
161 		if (cmm_pages_target > cmm_pages) {
162 			if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list))
163 				cmm_pages_target = cmm_pages;
164 		} else if (cmm_pages_target < cmm_pages) {
165 			cmm_free_pages(1, &cmm_pages, &cmm_page_list);
166 		}
167 		if (cmm_timed_pages_target > cmm_timed_pages) {
168 			if (cmm_alloc_pages(1, &cmm_timed_pages,
169 					   &cmm_timed_page_list))
170 				cmm_timed_pages_target = cmm_timed_pages;
171 		} else if (cmm_timed_pages_target < cmm_timed_pages) {
172 			cmm_free_pages(1, &cmm_timed_pages,
173 				       &cmm_timed_page_list);
174 		}
175 		if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer))
176 			cmm_set_timer();
177 	}
178 	return 0;
179 }
180 
181 static void cmm_kick_thread(void)
182 {
183 	wake_up(&cmm_thread_wait);
184 }
185 
186 static void cmm_set_timer(void)
187 {
188 	if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) {
189 		if (timer_pending(&cmm_timer))
190 			del_timer(&cmm_timer);
191 		return;
192 	}
193 	if (timer_pending(&cmm_timer)) {
194 		if (mod_timer(&cmm_timer, jiffies + cmm_timeout_seconds*HZ))
195 			return;
196 	}
197 	cmm_timer.function = cmm_timer_fn;
198 	cmm_timer.data = 0;
199 	cmm_timer.expires = jiffies + cmm_timeout_seconds*HZ;
200 	add_timer(&cmm_timer);
201 }
202 
203 static void cmm_timer_fn(unsigned long ignored)
204 {
205 	long nr;
206 
207 	nr = cmm_timed_pages_target - cmm_timeout_pages;
208 	if (nr < 0)
209 		cmm_timed_pages_target = 0;
210 	else
211 		cmm_timed_pages_target = nr;
212 	cmm_kick_thread();
213 	cmm_set_timer();
214 }
215 
216 static void cmm_set_pages(long nr)
217 {
218 	cmm_pages_target = nr;
219 	cmm_kick_thread();
220 }
221 
222 static long cmm_get_pages(void)
223 {
224 	return cmm_pages;
225 }
226 
227 static void cmm_add_timed_pages(long nr)
228 {
229 	cmm_timed_pages_target += nr;
230 	cmm_kick_thread();
231 }
232 
233 static long cmm_get_timed_pages(void)
234 {
235 	return cmm_timed_pages;
236 }
237 
238 static void cmm_set_timeout(long nr, long seconds)
239 {
240 	cmm_timeout_pages = nr;
241 	cmm_timeout_seconds = seconds;
242 	cmm_set_timer();
243 }
244 
245 static int cmm_skip_blanks(char *cp, char **endp)
246 {
247 	char *str;
248 
249 	for (str = cp; *str == ' ' || *str == '\t'; str++)
250 		;
251 	*endp = str;
252 	return str != cp;
253 }
254 
255 static struct ctl_table cmm_table[];
256 
257 static int cmm_pages_handler(struct ctl_table *ctl, int write,
258 			     void __user *buffer, size_t *lenp, loff_t *ppos)
259 {
260 	char buf[16], *p;
261 	unsigned int len;
262 	long nr;
263 
264 	if (!*lenp || (*ppos && !write)) {
265 		*lenp = 0;
266 		return 0;
267 	}
268 
269 	if (write) {
270 		len = *lenp;
271 		if (copy_from_user(buf, buffer,
272 				   len > sizeof(buf) ? sizeof(buf) : len))
273 			return -EFAULT;
274 		buf[sizeof(buf) - 1] = '\0';
275 		cmm_skip_blanks(buf, &p);
276 		nr = simple_strtoul(p, &p, 0);
277 		if (ctl == &cmm_table[0])
278 			cmm_set_pages(nr);
279 		else
280 			cmm_add_timed_pages(nr);
281 	} else {
282 		if (ctl == &cmm_table[0])
283 			nr = cmm_get_pages();
284 		else
285 			nr = cmm_get_timed_pages();
286 		len = sprintf(buf, "%ld\n", nr);
287 		if (len > *lenp)
288 			len = *lenp;
289 		if (copy_to_user(buffer, buf, len))
290 			return -EFAULT;
291 	}
292 	*lenp = len;
293 	*ppos += len;
294 	return 0;
295 }
296 
297 static int cmm_timeout_handler(struct ctl_table *ctl, int write,
298 			       void __user *buffer, size_t *lenp, loff_t *ppos)
299 {
300 	char buf[64], *p;
301 	long nr, seconds;
302 	unsigned int len;
303 
304 	if (!*lenp || (*ppos && !write)) {
305 		*lenp = 0;
306 		return 0;
307 	}
308 
309 	if (write) {
310 		len = *lenp;
311 		if (copy_from_user(buf, buffer,
312 				   len > sizeof(buf) ? sizeof(buf) : len))
313 			return -EFAULT;
314 		buf[sizeof(buf) - 1] = '\0';
315 		cmm_skip_blanks(buf, &p);
316 		nr = simple_strtoul(p, &p, 0);
317 		cmm_skip_blanks(p, &p);
318 		seconds = simple_strtoul(p, &p, 0);
319 		cmm_set_timeout(nr, seconds);
320 	} else {
321 		len = sprintf(buf, "%ld %ld\n",
322 			      cmm_timeout_pages, cmm_timeout_seconds);
323 		if (len > *lenp)
324 			len = *lenp;
325 		if (copy_to_user(buffer, buf, len))
326 			return -EFAULT;
327 	}
328 	*lenp = len;
329 	*ppos += len;
330 	return 0;
331 }
332 
333 static struct ctl_table cmm_table[] = {
334 	{
335 		.procname	= "cmm_pages",
336 		.mode		= 0644,
337 		.proc_handler	= cmm_pages_handler,
338 	},
339 	{
340 		.procname	= "cmm_timed_pages",
341 		.mode		= 0644,
342 		.proc_handler	= cmm_pages_handler,
343 	},
344 	{
345 		.procname	= "cmm_timeout",
346 		.mode		= 0644,
347 		.proc_handler	= cmm_timeout_handler,
348 	},
349 	{ }
350 };
351 
352 static struct ctl_table cmm_dir_table[] = {
353 	{
354 		.procname	= "vm",
355 		.maxlen		= 0,
356 		.mode		= 0555,
357 		.child		= cmm_table,
358 	},
359 	{ }
360 };
361 
362 #ifdef CONFIG_CMM_IUCV
363 #define SMSG_PREFIX "CMM"
364 static void cmm_smsg_target(const char *from, char *msg)
365 {
366 	long nr, seconds;
367 
368 	if (strlen(sender) > 0 && strcmp(from, sender) != 0)
369 		return;
370 	if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg))
371 		return;
372 	if (strncmp(msg, "SHRINK", 6) == 0) {
373 		if (!cmm_skip_blanks(msg + 6, &msg))
374 			return;
375 		nr = simple_strtoul(msg, &msg, 0);
376 		cmm_skip_blanks(msg, &msg);
377 		if (*msg == '\0')
378 			cmm_set_pages(nr);
379 	} else if (strncmp(msg, "RELEASE", 7) == 0) {
380 		if (!cmm_skip_blanks(msg + 7, &msg))
381 			return;
382 		nr = simple_strtoul(msg, &msg, 0);
383 		cmm_skip_blanks(msg, &msg);
384 		if (*msg == '\0')
385 			cmm_add_timed_pages(nr);
386 	} else if (strncmp(msg, "REUSE", 5) == 0) {
387 		if (!cmm_skip_blanks(msg + 5, &msg))
388 			return;
389 		nr = simple_strtoul(msg, &msg, 0);
390 		if (!cmm_skip_blanks(msg, &msg))
391 			return;
392 		seconds = simple_strtoul(msg, &msg, 0);
393 		cmm_skip_blanks(msg, &msg);
394 		if (*msg == '\0')
395 			cmm_set_timeout(nr, seconds);
396 	}
397 }
398 #endif
399 
400 static struct ctl_table_header *cmm_sysctl_header;
401 
402 static int cmm_suspend(void)
403 {
404 	cmm_suspended = 1;
405 	cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
406 	cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
407 	return 0;
408 }
409 
410 static int cmm_resume(void)
411 {
412 	cmm_suspended = 0;
413 	cmm_kick_thread();
414 	return 0;
415 }
416 
417 static int cmm_power_event(struct notifier_block *this,
418 			   unsigned long event, void *ptr)
419 {
420 	switch (event) {
421 	case PM_POST_HIBERNATION:
422 		return cmm_resume();
423 	case PM_HIBERNATION_PREPARE:
424 		return cmm_suspend();
425 	default:
426 		return NOTIFY_DONE;
427 	}
428 }
429 
430 static struct notifier_block cmm_power_notifier = {
431 	.notifier_call = cmm_power_event,
432 };
433 
434 static int __init cmm_init(void)
435 {
436 	int rc = -ENOMEM;
437 
438 	cmm_sysctl_header = register_sysctl_table(cmm_dir_table);
439 	if (!cmm_sysctl_header)
440 		goto out_sysctl;
441 #ifdef CONFIG_CMM_IUCV
442 	/* convert sender to uppercase characters */
443 	if (sender) {
444 		int len = strlen(sender);
445 		while (len--)
446 			sender[len] = toupper(sender[len]);
447 	} else {
448 		sender = cmm_default_sender;
449 	}
450 
451 	rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
452 	if (rc < 0)
453 		goto out_smsg;
454 #endif
455 	rc = register_oom_notifier(&cmm_oom_nb);
456 	if (rc < 0)
457 		goto out_oom_notify;
458 	rc = register_pm_notifier(&cmm_power_notifier);
459 	if (rc)
460 		goto out_pm;
461 	cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
462 	if (!IS_ERR(cmm_thread_ptr))
463 		return 0;
464 
465 	rc = PTR_ERR(cmm_thread_ptr);
466 	unregister_pm_notifier(&cmm_power_notifier);
467 out_pm:
468 	unregister_oom_notifier(&cmm_oom_nb);
469 out_oom_notify:
470 #ifdef CONFIG_CMM_IUCV
471 	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
472 out_smsg:
473 #endif
474 	unregister_sysctl_table(cmm_sysctl_header);
475 out_sysctl:
476 	del_timer_sync(&cmm_timer);
477 	return rc;
478 }
479 module_init(cmm_init);
480 
481 static void __exit cmm_exit(void)
482 {
483 	unregister_sysctl_table(cmm_sysctl_header);
484 #ifdef CONFIG_CMM_IUCV
485 	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
486 #endif
487 	unregister_pm_notifier(&cmm_power_notifier);
488 	unregister_oom_notifier(&cmm_oom_nb);
489 	kthread_stop(cmm_thread_ptr);
490 	del_timer_sync(&cmm_timer);
491 	cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
492 	cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
493 }
494 module_exit(cmm_exit);
495 
496 MODULE_LICENSE("GPL");
497