Skip to content

Commit

Permalink
fs/dyn_sync_cntrl: dynamic sync control
Browse files Browse the repository at this point in the history
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.

This is every commit wrapped into 1 including original author faux123, V2 by andip71, and V2.2 by pappschlumpf.

Signed-off-by: Paul Reioux <reioux@gmail.com>
  • Loading branch information
faux123 authored and acuicultor committed Jul 11, 2020
1 parent 22aecff commit 6229fd9
Show file tree
Hide file tree
Showing 6 changed files with 335 additions and 1 deletion.
1 change: 1 addition & 0 deletions arch/arm64/configs/radioactive_defconfig
Expand Up @@ -8,6 +8,7 @@ CONFIG_EXFAT_VIRTUAL_XATTR=y
CONFIG_EXFAT_VIRTUAL_XATTR_SELINUX_LABEL="u:object_r:exfat:s0"
# CONFIG_EXFAT_DEBUG is not set
# CONFIG_EXFAT_UEVENT is not set
CONFIG_DYNAMIC_FSYNC=y
# CONFIG_ARCH_ONEPLUS8PRO is not set

CONFIG_LOCALVERSION=""
Expand Down
6 changes: 6 additions & 0 deletions fs/Kconfig
Expand Up @@ -317,4 +317,10 @@ endif # NETWORK_FILESYSTEMS
source "fs/nls/Kconfig"
source "fs/dlm/Kconfig"

config DYNAMIC_FSYNC
bool "dynamic file sync control"
default n
help
An experimental file sync control using new power_suspend driver

endmenu
1 change: 1 addition & 0 deletions fs/Makefile
Expand Up @@ -130,3 +130,4 @@ obj-$(CONFIG_CEPH_FS) += ceph/
obj-$(CONFIG_PSTORE) += pstore/
obj-$(CONFIG_EFIVAR_FS) += efivarfs/
obj-$(CONFIG_EXFAT_FS) += exfat/
obj-$(CONFIG_DYNAMIC_FSYNC) += dyn_sync_cntrl.o
269 changes: 269 additions & 0 deletions fs/dyn_sync_cntrl.c
@@ -0,0 +1,269 @@
/*
* Dynamic sync control driver V2
*
* by andip71 (alias Lord Boeffla)
*
* V 2.2 for sdm845 pappschlumpf (Erik Müller)
*
* All credits for original implemenation to faux123
*
*/

#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/mutex.h>
#include <linux/notifier.h>
#include <linux/reboot.h>
#include <linux/writeback.h>
#include <linux/dyn_sync_cntrl.h>
#include <linux/msm_drm_notify.h>

// fsync_mutex protects dyn_fsync_active during suspend / late resume transitions
static DEFINE_MUTEX(fsync_mutex);


// Declarations

bool suspend_active __read_mostly = true;
bool dyn_fsync_active __read_mostly = false;

struct notifier_block msm_drm_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 msm_drm_notifier_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct msm_drm_notifier *evdata = data;
int blank;

if (!dyn_fsync_active)
return 0;

blank = *(int *)(evdata->data);

if (event != MSM_DRM_EVENT_BLANK && event != MSM_DRM_EARLY_EVENT_BLANK)
return 0;

if ((blank == MSM_DRM_BLANK_POWERDOWN)
&& (event == MSM_DRM_EARLY_EVENT_BLANK)) {
mutex_lock(&fsync_mutex);
suspend_active = false;

if (dyn_fsync_active)
{
dyn_fsync_force_flush();
}

mutex_unlock(&fsync_mutex);
pr_info("Screen off Fsync on");
}

if ((blank == MSM_DRM_BLANK_UNBLANK)
&& (event == MSM_DRM_EARLY_EVENT_BLANK)) {
mutex_lock(&fsync_mutex);
suspend_active = true;
mutex_unlock(&fsync_mutex);
pr_info("Screen on Fsync on");
}

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;
int ret;

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);
}

msm_drm_notif.notifier_call = msm_drm_notifier_cb;
ret = msm_drm_register_client(&msm_drm_notif);
if (ret)
{
pr_err("%s: Failed to register msm_drm_notifier 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);

msm_drm_unregister_client(&msm_drm_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");
45 changes: 44 additions & 1 deletion fs/sync.c
Expand Up @@ -17,10 +17,16 @@
#include <linux/quotaops.h>
#include <linux/backing-dev.h>
#include "internal.h"
#include <linux/module.h>

#ifdef CONFIG_ONEPLUS_HEALTHINFO
#include <linux/oem/oneplus_healthinfo.h>
#endif

#ifdef CONFIG_DYNAMIC_FSYNC
#include <linux/dyn_sync_cntrl.h>
#endif

#define VALID_FLAGS (SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE| \
SYNC_FILE_RANGE_WAIT_AFTER)

Expand Down Expand Up @@ -98,6 +104,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
Expand Down Expand Up @@ -166,6 +187,11 @@ SYSCALL_DEFINE1(syncfs, int, fd)
struct super_block *sb;
int ret;

#ifdef CONFIG_DYNAMIC_FSYNC
if (likely(dyn_fsync_active && suspend_active))
return 0;
#endif

if (!f.file)
return -EBADF;
sb = f.file->f_path.dentry->d_sb;
Expand All @@ -192,6 +218,11 @@ SYSCALL_DEFINE1(syncfs, int, fd)
int vfs_fsync_range(struct file *file, loff_t start, loff_t end, int datasync)
{
struct inode *inode = file->f_mapping->host;

#ifdef CONFIG_DYNAMIC_FSYNC
if (likely(dyn_fsync_active && suspend_active))
return 0;
#endif

if (!file->f_op->fsync)
return -EINVAL;
Expand Down Expand Up @@ -221,13 +252,20 @@ extern void ohm_schedstats_record(int sched_type, int fg, u64 delta_ms);

static int do_fsync(unsigned int fd, int datasync)
{
struct fd f = fdget(fd);
struct fd f;
int ret = -EBADF;

#ifdef CONFIG_ONEPLUS_HEALTHINFO
unsigned long oneplus_fsync_time = jiffies;
#endif


#ifdef CONFIG_DYNAMIC_FSYNC
if (likely(dyn_fsync_active && suspend_active))
return 0;
#endif

f = fdget(fd);
if (f.file) {
ret = vfs_fsync(f.file, datasync);
fdput(f);
Expand Down Expand Up @@ -306,6 +344,11 @@ int ksys_sync_file_range(int fd, loff_t offset, loff_t nbytes,
loff_t endbyte; /* inclusive */
umode_t i_mode;

#ifdef CONFIG_DYNAMIC_FSYNC
if (likely(dyn_fsync_active && suspend_active))
return 0;
#endif

ret = -EINVAL;
if (flags & ~VALID_FLAGS)
goto out;
Expand Down

0 comments on commit 6229fd9

Please sign in to comment.