diff options
| author | jollaman999 <admin@jollaman999.com> | 2016-02-05 22:07:15 +0900 |
|---|---|---|
| committer | Mister Oyster <oysterized@gmail.com> | 2017-05-29 03:52:08 +0200 |
| commit | 6e4b043748ce83d6c7ba6dbf9ba50bd857d659d6 (patch) | |
| tree | 6b1c3ddd4bf21c68af4fc4f8db13a3e3f5a1b74a | |
| parent | 1abef3b2cf1b835192ba2484e42f4a1dbba26807 (diff) | |
| download | android_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/Kconfig | 7 | ||||
| -rw-r--r-- | fs/Makefile | 2 | ||||
| -rw-r--r-- | fs/dyn_sync_cntrl.c | 171 | ||||
| -rw-r--r-- | fs/sync.c | 190 |
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); @@ -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); } |
