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