1*84af458bSBrian King /* 2*84af458bSBrian King * Collaborative memory management interface. 3*84af458bSBrian King * 4*84af458bSBrian King * Copyright (C) 2008 IBM Corporation 5*84af458bSBrian King * Author(s): Brian King (brking@linux.vnet.ibm.com), 6*84af458bSBrian King * 7*84af458bSBrian King * This program is free software; you can redistribute it and/or modify 8*84af458bSBrian King * it under the terms of the GNU General Public License as published by 9*84af458bSBrian King * the Free Software Foundation; either version 2 of the License, or 10*84af458bSBrian King * (at your option) any later version. 11*84af458bSBrian King * 12*84af458bSBrian King * This program is distributed in the hope that it will be useful, 13*84af458bSBrian King * but WITHOUT ANY WARRANTY; without even the implied warranty of 14*84af458bSBrian King * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15*84af458bSBrian King * GNU General Public License for more details. 16*84af458bSBrian King * 17*84af458bSBrian King * You should have received a copy of the GNU General Public License 18*84af458bSBrian King * along with this program; if not, write to the Free Software 19*84af458bSBrian King * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20*84af458bSBrian King * 21*84af458bSBrian King */ 22*84af458bSBrian King 23*84af458bSBrian King #include <linux/ctype.h> 24*84af458bSBrian King #include <linux/delay.h> 25*84af458bSBrian King #include <linux/errno.h> 26*84af458bSBrian King #include <linux/fs.h> 27*84af458bSBrian King #include <linux/init.h> 28*84af458bSBrian King #include <linux/kthread.h> 29*84af458bSBrian King #include <linux/module.h> 30*84af458bSBrian King #include <linux/oom.h> 31*84af458bSBrian King #include <linux/sched.h> 32*84af458bSBrian King #include <linux/stringify.h> 33*84af458bSBrian King #include <linux/swap.h> 34*84af458bSBrian King #include <linux/sysdev.h> 35*84af458bSBrian King #include <asm/firmware.h> 36*84af458bSBrian King #include <asm/hvcall.h> 37*84af458bSBrian King #include <asm/mmu.h> 38*84af458bSBrian King #include <asm/pgalloc.h> 39*84af458bSBrian King #include <asm/uaccess.h> 40*84af458bSBrian King 41*84af458bSBrian King #include "plpar_wrappers.h" 42*84af458bSBrian King 43*84af458bSBrian King #define CMM_DRIVER_VERSION "1.0.0" 44*84af458bSBrian King #define CMM_DEFAULT_DELAY 1 45*84af458bSBrian King #define CMM_DEBUG 0 46*84af458bSBrian King #define CMM_DISABLE 0 47*84af458bSBrian King #define CMM_OOM_KB 1024 48*84af458bSBrian King #define CMM_MIN_MEM_MB 256 49*84af458bSBrian King #define KB2PAGES(_p) ((_p)>>(PAGE_SHIFT-10)) 50*84af458bSBrian King #define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10)) 51*84af458bSBrian King 52*84af458bSBrian King static unsigned int delay = CMM_DEFAULT_DELAY; 53*84af458bSBrian King static unsigned int oom_kb = CMM_OOM_KB; 54*84af458bSBrian King static unsigned int cmm_debug = CMM_DEBUG; 55*84af458bSBrian King static unsigned int cmm_disabled = CMM_DISABLE; 56*84af458bSBrian King static unsigned long min_mem_mb = CMM_MIN_MEM_MB; 57*84af458bSBrian King static struct sys_device cmm_sysdev; 58*84af458bSBrian King 59*84af458bSBrian King MODULE_AUTHOR("Brian King <brking@linux.vnet.ibm.com>"); 60*84af458bSBrian King MODULE_DESCRIPTION("IBM System p Collaborative Memory Manager"); 61*84af458bSBrian King MODULE_LICENSE("GPL"); 62*84af458bSBrian King MODULE_VERSION(CMM_DRIVER_VERSION); 63*84af458bSBrian King 64*84af458bSBrian King module_param_named(delay, delay, uint, S_IRUGO | S_IWUSR); 65*84af458bSBrian King MODULE_PARM_DESC(delay, "Delay (in seconds) between polls to query hypervisor paging requests. " 66*84af458bSBrian King "[Default=" __stringify(CMM_DEFAULT_DELAY) "]"); 67*84af458bSBrian King module_param_named(oom_kb, oom_kb, uint, S_IRUGO | S_IWUSR); 68*84af458bSBrian King MODULE_PARM_DESC(oom_kb, "Amount of memory in kb to free on OOM. " 69*84af458bSBrian King "[Default=" __stringify(CMM_OOM_KB) "]"); 70*84af458bSBrian King module_param_named(min_mem_mb, min_mem_mb, ulong, S_IRUGO | S_IWUSR); 71*84af458bSBrian King MODULE_PARM_DESC(min_mem_mb, "Minimum amount of memory (in MB) to not balloon. " 72*84af458bSBrian King "[Default=" __stringify(CMM_MIN_MEM_MB) "]"); 73*84af458bSBrian King module_param_named(debug, cmm_debug, uint, S_IRUGO | S_IWUSR); 74*84af458bSBrian King MODULE_PARM_DESC(debug, "Enable module debugging logging. Set to 1 to enable. " 75*84af458bSBrian King "[Default=" __stringify(CMM_DEBUG) "]"); 76*84af458bSBrian King 77*84af458bSBrian King #define CMM_NR_PAGES ((PAGE_SIZE - sizeof(void *) - sizeof(unsigned long)) / sizeof(unsigned long)) 78*84af458bSBrian King 79*84af458bSBrian King #define cmm_dbg(...) if (cmm_debug) { printk(KERN_INFO "cmm: "__VA_ARGS__); } 80*84af458bSBrian King 81*84af458bSBrian King struct cmm_page_array { 82*84af458bSBrian King struct cmm_page_array *next; 83*84af458bSBrian King unsigned long index; 84*84af458bSBrian King unsigned long page[CMM_NR_PAGES]; 85*84af458bSBrian King }; 86*84af458bSBrian King 87*84af458bSBrian King static unsigned long loaned_pages; 88*84af458bSBrian King static unsigned long loaned_pages_target; 89*84af458bSBrian King static unsigned long oom_freed_pages; 90*84af458bSBrian King 91*84af458bSBrian King static struct cmm_page_array *cmm_page_list; 92*84af458bSBrian King static DEFINE_SPINLOCK(cmm_lock); 93*84af458bSBrian King 94*84af458bSBrian King static struct task_struct *cmm_thread_ptr; 95*84af458bSBrian King 96*84af458bSBrian King /** 97*84af458bSBrian King * cmm_alloc_pages - Allocate pages and mark them as loaned 98*84af458bSBrian King * @nr: number of pages to allocate 99*84af458bSBrian King * 100*84af458bSBrian King * Return value: 101*84af458bSBrian King * number of pages requested to be allocated which were not 102*84af458bSBrian King **/ 103*84af458bSBrian King static long cmm_alloc_pages(long nr) 104*84af458bSBrian King { 105*84af458bSBrian King struct cmm_page_array *pa, *npa; 106*84af458bSBrian King unsigned long addr; 107*84af458bSBrian King long rc; 108*84af458bSBrian King 109*84af458bSBrian King cmm_dbg("Begin request for %ld pages\n", nr); 110*84af458bSBrian King 111*84af458bSBrian King while (nr) { 112*84af458bSBrian King addr = __get_free_page(GFP_NOIO | __GFP_NOWARN | 113*84af458bSBrian King __GFP_NORETRY | __GFP_NOMEMALLOC); 114*84af458bSBrian King if (!addr) 115*84af458bSBrian King break; 116*84af458bSBrian King spin_lock(&cmm_lock); 117*84af458bSBrian King pa = cmm_page_list; 118*84af458bSBrian King if (!pa || pa->index >= CMM_NR_PAGES) { 119*84af458bSBrian King /* Need a new page for the page list. */ 120*84af458bSBrian King spin_unlock(&cmm_lock); 121*84af458bSBrian King npa = (struct cmm_page_array *)__get_free_page(GFP_NOIO | __GFP_NOWARN | 122*84af458bSBrian King __GFP_NORETRY | __GFP_NOMEMALLOC); 123*84af458bSBrian King if (!npa) { 124*84af458bSBrian King pr_info("%s: Can not allocate new page list\n", __FUNCTION__); 125*84af458bSBrian King free_page(addr); 126*84af458bSBrian King break; 127*84af458bSBrian King } 128*84af458bSBrian King spin_lock(&cmm_lock); 129*84af458bSBrian King pa = cmm_page_list; 130*84af458bSBrian King 131*84af458bSBrian King if (!pa || pa->index >= CMM_NR_PAGES) { 132*84af458bSBrian King npa->next = pa; 133*84af458bSBrian King npa->index = 0; 134*84af458bSBrian King pa = npa; 135*84af458bSBrian King cmm_page_list = pa; 136*84af458bSBrian King } else 137*84af458bSBrian King free_page((unsigned long) npa); 138*84af458bSBrian King } 139*84af458bSBrian King 140*84af458bSBrian King if ((rc = plpar_page_set_loaned(__pa(addr)))) { 141*84af458bSBrian King pr_err("%s: Can not set page to loaned. rc=%ld\n", __FUNCTION__, rc); 142*84af458bSBrian King spin_unlock(&cmm_lock); 143*84af458bSBrian King free_page(addr); 144*84af458bSBrian King break; 145*84af458bSBrian King } 146*84af458bSBrian King 147*84af458bSBrian King pa->page[pa->index++] = addr; 148*84af458bSBrian King loaned_pages++; 149*84af458bSBrian King totalram_pages--; 150*84af458bSBrian King spin_unlock(&cmm_lock); 151*84af458bSBrian King nr--; 152*84af458bSBrian King } 153*84af458bSBrian King 154*84af458bSBrian King cmm_dbg("End request with %ld pages unfulfilled\n", nr); 155*84af458bSBrian King return nr; 156*84af458bSBrian King } 157*84af458bSBrian King 158*84af458bSBrian King /** 159*84af458bSBrian King * cmm_free_pages - Free pages and mark them as active 160*84af458bSBrian King * @nr: number of pages to free 161*84af458bSBrian King * 162*84af458bSBrian King * Return value: 163*84af458bSBrian King * number of pages requested to be freed which were not 164*84af458bSBrian King **/ 165*84af458bSBrian King static long cmm_free_pages(long nr) 166*84af458bSBrian King { 167*84af458bSBrian King struct cmm_page_array *pa; 168*84af458bSBrian King unsigned long addr; 169*84af458bSBrian King 170*84af458bSBrian King cmm_dbg("Begin free of %ld pages.\n", nr); 171*84af458bSBrian King spin_lock(&cmm_lock); 172*84af458bSBrian King pa = cmm_page_list; 173*84af458bSBrian King while (nr) { 174*84af458bSBrian King if (!pa || pa->index <= 0) 175*84af458bSBrian King break; 176*84af458bSBrian King addr = pa->page[--pa->index]; 177*84af458bSBrian King 178*84af458bSBrian King if (pa->index == 0) { 179*84af458bSBrian King pa = pa->next; 180*84af458bSBrian King free_page((unsigned long) cmm_page_list); 181*84af458bSBrian King cmm_page_list = pa; 182*84af458bSBrian King } 183*84af458bSBrian King 184*84af458bSBrian King plpar_page_set_active(__pa(addr)); 185*84af458bSBrian King free_page(addr); 186*84af458bSBrian King loaned_pages--; 187*84af458bSBrian King nr--; 188*84af458bSBrian King totalram_pages++; 189*84af458bSBrian King } 190*84af458bSBrian King spin_unlock(&cmm_lock); 191*84af458bSBrian King cmm_dbg("End request with %ld pages unfulfilled\n", nr); 192*84af458bSBrian King return nr; 193*84af458bSBrian King } 194*84af458bSBrian King 195*84af458bSBrian King /** 196*84af458bSBrian King * cmm_oom_notify - OOM notifier 197*84af458bSBrian King * @self: notifier block struct 198*84af458bSBrian King * @dummy: not used 199*84af458bSBrian King * @parm: returned - number of pages freed 200*84af458bSBrian King * 201*84af458bSBrian King * Return value: 202*84af458bSBrian King * NOTIFY_OK 203*84af458bSBrian King **/ 204*84af458bSBrian King static int cmm_oom_notify(struct notifier_block *self, 205*84af458bSBrian King unsigned long dummy, void *parm) 206*84af458bSBrian King { 207*84af458bSBrian King unsigned long *freed = parm; 208*84af458bSBrian King long nr = KB2PAGES(oom_kb); 209*84af458bSBrian King 210*84af458bSBrian King cmm_dbg("OOM processing started\n"); 211*84af458bSBrian King nr = cmm_free_pages(nr); 212*84af458bSBrian King loaned_pages_target = loaned_pages; 213*84af458bSBrian King *freed += KB2PAGES(oom_kb) - nr; 214*84af458bSBrian King oom_freed_pages += KB2PAGES(oom_kb) - nr; 215*84af458bSBrian King cmm_dbg("OOM processing complete\n"); 216*84af458bSBrian King return NOTIFY_OK; 217*84af458bSBrian King } 218*84af458bSBrian King 219*84af458bSBrian King /** 220*84af458bSBrian King * cmm_get_mpp - Read memory performance parameters 221*84af458bSBrian King * 222*84af458bSBrian King * Makes hcall to query the current page loan request from the hypervisor. 223*84af458bSBrian King * 224*84af458bSBrian King * Return value: 225*84af458bSBrian King * nothing 226*84af458bSBrian King **/ 227*84af458bSBrian King static void cmm_get_mpp(void) 228*84af458bSBrian King { 229*84af458bSBrian King int rc; 230*84af458bSBrian King struct hvcall_mpp_data mpp_data; 231*84af458bSBrian King unsigned long active_pages_target; 232*84af458bSBrian King signed long page_loan_request; 233*84af458bSBrian King 234*84af458bSBrian King rc = h_get_mpp(&mpp_data); 235*84af458bSBrian King 236*84af458bSBrian King if (rc != H_SUCCESS) 237*84af458bSBrian King return; 238*84af458bSBrian King 239*84af458bSBrian King page_loan_request = div_s64((s64)mpp_data.loan_request, PAGE_SIZE); 240*84af458bSBrian King loaned_pages_target = page_loan_request + loaned_pages; 241*84af458bSBrian King if (loaned_pages_target > oom_freed_pages) 242*84af458bSBrian King loaned_pages_target -= oom_freed_pages; 243*84af458bSBrian King else 244*84af458bSBrian King loaned_pages_target = 0; 245*84af458bSBrian King 246*84af458bSBrian King active_pages_target = totalram_pages + loaned_pages - loaned_pages_target; 247*84af458bSBrian King 248*84af458bSBrian King if ((min_mem_mb * 1024 * 1024) > (active_pages_target * PAGE_SIZE)) 249*84af458bSBrian King loaned_pages_target = totalram_pages + loaned_pages - 250*84af458bSBrian King ((min_mem_mb * 1024 * 1024) / PAGE_SIZE); 251*84af458bSBrian King 252*84af458bSBrian King cmm_dbg("delta = %ld, loaned = %lu, target = %lu, oom = %lu, totalram = %lu\n", 253*84af458bSBrian King page_loan_request, loaned_pages, loaned_pages_target, 254*84af458bSBrian King oom_freed_pages, totalram_pages); 255*84af458bSBrian King } 256*84af458bSBrian King 257*84af458bSBrian King static struct notifier_block cmm_oom_nb = { 258*84af458bSBrian King .notifier_call = cmm_oom_notify 259*84af458bSBrian King }; 260*84af458bSBrian King 261*84af458bSBrian King /** 262*84af458bSBrian King * cmm_thread - CMM task thread 263*84af458bSBrian King * @dummy: not used 264*84af458bSBrian King * 265*84af458bSBrian King * Return value: 266*84af458bSBrian King * 0 267*84af458bSBrian King **/ 268*84af458bSBrian King static int cmm_thread(void *dummy) 269*84af458bSBrian King { 270*84af458bSBrian King unsigned long timeleft; 271*84af458bSBrian King 272*84af458bSBrian King while (1) { 273*84af458bSBrian King timeleft = msleep_interruptible(delay * 1000); 274*84af458bSBrian King 275*84af458bSBrian King if (kthread_should_stop() || timeleft) { 276*84af458bSBrian King loaned_pages_target = loaned_pages; 277*84af458bSBrian King break; 278*84af458bSBrian King } 279*84af458bSBrian King 280*84af458bSBrian King cmm_get_mpp(); 281*84af458bSBrian King 282*84af458bSBrian King if (loaned_pages_target > loaned_pages) { 283*84af458bSBrian King if (cmm_alloc_pages(loaned_pages_target - loaned_pages)) 284*84af458bSBrian King loaned_pages_target = loaned_pages; 285*84af458bSBrian King } else if (loaned_pages_target < loaned_pages) 286*84af458bSBrian King cmm_free_pages(loaned_pages - loaned_pages_target); 287*84af458bSBrian King } 288*84af458bSBrian King return 0; 289*84af458bSBrian King } 290*84af458bSBrian King 291*84af458bSBrian King #define CMM_SHOW(name, format, args...) \ 292*84af458bSBrian King static ssize_t show_##name(struct sys_device *dev, char *buf) \ 293*84af458bSBrian King { \ 294*84af458bSBrian King return sprintf(buf, format, ##args); \ 295*84af458bSBrian King } \ 296*84af458bSBrian King static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL) 297*84af458bSBrian King 298*84af458bSBrian King CMM_SHOW(loaned_kb, "%lu\n", PAGES2KB(loaned_pages)); 299*84af458bSBrian King CMM_SHOW(loaned_target_kb, "%lu\n", PAGES2KB(loaned_pages_target)); 300*84af458bSBrian King 301*84af458bSBrian King static ssize_t show_oom_pages(struct sys_device *dev, char *buf) 302*84af458bSBrian King { 303*84af458bSBrian King return sprintf(buf, "%lu\n", PAGES2KB(oom_freed_pages)); 304*84af458bSBrian King } 305*84af458bSBrian King 306*84af458bSBrian King static ssize_t store_oom_pages(struct sys_device *dev, 307*84af458bSBrian King const char *buf, size_t count) 308*84af458bSBrian King { 309*84af458bSBrian King unsigned long val = simple_strtoul (buf, NULL, 10); 310*84af458bSBrian King 311*84af458bSBrian King if (!capable(CAP_SYS_ADMIN)) 312*84af458bSBrian King return -EPERM; 313*84af458bSBrian King if (val != 0) 314*84af458bSBrian King return -EBADMSG; 315*84af458bSBrian King 316*84af458bSBrian King oom_freed_pages = 0; 317*84af458bSBrian King return count; 318*84af458bSBrian King } 319*84af458bSBrian King 320*84af458bSBrian King static SYSDEV_ATTR(oom_freed_kb, S_IWUSR| S_IRUGO, 321*84af458bSBrian King show_oom_pages, store_oom_pages); 322*84af458bSBrian King 323*84af458bSBrian King static struct sysdev_attribute *cmm_attrs[] = { 324*84af458bSBrian King &attr_loaned_kb, 325*84af458bSBrian King &attr_loaned_target_kb, 326*84af458bSBrian King &attr_oom_freed_kb, 327*84af458bSBrian King }; 328*84af458bSBrian King 329*84af458bSBrian King static struct sysdev_class cmm_sysdev_class = { 330*84af458bSBrian King .name = "cmm", 331*84af458bSBrian King }; 332*84af458bSBrian King 333*84af458bSBrian King /** 334*84af458bSBrian King * cmm_sysfs_register - Register with sysfs 335*84af458bSBrian King * 336*84af458bSBrian King * Return value: 337*84af458bSBrian King * 0 on success / other on failure 338*84af458bSBrian King **/ 339*84af458bSBrian King static int cmm_sysfs_register(struct sys_device *sysdev) 340*84af458bSBrian King { 341*84af458bSBrian King int i, rc; 342*84af458bSBrian King 343*84af458bSBrian King if ((rc = sysdev_class_register(&cmm_sysdev_class))) 344*84af458bSBrian King return rc; 345*84af458bSBrian King 346*84af458bSBrian King sysdev->id = 0; 347*84af458bSBrian King sysdev->cls = &cmm_sysdev_class; 348*84af458bSBrian King 349*84af458bSBrian King if ((rc = sysdev_register(sysdev))) 350*84af458bSBrian King goto class_unregister; 351*84af458bSBrian King 352*84af458bSBrian King for (i = 0; i < ARRAY_SIZE(cmm_attrs); i++) { 353*84af458bSBrian King if ((rc = sysdev_create_file(sysdev, cmm_attrs[i]))) 354*84af458bSBrian King goto fail; 355*84af458bSBrian King } 356*84af458bSBrian King 357*84af458bSBrian King return 0; 358*84af458bSBrian King 359*84af458bSBrian King fail: 360*84af458bSBrian King while (--i >= 0) 361*84af458bSBrian King sysdev_remove_file(sysdev, cmm_attrs[i]); 362*84af458bSBrian King sysdev_unregister(sysdev); 363*84af458bSBrian King class_unregister: 364*84af458bSBrian King sysdev_class_unregister(&cmm_sysdev_class); 365*84af458bSBrian King return rc; 366*84af458bSBrian King } 367*84af458bSBrian King 368*84af458bSBrian King /** 369*84af458bSBrian King * cmm_unregister_sysfs - Unregister from sysfs 370*84af458bSBrian King * 371*84af458bSBrian King **/ 372*84af458bSBrian King static void cmm_unregister_sysfs(struct sys_device *sysdev) 373*84af458bSBrian King { 374*84af458bSBrian King int i; 375*84af458bSBrian King 376*84af458bSBrian King for (i = 0; i < ARRAY_SIZE(cmm_attrs); i++) 377*84af458bSBrian King sysdev_remove_file(sysdev, cmm_attrs[i]); 378*84af458bSBrian King sysdev_unregister(sysdev); 379*84af458bSBrian King sysdev_class_unregister(&cmm_sysdev_class); 380*84af458bSBrian King } 381*84af458bSBrian King 382*84af458bSBrian King /** 383*84af458bSBrian King * cmm_init - Module initialization 384*84af458bSBrian King * 385*84af458bSBrian King * Return value: 386*84af458bSBrian King * 0 on success / other on failure 387*84af458bSBrian King **/ 388*84af458bSBrian King static int cmm_init(void) 389*84af458bSBrian King { 390*84af458bSBrian King int rc = -ENOMEM; 391*84af458bSBrian King 392*84af458bSBrian King if (!firmware_has_feature(FW_FEATURE_CMO)) 393*84af458bSBrian King return -EOPNOTSUPP; 394*84af458bSBrian King 395*84af458bSBrian King if ((rc = register_oom_notifier(&cmm_oom_nb)) < 0) 396*84af458bSBrian King return rc; 397*84af458bSBrian King 398*84af458bSBrian King if ((rc = cmm_sysfs_register(&cmm_sysdev))) 399*84af458bSBrian King goto out_oom_notifier; 400*84af458bSBrian King 401*84af458bSBrian King if (cmm_disabled) 402*84af458bSBrian King return rc; 403*84af458bSBrian King 404*84af458bSBrian King cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread"); 405*84af458bSBrian King if (IS_ERR(cmm_thread_ptr)) { 406*84af458bSBrian King rc = PTR_ERR(cmm_thread_ptr); 407*84af458bSBrian King goto out_unregister_sysfs; 408*84af458bSBrian King } 409*84af458bSBrian King 410*84af458bSBrian King return rc; 411*84af458bSBrian King 412*84af458bSBrian King out_unregister_sysfs: 413*84af458bSBrian King cmm_unregister_sysfs(&cmm_sysdev); 414*84af458bSBrian King out_oom_notifier: 415*84af458bSBrian King unregister_oom_notifier(&cmm_oom_nb); 416*84af458bSBrian King return rc; 417*84af458bSBrian King } 418*84af458bSBrian King 419*84af458bSBrian King /** 420*84af458bSBrian King * cmm_exit - Module exit 421*84af458bSBrian King * 422*84af458bSBrian King * Return value: 423*84af458bSBrian King * nothing 424*84af458bSBrian King **/ 425*84af458bSBrian King static void cmm_exit(void) 426*84af458bSBrian King { 427*84af458bSBrian King if (cmm_thread_ptr) 428*84af458bSBrian King kthread_stop(cmm_thread_ptr); 429*84af458bSBrian King unregister_oom_notifier(&cmm_oom_nb); 430*84af458bSBrian King cmm_free_pages(loaned_pages); 431*84af458bSBrian King cmm_unregister_sysfs(&cmm_sysdev); 432*84af458bSBrian King } 433*84af458bSBrian King 434*84af458bSBrian King /** 435*84af458bSBrian King * cmm_set_disable - Disable/Enable CMM 436*84af458bSBrian King * 437*84af458bSBrian King * Return value: 438*84af458bSBrian King * 0 on success / other on failure 439*84af458bSBrian King **/ 440*84af458bSBrian King static int cmm_set_disable(const char *val, struct kernel_param *kp) 441*84af458bSBrian King { 442*84af458bSBrian King int disable = simple_strtoul(val, NULL, 10); 443*84af458bSBrian King 444*84af458bSBrian King if (disable != 0 && disable != 1) 445*84af458bSBrian King return -EINVAL; 446*84af458bSBrian King 447*84af458bSBrian King if (disable && !cmm_disabled) { 448*84af458bSBrian King if (cmm_thread_ptr) 449*84af458bSBrian King kthread_stop(cmm_thread_ptr); 450*84af458bSBrian King cmm_thread_ptr = NULL; 451*84af458bSBrian King cmm_free_pages(loaned_pages); 452*84af458bSBrian King } else if (!disable && cmm_disabled) { 453*84af458bSBrian King cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread"); 454*84af458bSBrian King if (IS_ERR(cmm_thread_ptr)) 455*84af458bSBrian King return PTR_ERR(cmm_thread_ptr); 456*84af458bSBrian King } 457*84af458bSBrian King 458*84af458bSBrian King cmm_disabled = disable; 459*84af458bSBrian King return 0; 460*84af458bSBrian King } 461*84af458bSBrian King 462*84af458bSBrian King module_param_call(disable, cmm_set_disable, param_get_uint, 463*84af458bSBrian King &cmm_disabled, S_IRUGO | S_IWUSR); 464*84af458bSBrian King MODULE_PARM_DESC(disable, "Disable CMM. Set to 1 to disable. " 465*84af458bSBrian King "[Default=" __stringify(CMM_DISABLE) "]"); 466*84af458bSBrian King 467*84af458bSBrian King module_init(cmm_init); 468*84af458bSBrian King module_exit(cmm_exit); 469