From: Bart Samwel The XFS sync daemon is responsible for periodically syncing an XFS filesystem. The problem: in laptop mode, this can kick in at any time and spin up the disk, even when there is no other reason to do so. This patch wakes up the XFS sync daemon whenever an XFS syncfs takes place (e.g., at sys_sync()) so that it does its job and then sleeps for as long as it can. It does so only when laptop mode is active. Laptop mode calls sys_sync() to perform a writeback just before the disk is spun down, so the XFS sync daemon will be woken up just before the disk is spun down as well, and after that it will leave the disk alone for a full lm_sync_interval. --- 25-akpm/fs/xfs/linux/xfs_super.c | 22 ++++++++++++++++++++++ 25-akpm/fs/xfs/linux/xfs_vfs.c | 1 + 25-akpm/fs/xfs/linux/xfs_vfs.h | 2 ++ 3 files changed, 25 insertions(+) diff -puN fs/xfs/linux/xfs_super.c~xfs-laptop-mode-syncd-synchronization fs/xfs/linux/xfs_super.c --- 25/fs/xfs/linux/xfs_super.c~xfs-laptop-mode-syncd-synchronization 2004-03-31 23:18:33.874622600 -0800 +++ 25-akpm/fs/xfs/linux/xfs_super.c 2004-03-31 23:18:33.881621536 -0800 @@ -471,6 +471,10 @@ syncd(void *arg) if (vfsp->vfs_flag & VFS_RDONLY) continue; VFS_SYNC(vfsp, SYNCD_FLAGS, NULL, error); + + vfsp->vfs_sync_seq++; + wmb(); + wake_up(&vfsp->vfs_wait_single_sync_task); } vfsp->vfs_sync_task = NULL; @@ -554,6 +558,24 @@ linvfs_sync_super( VFS_SYNC(vfsp, flags, NULL, error); sb->s_dirt = 0; + if (unlikely(laptop_mode)) + { + int prev_sync_seq = vfsp->vfs_sync_seq; + /* + * The disk must be active because we're syncing. + * We schedule syncd now (now that the disk is + * active) instead of later (when it might not be). + */ + wake_up_process(vfsp->vfs_sync_task); + /* + * We have to wait for the sync iteration to complete. + * If we don't, the disk activity caused by the sync + * will come after the sync is completed, and that + * triggers another sync from laptop mode. + */ + wait_event(vfsp->vfs_wait_single_sync_task, vfsp->vfs_sync_seq != prev_sync_seq); + } + return -error; } diff -puN fs/xfs/linux/xfs_vfs.c~xfs-laptop-mode-syncd-synchronization fs/xfs/linux/xfs_vfs.c --- 25/fs/xfs/linux/xfs_vfs.c~xfs-laptop-mode-syncd-synchronization 2004-03-31 23:18:33.876622296 -0800 +++ 25-akpm/fs/xfs/linux/xfs_vfs.c 2004-03-31 23:18:33.882621384 -0800 @@ -238,6 +238,7 @@ vfs_allocate( void ) vfsp = kmem_zalloc(sizeof(vfs_t), KM_SLEEP); bhv_head_init(VFS_BHVHEAD(vfsp), "vfs"); init_waitqueue_head(&vfsp->vfs_wait_sync_task); + init_waitqueue_head(&vfsp->vfs_wait_single_sync_task); return vfsp; } diff -puN fs/xfs/linux/xfs_vfs.h~xfs-laptop-mode-syncd-synchronization fs/xfs/linux/xfs_vfs.h --- 25/fs/xfs/linux/xfs_vfs.h~xfs-laptop-mode-syncd-synchronization 2004-03-31 23:18:33.877622144 -0800 +++ 25-akpm/fs/xfs/linux/xfs_vfs.h 2004-03-31 23:18:33.882621384 -0800 @@ -52,6 +52,8 @@ typedef struct vfs { bhv_head_t vfs_bh; /* head of vfs behavior chain */ struct super_block *vfs_super; /* Linux superblock structure */ struct task_struct *vfs_sync_task; + int vfs_sync_seq; /* syncd iteration sequence number */ + wait_queue_head_t vfs_wait_single_sync_task; wait_queue_head_t vfs_wait_sync_task; } vfs_t; _