Trigger unmerge or remove a partition using the following sysfs interface: Triggering an unmerge for a specific partition: echo "pid" > /sys/kernel/mm/ksm/partition_name/trigger_unmerge Removing a partition: echo "partition_to_remove" > /sys/kernel/mm/ksm/control/remove_partition Limitation of current implementation: On carrying out trigger_unmerge, we unmerge all rmap items which is wrong. We should only unmerge the rmap items that belong to the partition where we called unmerge. Another limitation is that we do not specify the address range when echoing into trigger unmerge. Intentionally left out till until we determine the implementation feasibility. Signed-off-by: Sourav Panda <souravpanda@xxxxxxxxxx> --- mm/ksm.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) diff --git a/mm/ksm.c b/mm/ksm.c index b575250aaf45..fd7626d5d8c9 100644 --- a/mm/ksm.c +++ b/mm/ksm.c @@ -2556,6 +2556,31 @@ static void ksm_sync_merge(struct mm_struct *mm, put_page(page); } +static void ksm_sync_unmerge(struct mm_struct *mm) +{ + struct mm_slot *slot; + struct ksm_mm_slot *mm_slot; + + struct vm_area_struct *vma; + struct vma_iterator vmi; + + slot = mm_slot_lookup(mm_slots_hash, mm); + mm_slot = container_of(slot, struct ksm_mm_slot, slot); + + ksm_scan.address = 0; + vma_iter_init(&vmi, mm, ksm_scan.address); + + mmap_read_lock(mm); + for_each_vma(vmi, vma) { + if (!(vma->vm_flags & VM_MERGEABLE) || !vma->anon_vma) + continue; + unmerge_ksm_pages(vma, vma->vm_start, vma->vm_end, false); + } + remove_trailing_rmap_items(&mm_slot->rmap_list); + + mmap_read_unlock(mm); +} + #else /* CONFIG_SELECTIVE_KSM */ /* * Calculate skip age for the ksm page age. The age determines how often @@ -3644,6 +3669,58 @@ static ssize_t trigger_merge_store(struct kobject *kobj, } KSM_ATTR(trigger_merge); +static ssize_t trigger_unmerge_show(struct kobject *kobj, + struct kobj_attribute *attr, + char *buf) +{ + return -EINVAL; /* Not yet implemented */ +} + +static ssize_t trigger_unmerge_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + pid_t pid; + char *input, *ptr; + int ret; + struct task_struct *task; + struct mm_struct *mm; + + input = kstrdup(buf, GFP_KERNEL); + if (!input) + return -ENOMEM; + + ptr = strim(input); + ret = kstrtoint(ptr, 10, &pid); + kfree(input); + + /* Find the mm_struct */ + rcu_read_lock(); + task = find_task_by_vpid(pid); + if (!task) { + rcu_read_unlock(); + return -ESRCH; + } + + get_task_struct(task); + + rcu_read_unlock(); + mm = get_task_mm(task); + put_task_struct(task); + + if (!mm) + return -EINVAL; + + mutex_lock(&ksm_thread_mutex); + wait_while_offlining(); + ksm_sync_unmerge(mm); + mutex_unlock(&ksm_thread_mutex); + + mmput(mm); + return count; +} +KSM_ATTR(trigger_unmerge); + #ifdef CONFIG_NUMA static ssize_t merge_across_nodes_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) @@ -4044,6 +4121,7 @@ static struct attribute *ksm_attrs[] = { &pages_to_scan_attr.attr, &run_attr.attr, &trigger_merge_attr.attr, + &trigger_unmerge_attr.attr, &pages_scanned_attr.attr, &pages_shared_attr.attr, &pages_sharing_attr.attr, @@ -4156,9 +4234,51 @@ static ssize_t add_partition_store(struct kobject *kobj, static struct kobj_attribute add_kobj_attr = __ATTR(add_partition, 0220, NULL, add_partition_store); +static ssize_t remove_partition_store(struct kobject *kobj, + struct kobj_attribute *attr, + const char *buf, size_t count) +{ + struct partition_kobj *partition; + struct partition_kobj *partition_found = NULL; + char partition_name[50]; + int err = 0; + + if (sscanf(buf, "%31s", partition_name) != 1) + return -EINVAL; + + mutex_lock(&ksm_thread_mutex); + + list_for_each_entry(partition, &partition_list, list) { + if (strcmp(kobject_name(partition->kobj), partition_name) == 0) { + partition_found = partition; + break; + } + } + + if (!partition_found) { + err = -ENOENT; + goto unlock; + } + + unmerge_and_remove_all_rmap_items(); + + kobject_put(partition_found->kobj); + list_del(&partition_found->list); + kfree(partition_found->root_stable_tree); + kfree(partition_found); + +unlock: + mutex_unlock(&ksm_thread_mutex); + return err ? err : count; +} + +static struct kobj_attribute rm_kobj_attr = __ATTR(remove_partition, 0220, NULL, + remove_partition_store); + /* Array of attributes for base kobject */ static struct attribute *ksm_base_attrs[] = { &add_kobj_attr.attr, + &rm_kobj_attr.attr, NULL, /* NULL-terminated */ }; -- 2.49.0.395.g12beb8f557-goog