Preserve inode mtime/ctime in .writepage()
[zfs.git] / module / zfs / zfs_vnops.c
index 5765c9a..415ba71 100644 (file)
@@ -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);