
===========================================================================
Index: linux/fs/xfs/linux/xfs_ioctl.c
===========================================================================

--- /usr/tmp/TmpDir.18981-0/linux/fs/xfs/linux/xfs_ioctl.c_1.36	Mon May 14 10:25:20 2001
+++ linux/fs/xfs/linux/xfs_ioctl.c	Fri May 11 15:47:37 2001
@@ -1239,6 +1239,47 @@
 		return 0;
 	}
 
+	case XFS_IOC_FREEZE: {
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		/* Stop new writers */
+		xfs_start_freeze(mp, XFS_FREEZE_WRITE);
+
+		/* Flush delalloc and delwri data */
+		VFS_SYNC(vfsp, SYNC_DELWRI|SYNC_WAIT, sys_cred, error);
+
+		/* Pause transaction subsystem */
+		xfs_start_freeze(mp, XFS_FREEZE_TRANS);
+
+		/* Flush log to disk */
+		xfs_log_force(mp, (xfs_lsn_t)0, XFS_LOG_FORCE|XFS_LOG_SYNC);
+
+		/* Flush any remaining inodes into buffers */
+		VFS_SYNC(vfsp, SYNC_BDFLUSH|SYNC_ATTR, sys_cred, error);
+
+		/* Push all the buffers out to disk */
+		xfs_binval(mp->m_ddev_targ);
+		if (mp->m_rtdev != NODEV) {
+			xfs_binval(mp->m_rtdev_targ);
+		}
+
+		/* Push the superblock and write an unmount record */
+		xfs_log_unmount_write(mp);
+		xfs_unmountfs_writesb(mp);
+		return 0;
+	}
+
+	case XFS_IOC_THAW: {
+
+		if (!capable(CAP_SYS_ADMIN))
+			return -EPERM;
+
+		xfs_finish_freeze(mp);
+		return 0;
+	}
+
 #if (defined(DEBUG) || defined(INDUCE_IO_ERROR))
 	case XFS_IOC_ERROR_INJECTION: {
 		xfs_error_injection_t in;

===========================================================================
Index: linux/fs/xfs/linux/xfs_super.c
===========================================================================

--- /usr/tmp/TmpDir.18981-0/linux/fs/xfs/linux/xfs_super.c_1.121	Mon May 14 10:25:20 2001
+++ linux/fs/xfs/linux/xfs_super.c	Mon May 14 10:18:41 2001
@@ -725,6 +725,36 @@
 	return 0;
 }
 
+void
+linvfs_freeze_fs(
+	struct super_block *sb)
+{
+	vfs_t *vfsp;
+	vnode_t *vp;
+	int error;
+
+	vfsp = LINVFS_GET_VFS(sb);
+        if (sb->s_flags & MS_RDONLY) {
+                return;
+        }
+
+	VFS_ROOT(vfsp, &vp, error);
+	VOP_IOCTL(vp, LINVFS_GET_IP(vp), NULL, XFS_IOC_FREEZE, 0, error);
+	VN_RELE(vp);
+}
+
+void
+linvfs_unfreeze_fs(
+	struct super_block *sb)
+{
+	vfs_t *vfsp;
+	vnode_t *vp;
+	int error;
+
+	vfsp = LINVFS_GET_VFS(sb);
+	VFS_ROOT(vfsp, &vp, error);
+	VOP_IOCTL(vp, LINVFS_GET_IP(vp), NULL, XFS_IOC_THAW, 0, error);
+}
 
 int
 linvfs_dmapi_mount(
@@ -807,6 +837,8 @@
 	clear_inode:		linvfs_clear_inode,
 	put_super:		linvfs_put_super,
 	write_super:		linvfs_write_super,
+	write_super_lockfs:	linvfs_freeze_fs,
+	unlockfs:		linvfs_unfreeze_fs,
 	statfs:			linvfs_statfs,
 	remount_fs:		linvfs_remount
 };

===========================================================================
Index: linux/fs/xfs/xfs_log.c
===========================================================================

--- /usr/tmp/TmpDir.18981-0/linux/fs/xfs/xfs_log.c_1.236	Mon May 14 10:25:20 2001
+++ linux/fs/xfs/xfs_log.c	Fri May 11 15:21:49 2001
@@ -802,6 +802,9 @@
 	int 		spl, needed = 0, gen;
 	xlog_t		*log = mp->m_log; 
 
+	if (mp->m_frozen)
+		return 0;
+
 	spl = LOG_LOCK(log);
 	if (((log->l_covered_state == XLOG_STATE_COVER_NEED) ||
 		(log->l_covered_state == XLOG_STATE_COVER_NEED2))

===========================================================================
Index: linux/fs/xfs/xfs_mount.c
===========================================================================

--- /usr/tmp/TmpDir.18981-0/linux/fs/xfs/xfs_mount.c_1.255	Mon May 14 10:25:20 2001
+++ linux/fs/xfs/xfs_mount.c	Fri May 11 13:41:40 2001
@@ -116,6 +116,11 @@
 	 */
 	xfs_trans_ail_init(mp);
 
+	/* Init freeze sync structures */
+	spinlock_init(&mp->m_freeze_lock, "xfs_freeze");
+	init_sv(&mp->m_wait_unfreeze, SV_DEFAULT, "xfs_freeze", 0);
+	atomic_set(&mp->m_active_trans, 0);
+
 	return mp;
 }	/* xfs_mount_init */
 	
@@ -163,6 +168,8 @@
 	if (remove_bhv) {
 		VFS_REMOVEBHV(XFS_MTOVFS(mp), &mp->m_bhv);
 	}
+	spinlock_destroy(&mp->m_freeze_lock);
+	sv_destroy(&mp->m_wait_unfreeze);
 	kmem_free(mp, sizeof(xfs_mount_t));
 }
 
@@ -1579,3 +1586,64 @@
 	xfs_mod_sb(tp, fields);
 	(void)xfs_trans_commit(tp, 0, NULL);
 }
+
+/* Functions used to lock access out of the filesystem for snapshotting
+ * via special purpose hardware or via a logical volume manager
+ */
+
+void
+xfs_start_freeze(
+	xfs_mount_t	*mp,
+	int		level)
+{
+	int	s = mutex_spinlock(&mp->m_freeze_lock);
+
+	mp->m_frozen = level;
+	mutex_spinunlock(&mp->m_freeze_lock, s);
+
+	if (level == XFS_FREEZE_TRANS) {
+		while (atomic_read(&mp->m_active_trans) > 0)
+			delay(100);
+	}
+}
+
+void
+xfs_finish_freeze(
+	xfs_mount_t *mp)
+{
+	int	s = mutex_spinlock(&mp->m_freeze_lock);
+
+	if (mp->m_frozen) {
+		mp->m_frozen = 0;
+		sv_broadcast(&mp->m_wait_unfreeze);
+	}
+
+	mutex_spinunlock(&mp->m_freeze_lock, s);
+}
+
+void
+xfs_check_frozen(
+	xfs_mount_t *mp,
+	int	level)
+{
+	int	s;
+
+	if (!mp->m_frozen) {
+		if (level == XFS_FREEZE_TRANS)
+			atomic_inc(&mp->m_active_trans);
+		return;
+	}
+
+	s = mutex_spinlock(&mp->m_freeze_lock);
+
+	if (mp->m_frozen < level) {
+		mutex_spinunlock(&mp->m_freeze_lock, s);
+		if (level == XFS_FREEZE_TRANS)
+			atomic_inc(&mp->m_active_trans);
+		return;
+	}
+	sv_wait(&mp->m_wait_unfreeze, PINOD, &mp->m_freeze_lock, s);
+	if (level == XFS_FREEZE_TRANS)
+		atomic_inc(&mp->m_active_trans);
+}
+

===========================================================================
Index: linux/fs/xfs/xfs_mount.h
===========================================================================

--- /usr/tmp/TmpDir.18981-0/linux/fs/xfs/xfs_mount.h_1.126	Mon May 14 10:25:20 2001
+++ linux/fs/xfs/xfs_mount.h	Fri May 11 11:42:06 2001
@@ -308,6 +308,10 @@
 						/* which bits matter in rpc
 						   log item pin masks */
 	uint			m_cxfstype;	/* mounted shared, etc. */
+	lock_t			m_freeze_lock;
+	uint			m_frozen;
+	sv_t			m_wait_unfreeze;
+	atomic_t		m_active_trans;
 } xfs_mount_t;
 
 /*
@@ -481,6 +485,14 @@
 void		xfs_force_shutdown(struct xfs_mount *, int);
 int		xfs_syncsub(xfs_mount_t *, int, int, int *);
 void		xfs_xlatesb(void *, struct xfs_sb *, int, xfs_arch_t, __int64_t);
+
+#define XFS_FREEZE_WRITE	1
+#define XFS_FREEZE_TRANS	2
+
+void		xfs_start_freeze(xfs_mount_t *, int);
+void		xfs_finish_freeze(xfs_mount_t *);
+void		xfs_check_frozen(xfs_mount_t *, int);
+
 extern	struct vfsops xfs_vfsops;
 
 #endif	/* __KERNEL__ */

===========================================================================
Index: linux/fs/xfs/xfs_trans.c
===========================================================================

--- /usr/tmp/TmpDir.18981-0/linux/fs/xfs/xfs_trans.c_1.119	Mon May 14 10:25:20 2001
+++ linux/fs/xfs/xfs_trans.c	Fri May 11 15:50:11 2001
@@ -105,6 +105,8 @@
 {
 	xfs_trans_t	*tp;
 
+	xfs_check_frozen(mp, XFS_FREEZE_TRANS);
+
 	ASSERT(xfs_trans_zone != NULL);
 	tp = kmem_zone_zalloc(xfs_trans_zone, KM_SLEEP_IO);
 	tp->t_dqinfo = NULL;
@@ -165,6 +167,8 @@
 	 */
 	if (tp->t_dqinfo) 
 		xfs_trans_dup_dqinfo(tp, ntp);
+
+	atomic_inc(&tp->t_mountp->m_active_trans);
 	return ntp;
 }
 
@@ -1052,6 +1056,7 @@
 xfs_trans_free(
 	xfs_trans_t	*tp)
 {
+	atomic_dec(&tp->t_mountp->m_active_trans);
 	if (tp->t_dqinfo)
 		xfs_trans_free_dqinfo(tp);
 	kmem_zone_free(xfs_trans_zone, tp);

===========================================================================
Index: linux/fs/xfs/xfsidbg.c
===========================================================================

--- /usr/tmp/TmpDir.18981-0/linux/fs/xfs/xfsidbg.c_1.161	Mon May 14 10:25:20 2001
+++ linux/fs/xfs/xfsidbg.c	Fri May 11 12:48:48 2001
@@ -3999,6 +3999,8 @@
 		(xfs_dfiloff_t)mp->m_dirfreeblk);
 	kdb_printf("chsize %d chash 0x%p\n",
 		mp->m_chsize, mp->m_chash);
+	kdb_printf("m_frozen %d m_active_trans %d\n",
+		mp->m_frozen, mp->m_active_trans.counter);
 	if (mp->m_fsname != NULL)
 		kdb_printf("mountpoint \"%s\"\n", mp->m_fsname);
 	else

===========================================================================
Index: linux/include/linux/xfs_fs.h
===========================================================================

--- /usr/tmp/TmpDir.18981-0/linux/include/linux/xfs_fs.h_1.23	Mon May 14 10:25:20 2001
+++ linux/include/linux/xfs_fs.h	Thu May 10 17:00:49 2001
@@ -453,6 +453,8 @@
 #define XFS_IOC_ERROR_CLEARALL       _IOW('X', 117, struct xfs_error_injection)
 #endif /* DEBUG || INDUCE_IO_ERROR */
 #define	XFS_IOC_ATTRCTL_BY_HANDLE    _IOWR('X', 118, struct xfs_fsop_attr_handlereq)
+#define XFS_IOC_FREEZE		     _IOWR('X', 119, int)
+#define XFS_IOC_THAW		     _IOWR('X', 120, int)
 /*
  * ioctl command to export information not in standard interfaces
  * 	140: IRIX statvfs.f_fstr field - UUID from the superblock
