18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Collaborative memory management interface.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp 2003, 2010
68c2ecf20Sopenharmony_ci *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include <linux/errno.h>
118c2ecf20Sopenharmony_ci#include <linux/fs.h>
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/module.h>
148c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
158c2ecf20Sopenharmony_ci#include <linux/gfp.h>
168c2ecf20Sopenharmony_ci#include <linux/sched.h>
178c2ecf20Sopenharmony_ci#include <linux/sysctl.h>
188c2ecf20Sopenharmony_ci#include <linux/ctype.h>
198c2ecf20Sopenharmony_ci#include <linux/swap.h>
208c2ecf20Sopenharmony_ci#include <linux/kthread.h>
218c2ecf20Sopenharmony_ci#include <linux/oom.h>
228c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#include <asm/diag.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
278c2ecf20Sopenharmony_cistatic char *cmm_default_sender = "VMRMSVM";
288c2ecf20Sopenharmony_ci#endif
298c2ecf20Sopenharmony_cistatic char *sender;
308c2ecf20Sopenharmony_cimodule_param(sender, charp, 0400);
318c2ecf20Sopenharmony_ciMODULE_PARM_DESC(sender,
328c2ecf20Sopenharmony_ci		 "Guest name that may send SMSG messages (default VMRMSVM)");
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci#include "../../../drivers/s390/net/smsgiucv.h"
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2)
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistruct cmm_page_array {
398c2ecf20Sopenharmony_ci	struct cmm_page_array *next;
408c2ecf20Sopenharmony_ci	unsigned long index;
418c2ecf20Sopenharmony_ci	unsigned long pages[CMM_NR_PAGES];
428c2ecf20Sopenharmony_ci};
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic long cmm_pages;
458c2ecf20Sopenharmony_cistatic long cmm_timed_pages;
468c2ecf20Sopenharmony_cistatic volatile long cmm_pages_target;
478c2ecf20Sopenharmony_cistatic volatile long cmm_timed_pages_target;
488c2ecf20Sopenharmony_cistatic long cmm_timeout_pages;
498c2ecf20Sopenharmony_cistatic long cmm_timeout_seconds;
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_cistatic struct cmm_page_array *cmm_page_list;
528c2ecf20Sopenharmony_cistatic struct cmm_page_array *cmm_timed_page_list;
538c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cmm_lock);
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic struct task_struct *cmm_thread_ptr;
568c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(cmm_thread_wait);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic void cmm_timer_fn(struct timer_list *);
598c2ecf20Sopenharmony_cistatic void cmm_set_timer(void);
608c2ecf20Sopenharmony_cistatic DEFINE_TIMER(cmm_timer, cmm_timer_fn);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic long cmm_alloc_pages(long nr, long *counter,
638c2ecf20Sopenharmony_ci			    struct cmm_page_array **list)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct cmm_page_array *pa, *npa;
668c2ecf20Sopenharmony_ci	unsigned long addr;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	while (nr) {
698c2ecf20Sopenharmony_ci		addr = __get_free_page(GFP_NOIO);
708c2ecf20Sopenharmony_ci		if (!addr)
718c2ecf20Sopenharmony_ci			break;
728c2ecf20Sopenharmony_ci		spin_lock(&cmm_lock);
738c2ecf20Sopenharmony_ci		pa = *list;
748c2ecf20Sopenharmony_ci		if (!pa || pa->index >= CMM_NR_PAGES) {
758c2ecf20Sopenharmony_ci			/* Need a new page for the page list. */
768c2ecf20Sopenharmony_ci			spin_unlock(&cmm_lock);
778c2ecf20Sopenharmony_ci			npa = (struct cmm_page_array *)
788c2ecf20Sopenharmony_ci				__get_free_page(GFP_NOIO);
798c2ecf20Sopenharmony_ci			if (!npa) {
808c2ecf20Sopenharmony_ci				free_page(addr);
818c2ecf20Sopenharmony_ci				break;
828c2ecf20Sopenharmony_ci			}
838c2ecf20Sopenharmony_ci			spin_lock(&cmm_lock);
848c2ecf20Sopenharmony_ci			pa = *list;
858c2ecf20Sopenharmony_ci			if (!pa || pa->index >= CMM_NR_PAGES) {
868c2ecf20Sopenharmony_ci				npa->next = pa;
878c2ecf20Sopenharmony_ci				npa->index = 0;
888c2ecf20Sopenharmony_ci				pa = npa;
898c2ecf20Sopenharmony_ci				*list = pa;
908c2ecf20Sopenharmony_ci			} else
918c2ecf20Sopenharmony_ci				free_page((unsigned long) npa);
928c2ecf20Sopenharmony_ci		}
938c2ecf20Sopenharmony_ci		diag10_range(addr >> PAGE_SHIFT, 1);
948c2ecf20Sopenharmony_ci		pa->pages[pa->index++] = addr;
958c2ecf20Sopenharmony_ci		(*counter)++;
968c2ecf20Sopenharmony_ci		spin_unlock(&cmm_lock);
978c2ecf20Sopenharmony_ci		nr--;
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci	return nr;
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_cistatic long cmm_free_pages(long nr, long *counter, struct cmm_page_array **list)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct cmm_page_array *pa;
1058c2ecf20Sopenharmony_ci	unsigned long addr;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	spin_lock(&cmm_lock);
1088c2ecf20Sopenharmony_ci	pa = *list;
1098c2ecf20Sopenharmony_ci	while (nr) {
1108c2ecf20Sopenharmony_ci		if (!pa || pa->index <= 0)
1118c2ecf20Sopenharmony_ci			break;
1128c2ecf20Sopenharmony_ci		addr = pa->pages[--pa->index];
1138c2ecf20Sopenharmony_ci		if (pa->index == 0) {
1148c2ecf20Sopenharmony_ci			pa = pa->next;
1158c2ecf20Sopenharmony_ci			free_page((unsigned long) *list);
1168c2ecf20Sopenharmony_ci			*list = pa;
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci		free_page(addr);
1198c2ecf20Sopenharmony_ci		(*counter)--;
1208c2ecf20Sopenharmony_ci		nr--;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	spin_unlock(&cmm_lock);
1238c2ecf20Sopenharmony_ci	return nr;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic int cmm_oom_notify(struct notifier_block *self,
1278c2ecf20Sopenharmony_ci			  unsigned long dummy, void *parm)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	unsigned long *freed = parm;
1308c2ecf20Sopenharmony_ci	long nr = 256;
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	nr = cmm_free_pages(nr, &cmm_timed_pages, &cmm_timed_page_list);
1338c2ecf20Sopenharmony_ci	if (nr > 0)
1348c2ecf20Sopenharmony_ci		nr = cmm_free_pages(nr, &cmm_pages, &cmm_page_list);
1358c2ecf20Sopenharmony_ci	cmm_pages_target = cmm_pages;
1368c2ecf20Sopenharmony_ci	cmm_timed_pages_target = cmm_timed_pages;
1378c2ecf20Sopenharmony_ci	*freed += 256 - nr;
1388c2ecf20Sopenharmony_ci	return NOTIFY_OK;
1398c2ecf20Sopenharmony_ci}
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_cistatic struct notifier_block cmm_oom_nb = {
1428c2ecf20Sopenharmony_ci	.notifier_call = cmm_oom_notify,
1438c2ecf20Sopenharmony_ci};
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int cmm_thread(void *dummy)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	int rc;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	while (1) {
1508c2ecf20Sopenharmony_ci		rc = wait_event_interruptible(cmm_thread_wait,
1518c2ecf20Sopenharmony_ci			cmm_pages != cmm_pages_target ||
1528c2ecf20Sopenharmony_ci			cmm_timed_pages != cmm_timed_pages_target ||
1538c2ecf20Sopenharmony_ci			kthread_should_stop());
1548c2ecf20Sopenharmony_ci		if (kthread_should_stop() || rc == -ERESTARTSYS) {
1558c2ecf20Sopenharmony_ci			cmm_pages_target = cmm_pages;
1568c2ecf20Sopenharmony_ci			cmm_timed_pages_target = cmm_timed_pages;
1578c2ecf20Sopenharmony_ci			break;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci		if (cmm_pages_target > cmm_pages) {
1608c2ecf20Sopenharmony_ci			if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list))
1618c2ecf20Sopenharmony_ci				cmm_pages_target = cmm_pages;
1628c2ecf20Sopenharmony_ci		} else if (cmm_pages_target < cmm_pages) {
1638c2ecf20Sopenharmony_ci			cmm_free_pages(1, &cmm_pages, &cmm_page_list);
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci		if (cmm_timed_pages_target > cmm_timed_pages) {
1668c2ecf20Sopenharmony_ci			if (cmm_alloc_pages(1, &cmm_timed_pages,
1678c2ecf20Sopenharmony_ci					   &cmm_timed_page_list))
1688c2ecf20Sopenharmony_ci				cmm_timed_pages_target = cmm_timed_pages;
1698c2ecf20Sopenharmony_ci		} else if (cmm_timed_pages_target < cmm_timed_pages) {
1708c2ecf20Sopenharmony_ci			cmm_free_pages(1, &cmm_timed_pages,
1718c2ecf20Sopenharmony_ci				       &cmm_timed_page_list);
1728c2ecf20Sopenharmony_ci		}
1738c2ecf20Sopenharmony_ci		if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer))
1748c2ecf20Sopenharmony_ci			cmm_set_timer();
1758c2ecf20Sopenharmony_ci	}
1768c2ecf20Sopenharmony_ci	return 0;
1778c2ecf20Sopenharmony_ci}
1788c2ecf20Sopenharmony_ci
1798c2ecf20Sopenharmony_cistatic void cmm_kick_thread(void)
1808c2ecf20Sopenharmony_ci{
1818c2ecf20Sopenharmony_ci	wake_up(&cmm_thread_wait);
1828c2ecf20Sopenharmony_ci}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_cistatic void cmm_set_timer(void)
1858c2ecf20Sopenharmony_ci{
1868c2ecf20Sopenharmony_ci	if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) {
1878c2ecf20Sopenharmony_ci		if (timer_pending(&cmm_timer))
1888c2ecf20Sopenharmony_ci			del_timer(&cmm_timer);
1898c2ecf20Sopenharmony_ci		return;
1908c2ecf20Sopenharmony_ci	}
1918c2ecf20Sopenharmony_ci	mod_timer(&cmm_timer, jiffies + msecs_to_jiffies(cmm_timeout_seconds * MSEC_PER_SEC));
1928c2ecf20Sopenharmony_ci}
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_cistatic void cmm_timer_fn(struct timer_list *unused)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	long nr;
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	nr = cmm_timed_pages_target - cmm_timeout_pages;
1998c2ecf20Sopenharmony_ci	if (nr < 0)
2008c2ecf20Sopenharmony_ci		cmm_timed_pages_target = 0;
2018c2ecf20Sopenharmony_ci	else
2028c2ecf20Sopenharmony_ci		cmm_timed_pages_target = nr;
2038c2ecf20Sopenharmony_ci	cmm_kick_thread();
2048c2ecf20Sopenharmony_ci	cmm_set_timer();
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_cistatic void cmm_set_pages(long nr)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	cmm_pages_target = nr;
2108c2ecf20Sopenharmony_ci	cmm_kick_thread();
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic long cmm_get_pages(void)
2148c2ecf20Sopenharmony_ci{
2158c2ecf20Sopenharmony_ci	return cmm_pages;
2168c2ecf20Sopenharmony_ci}
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_cistatic void cmm_add_timed_pages(long nr)
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	cmm_timed_pages_target += nr;
2218c2ecf20Sopenharmony_ci	cmm_kick_thread();
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_cistatic long cmm_get_timed_pages(void)
2258c2ecf20Sopenharmony_ci{
2268c2ecf20Sopenharmony_ci	return cmm_timed_pages;
2278c2ecf20Sopenharmony_ci}
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic void cmm_set_timeout(long nr, long seconds)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	cmm_timeout_pages = nr;
2328c2ecf20Sopenharmony_ci	cmm_timeout_seconds = seconds;
2338c2ecf20Sopenharmony_ci	cmm_set_timer();
2348c2ecf20Sopenharmony_ci}
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_cistatic int cmm_skip_blanks(char *cp, char **endp)
2378c2ecf20Sopenharmony_ci{
2388c2ecf20Sopenharmony_ci	char *str;
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	for (str = cp; *str == ' ' || *str == '\t'; str++)
2418c2ecf20Sopenharmony_ci		;
2428c2ecf20Sopenharmony_ci	*endp = str;
2438c2ecf20Sopenharmony_ci	return str != cp;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_cistatic int cmm_pages_handler(struct ctl_table *ctl, int write,
2478c2ecf20Sopenharmony_ci			     void *buffer, size_t *lenp, loff_t *ppos)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	long nr = cmm_get_pages();
2508c2ecf20Sopenharmony_ci	struct ctl_table ctl_entry = {
2518c2ecf20Sopenharmony_ci		.procname	= ctl->procname,
2528c2ecf20Sopenharmony_ci		.data		= &nr,
2538c2ecf20Sopenharmony_ci		.maxlen		= sizeof(long),
2548c2ecf20Sopenharmony_ci	};
2558c2ecf20Sopenharmony_ci	int rc;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	rc = proc_doulongvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
2588c2ecf20Sopenharmony_ci	if (rc < 0 || !write)
2598c2ecf20Sopenharmony_ci		return rc;
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_ci	cmm_set_pages(nr);
2628c2ecf20Sopenharmony_ci	return 0;
2638c2ecf20Sopenharmony_ci}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_cistatic int cmm_timed_pages_handler(struct ctl_table *ctl, int write,
2668c2ecf20Sopenharmony_ci				   void *buffer, size_t *lenp,
2678c2ecf20Sopenharmony_ci				   loff_t *ppos)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	long nr = cmm_get_timed_pages();
2708c2ecf20Sopenharmony_ci	struct ctl_table ctl_entry = {
2718c2ecf20Sopenharmony_ci		.procname	= ctl->procname,
2728c2ecf20Sopenharmony_ci		.data		= &nr,
2738c2ecf20Sopenharmony_ci		.maxlen		= sizeof(long),
2748c2ecf20Sopenharmony_ci	};
2758c2ecf20Sopenharmony_ci	int rc;
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_ci	rc = proc_doulongvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
2788c2ecf20Sopenharmony_ci	if (rc < 0 || !write)
2798c2ecf20Sopenharmony_ci		return rc;
2808c2ecf20Sopenharmony_ci
2818c2ecf20Sopenharmony_ci	cmm_add_timed_pages(nr);
2828c2ecf20Sopenharmony_ci	return 0;
2838c2ecf20Sopenharmony_ci}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_cistatic int cmm_timeout_handler(struct ctl_table *ctl, int write,
2868c2ecf20Sopenharmony_ci			       void *buffer, size_t *lenp, loff_t *ppos)
2878c2ecf20Sopenharmony_ci{
2888c2ecf20Sopenharmony_ci	char buf[64], *p;
2898c2ecf20Sopenharmony_ci	long nr, seconds;
2908c2ecf20Sopenharmony_ci	unsigned int len;
2918c2ecf20Sopenharmony_ci
2928c2ecf20Sopenharmony_ci	if (!*lenp || (*ppos && !write)) {
2938c2ecf20Sopenharmony_ci		*lenp = 0;
2948c2ecf20Sopenharmony_ci		return 0;
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	if (write) {
2988c2ecf20Sopenharmony_ci		len = min(*lenp, sizeof(buf));
2998c2ecf20Sopenharmony_ci		memcpy(buf, buffer, len);
3008c2ecf20Sopenharmony_ci		buf[len - 1] = '\0';
3018c2ecf20Sopenharmony_ci		cmm_skip_blanks(buf, &p);
3028c2ecf20Sopenharmony_ci		nr = simple_strtoul(p, &p, 0);
3038c2ecf20Sopenharmony_ci		cmm_skip_blanks(p, &p);
3048c2ecf20Sopenharmony_ci		seconds = simple_strtoul(p, &p, 0);
3058c2ecf20Sopenharmony_ci		cmm_set_timeout(nr, seconds);
3068c2ecf20Sopenharmony_ci		*ppos += *lenp;
3078c2ecf20Sopenharmony_ci	} else {
3088c2ecf20Sopenharmony_ci		len = sprintf(buf, "%ld %ld\n",
3098c2ecf20Sopenharmony_ci			      cmm_timeout_pages, cmm_timeout_seconds);
3108c2ecf20Sopenharmony_ci		if (len > *lenp)
3118c2ecf20Sopenharmony_ci			len = *lenp;
3128c2ecf20Sopenharmony_ci		memcpy(buffer, buf, len);
3138c2ecf20Sopenharmony_ci		*lenp = len;
3148c2ecf20Sopenharmony_ci		*ppos += len;
3158c2ecf20Sopenharmony_ci	}
3168c2ecf20Sopenharmony_ci	return 0;
3178c2ecf20Sopenharmony_ci}
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_cistatic struct ctl_table cmm_table[] = {
3208c2ecf20Sopenharmony_ci	{
3218c2ecf20Sopenharmony_ci		.procname	= "cmm_pages",
3228c2ecf20Sopenharmony_ci		.mode		= 0644,
3238c2ecf20Sopenharmony_ci		.proc_handler	= cmm_pages_handler,
3248c2ecf20Sopenharmony_ci	},
3258c2ecf20Sopenharmony_ci	{
3268c2ecf20Sopenharmony_ci		.procname	= "cmm_timed_pages",
3278c2ecf20Sopenharmony_ci		.mode		= 0644,
3288c2ecf20Sopenharmony_ci		.proc_handler	= cmm_timed_pages_handler,
3298c2ecf20Sopenharmony_ci	},
3308c2ecf20Sopenharmony_ci	{
3318c2ecf20Sopenharmony_ci		.procname	= "cmm_timeout",
3328c2ecf20Sopenharmony_ci		.mode		= 0644,
3338c2ecf20Sopenharmony_ci		.proc_handler	= cmm_timeout_handler,
3348c2ecf20Sopenharmony_ci	},
3358c2ecf20Sopenharmony_ci	{ }
3368c2ecf20Sopenharmony_ci};
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_cistatic struct ctl_table cmm_dir_table[] = {
3398c2ecf20Sopenharmony_ci	{
3408c2ecf20Sopenharmony_ci		.procname	= "vm",
3418c2ecf20Sopenharmony_ci		.maxlen		= 0,
3428c2ecf20Sopenharmony_ci		.mode		= 0555,
3438c2ecf20Sopenharmony_ci		.child		= cmm_table,
3448c2ecf20Sopenharmony_ci	},
3458c2ecf20Sopenharmony_ci	{ }
3468c2ecf20Sopenharmony_ci};
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
3498c2ecf20Sopenharmony_ci#define SMSG_PREFIX "CMM"
3508c2ecf20Sopenharmony_cistatic void cmm_smsg_target(const char *from, char *msg)
3518c2ecf20Sopenharmony_ci{
3528c2ecf20Sopenharmony_ci	long nr, seconds;
3538c2ecf20Sopenharmony_ci
3548c2ecf20Sopenharmony_ci	if (strlen(sender) > 0 && strcmp(from, sender) != 0)
3558c2ecf20Sopenharmony_ci		return;
3568c2ecf20Sopenharmony_ci	if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg))
3578c2ecf20Sopenharmony_ci		return;
3588c2ecf20Sopenharmony_ci	if (strncmp(msg, "SHRINK", 6) == 0) {
3598c2ecf20Sopenharmony_ci		if (!cmm_skip_blanks(msg + 6, &msg))
3608c2ecf20Sopenharmony_ci			return;
3618c2ecf20Sopenharmony_ci		nr = simple_strtoul(msg, &msg, 0);
3628c2ecf20Sopenharmony_ci		cmm_skip_blanks(msg, &msg);
3638c2ecf20Sopenharmony_ci		if (*msg == '\0')
3648c2ecf20Sopenharmony_ci			cmm_set_pages(nr);
3658c2ecf20Sopenharmony_ci	} else if (strncmp(msg, "RELEASE", 7) == 0) {
3668c2ecf20Sopenharmony_ci		if (!cmm_skip_blanks(msg + 7, &msg))
3678c2ecf20Sopenharmony_ci			return;
3688c2ecf20Sopenharmony_ci		nr = simple_strtoul(msg, &msg, 0);
3698c2ecf20Sopenharmony_ci		cmm_skip_blanks(msg, &msg);
3708c2ecf20Sopenharmony_ci		if (*msg == '\0')
3718c2ecf20Sopenharmony_ci			cmm_add_timed_pages(nr);
3728c2ecf20Sopenharmony_ci	} else if (strncmp(msg, "REUSE", 5) == 0) {
3738c2ecf20Sopenharmony_ci		if (!cmm_skip_blanks(msg + 5, &msg))
3748c2ecf20Sopenharmony_ci			return;
3758c2ecf20Sopenharmony_ci		nr = simple_strtoul(msg, &msg, 0);
3768c2ecf20Sopenharmony_ci		if (!cmm_skip_blanks(msg, &msg))
3778c2ecf20Sopenharmony_ci			return;
3788c2ecf20Sopenharmony_ci		seconds = simple_strtoul(msg, &msg, 0);
3798c2ecf20Sopenharmony_ci		cmm_skip_blanks(msg, &msg);
3808c2ecf20Sopenharmony_ci		if (*msg == '\0')
3818c2ecf20Sopenharmony_ci			cmm_set_timeout(nr, seconds);
3828c2ecf20Sopenharmony_ci	}
3838c2ecf20Sopenharmony_ci}
3848c2ecf20Sopenharmony_ci#endif
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_cistatic struct ctl_table_header *cmm_sysctl_header;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_cistatic int __init cmm_init(void)
3898c2ecf20Sopenharmony_ci{
3908c2ecf20Sopenharmony_ci	int rc = -ENOMEM;
3918c2ecf20Sopenharmony_ci
3928c2ecf20Sopenharmony_ci	cmm_sysctl_header = register_sysctl_table(cmm_dir_table);
3938c2ecf20Sopenharmony_ci	if (!cmm_sysctl_header)
3948c2ecf20Sopenharmony_ci		goto out_sysctl;
3958c2ecf20Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
3968c2ecf20Sopenharmony_ci	/* convert sender to uppercase characters */
3978c2ecf20Sopenharmony_ci	if (sender) {
3988c2ecf20Sopenharmony_ci		int len = strlen(sender);
3998c2ecf20Sopenharmony_ci		while (len--)
4008c2ecf20Sopenharmony_ci			sender[len] = toupper(sender[len]);
4018c2ecf20Sopenharmony_ci	} else {
4028c2ecf20Sopenharmony_ci		sender = cmm_default_sender;
4038c2ecf20Sopenharmony_ci	}
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
4068c2ecf20Sopenharmony_ci	if (rc < 0)
4078c2ecf20Sopenharmony_ci		goto out_smsg;
4088c2ecf20Sopenharmony_ci#endif
4098c2ecf20Sopenharmony_ci	rc = register_oom_notifier(&cmm_oom_nb);
4108c2ecf20Sopenharmony_ci	if (rc < 0)
4118c2ecf20Sopenharmony_ci		goto out_oom_notify;
4128c2ecf20Sopenharmony_ci	cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
4138c2ecf20Sopenharmony_ci	if (!IS_ERR(cmm_thread_ptr))
4148c2ecf20Sopenharmony_ci		return 0;
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	rc = PTR_ERR(cmm_thread_ptr);
4178c2ecf20Sopenharmony_ci	unregister_oom_notifier(&cmm_oom_nb);
4188c2ecf20Sopenharmony_ciout_oom_notify:
4198c2ecf20Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
4208c2ecf20Sopenharmony_ci	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
4218c2ecf20Sopenharmony_ciout_smsg:
4228c2ecf20Sopenharmony_ci#endif
4238c2ecf20Sopenharmony_ci	unregister_sysctl_table(cmm_sysctl_header);
4248c2ecf20Sopenharmony_ciout_sysctl:
4258c2ecf20Sopenharmony_ci	del_timer_sync(&cmm_timer);
4268c2ecf20Sopenharmony_ci	return rc;
4278c2ecf20Sopenharmony_ci}
4288c2ecf20Sopenharmony_cimodule_init(cmm_init);
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic void __exit cmm_exit(void)
4318c2ecf20Sopenharmony_ci{
4328c2ecf20Sopenharmony_ci	unregister_sysctl_table(cmm_sysctl_header);
4338c2ecf20Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
4348c2ecf20Sopenharmony_ci	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
4358c2ecf20Sopenharmony_ci#endif
4368c2ecf20Sopenharmony_ci	unregister_oom_notifier(&cmm_oom_nb);
4378c2ecf20Sopenharmony_ci	kthread_stop(cmm_thread_ptr);
4388c2ecf20Sopenharmony_ci	del_timer_sync(&cmm_timer);
4398c2ecf20Sopenharmony_ci	cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
4408c2ecf20Sopenharmony_ci	cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_cimodule_exit(cmm_exit);
4438c2ecf20Sopenharmony_ci
4448c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
445