Skip to content

Commit

Permalink
laptop-mode: Make flushes per-device
Browse files Browse the repository at this point in the history
One of the features of laptop-mode is that it forces a writeout of dirty
pages if something else triggers a physical read or write from a device.
The current implementation flushes pages on all devices, rather than only
the one that triggered the flush. This patch alters the behaviour so that
only the recently accessed block device is flushed, preventing other
disks being spun up for no terribly good reason.

Signed-off-by: Matthew Garrett <[email protected]>
Signed-off-by: Jens Axboe <[email protected]>
  • Loading branch information
Matthew Garrett authored and Jens Axboe committed Apr 6, 2010
1 parent 9195291 commit 31373d0
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 21 deletions.
5 changes: 4 additions & 1 deletion block/blk-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ void blk_cleanup_queue(struct request_queue *q)
*/
blk_sync_queue(q);

del_timer_sync(&q->backing_dev_info.laptop_mode_wb_timer);
mutex_lock(&q->sysfs_lock);
queue_flag_set_unlocked(QUEUE_FLAG_DEAD, q);
mutex_unlock(&q->sysfs_lock);
Expand Down Expand Up @@ -511,6 +512,8 @@ struct request_queue *blk_alloc_queue_node(gfp_t gfp_mask, int node_id)
return NULL;
}

setup_timer(&q->backing_dev_info.laptop_mode_wb_timer,
laptop_mode_timer_fn, (unsigned long) q);
init_timer(&q->unplug_timer);
setup_timer(&q->timeout, blk_rq_timed_out_timer, (unsigned long) q);
INIT_LIST_HEAD(&q->timeout_list);
Expand Down Expand Up @@ -2101,7 +2104,7 @@ static void blk_finish_request(struct request *req, int error)
BUG_ON(blk_queued_rq(req));

if (unlikely(laptop_mode) && blk_fs_request(req))
laptop_io_completion();
laptop_io_completion(&req->q->backing_dev_info);

blk_delete_timer(req);

Expand Down
3 changes: 3 additions & 0 deletions include/linux/backing-dev.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/writeback.h>
#include <asm/atomic.h>

Expand Down Expand Up @@ -88,6 +89,8 @@ struct backing_dev_info {

struct device *dev;

struct timer_list laptop_mode_wb_timer;

#ifdef CONFIG_DEBUG_FS
struct dentry *debug_dir;
struct dentry *debug_stats;
Expand Down
4 changes: 3 additions & 1 deletion include/linux/writeback.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,10 @@ static inline void inode_sync_wait(struct inode *inode)
/*
* mm/page-writeback.c
*/
void laptop_io_completion(void);
void laptop_io_completion(struct backing_dev_info *info);
void laptop_sync_completion(void);
void laptop_mode_sync(struct work_struct *work);
void laptop_mode_timer_fn(unsigned long data);
void throttle_vm_writeout(gfp_t gfp_mask);

/* These are exported to sysctl. */
Expand Down
39 changes: 20 additions & 19 deletions mm/page-writeback.c
Original file line number Diff line number Diff line change
Expand Up @@ -683,10 +683,6 @@ void throttle_vm_writeout(gfp_t gfp_mask)
}
}

static void laptop_timer_fn(unsigned long unused);

static DEFINE_TIMER(laptop_mode_wb_timer, laptop_timer_fn, 0, 0);

/*
* sysctl handler for /proc/sys/vm/dirty_writeback_centisecs
*/
Expand All @@ -697,31 +693,29 @@ int dirty_writeback_centisecs_handler(ctl_table *table, int write,
return 0;
}

static void do_laptop_sync(struct work_struct *work)
void laptop_mode_timer_fn(unsigned long data)
{
wakeup_flusher_threads(0);
kfree(work);
}
struct request_queue *q = (struct request_queue *)data;
int nr_pages = global_page_state(NR_FILE_DIRTY) +
global_page_state(NR_UNSTABLE_NFS);

static void laptop_timer_fn(unsigned long unused)
{
struct work_struct *work;
/*
* We want to write everything out, not just down to the dirty
* threshold
*/

work = kmalloc(sizeof(*work), GFP_ATOMIC);
if (work) {
INIT_WORK(work, do_laptop_sync);
schedule_work(work);
}
if (bdi_has_dirty_io(&q->backing_dev_info))
bdi_start_writeback(&q->backing_dev_info, NULL, nr_pages);
}

/*
* We've spun up the disk and we're in laptop mode: schedule writeback
* of all dirty data a few seconds from now. If the flush is already scheduled
* then push it back - the user is still using the disk.
*/
void laptop_io_completion(void)
void laptop_io_completion(struct backing_dev_info *info)
{
mod_timer(&laptop_mode_wb_timer, jiffies + laptop_mode);
mod_timer(&info->laptop_mode_wb_timer, jiffies + laptop_mode);
}

/*
Expand All @@ -731,7 +725,14 @@ void laptop_io_completion(void)
*/
void laptop_sync_completion(void)
{
del_timer(&laptop_mode_wb_timer);
struct backing_dev_info *bdi;

rcu_read_lock();

list_for_each_entry_rcu(bdi, &bdi_list, bdi_list)
del_timer(&bdi->laptop_mode_wb_timer);

rcu_read_unlock();
}

/*
Expand Down

0 comments on commit 31373d0

Please sign in to comment.