[RFC PATCH] mm/page_idle: Avoid interferences from concurrent users

From: SeongJae Park
Date: Wed Sep 23 2020 - 11:36:58 EST


From: SeongJae Park <sjpark@xxxxxxxxx>

The flexible interface of Idle Page Tracking can allow concurrent users
interfere each other. They could implement their own use user space
synchronization, but applications developed by different users would not
know how to synchronize with other application. To help this situation,
this commit introduces a centralized synchronization infrastructure for
Idle Page Tracking.

In detail, this commit introduces a mutex for Idle Page Tracking, which
exposed to user space via a new sysfs file, 'lock'. By writing to and
reading from the file, users can acquire/release and read status of the
mutex. While reads of the Idle Page Tracking 'bitmap' file can be done
regardless of the mutex status, writes to the file fails if the lock is
not held.

Note that users could still interfere each other if they do not respect
the locking rule. Nevertheless, this will help them noticing the rule.

Signed-off-by: SeongJae Park <sjpark@xxxxxxxxx>
---
mm/page_idle.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)

diff --git a/mm/page_idle.c b/mm/page_idle.c
index 144fb4ed961d..0aa45f848570 100644
--- a/mm/page_idle.c
+++ b/mm/page_idle.c
@@ -16,6 +16,8 @@
#define BITMAP_CHUNK_SIZE sizeof(u64)
#define BITMAP_CHUNK_BITS (BITMAP_CHUNK_SIZE * BITS_PER_BYTE)

+static DEFINE_MUTEX(page_idle_lock);
+
/*
* Idle page tracking only considers user memory pages, for other types of
* pages the idle flag is always unset and an attempt to set it is silently
@@ -169,6 +171,9 @@ static ssize_t page_idle_bitmap_write(struct file *file, struct kobject *kobj,
unsigned long pfn, end_pfn;
int bit;

+ if (!mutex_is_locked(&page_idle_lock))
+ return -EPERM;
+
if (pos % BITMAP_CHUNK_SIZE || count % BITMAP_CHUNK_SIZE)
return -EINVAL;

@@ -197,17 +202,52 @@ static ssize_t page_idle_bitmap_write(struct file *file, struct kobject *kobj,
return (char *)in - buf;
}

+static ssize_t page_idle_lock_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%d\n", mutex_is_locked(&page_idle_lock));
+}
+
+static ssize_t page_idle_lock_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ bool do_lock;
+ int ret;
+
+ ret = kstrtobool(buf, &do_lock);
+ if (ret < 0)
+ return ret;
+
+ if (do_lock) {
+ if (!mutex_trylock(&page_idle_lock))
+ return -EBUSY;
+ } else {
+ mutex_unlock(&page_idle_lock);
+ }
+
+ return count;
+}
+
static struct bin_attribute page_idle_bitmap_attr =
__BIN_ATTR(bitmap, 0600,
page_idle_bitmap_read, page_idle_bitmap_write, 0);

+static struct kobj_attribute page_idle_lock_attr =
+ __ATTR(lock, 0600, page_idle_lock_show, page_idle_lock_store);
+
static struct bin_attribute *page_idle_bin_attrs[] = {
&page_idle_bitmap_attr,
NULL,
};

+static struct attribute *page_idle_lock_attrs[] = {
+ &page_idle_lock_attr.attr,
+ NULL,
+};
+
static const struct attribute_group page_idle_attr_group = {
.bin_attrs = page_idle_bin_attrs,
+ .attrs = page_idle_lock_attrs,
.name = "page_idle",
};

--
2.17.1