summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--config/kernel-dirty-inode.m423
-rw-r--r--config/kernel.m41
-rw-r--r--include/sys/zfs_vnops.h1
-rw-r--r--module/zfs/zfs_vnops.c73
-rw-r--r--module/zfs/zpl_super.c21
5 files changed, 96 insertions, 23 deletions
diff --git a/config/kernel-dirty-inode.m4 b/config/kernel-dirty-inode.m4
new file mode 100644
index 000000000..2ededf154
--- /dev/null
+++ b/config/kernel-dirty-inode.m4
@@ -0,0 +1,23 @@
+dnl #
+dnl # 3.0 API change
+dnl # The sops->dirty_inode() callbacks were updated to take a flags
+dnl # argument. This allows the greater control over whether the
+dnl # filesystem needs to push out a transaction or not.
+dnl #
+AC_DEFUN([ZFS_AC_KERNEL_DIRTY_INODE_WITH_FLAGS], [
+ AC_MSG_CHECKING([whether sops->dirty_inode() wants flags])
+ ZFS_LINUX_TRY_COMPILE([
+ #include <linux/fs.h>
+ ],[
+ void (*dirty_inode) (struct inode *, int) = NULL;
+ struct super_operations sops __attribute__ ((unused));
+
+ sops.dirty_inode = dirty_inode;
+ ],[
+ AC_MSG_RESULT([yes])
+ AC_DEFINE(HAVE_DIRTY_INODE_WITH_FLAGS, 1,
+ [sops->dirty_inode() wants flags])
+ ],[
+ AC_MSG_RESULT([no])
+ ])
+])
diff --git a/config/kernel.m4 b/config/kernel.m4
index 2312730c5..aab3a167b 100644
--- a/config/kernel.m4
+++ b/config/kernel.m4
@@ -47,6 +47,7 @@ AC_DEFUN([ZFS_AC_CONFIG_KERNEL], [
ZFS_AC_KERNEL_SHOW_OPTIONS
ZFS_AC_KERNEL_FSYNC
ZFS_AC_KERNEL_EVICT_INODE
+ ZFS_AC_KERNEL_DIRTY_INODE_WITH_FLAGS
ZFS_AC_KERNEL_NR_CACHED_OBJECTS
ZFS_AC_KERNEL_FREE_CACHED_OBJECTS
ZFS_AC_KERNEL_FALLOCATE
diff --git a/include/sys/zfs_vnops.h b/include/sys/zfs_vnops.h
index dd25fbcbc..5da5eaf1d 100644
--- a/include/sys/zfs_vnops.h
+++ b/include/sys/zfs_vnops.h
@@ -75,6 +75,7 @@ extern int zfs_setsecattr(struct inode *ip, vsecattr_t *vsecp, int flag,
extern int zfs_getpage(struct inode *ip, struct page *pl[], int nr_pages);
extern int zfs_putpage(struct inode *ip, struct page *pp,
struct writeback_control *wbc);
+extern int zfs_dirty_inode(struct inode *ip, int flags);
extern int zfs_map(struct inode *ip, offset_t off, caddr_t *addrp,
size_t len, unsigned long vm_flags);
diff --git a/module/zfs/zfs_vnops.c b/module/zfs/zfs_vnops.c
index 415ba7191..8ec4db26f 100644
--- a/module/zfs/zfs_vnops.c
+++ b/module/zfs/zfs_vnops.c
@@ -218,32 +218,11 @@ 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.
@@ -256,7 +235,7 @@ zfs_close(struct inode *ip, int flag, cred_t *cr)
VERIFY(zfs_vscan(ip, cr, 1) == 0);
ZFS_EXIT(zsb);
- return (error);
+ return (0);
}
EXPORT_SYMBOL(zfs_close);
@@ -3920,6 +3899,56 @@ zfs_putpage(struct inode *ip, struct page *pp, struct writeback_control *wbc)
return (err);
}
+/*
+ * Update the system attributes when the inode has been dirtied. For the
+ * moment we're conservative and only update the atime, mtime, and ctime.
+ */
+int
+zfs_dirty_inode(struct inode *ip, int flags)
+{
+ znode_t *zp = ITOZ(ip);
+ zfs_sb_t *zsb = ITOZSB(ip);
+ dmu_tx_t *tx;
+ uint64_t atime[2], mtime[2], ctime[2];
+ sa_bulk_attr_t bulk[3];
+ int error;
+ int cnt = 0;
+
+ ZFS_ENTER(zsb);
+ ZFS_VERIFY_ZP(zp);
+
+ tx = dmu_tx_create(zsb->z_os);
+
+ dmu_tx_hold_sa(tx, zp->z_sa_hdl, B_FALSE);
+ zfs_sa_upgrade_txholds(tx, zp);
+
+ error = dmu_tx_assign(tx, TXG_WAIT);
+ if (error) {
+ dmu_tx_abort(tx);
+ goto out;
+ }
+
+ mutex_enter(&zp->z_lock);
+ SA_ADD_BULK_ATTR(bulk, cnt, SA_ZPL_ATIME(zsb), NULL, &atime, 16);
+ 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);
+
+ /* Preserve the mtime and ctime provided by the inode */
+ ZFS_TIME_ENCODE(&ip->i_atime, atime);
+ ZFS_TIME_ENCODE(&ip->i_mtime, mtime);
+ ZFS_TIME_ENCODE(&ip->i_ctime, ctime);
+ zp->z_atime_dirty = 0;
+
+ error = sa_bulk_update(zp->z_sa_hdl, bulk, cnt, tx);
+ mutex_exit(&zp->z_lock);
+
+ dmu_tx_commit(tx);
+out:
+ ZFS_EXIT(zsb);
+ return (error);
+}
+EXPORT_SYMBOL(zfs_dirty_inode);
+
/*ARGSUSED*/
void
zfs_inactive(struct inode *ip)
diff --git a/module/zfs/zpl_super.c b/module/zfs/zpl_super.c
index fd4f691e1..d4d4e1b67 100644
--- a/module/zfs/zpl_super.c
+++ b/module/zfs/zpl_super.c
@@ -49,6 +49,25 @@ zpl_inode_destroy(struct inode *ip)
}
/*
+ * Called from __mark_inode_dirty() to reflect that something in the
+ * inode has changed. We use it to ensure the znode system attributes
+ * are always strictly update to date with respect to the inode.
+ */
+#ifdef HAVE_DIRTY_INODE_WITH_FLAGS
+static void
+zpl_dirty_inode(struct inode *ip, int flags)
+{
+ zfs_dirty_inode(ip, flags);
+}
+#else
+static void
+zpl_dirty_inode(struct inode *ip)
+{
+ zfs_dirty_inode(ip, 0);
+}
+#endif /* HAVE_DIRTY_INODE_WITH_FLAGS */
+
+/*
* When ->drop_inode() is called its return value indicates if the
* inode should be evicted from the inode cache. If the inode is
* unhashed and has no links the default policy is to evict it
@@ -306,7 +325,7 @@ zpl_free_cached_objects(struct super_block *sb, int nr_to_scan)
const struct super_operations zpl_super_operations = {
.alloc_inode = zpl_inode_alloc,
.destroy_inode = zpl_inode_destroy,
- .dirty_inode = NULL,
+ .dirty_inode = zpl_dirty_inode,
.write_inode = NULL,
.drop_inode = NULL,
#ifdef HAVE_EVICT_INODE