diff options
author | Brian Behlendorf <[email protected]> | 2013-01-15 16:41:09 -0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2013-01-17 09:51:20 -0800 |
commit | 7b3e34ba5a7ee8d0fda44d214f6f11eb16cdb26f (patch) | |
tree | 0f3777fcaf3861b4c17353a13e523d63a1a30fd5 /module/zfs/zpl_inode.c | |
parent | f1a05fa11484d67161606fd1ecf50bf2abc25ced (diff) |
Fix 'zfs rollback' on mounted file systems
Rolling back a mounted filesystem with open file handles and
cached dentries+inodes never worked properly in ZoL. The
major issue was that Linux provides no easy mechanism for
modules to invalidate the inode cache for a file system.
Because of this it was possible that an inode from the previous
filesystem would not get properly dropped from the cache during
rolling back. Then a new inode with the same inode number would
be create and collide with the existing cached inode. Ideally
this would trigger an VERIFY() but in practice the error wasn't
handled and it would just NULL reference.
Luckily, this issue can be resolved by sprucing up the existing
Solaris zfs_rezget() functionality for the Linux VFS.
The way it works now is that when a file system is rolled back
all the cached inodes will be traversed and refetched from disk.
If a version of the cached inode exists on disk the in-core
copy will be updated accordingly. If there is no match for that
object on disk it will be unhashed from the inode cache and
marked as stale.
This will effectively make the inode unfindable for lookups
allowing the inode number to be immediately recycled. The inode
will then only be accessible from the cached dentries. Subsequent
dentry lookups which reference a stale inode will result in the
dentry being invalidated. Once invalidated the dentry will drop
its reference on the inode allowing it to be safely pruned from
the cache.
Special care is taken for negative dentries since they do not
reference any inode. These dentires will be invalidate based
on when they were added to the dentry cache. Entries added
before the last rollback will be invalidate to prevent them
from masking real files in the dataset.
Two nice side effects of this fix are:
* Removes the dependency on spl_invalidate_inodes(), it can now
be safely removed from the SPL when we choose to do so.
* zfs_znode_alloc() no longer requires a dentry to be passed.
This effectively reverts this portition of the code to its
upstream counterpart. The dentry is not instantiated more
correctly in the Linux ZPL layer.
Signed-off-by: Brian Behlendorf <[email protected]>
Signed-off-by: Ned Bass <[email protected]>
Closes #795
Diffstat (limited to 'module/zfs/zpl_inode.c')
-rw-r--r-- | module/zfs/zpl_inode.c | 79 |
1 files changed, 70 insertions, 9 deletions
diff --git a/module/zfs/zpl_inode.c b/module/zfs/zpl_inode.c index 6175c2e93..15ee0f610 100644 --- a/module/zfs/zpl_inode.c +++ b/module/zfs/zpl_inode.c @@ -46,6 +46,10 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) ASSERT3S(error, <=, 0); crfree(cr); + spin_lock(&dentry->d_lock); + dentry->d_time = jiffies; + spin_unlock(&dentry->d_lock); + if (error) { if (error == -ENOENT) return d_splice_alias(NULL, dentry); @@ -57,12 +61,10 @@ zpl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) } void -zpl_vap_init(vattr_t *vap, struct inode *dir, struct dentry *dentry, - zpl_umode_t mode, cred_t *cr) +zpl_vap_init(vattr_t *vap, struct inode *dir, zpl_umode_t mode, cred_t *cr) { vap->va_mask = ATTR_MODE; vap->va_mode = mode; - vap->va_dentry = dentry; vap->va_uid = crgetfsuid(cr); if (dir && dir->i_mode & S_ISGID) { @@ -90,12 +92,14 @@ zpl_create(struct inode *dir, struct dentry *dentry, zpl_umode_t mode, crhold(cr); vap = kmem_zalloc(sizeof(vattr_t), KM_SLEEP); - zpl_vap_init(vap, dir, dentry, mode, cr); + zpl_vap_init(vap, dir, mode, cr); error = -zfs_create(dir, dname(dentry), vap, 0, mode, &ip, cr, 0, NULL); if (error == 0) { error = zpl_xattr_security_init(ip, dir, &dentry->d_name); VERIFY3S(error, ==, 0); + d_instantiate(dentry, ip); + d_set_d_op(dentry, &zpl_dentry_operations); } kmem_free(vap, sizeof(vattr_t)); @@ -123,11 +127,15 @@ zpl_mknod(struct inode *dir, struct dentry *dentry, zpl_umode_t mode, crhold(cr); vap = kmem_zalloc(sizeof(vattr_t), KM_SLEEP); - zpl_vap_init(vap, dir, dentry, mode, cr); + zpl_vap_init(vap, dir, mode, cr); vap->va_rdev = rdev; - error = -zfs_create(dir, (char *)dentry->d_name.name, - vap, 0, mode, &ip, cr, 0, NULL); + error = -zfs_create(dir, dname(dentry), vap, 0, mode, &ip, cr, 0, NULL); + if (error == 0) { + d_instantiate(dentry, ip); + d_set_d_op(dentry, &zpl_dentry_operations); + } + kmem_free(vap, sizeof(vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); @@ -159,9 +167,14 @@ zpl_mkdir(struct inode *dir, struct dentry *dentry, zpl_umode_t mode) crhold(cr); vap = kmem_zalloc(sizeof(vattr_t), KM_SLEEP); - zpl_vap_init(vap, dir, dentry, mode | S_IFDIR, cr); + zpl_vap_init(vap, dir, mode | S_IFDIR, cr); error = -zfs_mkdir(dir, dname(dentry), vap, &ip, cr, 0, NULL); + if (error == 0) { + d_instantiate(dentry, ip); + d_set_d_op(dentry, &zpl_dentry_operations); + } + kmem_free(vap, sizeof(vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); @@ -262,9 +275,14 @@ zpl_symlink(struct inode *dir, struct dentry *dentry, const char *name) crhold(cr); vap = kmem_zalloc(sizeof(vattr_t), KM_SLEEP); - zpl_vap_init(vap, dir, dentry, S_IFLNK | S_IRWXUGO, cr); + zpl_vap_init(vap, dir, S_IFLNK | S_IRWXUGO, cr); error = -zfs_symlink(dir, dname(dentry), vap, (char *)name, &ip, cr, 0); + if (error == 0) { + d_instantiate(dentry, ip); + d_set_d_op(dentry, &zpl_dentry_operations); + } + kmem_free(vap, sizeof(vattr_t)); crfree(cr); ASSERT3S(error, <=, 0); @@ -334,6 +352,7 @@ zpl_link(struct dentry *old_dentry, struct inode *dir, struct dentry *dentry) } d_instantiate(dentry, ip); + d_set_d_op(dentry, &zpl_dentry_operations); out: crfree(cr); ASSERT3S(error, <=, 0); @@ -378,6 +397,44 @@ zpl_fallocate(struct inode *ip, int mode, loff_t offset, loff_t len) } #endif /* HAVE_INODE_FALLOCATE */ +static int +#ifdef HAVE_D_REVALIDATE_NAMEIDATA +zpl_revalidate(struct dentry *dentry, struct nameidata *nd) +{ + unsigned int flags = nd->flags; +#else +zpl_revalidate(struct dentry *dentry, unsigned int flags) +{ +#endif /* HAVE_D_REVALIDATE_NAMEIDATA */ + zfs_sb_t *zsb = dentry->d_sb->s_fs_info; + int error; + + if (flags & LOOKUP_RCU) + return (-ECHILD); + + /* + * After a rollback negative dentries created before the rollback + * time must be invalidated. Otherwise they can obscure files which + * are only present in the rolled back dataset. + */ + if (dentry->d_inode == NULL) { + spin_lock(&dentry->d_lock); + error = time_before(dentry->d_time, zsb->z_rollback_time); + spin_unlock(&dentry->d_lock); + + if (error) + return (0); + } + + /* + * The dentry may reference a stale inode if a mounted file system + * was rolled back to a point in time where the object didn't exist. + */ + if (dentry->d_inode && ITOZ(dentry->d_inode)->z_is_stale) + return (0); + + return (1); +} const struct inode_operations zpl_inode_operations = { .create = zpl_create, @@ -440,3 +497,7 @@ const struct inode_operations zpl_special_inode_operations = { .removexattr = generic_removexattr, .listxattr = zpl_xattr_list, }; + +dentry_operations_t zpl_dentry_operations = { + .d_revalidate = zpl_revalidate, +}; |