diff --git a/Documentation/vm/ksm.txt b/Documentation/vm/ksm.txt index b392e496f816..8e549819277b 100644 --- a/Documentation/vm/ksm.txt +++ b/Documentation/vm/ksm.txt @@ -72,6 +72,13 @@ pages_sharing - how many more sites are sharing them i.e. how much saved pages_unshared - how many pages unique but repeatedly checked for merging pages_volatile - how many pages changing too fast to be placed in a tree full_scans - how many times all mergeable areas have been scanned +deferred_timer - whether to use deferred timers or not + e.g. "echo 1 > /sys/kernel/mm/ksm/deferred_timer" + Default: 0 (means, we are not using deferred timers. Users + might want to set deferred_timer option if they donot want + ksm thread to wakeup CPU to carryout ksm activities thus + gaining on battery while compromising slightly on memory + that could have been saved.) A high ratio of pages_sharing to pages_shared indicates good sharing, but a high ratio of pages_unshared to pages_sharing indicates wasted effort. diff --git a/mm/ksm.c b/mm/ksm.c index 47c885368890..fa73fc610686 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -189,6 +189,9 @@ static unsigned int ksm_thread_pages_to_scan = 100; /* Milliseconds ksmd should sleep between batches */ static unsigned int ksm_thread_sleep_millisecs = 20; +/* Boolean to indicate whether to use deferred timer or not */ +static bool use_deferred_timer; + #define KSM_RUN_STOP 0 #define KSM_RUN_MERGE 1 #define KSM_RUN_UNMERGE 2 @@ -1427,6 +1430,41 @@ static void ksm_do_scan(unsigned int scan_npages) } } +static void process_timeout(unsigned long __data) +{ + wake_up_process((struct task_struct *)__data); +} + +static signed long __sched deferred_schedule_timeout(signed long timeout) +{ + struct timer_list timer; + unsigned long expire; + + __set_current_state(TASK_INTERRUPTIBLE); + if (timeout < 0) { + pr_err("schedule_timeout: wrong timeout value %lx\n", + timeout); + __set_current_state(TASK_RUNNING); + goto out; + } + + expire = timeout + jiffies; + + setup_deferrable_timer_on_stack(&timer, process_timeout, + (unsigned long)current); + mod_timer(&timer, expire); + schedule(); + del_singleshot_timer_sync(&timer); + + /* Remove the timer from the object tracker */ + destroy_timer_on_stack(&timer); + + timeout = expire - jiffies; + +out: + return timeout < 0 ? 0 : timeout; +} + static int ksmd_should_run(void) { return (ksm_run & KSM_RUN_MERGE) && !list_empty(&ksm_mm_head.mm_list); @@ -1446,7 +1484,11 @@ static int ksm_scan_thread(void *nothing) try_to_freeze(); if (ksmd_should_run()) { - schedule_timeout_interruptible( + if (use_deferred_timer) + deferred_schedule_timeout( + msecs_to_jiffies(ksm_thread_sleep_millisecs)); + else + schedule_timeout_interruptible( msecs_to_jiffies(ksm_thread_sleep_millisecs)); } else { wait_event_freezable(ksm_thread_wait, @@ -1926,6 +1968,26 @@ static ssize_t run_store(struct kobject *kobj, struct kobj_attribute *attr, } KSM_ATTR(run); +static ssize_t deferred_timer_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return snprintf(buf, 8, "%d\n", use_deferred_timer); +} + +static ssize_t deferred_timer_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + unsigned long enable; + int err; + + err = kstrtoul(buf, 10, &enable); + use_deferred_timer = enable; + + return count; +} +KSM_ATTR(deferred_timer); + static ssize_t pages_shared_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -1980,6 +2042,7 @@ static struct attribute *ksm_attrs[] = { &pages_unshared_attr.attr, &pages_volatile_attr.attr, &full_scans_attr.attr, + &deferred_timer_attr.attr, NULL, };