2014-11-08 23:01:51

by Timofey Titovets

[permalink] [raw]
Subject: [PATCH v2 0/3] KSM: Mark new vma for deduplication

Good time of day List,
this tiny series of patches implement feature for auto deduping all anonymous memory.
mark_new_vma - new ksm sysfs interface
Every time then new vma created and mark_new_vma set to 1, then will be vma marked as VM_MERGEABLE and added to ksm queue.
This can produce small overheads
(I have not catch any problems or slowdown)

This is useful for:
Android (CM) devs which implement ksm support with patching system.
Users of tiny pc.
Servers what not use KVM but use something very releated, like containers.

For tests:
I have tested it and it working very good. For testing apply it and enable ksm:
echo 1 | sudo tee /sys/kernel/mm/ksm/run
This show how much memory saved:
echo $[$(cat /sys/kernel/mm/ksm/pages_shared)*$(getconf PAGE_SIZE)/1024 ]KB

On my system i save ~1% of memory 26 Mb/2100 Mb (deduped)/(used)

Timofey Titovets (3):
KSM: Add auto flag new VMA as VM_MERGEABLE
KSM: Add to sysfs - mark_new_vma
KSM: Add config to control mark_new_vma

include/linux/ksm.h | 39 +++++++++++++++++++++++++++++++++++++++
mm/Kconfig | 7 +++++++
mm/ksm.c | 39 ++++++++++++++++++++++++++++++++++++++-
mm/mmap.c | 17 +++++++++++++++++
4 files changed, 101 insertions(+), 1 deletion(-)

--
2.1.3


2014-11-08 23:01:57

by Timofey Titovets

[permalink] [raw]
Subject: [PATCH v2 2/3] KSM: Add to sysfs - mark_new_vma

It allow for user to control process of marking new vma as VM_MERGEABLE
Create new sysfs interface /sys/kernel/mm/ksm/mark_new_vma
1 - enabled - mark new allocated vma as VM_MERGEABLE and add it to ksm queue
0 - disable it

Signed-off-by: Timofey Titovets <[email protected]>
---
include/linux/ksm.h | 10 +++++++++-
mm/ksm.c | 39 ++++++++++++++++++++++++++++++++++++++-
2 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/include/linux/ksm.h b/include/linux/ksm.h
index c3fff64..deb60fc 100644
--- a/include/linux/ksm.h
+++ b/include/linux/ksm.h
@@ -76,6 +76,12 @@ struct page *ksm_might_need_to_copy(struct page *page,
int rmap_walk_ksm(struct page *page, struct rmap_walk_control *rwc);
void ksm_migrate_page(struct page *newpage, struct page *oldpage);

+
+/* Mark new vma as mergeable */
+#define ALLOW 1
+#define DENY 0
+extern unsigned mark_new_vma __read_mostly;
+
/*
* Allow to mark new vma as VM_MERGEABLE
*/
@@ -84,6 +90,8 @@ void ksm_migrate_page(struct page *newpage, struct page *oldpage);
#endif
static inline void ksm_vm_flags_mod(unsigned long *vm_flags)
{
+ if (!mark_new_vma)
+ return;
if (*vm_flags & (VM_MERGEABLE | VM_SHARED | VM_MAYSHARE |
VM_PFNMAP | VM_IO | VM_DONTEXPAND |
VM_HUGETLB | VM_NONLINEAR | VM_MIXEDMAP | VM_SAO) )
@@ -94,7 +102,7 @@ static inline void ksm_vm_flags_mod(unsigned long *vm_flags)
static inline void ksm_vma_add_new(struct vm_area_struct *vma)
{
struct mm_struct *mm = vma->vm_mm;
- if (!test_bit(MMF_VM_MERGEABLE, &mm->flags)) {
+ if (mark_new_vma && !test_bit(MMF_VM_MERGEABLE, &mm->flags)) {
__ksm_enter(mm);
}
}
diff --git a/mm/ksm.c b/mm/ksm.c
index 6b2e337..7bfb616 100644
--- a/mm/ksm.c
+++ b/mm/ksm.c
@@ -223,6 +223,12 @@ static unsigned int ksm_thread_pages_to_scan = 100;
/* Milliseconds ksmd should sleep between batches */
static unsigned int ksm_thread_sleep_millisecs = 20;

+/* Mark new vma as mergeable */
+#ifndef CONFIG_KSM_MARK_NEW_VMA
+#define CONFIG_KSM_MARK_NEW_VMA DENY
+#endif
+unsigned mark_new_vma = CONFIG_KSM_MARK_NEW_VMA;
+
#ifdef CONFIG_NUMA
/* Zeroed when merging across nodes is not allowed */
static unsigned int ksm_merge_across_nodes = 1;
@@ -2127,6 +2133,34 @@ static ssize_t pages_to_scan_store(struct kobject *kobj,
}
KSM_ATTR(pages_to_scan);

+static ssize_t mark_new_vma_show(struct kobject *kobj, struct kobj_attribute *attr,
+ char *buf)
+{
+ return sprintf(buf, "%u\n", mark_new_vma);
+}
+static ssize_t mark_new_vma_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t count)
+{
+ int err;
+ unsigned long flags;
+
+ err = kstrtoul(buf, 10, &flags);
+ if (err || flags > UINT_MAX || flags > ALLOW)
+ return -EINVAL;
+
+ /*
+ * ALLOW = 1 - sets allow for mark new vma as
+ * VM_MERGEABLE and adding it to ksm
+ * DENY = 0 - disable it
+ */
+ if (mark_new_vma != flags) {
+ mark_new_vma = flags;
+ }
+ return count;
+}
+KSM_ATTR(mark_new_vma);
+
static ssize_t run_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
@@ -2287,6 +2321,7 @@ static struct attribute *ksm_attrs[] = {
&pages_unshared_attr.attr,
&pages_volatile_attr.attr,
&full_scans_attr.attr,
+ &mark_new_vma_attr.attr,
#ifdef CONFIG_NUMA
&merge_across_nodes_attr.attr,
#endif
@@ -2323,7 +2358,9 @@ static int __init ksm_init(void)
goto out_free;
}
#else
- ksm_run = KSM_RUN_MERGE; /* no way for user to start it */
+ /* no way for user to start it */
+ mark_new_vma = ALLOW;
+ ksm_run = KSM_RUN_MERGE;

#endif /* CONFIG_SYSFS */

--
2.1.3

2014-11-08 23:01:55

by Timofey Titovets

[permalink] [raw]
Subject: [PATCH v2 1/3] KSM: Add auto flag new VMA as VM_MERGEABLE

Implement two functions:
ksm_vm_flags_mod() - if existing flags supported by ksm - then mark like VM_MERGEABLE
ksm_vma_add_new() - If vma marked as VM_MERGEABLE add it to ksm page queue

Signed-off-by: Timofey Titovets <[email protected]>
---
include/linux/ksm.h | 31 +++++++++++++++++++++++++++++++
mm/mmap.c | 17 +++++++++++++++++
2 files changed, 48 insertions(+)

diff --git a/include/linux/ksm.h b/include/linux/ksm.h
index 3be6bb1..c3fff64 100644
--- a/include/linux/ksm.h
+++ b/include/linux/ksm.h
@@ -76,6 +76,29 @@ struct page *ksm_might_need_to_copy(struct page *page,
int rmap_walk_ksm(struct page *page, struct rmap_walk_control *rwc);
void ksm_migrate_page(struct page *newpage, struct page *oldpage);

+/*
+ * Allow to mark new vma as VM_MERGEABLE
+ */
+#ifndef VM_SAO
+#define VM_SAO 0
+#endif
+static inline void ksm_vm_flags_mod(unsigned long *vm_flags)
+{
+ if (*vm_flags & (VM_MERGEABLE | VM_SHARED | VM_MAYSHARE |
+ VM_PFNMAP | VM_IO | VM_DONTEXPAND |
+ VM_HUGETLB | VM_NONLINEAR | VM_MIXEDMAP | VM_SAO) )
+ return;
+ *vm_flags |= VM_MERGEABLE;
+}
+
+static inline void ksm_vma_add_new(struct vm_area_struct *vma)
+{
+ struct mm_struct *mm = vma->vm_mm;
+ if (!test_bit(MMF_VM_MERGEABLE, &mm->flags)) {
+ __ksm_enter(mm);
+ }
+}
+
#else /* !CONFIG_KSM */

static inline int ksm_fork(struct mm_struct *mm, struct mm_struct *oldmm)
@@ -92,6 +115,14 @@ static inline int PageKsm(struct page *page)
return 0;
}

+static inline void ksm_vm_flags_mod(unsigned long *vm_flags_p)
+{
+}
+
+void ksm_vma_add_new(struct vm_area_struct *vma)
+{
+}
+
#ifdef CONFIG_MMU
static inline int ksm_madvise(struct vm_area_struct *vma, unsigned long start,
unsigned long end, int advice, unsigned long *vm_flags)
diff --git a/mm/mmap.c b/mm/mmap.c
index 7f85520..ce0073e 100644
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -41,6 +41,7 @@
#include <linux/notifier.h>
#include <linux/memory.h>
#include <linux/printk.h>
+#include <linux/ksm.h>

#include <asm/uaccess.h>
#include <asm/cacheflush.h>
@@ -911,10 +912,14 @@ again: remove_next = 1 + (end > next->vm_end);
vma_gap_update(next);
else
mm->highest_vm_end = end;
+ } else {
+ if (next && !insert)
+ ksm_vma_add_new(next);
}
if (insert && file)
uprobe_mmap(insert);

+ ksm_vma_add_new(vma);
validate_mm(mm);

return 0;
@@ -1307,6 +1312,9 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
vm_flags = calc_vm_prot_bits(prot) | calc_vm_flag_bits(flags) |
mm->def_flags | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC;

+ /* If ksm is enabled, we add VM_MERGABLE to new VMAs. */
+ ksm_vm_flags_mod(&vm_flags);
+
if (flags & MAP_LOCKED)
if (!can_do_mlock())
return -EPERM;
@@ -1648,6 +1656,7 @@ munmap_back:
allow_write_access(file);
}
file = vma->vm_file;
+ ksm_vma_add_new(vma);
out:
perf_event_mmap(vma);

@@ -2484,6 +2493,8 @@ static int __split_vma(struct mm_struct *mm, struct vm_area_struct *vma,
else
err = vma_adjust(vma, vma->vm_start, addr, vma->vm_pgoff, new);

+ ksm_vma_add_new(vma);
+
/* Success. */
if (!err)
return 0;
@@ -2659,6 +2670,9 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
if (error)
return error;

+ /* If ksm is enabled, we add VM_MERGABLE to new VMAs. */
+ ksm_vm_flags_mod(&flags);
+
/*
* mm->mmap_sem is required to protect against another thread
* changing the mappings in case we sleep.
@@ -2708,6 +2722,7 @@ static unsigned long do_brk(unsigned long addr, unsigned long len)
vma->vm_flags = flags;
vma->vm_page_prot = vm_get_page_prot(flags);
vma_link(mm, vma, prev, rb_link, rb_parent);
+ ksm_vma_add_new(vma);
out:
perf_event_mmap(vma);
mm->total_vm += len >> PAGE_SHIFT;
@@ -2887,6 +2902,7 @@ struct vm_area_struct *copy_vma(struct vm_area_struct **vmap,
new_vma->vm_ops->open(new_vma);
vma_link(mm, new_vma, prev, rb_link, rb_parent);
*need_rmap_locks = false;
+ ksm_vma_add_new(new_vma);
}
}
return new_vma;
@@ -3004,6 +3020,7 @@ static struct vm_area_struct *__install_special_mapping(
mm->total_vm += len >> PAGE_SHIFT;

perf_event_mmap(vma);
+ ksm_vma_add_new(vma);

return vma;

--
2.1.3

2014-11-08 23:02:28

by Timofey Titovets

[permalink] [raw]
Subject: [PATCH v2 3/3] KSM: Add config to control mark_new_vma

Allowing to control mark_new_vma default value
Allowing work ksm on early allocated vmas

Signed-off-by: Timofey Titovets <[email protected]>
---
mm/Kconfig | 7 +++++++
1 file changed, 7 insertions(+)

diff --git a/mm/Kconfig b/mm/Kconfig
index 1d1ae6b..90f40a6 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -340,6 +340,13 @@ config KSM
until a program has madvised that an area is MADV_MERGEABLE, and
root has set /sys/kernel/mm/ksm/run to 1 (if CONFIG_SYSFS is set).

+config KSM_MARK_NEW_VMA
+ int "Marking new vma pages as VM_MERGEABLE"
+ depends on KSM
+ default 0
+ range 0 1
+ help
+
config DEFAULT_MMAP_MIN_ADDR
int "Low address space to protect from user allocation"
depends on MMU
--
2.1.3