summaryrefslogtreecommitdiffstats
path: root/module/zfs/zfs_vnops.c
diff options
context:
space:
mode:
authorBrian Behlendorf <[email protected]>2012-12-04 12:11:02 -0800
committerBrian Behlendorf <[email protected]>2012-12-05 13:00:25 -0800
commitd3aa3ea96e02547166563bbd60bc8581567a140a (patch)
treeb0b5a37e2f44e2835a6cc03e5052bb51d45f89d0 /module/zfs/zfs_vnops.c
parent53c2ec1d1b3ffdc90c1c6516558e6b4da77c2446 (diff)
Preserve inode mtime/ctime in .writepage()
When updating a file via mmap()'ed I/O preserve the mtime/ctime which were updated when the page was made writable by the generic callback filemap_page_mkwrite(). But more importantly than preserving the exact time add the missing call to sa_bulk_update(). This ensures that the znode modifications are written to disk as part of the transaction. Without this the inode may mistaken rollback to the previous on-disk znode state. Additionally, for mmap()'ed znodes explicitly set the atime, mtime, and ctime on close using the up to date values in the inode. This is critical because writepage() may occur after close and on close we need to ensure the values are correct. Original-patch-by: Richard Yao <[email protected]> Signed-off-by: Richard Yao <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Closes #764
Diffstat (limited to 'module/zfs/zfs_vnops.c')
-rw-r--r--module/zfs/zfs_vnops.c36
1 files changed, 32 insertions, 4 deletions
diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
index 5765c9aa6..415ba7191 100644
--- a/module/zfs/zfs_vnops.c
+++ b/module/zfs/zfs_vnops.c
@@ -218,11 +218,32 @@ zfs_close(struct inode *ip, int flag, cred_t *cr)
{
znode_t *zp = ITOZ(ip);
zfs_sb_t *zsb = ITOZSB(ip);
+ int error = 0;
ZFS_ENTER(zsb);
ZFS_VERIFY_ZP(zp);
/*
+ * When closing an mmap()'ed file ensure the inode atime, mtime, and
+ * ctime are written to disk. These values may have been updated in
+ * memory by filemap_page_mkwrite() bit are not yet reflected in the
+ * znode since writepage() may occur after the close.
+ */
+ if (zp->z_is_mapped) {
+ vattr_t *vap;
+
+ vap = kmem_zalloc(sizeof(vattr_t), KM_SLEEP);
+ vap->va_mask = ATTR_ATIME | ATTR_MTIME | ATTR_CTIME;
+ vap->va_atime = ip->i_atime;
+ vap->va_mtime = ip->i_mtime;
+ vap->va_ctime = ip->i_ctime;
+
+ error = zfs_setattr(ip, vap, 0, cr);
+
+ kmem_free(vap, sizeof(vattr_t));
+ }
+
+ /*
* Zero the synchronous opens in the znode. Under Linux the
* zfs_close() hook is not symmetric with zfs_open(), it is
* only called once when the last reference is dropped.
@@ -235,7 +256,7 @@ zfs_close(struct inode *ip, int flag, cred_t *cr)
VERIFY(zfs_vscan(ip, cr, 1) == 0);
ZFS_EXIT(zsb);
- return (0);
+ return (error);
}
EXPORT_SYMBOL(zfs_close);
@@ -3876,12 +3897,19 @@ zfs_putpage(struct inode *ip, struct page *pp, struct writeback_control *wbc)
SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_MTIME(zsb), NULL, &mtime, 16);
SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_CTIME(zsb), NULL, &ctime, 16);
SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_FLAGS(zsb), NULL, &zp->z_pflags, 8);
- zfs_tstamp_update_setup(zp, CONTENT_MODIFIED, mtime, ctime, B_TRUE);
- zfs_log_write(zsb->z_log, tx, TX_WRITE, zp, pgoff, pglen, 0);
+ /* Preserve the mtime and ctime provided by the inode */
+ ZFS_TIME_ENCODE(&ip->i_mtime, mtime);
+ ZFS_TIME_ENCODE(&ip->i_ctime, ctime);
+ zp->z_atime_dirty = 0;
+ zp->z_seq++;
+
+ err = sa_bulk_update(zp->z_sa_hdl, bulk, cnt, tx);
+
+ zfs_log_write(zsb->z_log, tx, TX_WRITE, zp, pgoff, pglen, 0);
dmu_tx_commit(tx);
+
zfs_range_unlock(rl);
- ASSERT3S(err, ==, 0);
if (sync) {
zil_commit(zsb->z_log, zp->z_id);