Skip to content

Commit

Permalink
mm: Dynamic management of dirty page writebacks
Browse files Browse the repository at this point in the history
This feature allows to have two different intervals for dirty page
writebacks and to dynamically manage them when the system has been
resumed (it's active) or when has been suspended.

Three new procfs parameters are exposed inside /proc/sys/vm path:
- "dynamic_dirty_writeback" is the activation status of this feature,
  set 1 to enable it, set 0 to disable it and use the standard behaviour
- "dirty_writeback_active_centisecs" is the interval for the dirty page
  writebacks when the system is active (screen on)
- "dirty_writeback_suspend_centisecs" is the interval for the dirty page
  writebacks when the system is suspended (screen off)

This feature is in part inspired on Francisco Franco's patch:
franciscofranco/mako@34d7954

Signed-off-by: Cristoforo Cataldo <cristoforo.cataldo@gmail.com>
  • Loading branch information
Christopher83 authored and Ryuinferno committed Jun 22, 2013
1 parent e9bdc79 commit 6faac13
Show file tree
Hide file tree
Showing 4 changed files with 176 additions and 1 deletion.
14 changes: 14 additions & 0 deletions include/linux/writeback.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,11 @@ extern unsigned int dirty_expire_interval;
extern int vm_highmem_is_dirtyable;
extern int block_dump;
extern int laptop_mode;
#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK
extern int dyn_dirty_writeback_enabled;
extern unsigned int dirty_writeback_active_interval;
extern unsigned int dirty_writeback_suspend_interval;
#endif

extern unsigned long determine_dirtyable_memory(void);

Expand All @@ -125,6 +130,15 @@ struct ctl_table;
int dirty_writeback_centisecs_handler(struct ctl_table *, int,
void __user *, size_t *, loff_t *);

#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK
int dynamic_dirty_writeback_handler(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
int dirty_writeback_active_centisecs_handler(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
int dirty_writeback_suspend_centisecs_handler(struct ctl_table *, int,
void __user *, size_t *, loff_t *);
#endif

void global_dirty_limits(unsigned long *pbackground, unsigned long *pdirty);
unsigned long bdi_dirty_limit(struct backing_dev_info *bdi,
unsigned long dirty);
Expand Down
27 changes: 27 additions & 0 deletions kernel/sysctl.c
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,33 @@ static struct ctl_table vm_table[] = {
.mode = 0644,
.proc_handler = dirty_writeback_centisecs_handler,
},
#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK
{
.procname = "dynamic_dirty_writeback",
.data = &dyn_dirty_writeback_enabled,
.maxlen = sizeof(dyn_dirty_writeback_enabled),
.mode = 0644,
.proc_handler = dynamic_dirty_writeback_handler,
.extra1 = &zero,
.extra2 = &one,
},
{
.procname = "dirty_writeback_active_centisecs",
.data = &dirty_writeback_active_interval,
.maxlen = sizeof(dirty_writeback_active_interval),
.mode = 0644,
.proc_handler = dirty_writeback_active_centisecs_handler,
.extra1 = &zero,
},
{
.procname = "dirty_writeback_suspend_centisecs",
.data = &dirty_writeback_suspend_interval,
.maxlen = sizeof(dirty_writeback_suspend_interval),
.mode = 0644,
.proc_handler = dirty_writeback_suspend_centisecs_handler,
.extra1 = &zero,
},
#endif
{
.procname = "dirty_expire_centisecs",
.data = &dirty_expire_interval,
Expand Down
9 changes: 9 additions & 0 deletions mm/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -391,3 +391,12 @@ config COMPACTION_RETRY_DEBUG
This shows us the buddy information
before and after compation retrial.
This should be turned off later.

config DYNAMIC_PAGE_WRITEBACK
bool "Dynamically manage the dirty page writebacks during suspend/resume"
default y
help
This feature allows to use different dirty page writeback intervals
when the system is suspended and when the system has been resumed.

If unsure, say N to disable this feature
127 changes: 126 additions & 1 deletion mm/page-writeback.c
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@
#include <linux/buffer_head.h>
#include <linux/pagevec.h>
#include <trace/events/writeback.h>
#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK
#include <linux/earlysuspend.h>
#endif

/*
* After a CPU has dirtied this many pages, balance_dirty_pages_ratelimited
Expand Down Expand Up @@ -86,10 +89,37 @@ int vm_dirty_ratio = 20;
*/
unsigned long vm_dirty_bytes;

/*
* The default intervals between `kupdate'-style writebacks
*/
#define DEFAULT_DIRTY_WRITEBACK_INTERVAL 5 * 100 /* centiseconds */
#define HIGH_DIRTY_WRITEBACK_INTERVAL 15 * 100 /* centiseconds */

/*
* The interval between `kupdate'-style writebacks
*/
unsigned int dirty_writeback_interval = 5 * 100; /* centiseconds */
unsigned int dirty_writeback_interval = DEFAULT_DIRTY_WRITEBACK_INTERVAL; /* centiseconds */
EXPORT_SYMBOL_GPL(dirty_writeback_interval);

#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK
/*
* The dynamic writeback activation status
*/
int dyn_dirty_writeback_enabled = 1;
EXPORT_SYMBOL_GPL(dyn_dirty_writeback_enabled);

/*
* The interval between `kupdate'-style writebacks when the system is active
*/
unsigned int dirty_writeback_active_interval = HIGH_DIRTY_WRITEBACK_INTERVAL; /* centiseconds */
EXPORT_SYMBOL_GPL(dirty_writeback_active_interval);

/*
* The interval between `kupdate'-style writebacks when the system is suspended
*/
unsigned int dirty_writeback_suspend_interval = DEFAULT_DIRTY_WRITEBACK_INTERVAL; /* centiseconds */
EXPORT_SYMBOL_GPL(dirty_writeback_suspend_interval);
#endif

/*
* The longest time for which data is allowed to remain dirty
Expand Down Expand Up @@ -691,6 +721,68 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write,
return 0;
}

#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK
/*
* Manages the dirty page writebacks activation status
*/
static void set_dirty_writeback_status(bool active) {
/* Change the current dirty writeback interval according to the
* status provided */
dirty_writeback_interval = (active) ?
dirty_writeback_active_interval :
dirty_writeback_suspend_interval;

/* Update the timer related to dirty writebacks interval */
bdi_arm_supers_timer();

/* Print debug info */
pr_debug("%s: Set dirty_writeback_interval = %d centisecs\n",
__func__, dirty_writeback_interval);
}

/*
* sysctl handler for /proc/sys/vm/dyn_dirty_writeback_enabled
*/
int dynamic_dirty_writeback_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *lenp, loff_t *ppos)
{
int ret;
int old_status = dyn_dirty_writeback_enabled;

/* Get and store the new status */
ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos);

/* If the dynamic writeback has been enabled then set the active
* dirty writebacks interval, otherwise if the feature has been
* disabled, set the suspend interval (the default interval)
* to restore the standard functionality */
if (ret == 0 && write && dyn_dirty_writeback_enabled != old_status)
set_dirty_writeback_status(!!dyn_dirty_writeback_enabled);

return ret;
}

/*
* sysctl handler for /proc/sys/vm/dirty_writeback_active_centisecs
*/
int dirty_writeback_active_centisecs_handler(ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos)
{
proc_dointvec_minmax(table, write, buffer, length, ppos);
return 0;
}

/*
* sysctl handler for /proc/sys/vm/dirty_writeback_suspend_centisecs
*/
int dirty_writeback_suspend_centisecs_handler(ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos)
{
proc_dointvec_minmax(table, write, buffer, length, ppos);
return 0;
}
#endif

#ifdef CONFIG_BLOCK
void laptop_mode_timer_fn(unsigned long data)
{
Expand Down Expand Up @@ -772,6 +864,34 @@ static struct notifier_block __cpuinitdata ratelimit_nb = {
.next = NULL,
};

#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK
/*
* Sets the dirty page writebacks interval for suspended system
*/
static void dirty_writeback_early_suspend(struct early_suspend *handler)
{
if (dyn_dirty_writeback_enabled)
set_dirty_writeback_status(false);
}

/*
* Sets the dirty page writebacks interval for active system
*/
static void dirty_writeback_late_resume(struct early_suspend *handler)
{
if (dyn_dirty_writeback_enabled)
set_dirty_writeback_status(true);
}

/*
* Struct for the dirty page writeback management during suspend/resume
*/
static struct early_suspend dirty_writeback_suspend = {
.suspend = dirty_writeback_early_suspend,
.resume = dirty_writeback_late_resume,
};
#endif

/*
* Called early on to tune the page writeback dirty limits.
*
Expand All @@ -794,6 +914,11 @@ void __init page_writeback_init(void)
{
int shift;

#ifdef CONFIG_DYNAMIC_PAGE_WRITEBACK
/* Register the dirty page writeback management during suspend/resume */
register_early_suspend(&dirty_writeback_suspend);
#endif

writeback_set_ratelimit();
register_cpu_notifier(&ratelimit_nb);

Expand Down

0 comments on commit 6faac13

Please sign in to comment.