Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/arch/powerpc/platforms/pseries/cmm.c
10818 views
1
/*
2
* Collaborative memory management interface.
3
*
4
* Copyright (C) 2008 IBM Corporation
5
* Author(s): Brian King ([email protected]),
6
*
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
11
*
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
16
*
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
*
21
*/
22
23
#include <linux/ctype.h>
24
#include <linux/delay.h>
25
#include <linux/errno.h>
26
#include <linux/fs.h>
27
#include <linux/gfp.h>
28
#include <linux/init.h>
29
#include <linux/kthread.h>
30
#include <linux/module.h>
31
#include <linux/oom.h>
32
#include <linux/reboot.h>
33
#include <linux/sched.h>
34
#include <linux/stringify.h>
35
#include <linux/swap.h>
36
#include <linux/sysdev.h>
37
#include <asm/firmware.h>
38
#include <asm/hvcall.h>
39
#include <asm/mmu.h>
40
#include <asm/pgalloc.h>
41
#include <asm/uaccess.h>
42
#include <linux/memory.h>
43
44
#include "plpar_wrappers.h"
45
46
#define CMM_DRIVER_VERSION "1.0.0"
47
#define CMM_DEFAULT_DELAY 1
48
#define CMM_HOTPLUG_DELAY 5
49
#define CMM_DEBUG 0
50
#define CMM_DISABLE 0
51
#define CMM_OOM_KB 1024
52
#define CMM_MIN_MEM_MB 256
53
#define KB2PAGES(_p) ((_p)>>(PAGE_SHIFT-10))
54
#define PAGES2KB(_p) ((_p)<<(PAGE_SHIFT-10))
55
/*
56
* The priority level tries to ensure that this notifier is called as
57
* late as possible to reduce thrashing in the shared memory pool.
58
*/
59
#define CMM_MEM_HOTPLUG_PRI 1
60
#define CMM_MEM_ISOLATE_PRI 15
61
62
static unsigned int delay = CMM_DEFAULT_DELAY;
63
static unsigned int hotplug_delay = CMM_HOTPLUG_DELAY;
64
static unsigned int oom_kb = CMM_OOM_KB;
65
static unsigned int cmm_debug = CMM_DEBUG;
66
static unsigned int cmm_disabled = CMM_DISABLE;
67
static unsigned long min_mem_mb = CMM_MIN_MEM_MB;
68
static struct sys_device cmm_sysdev;
69
70
MODULE_AUTHOR("Brian King <[email protected]>");
71
MODULE_DESCRIPTION("IBM System p Collaborative Memory Manager");
72
MODULE_LICENSE("GPL");
73
MODULE_VERSION(CMM_DRIVER_VERSION);
74
75
module_param_named(delay, delay, uint, S_IRUGO | S_IWUSR);
76
MODULE_PARM_DESC(delay, "Delay (in seconds) between polls to query hypervisor paging requests. "
77
"[Default=" __stringify(CMM_DEFAULT_DELAY) "]");
78
module_param_named(hotplug_delay, hotplug_delay, uint, S_IRUGO | S_IWUSR);
79
MODULE_PARM_DESC(delay, "Delay (in seconds) after memory hotplug remove "
80
"before loaning resumes. "
81
"[Default=" __stringify(CMM_HOTPLUG_DELAY) "]");
82
module_param_named(oom_kb, oom_kb, uint, S_IRUGO | S_IWUSR);
83
MODULE_PARM_DESC(oom_kb, "Amount of memory in kb to free on OOM. "
84
"[Default=" __stringify(CMM_OOM_KB) "]");
85
module_param_named(min_mem_mb, min_mem_mb, ulong, S_IRUGO | S_IWUSR);
86
MODULE_PARM_DESC(min_mem_mb, "Minimum amount of memory (in MB) to not balloon. "
87
"[Default=" __stringify(CMM_MIN_MEM_MB) "]");
88
module_param_named(debug, cmm_debug, uint, S_IRUGO | S_IWUSR);
89
MODULE_PARM_DESC(debug, "Enable module debugging logging. Set to 1 to enable. "
90
"[Default=" __stringify(CMM_DEBUG) "]");
91
92
#define CMM_NR_PAGES ((PAGE_SIZE - sizeof(void *) - sizeof(unsigned long)) / sizeof(unsigned long))
93
94
#define cmm_dbg(...) if (cmm_debug) { printk(KERN_INFO "cmm: "__VA_ARGS__); }
95
96
struct cmm_page_array {
97
struct cmm_page_array *next;
98
unsigned long index;
99
unsigned long page[CMM_NR_PAGES];
100
};
101
102
static unsigned long loaned_pages;
103
static unsigned long loaned_pages_target;
104
static unsigned long oom_freed_pages;
105
106
static struct cmm_page_array *cmm_page_list;
107
static DEFINE_SPINLOCK(cmm_lock);
108
109
static DEFINE_MUTEX(hotplug_mutex);
110
static int hotplug_occurred; /* protected by the hotplug mutex */
111
112
static struct task_struct *cmm_thread_ptr;
113
114
/**
115
* cmm_alloc_pages - Allocate pages and mark them as loaned
116
* @nr: number of pages to allocate
117
*
118
* Return value:
119
* number of pages requested to be allocated which were not
120
**/
121
static long cmm_alloc_pages(long nr)
122
{
123
struct cmm_page_array *pa, *npa;
124
unsigned long addr;
125
long rc;
126
127
cmm_dbg("Begin request for %ld pages\n", nr);
128
129
while (nr) {
130
/* Exit if a hotplug operation is in progress or occurred */
131
if (mutex_trylock(&hotplug_mutex)) {
132
if (hotplug_occurred) {
133
mutex_unlock(&hotplug_mutex);
134
break;
135
}
136
mutex_unlock(&hotplug_mutex);
137
} else {
138
break;
139
}
140
141
addr = __get_free_page(GFP_NOIO | __GFP_NOWARN |
142
__GFP_NORETRY | __GFP_NOMEMALLOC);
143
if (!addr)
144
break;
145
spin_lock(&cmm_lock);
146
pa = cmm_page_list;
147
if (!pa || pa->index >= CMM_NR_PAGES) {
148
/* Need a new page for the page list. */
149
spin_unlock(&cmm_lock);
150
npa = (struct cmm_page_array *)__get_free_page(
151
GFP_NOIO | __GFP_NOWARN |
152
__GFP_NORETRY | __GFP_NOMEMALLOC);
153
if (!npa) {
154
pr_info("%s: Can not allocate new page list\n", __func__);
155
free_page(addr);
156
break;
157
}
158
spin_lock(&cmm_lock);
159
pa = cmm_page_list;
160
161
if (!pa || pa->index >= CMM_NR_PAGES) {
162
npa->next = pa;
163
npa->index = 0;
164
pa = npa;
165
cmm_page_list = pa;
166
} else
167
free_page((unsigned long) npa);
168
}
169
170
if ((rc = plpar_page_set_loaned(__pa(addr)))) {
171
pr_err("%s: Can not set page to loaned. rc=%ld\n", __func__, rc);
172
spin_unlock(&cmm_lock);
173
free_page(addr);
174
break;
175
}
176
177
pa->page[pa->index++] = addr;
178
loaned_pages++;
179
totalram_pages--;
180
spin_unlock(&cmm_lock);
181
nr--;
182
}
183
184
cmm_dbg("End request with %ld pages unfulfilled\n", nr);
185
return nr;
186
}
187
188
/**
189
* cmm_free_pages - Free pages and mark them as active
190
* @nr: number of pages to free
191
*
192
* Return value:
193
* number of pages requested to be freed which were not
194
**/
195
static long cmm_free_pages(long nr)
196
{
197
struct cmm_page_array *pa;
198
unsigned long addr;
199
200
cmm_dbg("Begin free of %ld pages.\n", nr);
201
spin_lock(&cmm_lock);
202
pa = cmm_page_list;
203
while (nr) {
204
if (!pa || pa->index <= 0)
205
break;
206
addr = pa->page[--pa->index];
207
208
if (pa->index == 0) {
209
pa = pa->next;
210
free_page((unsigned long) cmm_page_list);
211
cmm_page_list = pa;
212
}
213
214
plpar_page_set_active(__pa(addr));
215
free_page(addr);
216
loaned_pages--;
217
nr--;
218
totalram_pages++;
219
}
220
spin_unlock(&cmm_lock);
221
cmm_dbg("End request with %ld pages unfulfilled\n", nr);
222
return nr;
223
}
224
225
/**
226
* cmm_oom_notify - OOM notifier
227
* @self: notifier block struct
228
* @dummy: not used
229
* @parm: returned - number of pages freed
230
*
231
* Return value:
232
* NOTIFY_OK
233
**/
234
static int cmm_oom_notify(struct notifier_block *self,
235
unsigned long dummy, void *parm)
236
{
237
unsigned long *freed = parm;
238
long nr = KB2PAGES(oom_kb);
239
240
cmm_dbg("OOM processing started\n");
241
nr = cmm_free_pages(nr);
242
loaned_pages_target = loaned_pages;
243
*freed += KB2PAGES(oom_kb) - nr;
244
oom_freed_pages += KB2PAGES(oom_kb) - nr;
245
cmm_dbg("OOM processing complete\n");
246
return NOTIFY_OK;
247
}
248
249
/**
250
* cmm_get_mpp - Read memory performance parameters
251
*
252
* Makes hcall to query the current page loan request from the hypervisor.
253
*
254
* Return value:
255
* nothing
256
**/
257
static void cmm_get_mpp(void)
258
{
259
int rc;
260
struct hvcall_mpp_data mpp_data;
261
signed long active_pages_target, page_loan_request, target;
262
signed long total_pages = totalram_pages + loaned_pages;
263
signed long min_mem_pages = (min_mem_mb * 1024 * 1024) / PAGE_SIZE;
264
265
rc = h_get_mpp(&mpp_data);
266
267
if (rc != H_SUCCESS)
268
return;
269
270
page_loan_request = div_s64((s64)mpp_data.loan_request, PAGE_SIZE);
271
target = page_loan_request + (signed long)loaned_pages;
272
273
if (target < 0 || total_pages < min_mem_pages)
274
target = 0;
275
276
if (target > oom_freed_pages)
277
target -= oom_freed_pages;
278
else
279
target = 0;
280
281
active_pages_target = total_pages - target;
282
283
if (min_mem_pages > active_pages_target)
284
target = total_pages - min_mem_pages;
285
286
if (target < 0)
287
target = 0;
288
289
loaned_pages_target = target;
290
291
cmm_dbg("delta = %ld, loaned = %lu, target = %lu, oom = %lu, totalram = %lu\n",
292
page_loan_request, loaned_pages, loaned_pages_target,
293
oom_freed_pages, totalram_pages);
294
}
295
296
static struct notifier_block cmm_oom_nb = {
297
.notifier_call = cmm_oom_notify
298
};
299
300
/**
301
* cmm_thread - CMM task thread
302
* @dummy: not used
303
*
304
* Return value:
305
* 0
306
**/
307
static int cmm_thread(void *dummy)
308
{
309
unsigned long timeleft;
310
311
while (1) {
312
timeleft = msleep_interruptible(delay * 1000);
313
314
if (kthread_should_stop() || timeleft)
315
break;
316
317
if (mutex_trylock(&hotplug_mutex)) {
318
if (hotplug_occurred) {
319
hotplug_occurred = 0;
320
mutex_unlock(&hotplug_mutex);
321
cmm_dbg("Hotplug operation has occurred, "
322
"loaning activity suspended "
323
"for %d seconds.\n",
324
hotplug_delay);
325
timeleft = msleep_interruptible(hotplug_delay *
326
1000);
327
if (kthread_should_stop() || timeleft)
328
break;
329
continue;
330
}
331
mutex_unlock(&hotplug_mutex);
332
} else {
333
cmm_dbg("Hotplug operation in progress, activity "
334
"suspended\n");
335
continue;
336
}
337
338
cmm_get_mpp();
339
340
if (loaned_pages_target > loaned_pages) {
341
if (cmm_alloc_pages(loaned_pages_target - loaned_pages))
342
loaned_pages_target = loaned_pages;
343
} else if (loaned_pages_target < loaned_pages)
344
cmm_free_pages(loaned_pages - loaned_pages_target);
345
}
346
return 0;
347
}
348
349
#define CMM_SHOW(name, format, args...) \
350
static ssize_t show_##name(struct sys_device *dev, \
351
struct sysdev_attribute *attr, \
352
char *buf) \
353
{ \
354
return sprintf(buf, format, ##args); \
355
} \
356
static SYSDEV_ATTR(name, S_IRUGO, show_##name, NULL)
357
358
CMM_SHOW(loaned_kb, "%lu\n", PAGES2KB(loaned_pages));
359
CMM_SHOW(loaned_target_kb, "%lu\n", PAGES2KB(loaned_pages_target));
360
361
static ssize_t show_oom_pages(struct sys_device *dev,
362
struct sysdev_attribute *attr, char *buf)
363
{
364
return sprintf(buf, "%lu\n", PAGES2KB(oom_freed_pages));
365
}
366
367
static ssize_t store_oom_pages(struct sys_device *dev,
368
struct sysdev_attribute *attr,
369
const char *buf, size_t count)
370
{
371
unsigned long val = simple_strtoul (buf, NULL, 10);
372
373
if (!capable(CAP_SYS_ADMIN))
374
return -EPERM;
375
if (val != 0)
376
return -EBADMSG;
377
378
oom_freed_pages = 0;
379
return count;
380
}
381
382
static SYSDEV_ATTR(oom_freed_kb, S_IWUSR| S_IRUGO,
383
show_oom_pages, store_oom_pages);
384
385
static struct sysdev_attribute *cmm_attrs[] = {
386
&attr_loaned_kb,
387
&attr_loaned_target_kb,
388
&attr_oom_freed_kb,
389
};
390
391
static struct sysdev_class cmm_sysdev_class = {
392
.name = "cmm",
393
};
394
395
/**
396
* cmm_sysfs_register - Register with sysfs
397
*
398
* Return value:
399
* 0 on success / other on failure
400
**/
401
static int cmm_sysfs_register(struct sys_device *sysdev)
402
{
403
int i, rc;
404
405
if ((rc = sysdev_class_register(&cmm_sysdev_class)))
406
return rc;
407
408
sysdev->id = 0;
409
sysdev->cls = &cmm_sysdev_class;
410
411
if ((rc = sysdev_register(sysdev)))
412
goto class_unregister;
413
414
for (i = 0; i < ARRAY_SIZE(cmm_attrs); i++) {
415
if ((rc = sysdev_create_file(sysdev, cmm_attrs[i])))
416
goto fail;
417
}
418
419
return 0;
420
421
fail:
422
while (--i >= 0)
423
sysdev_remove_file(sysdev, cmm_attrs[i]);
424
sysdev_unregister(sysdev);
425
class_unregister:
426
sysdev_class_unregister(&cmm_sysdev_class);
427
return rc;
428
}
429
430
/**
431
* cmm_unregister_sysfs - Unregister from sysfs
432
*
433
**/
434
static void cmm_unregister_sysfs(struct sys_device *sysdev)
435
{
436
int i;
437
438
for (i = 0; i < ARRAY_SIZE(cmm_attrs); i++)
439
sysdev_remove_file(sysdev, cmm_attrs[i]);
440
sysdev_unregister(sysdev);
441
sysdev_class_unregister(&cmm_sysdev_class);
442
}
443
444
/**
445
* cmm_reboot_notifier - Make sure pages are not still marked as "loaned"
446
*
447
**/
448
static int cmm_reboot_notifier(struct notifier_block *nb,
449
unsigned long action, void *unused)
450
{
451
if (action == SYS_RESTART) {
452
if (cmm_thread_ptr)
453
kthread_stop(cmm_thread_ptr);
454
cmm_thread_ptr = NULL;
455
cmm_free_pages(loaned_pages);
456
}
457
return NOTIFY_DONE;
458
}
459
460
static struct notifier_block cmm_reboot_nb = {
461
.notifier_call = cmm_reboot_notifier,
462
};
463
464
/**
465
* cmm_count_pages - Count the number of pages loaned in a particular range.
466
*
467
* @arg: memory_isolate_notify structure with address range and count
468
*
469
* Return value:
470
* 0 on success
471
**/
472
static unsigned long cmm_count_pages(void *arg)
473
{
474
struct memory_isolate_notify *marg = arg;
475
struct cmm_page_array *pa;
476
unsigned long start = (unsigned long)pfn_to_kaddr(marg->start_pfn);
477
unsigned long end = start + (marg->nr_pages << PAGE_SHIFT);
478
unsigned long idx;
479
480
spin_lock(&cmm_lock);
481
pa = cmm_page_list;
482
while (pa) {
483
if ((unsigned long)pa >= start && (unsigned long)pa < end)
484
marg->pages_found++;
485
for (idx = 0; idx < pa->index; idx++)
486
if (pa->page[idx] >= start && pa->page[idx] < end)
487
marg->pages_found++;
488
pa = pa->next;
489
}
490
spin_unlock(&cmm_lock);
491
return 0;
492
}
493
494
/**
495
* cmm_memory_isolate_cb - Handle memory isolation notifier calls
496
* @self: notifier block struct
497
* @action: action to take
498
* @arg: struct memory_isolate_notify data for handler
499
*
500
* Return value:
501
* NOTIFY_OK or notifier error based on subfunction return value
502
**/
503
static int cmm_memory_isolate_cb(struct notifier_block *self,
504
unsigned long action, void *arg)
505
{
506
int ret = 0;
507
508
if (action == MEM_ISOLATE_COUNT)
509
ret = cmm_count_pages(arg);
510
511
return notifier_from_errno(ret);
512
}
513
514
static struct notifier_block cmm_mem_isolate_nb = {
515
.notifier_call = cmm_memory_isolate_cb,
516
.priority = CMM_MEM_ISOLATE_PRI
517
};
518
519
/**
520
* cmm_mem_going_offline - Unloan pages where memory is to be removed
521
* @arg: memory_notify structure with page range to be offlined
522
*
523
* Return value:
524
* 0 on success
525
**/
526
static int cmm_mem_going_offline(void *arg)
527
{
528
struct memory_notify *marg = arg;
529
unsigned long start_page = (unsigned long)pfn_to_kaddr(marg->start_pfn);
530
unsigned long end_page = start_page + (marg->nr_pages << PAGE_SHIFT);
531
struct cmm_page_array *pa_curr, *pa_last, *npa;
532
unsigned long idx;
533
unsigned long freed = 0;
534
535
cmm_dbg("Memory going offline, searching 0x%lx (%ld pages).\n",
536
start_page, marg->nr_pages);
537
spin_lock(&cmm_lock);
538
539
/* Search the page list for pages in the range to be offlined */
540
pa_last = pa_curr = cmm_page_list;
541
while (pa_curr) {
542
for (idx = (pa_curr->index - 1); (idx + 1) > 0; idx--) {
543
if ((pa_curr->page[idx] < start_page) ||
544
(pa_curr->page[idx] >= end_page))
545
continue;
546
547
plpar_page_set_active(__pa(pa_curr->page[idx]));
548
free_page(pa_curr->page[idx]);
549
freed++;
550
loaned_pages--;
551
totalram_pages++;
552
pa_curr->page[idx] = pa_last->page[--pa_last->index];
553
if (pa_last->index == 0) {
554
if (pa_curr == pa_last)
555
pa_curr = pa_last->next;
556
pa_last = pa_last->next;
557
free_page((unsigned long)cmm_page_list);
558
cmm_page_list = pa_last;
559
continue;
560
}
561
}
562
pa_curr = pa_curr->next;
563
}
564
565
/* Search for page list structures in the range to be offlined */
566
pa_last = NULL;
567
pa_curr = cmm_page_list;
568
while (pa_curr) {
569
if (((unsigned long)pa_curr >= start_page) &&
570
((unsigned long)pa_curr < end_page)) {
571
npa = (struct cmm_page_array *)__get_free_page(
572
GFP_NOIO | __GFP_NOWARN |
573
__GFP_NORETRY | __GFP_NOMEMALLOC);
574
if (!npa) {
575
spin_unlock(&cmm_lock);
576
cmm_dbg("Failed to allocate memory for list "
577
"management. Memory hotplug "
578
"failed.\n");
579
return ENOMEM;
580
}
581
memcpy(npa, pa_curr, PAGE_SIZE);
582
if (pa_curr == cmm_page_list)
583
cmm_page_list = npa;
584
if (pa_last)
585
pa_last->next = npa;
586
free_page((unsigned long) pa_curr);
587
freed++;
588
pa_curr = npa;
589
}
590
591
pa_last = pa_curr;
592
pa_curr = pa_curr->next;
593
}
594
595
spin_unlock(&cmm_lock);
596
cmm_dbg("Released %ld pages in the search range.\n", freed);
597
598
return 0;
599
}
600
601
/**
602
* cmm_memory_cb - Handle memory hotplug notifier calls
603
* @self: notifier block struct
604
* @action: action to take
605
* @arg: struct memory_notify data for handler
606
*
607
* Return value:
608
* NOTIFY_OK or notifier error based on subfunction return value
609
*
610
**/
611
static int cmm_memory_cb(struct notifier_block *self,
612
unsigned long action, void *arg)
613
{
614
int ret = 0;
615
616
switch (action) {
617
case MEM_GOING_OFFLINE:
618
mutex_lock(&hotplug_mutex);
619
hotplug_occurred = 1;
620
ret = cmm_mem_going_offline(arg);
621
break;
622
case MEM_OFFLINE:
623
case MEM_CANCEL_OFFLINE:
624
mutex_unlock(&hotplug_mutex);
625
cmm_dbg("Memory offline operation complete.\n");
626
break;
627
case MEM_GOING_ONLINE:
628
case MEM_ONLINE:
629
case MEM_CANCEL_ONLINE:
630
break;
631
}
632
633
return notifier_from_errno(ret);
634
}
635
636
static struct notifier_block cmm_mem_nb = {
637
.notifier_call = cmm_memory_cb,
638
.priority = CMM_MEM_HOTPLUG_PRI
639
};
640
641
/**
642
* cmm_init - Module initialization
643
*
644
* Return value:
645
* 0 on success / other on failure
646
**/
647
static int cmm_init(void)
648
{
649
int rc = -ENOMEM;
650
651
if (!firmware_has_feature(FW_FEATURE_CMO))
652
return -EOPNOTSUPP;
653
654
if ((rc = register_oom_notifier(&cmm_oom_nb)) < 0)
655
return rc;
656
657
if ((rc = register_reboot_notifier(&cmm_reboot_nb)))
658
goto out_oom_notifier;
659
660
if ((rc = cmm_sysfs_register(&cmm_sysdev)))
661
goto out_reboot_notifier;
662
663
if (register_memory_notifier(&cmm_mem_nb) ||
664
register_memory_isolate_notifier(&cmm_mem_isolate_nb))
665
goto out_unregister_notifier;
666
667
if (cmm_disabled)
668
return rc;
669
670
cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
671
if (IS_ERR(cmm_thread_ptr)) {
672
rc = PTR_ERR(cmm_thread_ptr);
673
goto out_unregister_notifier;
674
}
675
676
return rc;
677
678
out_unregister_notifier:
679
unregister_memory_notifier(&cmm_mem_nb);
680
unregister_memory_isolate_notifier(&cmm_mem_isolate_nb);
681
cmm_unregister_sysfs(&cmm_sysdev);
682
out_reboot_notifier:
683
unregister_reboot_notifier(&cmm_reboot_nb);
684
out_oom_notifier:
685
unregister_oom_notifier(&cmm_oom_nb);
686
return rc;
687
}
688
689
/**
690
* cmm_exit - Module exit
691
*
692
* Return value:
693
* nothing
694
**/
695
static void cmm_exit(void)
696
{
697
if (cmm_thread_ptr)
698
kthread_stop(cmm_thread_ptr);
699
unregister_oom_notifier(&cmm_oom_nb);
700
unregister_reboot_notifier(&cmm_reboot_nb);
701
unregister_memory_notifier(&cmm_mem_nb);
702
unregister_memory_isolate_notifier(&cmm_mem_isolate_nb);
703
cmm_free_pages(loaned_pages);
704
cmm_unregister_sysfs(&cmm_sysdev);
705
}
706
707
/**
708
* cmm_set_disable - Disable/Enable CMM
709
*
710
* Return value:
711
* 0 on success / other on failure
712
**/
713
static int cmm_set_disable(const char *val, struct kernel_param *kp)
714
{
715
int disable = simple_strtoul(val, NULL, 10);
716
717
if (disable != 0 && disable != 1)
718
return -EINVAL;
719
720
if (disable && !cmm_disabled) {
721
if (cmm_thread_ptr)
722
kthread_stop(cmm_thread_ptr);
723
cmm_thread_ptr = NULL;
724
cmm_free_pages(loaned_pages);
725
} else if (!disable && cmm_disabled) {
726
cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
727
if (IS_ERR(cmm_thread_ptr))
728
return PTR_ERR(cmm_thread_ptr);
729
}
730
731
cmm_disabled = disable;
732
return 0;
733
}
734
735
module_param_call(disable, cmm_set_disable, param_get_uint,
736
&cmm_disabled, S_IRUGO | S_IWUSR);
737
MODULE_PARM_DESC(disable, "Disable CMM. Set to 1 to disable. "
738
"[Default=" __stringify(CMM_DISABLE) "]");
739
740
module_init(cmm_init);
741
module_exit(cmm_exit);
742
743