From 40c33a5cc2dbf0d6cce4ecbbf03027c6fa3099a2 Mon Sep 17 00:00:00 2001 From: googyanas Date: Wed, 18 Jul 2018 23:41:35 +0200 Subject: [PATCH] fs: implement Dynamic FSync 2.0 (thx andip71) --- fs/Kconfig | 6 + fs/Makefile | 1 + fs/dyn_sync_cntrl.c | 257 +++++++++++++++++++++++++++++++++ fs/sync.c | 43 ++++++ include/linux/dyn_sync_cntrl.h | 14 ++ 5 files changed, 321 insertions(+) create mode 100644 fs/dyn_sync_cntrl.c create mode 100644 include/linux/dyn_sync_cntrl.h diff --git a/fs/Kconfig b/fs/Kconfig index edec45aab932..9d77f1fa758a 100644 --- a/fs/Kconfig +++ b/fs/Kconfig @@ -12,6 +12,12 @@ config INTERRUPTIBLE_SYNC bool "Support interruptible sync for Samsung Mobile Device" default y +config DYNAMIC_FSYNC + bool "dynamic file sync control" + default n + help + An experimental file sync control using new power_suspend driver + if BLOCK source "fs/ext2/Kconfig" diff --git a/fs/Makefile b/fs/Makefile index 55bbaa0799a5..e2fccbf3a7c7 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -128,3 +128,4 @@ obj-y += exofs/ # Multiple modules obj-$(CONFIG_CEPH_FS) += ceph/ obj-$(CONFIG_PSTORE) += pstore/ obj-$(CONFIG_EFIVAR_FS) += efivarfs/ +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 000000000000..54e7f6dbdeaa --- /dev/null +++ b/fs/dyn_sync_cntrl.c @@ -0,0 +1,257 @@ +/* + * Dynamic sync control driver V2 + * + * by andip71 (alias Lord Boeffla) + * + * All credits for original implemenation to faux123 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// fsync_mutex protects dyn_fsync_active during suspend / late resume transitions +static DEFINE_MUTEX(fsync_mutex); + + +// Declarations + +bool suspend_active __read_mostly = false; +bool dyn_fsync_active __read_mostly = DYN_FSYNC_ACTIVE_DEFAULT; + +static struct notifier_block lcd_notif; + +extern void sync_filesystems(int wait); + + +// Functions + +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: dynamic 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\n", + DYN_FSYNC_VERSION_MAJOR, + DYN_FSYNC_VERSION_MINOR); +} + + +static ssize_t dyn_fsync_suspend_show(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sprintf(buf, "suspend active: %u\n", suspend_active); +} + + +static void dyn_fsync_force_flush(void) +{ + sync_filesystems(0); + sync_filesystems(1); +} + + +static int dyn_fsync_panic_event(struct notifier_block *this, + unsigned long event, void *ptr) +{ + suspend_active = false; + dyn_fsync_force_flush(); + pr_warn("dynamic fsync: panic - force flush!\n"); + + return NOTIFY_DONE; +} + + +static int dyn_fsync_notify_sys(struct notifier_block *this, unsigned long code, + void *unused) +{ + if (code == SYS_DOWN || code == SYS_HALT) + { + suspend_active = false; + dyn_fsync_force_flush(); + pr_warn("dynamic fsync: reboot - force flush!\n"); + } + return NOTIFY_DONE; +} + +static int lcd_notifier_callback(struct notifier_block *this, + unsigned long event, void *data) +{ + switch (event) + { + case LCD_EVENT_OFF_START: + mutex_lock(&fsync_mutex); + + suspend_active = false; + + if (dyn_fsync_active) + { + dyn_fsync_force_flush(); + } + + mutex_unlock(&fsync_mutex); + break; + + case LCD_EVENT_ON_END: + mutex_lock(&fsync_mutex); + suspend_active = true; + mutex_unlock(&fsync_mutex); + break; + + default: + break; + } + + return 0; +} + +// Module structures + +static struct notifier_block dyn_fsync_notifier = +{ + .notifier_call = dyn_fsync_notify_sys, +}; + +static struct kobj_attribute dyn_fsync_active_attribute = + __ATTR(Dyn_fsync_active, 0660, + 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 kobj_attribute dyn_fsync_suspend_attribute = + __ATTR(Dyn_fsync_suspend, 0444, dyn_fsync_suspend_show, NULL); + +static struct attribute *dyn_fsync_active_attrs[] = +{ + &dyn_fsync_active_attribute.attr, + &dyn_fsync_version_attribute.attr, + &dyn_fsync_suspend_attribute.attr, + NULL, +}; + +static struct attribute_group dyn_fsync_active_attr_group = +{ + .attrs = dyn_fsync_active_attrs, +}; + +static struct notifier_block dyn_fsync_panic_block = +{ + .notifier_call = dyn_fsync_panic_event, + .priority = INT_MAX, +}; + +static struct kobject *dyn_fsync_kobj; + + +// Module init/exit + +static int dyn_fsync_init(void) +{ + int sysfs_result; + + register_reboot_notifier(&dyn_fsync_notifier); + + atomic_notifier_chain_register(&panic_notifier_list, + &dyn_fsync_panic_block); + + dyn_fsync_kobj = kobject_create_and_add("dyn_fsync", kernel_kobj); + + if (!dyn_fsync_kobj) + { + pr_err("%s dyn_fsync_kobj create failed!\n", __FUNCTION__); + return -ENOMEM; + } + + sysfs_result = sysfs_create_group(dyn_fsync_kobj, + &dyn_fsync_active_attr_group); + + if (sysfs_result) + { + pr_err("%s dyn_fsync sysfs create failed!\n", __FUNCTION__); + kobject_put(dyn_fsync_kobj); + } + + lcd_notif.notifier_call = lcd_notifier_callback; + if (lcd_register_client(&lcd_notif) != 0) + { + pr_err("%s: Failed to register lcd callback\n", __func__); + + unregister_reboot_notifier(&dyn_fsync_notifier); + + atomic_notifier_chain_unregister(&panic_notifier_list, + &dyn_fsync_panic_block); + + if (dyn_fsync_kobj != NULL) + kobject_put(dyn_fsync_kobj); + + return -EFAULT; + } + + pr_info("%s dynamic fsync initialisation complete\n", __FUNCTION__); + + return sysfs_result; +} + + +static void dyn_fsync_exit(void) +{ + unregister_reboot_notifier(&dyn_fsync_notifier); + + atomic_notifier_chain_unregister(&panic_notifier_list, + &dyn_fsync_panic_block); + + if (dyn_fsync_kobj != NULL) + kobject_put(dyn_fsync_kobj); + + lcd_unregister_client(&lcd_notif); + + pr_info("%s dynamic fsync unregistration complete\n", __FUNCTION__); +} + +module_init(dyn_fsync_init); +module_exit(dyn_fsync_exit); + +MODULE_AUTHOR("andip71"); +MODULE_DESCRIPTION("dynamic fsync - automatic fs sync optimization for msm8974"); +MODULE_LICENSE("GPL v2"); diff --git a/fs/sync.c b/fs/sync.c index 8063ab336cbf..ade4b2b389ee 100644 --- a/fs/sync.c +++ b/fs/sync.c @@ -18,6 +18,10 @@ #include #include "internal.h" +#ifdef CONFIG_DYNAMIC_FSYNC +#include +#endif + bool fsync_enabled = true; module_param(fsync_enabled, bool, 0755); @@ -348,6 +352,21 @@ static void fdatawait_one_bdev(struct block_device *bdev, void *arg) filemap_fdatawait_keep_errors(bdev->bd_inode->i_mapping); } +#ifdef CONFIG_DYNAMIC_FSYNC +/* + * Sync all the data for all the filesystems (called by sys_sync() and + * emergency sync) + */ +void sync_filesystems(int wait) +{ + iterate_supers(sync_inodes_one_sb, NULL); + iterate_supers(sync_fs_one_sb, &wait); + iterate_supers(sync_fs_one_sb, &wait); + iterate_bdevs(fdatawrite_one_bdev, NULL); + iterate_bdevs(fdatawait_one_bdev, NULL); +} +#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 @@ -500,6 +519,11 @@ int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync) if (!fsync_enabled) return 0; +#ifdef CONFIG_DYNAMIC_FSYNC + if (likely(dyn_fsync_active && suspend_active)) + return 0; +#endif + if (!file->f_op->fsync) return -EINVAL; if (!datasync && (inode->i_state & I_DIRTY_TIME)) { @@ -552,6 +576,11 @@ SYSCALL_DEFINE1(fsync, unsigned int, fd) if (!fsync_enabled) return 0; +#ifdef CONFIG_DYNAMIC_FSYNC + if (likely(dyn_fsync_active && suspend_active)) + return 0; +#endif + return do_fsync(fd, 0); } @@ -560,6 +589,11 @@ SYSCALL_DEFINE1(fdatasync, unsigned int, fd) if (!fsync_enabled) return 0; +#ifdef CONFIG_DYNAMIC_FSYNC + if (likely(dyn_fsync_active && suspend_active)) + return 0; +#endif + return do_fsync(fd, 1); } @@ -622,6 +656,11 @@ SYSCALL_DEFINE4(sync_file_range, int, fd, loff_t, offset, loff_t, nbytes, if (!fsync_enabled) return 0; +#ifdef CONFIG_DYNAMIC_FSYNC + if (likely(dyn_fsync_active && suspend_active)) + return 0; +#endif + ret = -EINVAL; if (flags & ~VALID_FLAGS) goto out; @@ -702,5 +741,9 @@ out: SYSCALL_DEFINE4(sync_file_range2, int, fd, unsigned int, flags, loff_t, offset, loff_t, nbytes) { +#ifdef CONFIG_DYNAMIC_FSYNC + if (likely(dyn_fsync_active && suspend_active)) + return 0; +#endif return sys_sync_file_range(fd, offset, nbytes, flags); } diff --git a/include/linux/dyn_sync_cntrl.h b/include/linux/dyn_sync_cntrl.h new file mode 100644 index 000000000000..e4c2a74ba98f --- /dev/null +++ b/include/linux/dyn_sync_cntrl.h @@ -0,0 +1,14 @@ +/* + * Dynamic sync control driver definitions (V2) + * + * by andip71 (alias Lord Boeffla) + * + */ + +#define DYN_FSYNC_ACTIVE_DEFAULT true +#define DYN_FSYNC_VERSION_MAJOR 2 +#define DYN_FSYNC_VERSION_MINOR 0 + +extern bool suspend_active; +extern bool dyn_fsync_active; +