aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjollaman999 <admin@jollaman999.com>2016-02-05 22:07:15 +0900
committerMister Oyster <oysterized@gmail.com>2017-05-29 03:52:08 +0200
commit6e4b043748ce83d6c7ba6dbf9ba50bd857d659d6 (patch)
tree6b1c3ddd4bf21c68af4fc4f8db13a3e3f5a1b74a
parent1abef3b2cf1b835192ba2484e42f4a1dbba26807 (diff)
downloadandroid_kernel_m2note-6e4b043748ce83d6c7ba6dbf9ba50bd857d659d6.tar.gz
fs: Add dynamic sync control
Adative for jolla-kernel Original by @faux123 The dynamic sync control interface uses Android kernel's unique early suspend / lat resume interface. While screen is on, file sync is disabled when screen is off, a file sync is called to flush all outstanding writes and restore file sync operation as normal. Signed-off-by: Paul Reioux <reioux@gmail.com>
-rw-r--r--fs/Kconfig7
-rw-r--r--fs/Makefile2
-rw-r--r--fs/dyn_sync_cntrl.c171
-rw-r--r--fs/sync.c190
4 files changed, 298 insertions, 72 deletions
diff --git a/fs/Kconfig b/fs/Kconfig
index c043da0ec..891a07f9b 100644
--- a/fs/Kconfig
+++ b/fs/Kconfig
@@ -272,4 +272,11 @@ source "fs/nls/Kconfig"
source "fs/dlm/Kconfig"
source "fs/rawfs/Kconfig"
+config DYNAMIC_FSYNC
+ bool "dynamic file sync control"
+ default n
+ help
+ An experimental file sync control using pm suspend / resume drivers
+
+
endmenu
diff --git a/fs/Makefile b/fs/Makefile
index 0c36ba7a0..a25a0137d 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -129,3 +129,5 @@ obj-$(CONFIG_CEPH_FS) += ceph/
obj-$(CONFIG_PSTORE) += pstore/
obj-$(CONFIG_EFIVAR_FS) += efivarfs/
obj-$(CONFIG_RAWFS_FS) += rawfs/
+
+obj-$(CONFIG_DYNAMIC_FSYNC) += dyn_sync_cntrl.o
diff --git a/fs/dyn_sync_cntrl.c b/fs/dyn_sync_cntrl.c
new file mode 100644
index 000000000..239e1ecab
--- /dev/null
+++ b/fs/dyn_sync_cntrl.c
@@ -0,0 +1,171 @@
+/*
+ * Author: Paul Reioux aka Faux123 <reioux@gmail.com>
+ *
+ * Copyright 2013 Paul Reioux
+ * Copyright 2012 Paul Reioux
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kobject.h>
+#include <linux/sysfs.h>
+#include <linux/mutex.h>
+#include <linux/writeback.h>
+#include <linux/fb.h>
+
+#define DYN_FSYNC_VERSION_MAJOR 1
+#define DYN_FSYNC_VERSION_MINOR 1
+
+struct notifier_block dyn_fsync_fb_notif;
+
+/*
+ * fsync_mutex protects dyn_fsync_active during fb suspend / resume
+ * transitions
+ */
+static DEFINE_MUTEX(fsync_mutex);
+bool dyn_sync_scr_suspended = false;
+bool dyn_fsync_active __read_mostly = true;
+
+extern void dyn_fsync_suspend_actions(void);
+
+static ssize_t dyn_fsync_active_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", (dyn_fsync_active ? 1 : 0));
+}
+
+static ssize_t dyn_fsync_active_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ unsigned int data;
+
+ if(sscanf(buf, "%u\n", &data) == 1) {
+ if (data == 1) {
+ pr_info("%s: dynamic fsync enabled\n", __FUNCTION__);
+ dyn_fsync_active = true;
+ }
+ else if (data == 0) {
+ pr_info("%s: dyanamic fsync disabled\n", __FUNCTION__);
+ dyn_fsync_active = false;
+ }
+ else
+ pr_info("%s: bad value: %u\n", __FUNCTION__, data);
+ } else
+ pr_info("%s: unknown input!\n", __FUNCTION__);
+
+ return count;
+}
+
+static ssize_t dyn_fsync_version_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ return sprintf(buf, "version: %u.%u by faux123\n",
+ DYN_FSYNC_VERSION_MAJOR,
+ DYN_FSYNC_VERSION_MINOR);
+}
+
+static struct kobj_attribute dyn_fsync_active_attribute =
+ __ATTR(Dyn_fsync_active, 0666,
+ dyn_fsync_active_show,
+ dyn_fsync_active_store);
+
+static struct kobj_attribute dyn_fsync_version_attribute =
+ __ATTR(Dyn_fsync_version, 0444, dyn_fsync_version_show, NULL);
+
+static struct attribute *dyn_fsync_active_attrs[] =
+ {
+ &dyn_fsync_active_attribute.attr,
+ &dyn_fsync_version_attribute.attr,
+ NULL,
+ };
+
+static struct attribute_group dyn_fsync_active_attr_group =
+ {
+ .attrs = dyn_fsync_active_attrs,
+ };
+
+static struct kobject *dyn_fsync_kobj;
+
+static void dyn_fsync_suspend(void)
+{
+ mutex_lock(&fsync_mutex);
+ /* flush all outstanding buffers */
+ if (dyn_fsync_active)
+ dyn_fsync_suspend_actions();
+ mutex_unlock(&fsync_mutex);
+
+ pr_info("%s: flushing work finished.\n", __FUNCTION__);
+}
+
+static int dyn_fsync_fb_notifier_callback(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ struct fb_event *evdata = data;
+ int *blank;
+
+ if (event == FB_EVENT_BLANK) {
+ blank = evdata->data;
+
+ switch (*blank) {
+ case FB_BLANK_UNBLANK:
+ dyn_sync_scr_suspended = false;
+ break;
+ case FB_BLANK_POWERDOWN:
+ dyn_sync_scr_suspended = true;
+ if (dyn_fsync_active)
+ dyn_fsync_suspend();
+ break;
+ }
+ }
+
+ return 0;
+}
+
+struct notifier_block dyn_fsync_fb_notif = {
+ .notifier_call = dyn_fsync_fb_notifier_callback,
+};
+
+static int __init dyn_fsync_init(void)
+{
+ int ret;
+
+ ret = fb_register_client(&dyn_fsync_fb_notif);
+ if (ret) {
+ pr_info("%s fb register failed!\n", __FUNCTION__);
+ return ret;
+ }
+
+ dyn_fsync_kobj = kobject_create_and_add("dyn_fsync", kernel_kobj);
+ if (!dyn_fsync_kobj) {
+ pr_err("%s dyn_fsync kobject create failed!\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ ret = sysfs_create_group(dyn_fsync_kobj,
+ &dyn_fsync_active_attr_group);
+ if (ret) {
+ pr_info("%s dyn_fsync sysfs create failed!\n", __FUNCTION__);
+ kobject_put(dyn_fsync_kobj);
+ }
+
+ return ret;
+}
+
+static void __exit dyn_fsync_exit(void)
+{
+ if (dyn_fsync_kobj != NULL)
+ kobject_put(dyn_fsync_kobj);
+ fb_unregister_client(&dyn_fsync_fb_notif);
+}
+
+module_init(dyn_fsync_init);
+module_exit(dyn_fsync_exit);
diff --git a/fs/sync.c b/fs/sync.c
index 71ec82018..e98d2608c 100644
--- a/fs/sync.c
+++ b/fs/sync.c
@@ -17,6 +17,11 @@
#include <linux/backing-dev.h>
#include "internal.h"
+#ifdef CONFIG_DYNAMIC_FSYNC
+extern bool dyn_sync_scr_suspended;
+extern bool dyn_fsync_active __read_mostly;
+#endif
+
#define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \
SYNC_FILE_RANGE_WAIT_AFTER)
@@ -89,6 +94,21 @@ static void fdatawait_one_bdev(struct block_device *bdev, void *arg)
filemap_fdatawait(bdev->bd_inode->i_mapping);
}
+#ifdef CONFIG_DYNAMIC_FSYNC
+void dyn_fsync_suspend_actions(void)
+{
+ int nowait = 0, wait = 1;
+
+ wakeup_flusher_threads(0, WB_REASON_SYNC);
+ iterate_supers(sync_inodes_one_sb, NULL);
+ iterate_supers(sync_fs_one_sb, &nowait);
+ iterate_supers(sync_fs_one_sb, &wait);
+ iterate_bdevs(fdatawrite_one_bdev, NULL);
+ iterate_bdevs(fdatawait_one_bdev, NULL);
+}
+EXPORT_SYMBOL(dyn_fsync_suspend_actions);
+#endif
+
/*
* Sync everything. We start by waking flusher threads so that most of
* writeback runs on all devices in parallel. Then we sync all inodes reliably
@@ -177,9 +197,17 @@ SYSCALL_DEFINE1(syncfs, int, fd)
*/
int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)
{
- if (!file->f_op || !file->f_op->fsync)
- return -EINVAL;
- return file->f_op->fsync(file, start, end, datasync);
+#ifdef CONFIG_DYNAMIC_FSYNC
+ if (likely(dyn_fsync_active && !dyn_sync_scr_suspended))
+ return 0;
+ else {
+#endif
+ if (!file->f_op || !file->f_op->fsync)
+ return -EINVAL;
+ return file->f_op->fsync(file, start, end, datasync);
+#ifdef CONFIG_DYNAMIC_FSYNC
+ }
+#endif
}
EXPORT_SYMBOL(vfs_fsync_range);
@@ -212,7 +240,12 @@ static int do_fsync(unsigned int fd, int datasync)
SYSCALL_DEFINE1(fsync, unsigned int, fd)
{
- return do_fsync(fd, 0);
+#ifdef CONFIG_DYNAMIC_FSYNC
+ if (likely(dyn_fsync_active && !dyn_sync_scr_suspended))
+ return 0;
+ else
+#endif
+ return do_fsync(fd, 0);
}
SYSCALL_DEFINE1(fdatasync, unsigned int, fd)
@@ -287,84 +320,92 @@ EXPORT_SYMBOL(generic_write_sync);
SYSCALL_DEFINE4(sync_file_range, int, fd, loff_t, offset, loff_t, nbytes,
unsigned int, flags)
{
- int ret;
- struct fd f;
- struct address_space *mapping;
- loff_t endbyte; /* inclusive */
- umode_t i_mode;
-
- ret = -EINVAL;
- if (flags & ~VALID_FLAGS)
- goto out;
-
- endbyte = offset + nbytes;
-
- if ((s64)offset < 0)
- goto out;
- if ((s64)endbyte < 0)
- goto out;
- if (endbyte < offset)
- goto out;
-
- if (sizeof(pgoff_t) == 4) {
- if (offset >= (0x100000000ULL << PAGE_CACHE_SHIFT)) {
- /*
- * The range starts outside a 32 bit machine's
- * pagecache addressing capabilities. Let it "succeed"
- */
- ret = 0;
+#ifdef CONFIG_DYNAMIC_FSYNC
+ if (likely(dyn_fsync_active && !dyn_sync_scr_suspended))
+ return 0;
+ else {
+#endif
+ int ret;
+ struct fd f;
+ struct address_space *mapping;
+ loff_t endbyte; /* inclusive */
+ umode_t i_mode;
+
+ ret = -EINVAL;
+ if (flags & ~VALID_FLAGS)
goto out;
- }
- if (endbyte >= (0x100000000ULL << PAGE_CACHE_SHIFT)) {
- /*
- * Out to EOF
- */
- nbytes = 0;
- }
- }
- if (nbytes == 0)
- endbyte = LLONG_MAX;
- else
- endbyte--; /* inclusive */
+ endbyte = offset + nbytes;
- ret = -EBADF;
- f = fdget(fd);
- if (!f.file)
- goto out;
+ if ((s64)offset < 0)
+ goto out;
+ if ((s64)endbyte < 0)
+ goto out;
+ if (endbyte < offset)
+ goto out;
- i_mode = file_inode(f.file)->i_mode;
- ret = -ESPIPE;
- if (!S_ISREG(i_mode) && !S_ISBLK(i_mode) && !S_ISDIR(i_mode) &&
- !S_ISLNK(i_mode))
- goto out_put;
+ if (sizeof(pgoff_t) == 4) {
+ if (offset >= (0x100000000ULL << PAGE_CACHE_SHIFT)) {
+ /*
+ * The range starts outside a 32 bit machine's
+ * pagecache addressing capabilities. Let it "succeed"
+ */
+ ret = 0;
+ goto out;
+ }
+ if (endbyte >= (0x100000000ULL << PAGE_CACHE_SHIFT)) {
+ /*
+ * Out to EOF
+ */
+ nbytes = 0;
+ }
+ }
- mapping = f.file->f_mapping;
- if (!mapping) {
- ret = -EINVAL;
- goto out_put;
- }
+ if (nbytes == 0)
+ endbyte = LLONG_MAX;
+ else
+ endbyte--; /* inclusive */
+
+ ret = -EBADF;
+ f = fdget(fd);
+ if (!f.file)
+ goto out;
- ret = 0;
- if (flags & SYNC_FILE_RANGE_WAIT_BEFORE) {
- ret = filemap_fdatawait_range(mapping, offset, endbyte);
- if (ret < 0)
+ i_mode = file_inode(f.file)->i_mode;
+ ret = -ESPIPE;
+ if (!S_ISREG(i_mode) && !S_ISBLK(i_mode) && !S_ISDIR(i_mode) &&
+ !S_ISLNK(i_mode))
goto out_put;
- }
- if (flags & SYNC_FILE_RANGE_WRITE) {
- ret = filemap_fdatawrite_range(mapping, offset, endbyte);
- if (ret < 0)
+ mapping = f.file->f_mapping;
+ if (!mapping) {
+ ret = -EINVAL;
goto out_put;
- }
+ }
- if (flags & SYNC_FILE_RANGE_WAIT_AFTER)
- ret = filemap_fdatawait_range(mapping, offset, endbyte);
+ ret = 0;
+ if (flags & SYNC_FILE_RANGE_WAIT_BEFORE) {
+ ret = filemap_fdatawait_range(mapping, offset, endbyte);
+ if (ret < 0)
+ goto out_put;
+ }
-out_put:
- fdput(f);
-out:
- return ret;
+ if (flags & SYNC_FILE_RANGE_WRITE) {
+ ret = filemap_fdatawrite_range(mapping, offset, endbyte);
+ if (ret < 0)
+ goto out_put;
+ }
+
+ if (flags & SYNC_FILE_RANGE_WAIT_AFTER)
+ ret = filemap_fdatawait_range(mapping, offset, endbyte);
+
+ out_put:
+ fdput(f);
+ out:
+ return ret;
+#ifdef CONFIG_DYNAMIC_FSYNC
+ }
+#endif
}
/* It would be nice if people remember that not all the world's an i386
@@ -372,5 +413,10 @@ out:
SYSCALL_DEFINE4(sync_file_range2, int, fd, unsigned int, flags,
loff_t, offset, loff_t, nbytes)
{
- return sys_sync_file_range(fd, offset, nbytes, flags);
+#ifdef CONFIG_DYNAMIC_FSYNC
+ if (likely(dyn_fsync_active && !dyn_sync_scr_suspended))
+ return 0;
+ else
+#endif
+ return sys_sync_file_range(fd, offset, nbytes, flags);
}