162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *  Collaborative memory management interface.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci *    Copyright IBM Corp 2003, 2010
662306a36Sopenharmony_ci *    Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>,
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/fs.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/module.h>
1462306a36Sopenharmony_ci#include <linux/moduleparam.h>
1562306a36Sopenharmony_ci#include <linux/gfp.h>
1662306a36Sopenharmony_ci#include <linux/sched.h>
1762306a36Sopenharmony_ci#include <linux/string_helpers.h>
1862306a36Sopenharmony_ci#include <linux/sysctl.h>
1962306a36Sopenharmony_ci#include <linux/swap.h>
2062306a36Sopenharmony_ci#include <linux/kthread.h>
2162306a36Sopenharmony_ci#include <linux/oom.h>
2262306a36Sopenharmony_ci#include <linux/uaccess.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include <asm/diag.h>
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
2762306a36Sopenharmony_cistatic char *cmm_default_sender = "VMRMSVM";
2862306a36Sopenharmony_ci#endif
2962306a36Sopenharmony_cistatic char *sender;
3062306a36Sopenharmony_cimodule_param(sender, charp, 0400);
3162306a36Sopenharmony_ciMODULE_PARM_DESC(sender,
3262306a36Sopenharmony_ci		 "Guest name that may send SMSG messages (default VMRMSVM)");
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci#include "../../../drivers/s390/net/smsgiucv.h"
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci#define CMM_NR_PAGES ((PAGE_SIZE / sizeof(unsigned long)) - 2)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_cistruct cmm_page_array {
3962306a36Sopenharmony_ci	struct cmm_page_array *next;
4062306a36Sopenharmony_ci	unsigned long index;
4162306a36Sopenharmony_ci	unsigned long pages[CMM_NR_PAGES];
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic long cmm_pages;
4562306a36Sopenharmony_cistatic long cmm_timed_pages;
4662306a36Sopenharmony_cistatic volatile long cmm_pages_target;
4762306a36Sopenharmony_cistatic volatile long cmm_timed_pages_target;
4862306a36Sopenharmony_cistatic long cmm_timeout_pages;
4962306a36Sopenharmony_cistatic long cmm_timeout_seconds;
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic struct cmm_page_array *cmm_page_list;
5262306a36Sopenharmony_cistatic struct cmm_page_array *cmm_timed_page_list;
5362306a36Sopenharmony_cistatic DEFINE_SPINLOCK(cmm_lock);
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic struct task_struct *cmm_thread_ptr;
5662306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(cmm_thread_wait);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_cistatic void cmm_timer_fn(struct timer_list *);
5962306a36Sopenharmony_cistatic void cmm_set_timer(void);
6062306a36Sopenharmony_cistatic DEFINE_TIMER(cmm_timer, cmm_timer_fn);
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistatic long cmm_alloc_pages(long nr, long *counter,
6362306a36Sopenharmony_ci			    struct cmm_page_array **list)
6462306a36Sopenharmony_ci{
6562306a36Sopenharmony_ci	struct cmm_page_array *pa, *npa;
6662306a36Sopenharmony_ci	unsigned long addr;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	while (nr) {
6962306a36Sopenharmony_ci		addr = __get_free_page(GFP_NOIO);
7062306a36Sopenharmony_ci		if (!addr)
7162306a36Sopenharmony_ci			break;
7262306a36Sopenharmony_ci		spin_lock(&cmm_lock);
7362306a36Sopenharmony_ci		pa = *list;
7462306a36Sopenharmony_ci		if (!pa || pa->index >= CMM_NR_PAGES) {
7562306a36Sopenharmony_ci			/* Need a new page for the page list. */
7662306a36Sopenharmony_ci			spin_unlock(&cmm_lock);
7762306a36Sopenharmony_ci			npa = (struct cmm_page_array *)
7862306a36Sopenharmony_ci				__get_free_page(GFP_NOIO);
7962306a36Sopenharmony_ci			if (!npa) {
8062306a36Sopenharmony_ci				free_page(addr);
8162306a36Sopenharmony_ci				break;
8262306a36Sopenharmony_ci			}
8362306a36Sopenharmony_ci			spin_lock(&cmm_lock);
8462306a36Sopenharmony_ci			pa = *list;
8562306a36Sopenharmony_ci			if (!pa || pa->index >= CMM_NR_PAGES) {
8662306a36Sopenharmony_ci				npa->next = pa;
8762306a36Sopenharmony_ci				npa->index = 0;
8862306a36Sopenharmony_ci				pa = npa;
8962306a36Sopenharmony_ci				*list = pa;
9062306a36Sopenharmony_ci			} else
9162306a36Sopenharmony_ci				free_page((unsigned long) npa);
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci		diag10_range(virt_to_pfn((void *)addr), 1);
9462306a36Sopenharmony_ci		pa->pages[pa->index++] = addr;
9562306a36Sopenharmony_ci		(*counter)++;
9662306a36Sopenharmony_ci		spin_unlock(&cmm_lock);
9762306a36Sopenharmony_ci		nr--;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci	return nr;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic long cmm_free_pages(long nr, long *counter, struct cmm_page_array **list)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct cmm_page_array *pa;
10562306a36Sopenharmony_ci	unsigned long addr;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	spin_lock(&cmm_lock);
10862306a36Sopenharmony_ci	pa = *list;
10962306a36Sopenharmony_ci	while (nr) {
11062306a36Sopenharmony_ci		if (!pa || pa->index <= 0)
11162306a36Sopenharmony_ci			break;
11262306a36Sopenharmony_ci		addr = pa->pages[--pa->index];
11362306a36Sopenharmony_ci		if (pa->index == 0) {
11462306a36Sopenharmony_ci			pa = pa->next;
11562306a36Sopenharmony_ci			free_page((unsigned long) *list);
11662306a36Sopenharmony_ci			*list = pa;
11762306a36Sopenharmony_ci		}
11862306a36Sopenharmony_ci		free_page(addr);
11962306a36Sopenharmony_ci		(*counter)--;
12062306a36Sopenharmony_ci		nr--;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci	spin_unlock(&cmm_lock);
12362306a36Sopenharmony_ci	return nr;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_cistatic int cmm_oom_notify(struct notifier_block *self,
12762306a36Sopenharmony_ci			  unsigned long dummy, void *parm)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	unsigned long *freed = parm;
13062306a36Sopenharmony_ci	long nr = 256;
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	nr = cmm_free_pages(nr, &cmm_timed_pages, &cmm_timed_page_list);
13362306a36Sopenharmony_ci	if (nr > 0)
13462306a36Sopenharmony_ci		nr = cmm_free_pages(nr, &cmm_pages, &cmm_page_list);
13562306a36Sopenharmony_ci	cmm_pages_target = cmm_pages;
13662306a36Sopenharmony_ci	cmm_timed_pages_target = cmm_timed_pages;
13762306a36Sopenharmony_ci	*freed += 256 - nr;
13862306a36Sopenharmony_ci	return NOTIFY_OK;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic struct notifier_block cmm_oom_nb = {
14262306a36Sopenharmony_ci	.notifier_call = cmm_oom_notify,
14362306a36Sopenharmony_ci};
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic int cmm_thread(void *dummy)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	int rc;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	while (1) {
15062306a36Sopenharmony_ci		rc = wait_event_interruptible(cmm_thread_wait,
15162306a36Sopenharmony_ci			cmm_pages != cmm_pages_target ||
15262306a36Sopenharmony_ci			cmm_timed_pages != cmm_timed_pages_target ||
15362306a36Sopenharmony_ci			kthread_should_stop());
15462306a36Sopenharmony_ci		if (kthread_should_stop() || rc == -ERESTARTSYS) {
15562306a36Sopenharmony_ci			cmm_pages_target = cmm_pages;
15662306a36Sopenharmony_ci			cmm_timed_pages_target = cmm_timed_pages;
15762306a36Sopenharmony_ci			break;
15862306a36Sopenharmony_ci		}
15962306a36Sopenharmony_ci		if (cmm_pages_target > cmm_pages) {
16062306a36Sopenharmony_ci			if (cmm_alloc_pages(1, &cmm_pages, &cmm_page_list))
16162306a36Sopenharmony_ci				cmm_pages_target = cmm_pages;
16262306a36Sopenharmony_ci		} else if (cmm_pages_target < cmm_pages) {
16362306a36Sopenharmony_ci			cmm_free_pages(1, &cmm_pages, &cmm_page_list);
16462306a36Sopenharmony_ci		}
16562306a36Sopenharmony_ci		if (cmm_timed_pages_target > cmm_timed_pages) {
16662306a36Sopenharmony_ci			if (cmm_alloc_pages(1, &cmm_timed_pages,
16762306a36Sopenharmony_ci					   &cmm_timed_page_list))
16862306a36Sopenharmony_ci				cmm_timed_pages_target = cmm_timed_pages;
16962306a36Sopenharmony_ci		} else if (cmm_timed_pages_target < cmm_timed_pages) {
17062306a36Sopenharmony_ci			cmm_free_pages(1, &cmm_timed_pages,
17162306a36Sopenharmony_ci				       &cmm_timed_page_list);
17262306a36Sopenharmony_ci		}
17362306a36Sopenharmony_ci		if (cmm_timed_pages > 0 && !timer_pending(&cmm_timer))
17462306a36Sopenharmony_ci			cmm_set_timer();
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	return 0;
17762306a36Sopenharmony_ci}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_cistatic void cmm_kick_thread(void)
18062306a36Sopenharmony_ci{
18162306a36Sopenharmony_ci	wake_up(&cmm_thread_wait);
18262306a36Sopenharmony_ci}
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_cistatic void cmm_set_timer(void)
18562306a36Sopenharmony_ci{
18662306a36Sopenharmony_ci	if (cmm_timed_pages_target <= 0 || cmm_timeout_seconds <= 0) {
18762306a36Sopenharmony_ci		if (timer_pending(&cmm_timer))
18862306a36Sopenharmony_ci			del_timer(&cmm_timer);
18962306a36Sopenharmony_ci		return;
19062306a36Sopenharmony_ci	}
19162306a36Sopenharmony_ci	mod_timer(&cmm_timer, jiffies + msecs_to_jiffies(cmm_timeout_seconds * MSEC_PER_SEC));
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void cmm_timer_fn(struct timer_list *unused)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	long nr;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	nr = cmm_timed_pages_target - cmm_timeout_pages;
19962306a36Sopenharmony_ci	if (nr < 0)
20062306a36Sopenharmony_ci		cmm_timed_pages_target = 0;
20162306a36Sopenharmony_ci	else
20262306a36Sopenharmony_ci		cmm_timed_pages_target = nr;
20362306a36Sopenharmony_ci	cmm_kick_thread();
20462306a36Sopenharmony_ci	cmm_set_timer();
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_cistatic void cmm_set_pages(long nr)
20862306a36Sopenharmony_ci{
20962306a36Sopenharmony_ci	cmm_pages_target = nr;
21062306a36Sopenharmony_ci	cmm_kick_thread();
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic long cmm_get_pages(void)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	return cmm_pages;
21662306a36Sopenharmony_ci}
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_cistatic void cmm_add_timed_pages(long nr)
21962306a36Sopenharmony_ci{
22062306a36Sopenharmony_ci	cmm_timed_pages_target += nr;
22162306a36Sopenharmony_ci	cmm_kick_thread();
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic long cmm_get_timed_pages(void)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	return cmm_timed_pages;
22762306a36Sopenharmony_ci}
22862306a36Sopenharmony_ci
22962306a36Sopenharmony_cistatic void cmm_set_timeout(long nr, long seconds)
23062306a36Sopenharmony_ci{
23162306a36Sopenharmony_ci	cmm_timeout_pages = nr;
23262306a36Sopenharmony_ci	cmm_timeout_seconds = seconds;
23362306a36Sopenharmony_ci	cmm_set_timer();
23462306a36Sopenharmony_ci}
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_cistatic int cmm_skip_blanks(char *cp, char **endp)
23762306a36Sopenharmony_ci{
23862306a36Sopenharmony_ci	char *str;
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	for (str = cp; *str == ' ' || *str == '\t'; str++)
24162306a36Sopenharmony_ci		;
24262306a36Sopenharmony_ci	*endp = str;
24362306a36Sopenharmony_ci	return str != cp;
24462306a36Sopenharmony_ci}
24562306a36Sopenharmony_ci
24662306a36Sopenharmony_cistatic int cmm_pages_handler(struct ctl_table *ctl, int write,
24762306a36Sopenharmony_ci			     void *buffer, size_t *lenp, loff_t *ppos)
24862306a36Sopenharmony_ci{
24962306a36Sopenharmony_ci	long nr = cmm_get_pages();
25062306a36Sopenharmony_ci	struct ctl_table ctl_entry = {
25162306a36Sopenharmony_ci		.procname	= ctl->procname,
25262306a36Sopenharmony_ci		.data		= &nr,
25362306a36Sopenharmony_ci		.maxlen		= sizeof(long),
25462306a36Sopenharmony_ci	};
25562306a36Sopenharmony_ci	int rc;
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	rc = proc_doulongvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
25862306a36Sopenharmony_ci	if (rc < 0 || !write)
25962306a36Sopenharmony_ci		return rc;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	cmm_set_pages(nr);
26262306a36Sopenharmony_ci	return 0;
26362306a36Sopenharmony_ci}
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_cistatic int cmm_timed_pages_handler(struct ctl_table *ctl, int write,
26662306a36Sopenharmony_ci				   void *buffer, size_t *lenp,
26762306a36Sopenharmony_ci				   loff_t *ppos)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	long nr = cmm_get_timed_pages();
27062306a36Sopenharmony_ci	struct ctl_table ctl_entry = {
27162306a36Sopenharmony_ci		.procname	= ctl->procname,
27262306a36Sopenharmony_ci		.data		= &nr,
27362306a36Sopenharmony_ci		.maxlen		= sizeof(long),
27462306a36Sopenharmony_ci	};
27562306a36Sopenharmony_ci	int rc;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	rc = proc_doulongvec_minmax(&ctl_entry, write, buffer, lenp, ppos);
27862306a36Sopenharmony_ci	if (rc < 0 || !write)
27962306a36Sopenharmony_ci		return rc;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	cmm_add_timed_pages(nr);
28262306a36Sopenharmony_ci	return 0;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic int cmm_timeout_handler(struct ctl_table *ctl, int write,
28662306a36Sopenharmony_ci			       void *buffer, size_t *lenp, loff_t *ppos)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	char buf[64], *p;
28962306a36Sopenharmony_ci	long nr, seconds;
29062306a36Sopenharmony_ci	unsigned int len;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	if (!*lenp || (*ppos && !write)) {
29362306a36Sopenharmony_ci		*lenp = 0;
29462306a36Sopenharmony_ci		return 0;
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (write) {
29862306a36Sopenharmony_ci		len = min(*lenp, sizeof(buf));
29962306a36Sopenharmony_ci		memcpy(buf, buffer, len);
30062306a36Sopenharmony_ci		buf[len - 1] = '\0';
30162306a36Sopenharmony_ci		cmm_skip_blanks(buf, &p);
30262306a36Sopenharmony_ci		nr = simple_strtoul(p, &p, 0);
30362306a36Sopenharmony_ci		cmm_skip_blanks(p, &p);
30462306a36Sopenharmony_ci		seconds = simple_strtoul(p, &p, 0);
30562306a36Sopenharmony_ci		cmm_set_timeout(nr, seconds);
30662306a36Sopenharmony_ci		*ppos += *lenp;
30762306a36Sopenharmony_ci	} else {
30862306a36Sopenharmony_ci		len = sprintf(buf, "%ld %ld\n",
30962306a36Sopenharmony_ci			      cmm_timeout_pages, cmm_timeout_seconds);
31062306a36Sopenharmony_ci		if (len > *lenp)
31162306a36Sopenharmony_ci			len = *lenp;
31262306a36Sopenharmony_ci		memcpy(buffer, buf, len);
31362306a36Sopenharmony_ci		*lenp = len;
31462306a36Sopenharmony_ci		*ppos += len;
31562306a36Sopenharmony_ci	}
31662306a36Sopenharmony_ci	return 0;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_cistatic struct ctl_table cmm_table[] = {
32062306a36Sopenharmony_ci	{
32162306a36Sopenharmony_ci		.procname	= "cmm_pages",
32262306a36Sopenharmony_ci		.mode		= 0644,
32362306a36Sopenharmony_ci		.proc_handler	= cmm_pages_handler,
32462306a36Sopenharmony_ci	},
32562306a36Sopenharmony_ci	{
32662306a36Sopenharmony_ci		.procname	= "cmm_timed_pages",
32762306a36Sopenharmony_ci		.mode		= 0644,
32862306a36Sopenharmony_ci		.proc_handler	= cmm_timed_pages_handler,
32962306a36Sopenharmony_ci	},
33062306a36Sopenharmony_ci	{
33162306a36Sopenharmony_ci		.procname	= "cmm_timeout",
33262306a36Sopenharmony_ci		.mode		= 0644,
33362306a36Sopenharmony_ci		.proc_handler	= cmm_timeout_handler,
33462306a36Sopenharmony_ci	},
33562306a36Sopenharmony_ci	{ }
33662306a36Sopenharmony_ci};
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
33962306a36Sopenharmony_ci#define SMSG_PREFIX "CMM"
34062306a36Sopenharmony_cistatic void cmm_smsg_target(const char *from, char *msg)
34162306a36Sopenharmony_ci{
34262306a36Sopenharmony_ci	long nr, seconds;
34362306a36Sopenharmony_ci
34462306a36Sopenharmony_ci	if (strlen(sender) > 0 && strcmp(from, sender) != 0)
34562306a36Sopenharmony_ci		return;
34662306a36Sopenharmony_ci	if (!cmm_skip_blanks(msg + strlen(SMSG_PREFIX), &msg))
34762306a36Sopenharmony_ci		return;
34862306a36Sopenharmony_ci	if (strncmp(msg, "SHRINK", 6) == 0) {
34962306a36Sopenharmony_ci		if (!cmm_skip_blanks(msg + 6, &msg))
35062306a36Sopenharmony_ci			return;
35162306a36Sopenharmony_ci		nr = simple_strtoul(msg, &msg, 0);
35262306a36Sopenharmony_ci		cmm_skip_blanks(msg, &msg);
35362306a36Sopenharmony_ci		if (*msg == '\0')
35462306a36Sopenharmony_ci			cmm_set_pages(nr);
35562306a36Sopenharmony_ci	} else if (strncmp(msg, "RELEASE", 7) == 0) {
35662306a36Sopenharmony_ci		if (!cmm_skip_blanks(msg + 7, &msg))
35762306a36Sopenharmony_ci			return;
35862306a36Sopenharmony_ci		nr = simple_strtoul(msg, &msg, 0);
35962306a36Sopenharmony_ci		cmm_skip_blanks(msg, &msg);
36062306a36Sopenharmony_ci		if (*msg == '\0')
36162306a36Sopenharmony_ci			cmm_add_timed_pages(nr);
36262306a36Sopenharmony_ci	} else if (strncmp(msg, "REUSE", 5) == 0) {
36362306a36Sopenharmony_ci		if (!cmm_skip_blanks(msg + 5, &msg))
36462306a36Sopenharmony_ci			return;
36562306a36Sopenharmony_ci		nr = simple_strtoul(msg, &msg, 0);
36662306a36Sopenharmony_ci		if (!cmm_skip_blanks(msg, &msg))
36762306a36Sopenharmony_ci			return;
36862306a36Sopenharmony_ci		seconds = simple_strtoul(msg, &msg, 0);
36962306a36Sopenharmony_ci		cmm_skip_blanks(msg, &msg);
37062306a36Sopenharmony_ci		if (*msg == '\0')
37162306a36Sopenharmony_ci			cmm_set_timeout(nr, seconds);
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci}
37462306a36Sopenharmony_ci#endif
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_cistatic struct ctl_table_header *cmm_sysctl_header;
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic int __init cmm_init(void)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	int rc = -ENOMEM;
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	cmm_sysctl_header = register_sysctl("vm", cmm_table);
38362306a36Sopenharmony_ci	if (!cmm_sysctl_header)
38462306a36Sopenharmony_ci		goto out_sysctl;
38562306a36Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
38662306a36Sopenharmony_ci	/* convert sender to uppercase characters */
38762306a36Sopenharmony_ci	if (sender)
38862306a36Sopenharmony_ci		string_upper(sender, sender);
38962306a36Sopenharmony_ci	else
39062306a36Sopenharmony_ci		sender = cmm_default_sender;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
39362306a36Sopenharmony_ci	if (rc < 0)
39462306a36Sopenharmony_ci		goto out_smsg;
39562306a36Sopenharmony_ci#endif
39662306a36Sopenharmony_ci	rc = register_oom_notifier(&cmm_oom_nb);
39762306a36Sopenharmony_ci	if (rc < 0)
39862306a36Sopenharmony_ci		goto out_oom_notify;
39962306a36Sopenharmony_ci	cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
40062306a36Sopenharmony_ci	if (!IS_ERR(cmm_thread_ptr))
40162306a36Sopenharmony_ci		return 0;
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_ci	rc = PTR_ERR(cmm_thread_ptr);
40462306a36Sopenharmony_ci	unregister_oom_notifier(&cmm_oom_nb);
40562306a36Sopenharmony_ciout_oom_notify:
40662306a36Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
40762306a36Sopenharmony_ci	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
40862306a36Sopenharmony_ciout_smsg:
40962306a36Sopenharmony_ci#endif
41062306a36Sopenharmony_ci	unregister_sysctl_table(cmm_sysctl_header);
41162306a36Sopenharmony_ciout_sysctl:
41262306a36Sopenharmony_ci	del_timer_sync(&cmm_timer);
41362306a36Sopenharmony_ci	return rc;
41462306a36Sopenharmony_ci}
41562306a36Sopenharmony_cimodule_init(cmm_init);
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_cistatic void __exit cmm_exit(void)
41862306a36Sopenharmony_ci{
41962306a36Sopenharmony_ci	unregister_sysctl_table(cmm_sysctl_header);
42062306a36Sopenharmony_ci#ifdef CONFIG_CMM_IUCV
42162306a36Sopenharmony_ci	smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
42262306a36Sopenharmony_ci#endif
42362306a36Sopenharmony_ci	unregister_oom_notifier(&cmm_oom_nb);
42462306a36Sopenharmony_ci	kthread_stop(cmm_thread_ptr);
42562306a36Sopenharmony_ci	del_timer_sync(&cmm_timer);
42662306a36Sopenharmony_ci	cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
42762306a36Sopenharmony_ci	cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
42862306a36Sopenharmony_ci}
42962306a36Sopenharmony_cimodule_exit(cmm_exit);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
432