From: Bart Samwel <bart@samwel.tk>

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;
 

_