summaryrefslogtreecommitdiff
path: root/fs/aufs
diff options
context:
space:
mode:
Diffstat (limited to 'fs/aufs')
-rw-r--r--fs/aufs/branch.c11
-rw-r--r--fs/aufs/cpup.c4
-rw-r--r--fs/aufs/dentry.c35
-rw-r--r--fs/aufs/dentry.h3
-rw-r--r--fs/aufs/dir.c4
-rw-r--r--fs/aufs/file.c5
-rw-r--r--fs/aufs/fstype.h2
-rw-r--r--fs/aufs/i_op.c108
-rw-r--r--fs/aufs/i_op_ren.c8
-rw-r--r--fs/aufs/inode.c36
-rw-r--r--fs/aufs/inode.h10
-rw-r--r--fs/aufs/loop.c5
-rw-r--r--fs/aufs/module.c13
-rw-r--r--fs/aufs/mvdown.c21
-rw-r--r--fs/aufs/opts.c32
-rw-r--r--fs/aufs/opts.h1
-rw-r--r--fs/aufs/sbinfo.c10
-rw-r--r--fs/aufs/super.c95
-rw-r--r--fs/aufs/super.h14
-rw-r--r--fs/aufs/sysfs.c10
-rw-r--r--fs/aufs/sysrq.c4
-rw-r--r--fs/aufs/vfsub.h1
22 files changed, 296 insertions, 136 deletions
diff --git a/fs/aufs/branch.c b/fs/aufs/branch.c
index d34e789ca..72a8ee665 100644
--- a/fs/aufs/branch.c
+++ b/fs/aufs/branch.c
@@ -541,7 +541,7 @@ out:
/* ---------------------------------------------------------------------- */
-static unsigned long long au_farray_cb(void *a,
+static unsigned long long au_farray_cb(struct super_block *sb, void *a,
unsigned long long max __maybe_unused,
void *arg)
{
@@ -549,7 +549,6 @@ static unsigned long long au_farray_cb(void *a,
struct file **p, *f;
struct au_sphlhead *files;
struct au_finfo *finfo;
- struct super_block *sb = arg;
n = 0;
p = a;
@@ -574,7 +573,7 @@ static struct file **au_farray_alloc(struct super_block *sb,
unsigned long long *max)
{
*max = atomic_long_read(&au_sbi(sb)->si_nfiles);
- return au_array_alloc(max, au_farray_cb, sb);
+ return au_array_alloc(max, au_farray_cb, sb, /*arg*/NULL);
}
static void au_farray_free(struct file **a, unsigned long long max)
@@ -972,8 +971,8 @@ static void au_br_do_del(struct super_block *sb, aufs_bindex_t bindex,
au_br_do_free(br);
}
-static unsigned long long empty_cb(void *array, unsigned long long max,
- void *arg)
+static unsigned long long empty_cb(struct super_block *sb, void *array,
+ unsigned long long max, void *arg)
{
return max;
}
@@ -1018,7 +1017,7 @@ int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount)
br_id = br->br_id;
opened = atomic_read(&br->br_count);
if (unlikely(opened)) {
- to_free = au_array_alloc(&opened, empty_cb, NULL);
+ to_free = au_array_alloc(&opened, empty_cb, sb, NULL);
err = PTR_ERR(to_free);
if (IS_ERR(to_free))
goto out;
diff --git a/fs/aufs/cpup.c b/fs/aufs/cpup.c
index f6f6818ba..cd322746c 100644
--- a/fs/aufs/cpup.c
+++ b/fs/aufs/cpup.c
@@ -710,9 +710,9 @@ static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent)
goto out_parent;
if (unlikely(d_is_negative(h_src))) {
err = -EIO;
- AuIOErr("i%lu exists on a upper branch "
+ AuIOErr("i%lu exists on b%d "
"but not pseudo-linked\n",
- inode->i_ino);
+ inode->i_ino, cpg->bdst);
dput(h_src);
goto out_parent;
}
diff --git a/fs/aufs/dentry.c b/fs/aufs/dentry.c
index d03087c84..c33fb8318 100644
--- a/fs/aufs/dentry.c
+++ b/fs/aufs/dentry.c
@@ -685,6 +685,28 @@ out:
return err;
}
+void au_refresh_dop(struct dentry *dentry, int force_reval)
+{
+ const struct dentry_operations *dop
+ = force_reval ? &aufs_dop : dentry->d_sb->s_d_op;
+ static const unsigned int mask
+ = DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE;
+
+ BUILD_BUG_ON(sizeof(mask) != sizeof(dentry->d_flags));
+
+ if (dentry->d_op == dop)
+ return;
+
+ AuDbg("%pd\n", dentry);
+ spin_lock(&dentry->d_lock);
+ if (dop == &aufs_dop)
+ dentry->d_flags |= mask;
+ else
+ dentry->d_flags &= ~mask;
+ dentry->d_op = dop;
+ spin_unlock(&dentry->d_lock);
+}
+
int au_refresh_dentry(struct dentry *dentry, struct dentry *parent)
{
int err, ebrange;
@@ -1040,8 +1062,10 @@ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags)
if (!(flags & (LOOKUP_OPEN | LOOKUP_EMPTY))
&& inode
&& !(inode->i_state && I_LINKABLE)
- && (IS_DEADDIR(inode) || !inode->i_nlink))
+ && (IS_DEADDIR(inode) || !inode->i_nlink)) {
+ AuTraceErr(err);
goto out_inval;
+ }
do_udba = !au_opt_test(au_mntflags(sb), UDBA_NONE);
if (do_udba && inode) {
@@ -1050,8 +1074,10 @@ static int aufs_d_revalidate(struct dentry *dentry, unsigned int flags)
if (bstart >= 0) {
h_inode = au_h_iptr(inode, bstart);
- if (h_inode && au_test_higen(inode, h_inode))
+ if (h_inode && au_test_higen(inode, h_inode)) {
+ AuTraceErr(err);
goto out_inval;
+ }
}
}
@@ -1090,3 +1116,8 @@ const struct dentry_operations aufs_dop = {
.d_weak_revalidate = aufs_d_revalidate,
.d_release = aufs_d_release
};
+
+/* aufs_dop without d_revalidate */
+const struct dentry_operations aufs_dop_noreval = {
+ .d_release = aufs_d_release
+};
diff --git a/fs/aufs/dentry.h b/fs/aufs/dentry.h
index 65161462b..522522179 100644
--- a/fs/aufs/dentry.h
+++ b/fs/aufs/dentry.h
@@ -31,7 +31,7 @@ struct au_dinfo {
/* ---------------------------------------------------------------------- */
/* dentry.c */
-extern const struct dentry_operations aufs_dop;
+extern const struct dentry_operations aufs_dop, aufs_dop_noreval;
struct au_branch;
struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent);
int au_h_verify(struct dentry *h_dentry, unsigned int udba, struct inode *h_dir,
@@ -41,6 +41,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type);
int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh);
int au_refresh_dentry(struct dentry *dentry, struct dentry *parent);
int au_reval_dpath(struct dentry *dentry, unsigned int sigen);
+void au_refresh_dop(struct dentry *dentry, int force_reval);
/* dinfo.c */
void au_di_init_once(void *_di);
diff --git a/fs/aufs/dir.c b/fs/aufs/dir.c
index 630819083..ccdcebeb3 100644
--- a/fs/aufs/dir.c
+++ b/fs/aufs/dir.c
@@ -102,9 +102,9 @@ static void au_do_dir_ts(void *arg)
sb = a->dentry->d_sb;
if (d_really_is_negative(a->dentry))
goto out;
- aufs_read_lock(a->dentry, AuLock_DW | AuLock_DIR); /* noflush */
-
/* no dir->i_mutex lock */
+ aufs_read_lock(a->dentry, AuLock_DW); /* noflush */
+
dir = d_inode(a->dentry);
bstart = au_ibstart(dir);
bindex = au_br_index(sb, a->brid);
diff --git a/fs/aufs/file.c b/fs/aufs/file.c
index 8b17fbac1..72316b122 100644
--- a/fs/aufs/file.c
+++ b/fs/aufs/file.c
@@ -782,9 +782,11 @@ static void aufs_invalidatepage(struct page *page, unsigned int offset,
{ AuUnsupport(); }
static int aufs_releasepage(struct page *page, gfp_t gfp)
{ AuUnsupport(); return 0; }
+#if 0 /* called by memory compaction regardless file */
static int aufs_migratepage(struct address_space *mapping, struct page *newpage,
struct page *page, enum migrate_mode mode)
{ AuUnsupport(); return 0; }
+#endif
static int aufs_launder_page(struct page *page)
{ AuUnsupport(); return 0; }
static int aufs_is_partially_uptodate(struct page *page,
@@ -817,7 +819,8 @@ const struct address_space_operations aufs_aop = {
/* no bmap, no block device */
.invalidatepage = aufs_invalidatepage,
.releasepage = aufs_releasepage,
- .migratepage = aufs_migratepage,
+ /* is fallback_migrate_page ok? */
+ /* .migratepage = aufs_migratepage, */
.launder_page = aufs_launder_page,
.is_partially_uptodate = aufs_is_partially_uptodate,
.is_dirty_writeback = aufs_is_dirty_writeback,
diff --git a/fs/aufs/fstype.h b/fs/aufs/fstype.h
index 6c3e1debf..6196c606b 100644
--- a/fs/aufs/fstype.h
+++ b/fs/aufs/fstype.h
@@ -13,8 +13,8 @@
#include <linux/fs.h>
#include <linux/magic.h>
-#include <linux/romfs_fs.h>
#include <linux/nfs_fs.h>
+#include <linux/romfs_fs.h>
static inline int au_test_aufs(struct super_block *sb)
{
diff --git a/fs/aufs/i_op.c b/fs/aufs/i_op.c
index 9c60a9eed..daad67aa1 100644
--- a/fs/aufs/i_op.c
+++ b/fs/aufs/i_op.c
@@ -1392,78 +1392,80 @@ static int aufs_update_time(struct inode *inode, struct timespec *ts, int flags)
/* ---------------------------------------------------------------------- */
-struct inode_operations aufs_symlink_iop = {
- .permission = aufs_permission,
+/* no getattr version will be set by module.c:aufs_init() */
+struct inode_operations aufs_iop_nogetattr[AuIop_Last],
+ aufs_iop[] = {
+ [AuIop_SYMLINK] = {
+ .permission = aufs_permission,
#ifdef CONFIG_FS_POSIX_ACL
- .get_acl = aufs_get_acl,
- .set_acl = aufs_set_acl, /* unsupport for symlink? */
+ .get_acl = aufs_get_acl,
+ .set_acl = aufs_set_acl, /* unsupport for symlink? */
#endif
- .setattr = aufs_setattr,
- .getattr = aufs_getattr,
+ .setattr = aufs_setattr,
+ .getattr = aufs_getattr,
#ifdef CONFIG_AUFS_XATTR
- .setxattr = aufs_setxattr,
- .getxattr = aufs_getxattr,
- .listxattr = aufs_listxattr,
- .removexattr = aufs_removexattr,
+ .setxattr = aufs_setxattr,
+ .getxattr = aufs_getxattr,
+ .listxattr = aufs_listxattr,
+ .removexattr = aufs_removexattr,
#endif
- .readlink = generic_readlink,
- .follow_link = aufs_follow_link,
- .put_link = aufs_put_link,
-
- /* .update_time = aufs_update_time */
-};
-
-struct inode_operations aufs_dir_iop = {
- .create = aufs_create,
- .lookup = aufs_lookup,
- .link = aufs_link,
- .unlink = aufs_unlink,
- .symlink = aufs_symlink,
- .mkdir = aufs_mkdir,
- .rmdir = aufs_rmdir,
- .mknod = aufs_mknod,
- .rename = aufs_rename,
-
- .permission = aufs_permission,
+ .readlink = generic_readlink,
+ .follow_link = aufs_follow_link,
+ .put_link = aufs_put_link,
+
+ /* .update_time = aufs_update_time */
+ },
+ [AuIop_DIR] = {
+ .create = aufs_create,
+ .lookup = aufs_lookup,
+ .link = aufs_link,
+ .unlink = aufs_unlink,
+ .symlink = aufs_symlink,
+ .mkdir = aufs_mkdir,
+ .rmdir = aufs_rmdir,
+ .mknod = aufs_mknod,
+ .rename = aufs_rename,
+
+ .permission = aufs_permission,
#ifdef CONFIG_FS_POSIX_ACL
- .get_acl = aufs_get_acl,
- .set_acl = aufs_set_acl,
+ .get_acl = aufs_get_acl,
+ .set_acl = aufs_set_acl,
#endif
- .setattr = aufs_setattr,
- .getattr = aufs_getattr,
+ .setattr = aufs_setattr,
+ .getattr = aufs_getattr,
#ifdef CONFIG_AUFS_XATTR
- .setxattr = aufs_setxattr,
- .getxattr = aufs_getxattr,
- .listxattr = aufs_listxattr,
- .removexattr = aufs_removexattr,
+ .setxattr = aufs_setxattr,
+ .getxattr = aufs_getxattr,
+ .listxattr = aufs_listxattr,
+ .removexattr = aufs_removexattr,
#endif
- .update_time = aufs_update_time,
- .atomic_open = aufs_atomic_open,
- .tmpfile = aufs_tmpfile
-};
-
-struct inode_operations aufs_iop = {
- .permission = aufs_permission,
+ .update_time = aufs_update_time,
+ .atomic_open = aufs_atomic_open,
+ .tmpfile = aufs_tmpfile
+ },
+ [AuIop_OTHER] = {
+ .permission = aufs_permission,
#ifdef CONFIG_FS_POSIX_ACL
- .get_acl = aufs_get_acl,
- .set_acl = aufs_set_acl,
+ .get_acl = aufs_get_acl,
+ .set_acl = aufs_set_acl,
#endif
- .setattr = aufs_setattr,
- .getattr = aufs_getattr,
+ .setattr = aufs_setattr,
+ .getattr = aufs_getattr,
#ifdef CONFIG_AUFS_XATTR
- .setxattr = aufs_setxattr,
- .getxattr = aufs_getxattr,
- .listxattr = aufs_listxattr,
- .removexattr = aufs_removexattr,
+ .setxattr = aufs_setxattr,
+ .getxattr = aufs_getxattr,
+ .listxattr = aufs_listxattr,
+ .removexattr = aufs_removexattr,
#endif
- .update_time = aufs_update_time
+ .update_time = aufs_update_time
+ }
};
diff --git a/fs/aufs/i_op_ren.c b/fs/aufs/i_op_ren.c
index 652e973ce..fd815fdbc 100644
--- a/fs/aufs/i_op_ren.c
+++ b/fs/aufs/i_op_ren.c
@@ -829,11 +829,9 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry,
if (unlikely(d_really_is_positive(a->dst_dentry)
&& !d_is_dir(a->dst_dentry)))
goto out_free;
- err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
- AuLock_DIR | flags);
- } else
- err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry,
- flags);
+ flags |= AuLock_DIRS;
+ }
+ err = aufs_read_and_write_lock2(a->dst_dentry, a->src_dentry, flags);
if (unlikely(err))
goto out_free;
diff --git a/fs/aufs/inode.c b/fs/aufs/inode.c
index 559da3358..6db3d6f62 100644
--- a/fs/aufs/inode.c
+++ b/fs/aufs/inode.c
@@ -87,6 +87,32 @@ out:
return err;
}
+void au_refresh_iop(struct inode *inode, int force_getattr)
+{
+ int type;
+ struct au_sbinfo *sbi = au_sbi(inode->i_sb);
+ const struct inode_operations *iop
+ = force_getattr ? aufs_iop : sbi->si_iop_array;
+
+ if (inode->i_op == iop)
+ return;
+
+ switch (inode->i_mode & S_IFMT) {
+ case S_IFDIR:
+ type = AuIop_DIR;
+ break;
+ case S_IFLNK:
+ type = AuIop_SYMLINK;
+ break;
+ default:
+ type = AuIop_OTHER;
+ break;
+ }
+
+ inode->i_op = iop + type;
+ /* unnecessary smp_wmb() */
+}
+
int au_refresh_hinode_self(struct inode *inode)
{
int err, update;
@@ -168,11 +194,13 @@ static int set_inode(struct inode *inode, struct dentry *dentry)
struct dentry *h_dentry;
struct inode *h_inode;
struct au_iinfo *iinfo;
+ struct inode_operations *iop;
IiMustWriteLock(inode);
err = 0;
isdir = 0;
+ iop = au_sbi(inode->i_sb)->si_iop_array;
bstart = au_dbstart(dentry);
h_dentry = au_h_dptr(dentry, bstart);
h_inode = d_inode(h_dentry);
@@ -180,7 +208,7 @@ static int set_inode(struct inode *inode, struct dentry *dentry)
switch (mode & S_IFMT) {
case S_IFREG:
btail = au_dbtail(dentry);
- inode->i_op = &aufs_iop;
+ inode->i_op = iop + AuIop_OTHER;
inode->i_fop = &aufs_file_fop;
err = au_dy_iaop(inode, bstart, h_inode);
if (unlikely(err))
@@ -189,19 +217,19 @@ static int set_inode(struct inode *inode, struct dentry *dentry)
case S_IFDIR:
isdir = 1;
btail = au_dbtaildir(dentry);
- inode->i_op = &aufs_dir_iop;
+ inode->i_op = iop + AuIop_DIR;
inode->i_fop = &aufs_dir_fop;
break;
case S_IFLNK:
btail = au_dbtail(dentry);
- inode->i_op = &aufs_symlink_iop;
+ inode->i_op = iop + AuIop_SYMLINK;
break;
case S_IFBLK:
case S_IFCHR:
case S_IFIFO:
case S_IFSOCK:
btail = au_dbtail(dentry);
- inode->i_op = &aufs_iop;
+ inode->i_op = iop + AuIop_OTHER;
init_special_inode(inode, mode, h_inode->i_rdev);
break;
default:
diff --git a/fs/aufs/inode.h b/fs/aufs/inode.h
index 003534a44..a3a187616 100644
--- a/fs/aufs/inode.h
+++ b/fs/aufs/inode.h
@@ -117,6 +117,7 @@ static inline struct au_iinfo *au_ii(struct inode *inode)
/* inode.c */
struct inode *au_igrab(struct inode *inode);
+void au_refresh_iop(struct inode *inode, int force_getattr);
int au_refresh_hinode_self(struct inode *inode);
int au_refresh_hinode(struct inode *inode, struct dentry *dentry);
int au_ino(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino,
@@ -138,7 +139,14 @@ static inline int au_wh_ino(struct super_block *sb, aufs_bindex_t bindex,
}
/* i_op.c */
-extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop;
+enum {
+ AuIop_SYMLINK,
+ AuIop_DIR,
+ AuIop_OTHER,
+ AuIop_Last
+};
+extern struct inode_operations aufs_iop[AuIop_Last],
+ aufs_iop_nogetattr[AuIop_Last];
/* au_wr_dir flags */
#define AuWrDir_ADD_ENTRY 1
diff --git a/fs/aufs/loop.c b/fs/aufs/loop.c
index 69f7e9647..f324758e9 100644
--- a/fs/aufs/loop.c
+++ b/fs/aufs/loop.c
@@ -114,7 +114,7 @@ int au_loopback_init(void)
int err;
struct super_block *sb __maybe_unused;
- AuDebugOn(sizeof(sb->s_magic) != sizeof(unsigned long));
+ BUILD_BUG_ON(sizeof(sb->s_magic) != sizeof(unsigned long));
err = 0;
au_warn_loopback_array = kcalloc(au_warn_loopback_step,
@@ -127,6 +127,7 @@ int au_loopback_init(void)
void au_loopback_fin(void)
{
- symbol_put(loop_backing_file);
+ if (backing_file_func)
+ symbol_put(loop_backing_file);
kfree(au_warn_loopback_array);
}
diff --git a/fs/aufs/module.c b/fs/aufs/module.c
index a7a4b3647..3268e62e4 100644
--- a/fs/aufs/module.c
+++ b/fs/aufs/module.c
@@ -59,11 +59,10 @@ static void au_cache_fin(void)
/* excluding AuCache_HNOTIFY */
BUILD_BUG_ON(AuCache_HNOTIFY + 1 != AuCache_Last);
- for (i = 0; i < AuCache_HNOTIFY; i++)
- if (au_cachep[i]) {
- kmem_cache_destroy(au_cachep[i]);
- au_cachep[i] = NULL;
- }
+ for (i = 0; i < AuCache_HNOTIFY; i++) {
+ kmem_cache_destroy(au_cachep[i]);
+ au_cachep[i] = NULL;
+ }
}
/* ---------------------------------------------------------------------- */
@@ -133,6 +132,10 @@ static int __init aufs_init(void)
au_dir_roflags = au_file_roflags(O_DIRECTORY | O_LARGEFILE);
+ memcpy(aufs_iop_nogetattr, aufs_iop, sizeof(aufs_iop));
+ for (i = 0; i < AuIop_Last; i++)
+ aufs_iop_nogetattr[i].getattr = NULL;
+
au_sbilist_init();
sysaufs_brs_init();
au_debug_init();
diff --git a/fs/aufs/mvdown.c b/fs/aufs/mvdown.c
index bec20d360..53e0f6af9 100644
--- a/fs/aufs/mvdown.c
+++ b/fs/aufs/mvdown.c
@@ -353,6 +353,12 @@ static int au_do_mvdown(const unsigned char dmsg, struct au_mvd_args *a)
au_set_dbstart(a->dentry, a->mvd_bdst);
au_set_h_iptr(a->inode, a->mvd_bsrc, NULL, /*flags*/0);
au_set_ibstart(a->inode, a->mvd_bdst);
+ } else {
+ /* hide the lower */
+ au_set_h_dptr(a->dentry, a->mvd_bdst, NULL);
+ au_set_dbend(a->dentry, a->mvd_bsrc);
+ au_set_h_iptr(a->inode, a->mvd_bdst, NULL, /*flags*/0);
+ au_set_ibend(a->inode, a->mvd_bsrc);
}
if (au_dbend(a->dentry) < a->mvd_bdst)
au_set_dbend(a->dentry, a->mvd_bdst);
@@ -608,7 +614,9 @@ int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg)
int err, e;
unsigned char dmsg;
struct au_mvd_args *args;
+ struct inode *inode;
+ inode = d_inode(dentry);
err = -EPERM;
if (unlikely(!capable(CAP_SYS_ADMIN)))
goto out;
@@ -630,7 +638,7 @@ int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg)
args->mvdown.flags &= ~(AUFS_MVDOWN_ROLOWER_R | AUFS_MVDOWN_ROUPPER_R);
args->mvdown.au_errno = 0;
args->dentry = dentry;
- args->inode = d_inode(dentry);
+ args->inode = inode;
args->sb = dentry->d_sb;
err = -ENOENT;
@@ -644,8 +652,8 @@ int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg)
goto out_dir;
}
- mutex_lock_nested(&args->inode->i_mutex, I_MUTEX_CHILD);
- err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH);
+ mutex_lock_nested(&inode->i_mutex, I_MUTEX_CHILD);
+ err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_NOPLMW);
if (unlikely(err))
goto out_inode;
@@ -659,15 +667,16 @@ int au_mvdown(struct dentry *dentry, struct aufs_mvdown __user *uarg)
goto out_parent;
au_cpup_attr_timesizes(args->dir);
- au_cpup_attr_timesizes(args->inode);
- au_cpup_igen(args->inode, au_h_iptr(args->inode, args->mvd_bdst));
+ au_cpup_attr_timesizes(inode);
+ if (!(args->mvdown.flags & AUFS_MVDOWN_KUPPER))
+ au_cpup_igen(inode, au_h_iptr(inode, args->mvd_bdst));
/* au_digen_dec(dentry); */
out_parent:
di_write_unlock(args->parent);
aufs_read_unlock(dentry, AuLock_DW);
out_inode:
- mutex_unlock(&args->inode->i_mutex);
+ mutex_unlock(&inode->i_mutex);
out_dir:
mutex_unlock(&args->dir->i_mutex);
out_free:
diff --git a/fs/aufs/opts.c b/fs/aufs/opts.c
index 90098fe7f..f79d15e32 100644
--- a/fs/aufs/opts.c
+++ b/fs/aufs/opts.c
@@ -1555,10 +1555,10 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
{
int err, fhsm;
aufs_bindex_t bindex, bend;
- unsigned char do_plink, skip, do_free;
+ unsigned char do_plink, skip, do_free, can_no_dreval;
struct au_branch *br;
struct au_wbr *wbr;
- struct dentry *root;
+ struct dentry *root, *dentry;
struct inode *dir, *h_dir;
struct au_sbinfo *sbinfo;
struct au_hinode *hdir;
@@ -1588,6 +1588,8 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
root = sb->s_root;
dir = d_inode(root);
do_plink = !!au_opt_test(sbinfo->si_mntflags, PLINK);
+ can_no_dreval = !!au_opt_test((sbinfo->si_mntflags | pending),
+ UDBA_NONE);
bend = au_sbend(sb);
for (bindex = 0; !err && bindex <= bend; bindex++) {
skip = 0;
@@ -1636,6 +1638,15 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
if (wbr)
wbr_wh_read_unlock(wbr);
+ if (can_no_dreval) {
+ dentry = br->br_path.dentry;
+ spin_lock(&dentry->d_lock);
+ if (dentry->d_flags &
+ (DCACHE_OP_REVALIDATE | DCACHE_OP_WEAK_REVALIDATE))
+ can_no_dreval = 0;
+ spin_unlock(&dentry->d_lock);
+ }
+
if (au_br_fhsm(br->br_perm)) {
fhsm++;
AuDebugOn(!br->br_fhsm);
@@ -1659,6 +1670,11 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags,
}
}
+ if (can_no_dreval)
+ au_fset_si(sbinfo, NO_DREVAL);
+ else
+ au_fclr_si(sbinfo, NO_DREVAL);
+
if (fhsm >= 2) {
au_fset_si(sbinfo, FHSM);
for (bindex = bend; bindex >= 0; bindex--) {
@@ -1770,6 +1786,7 @@ out:
int au_opts_remount(struct super_block *sb, struct au_opts *opts)
{
int err, rerr;
+ unsigned char no_dreval;
struct inode *dir;
struct au_opt_xino *opt_xino;
struct au_opt *opt;
@@ -1777,9 +1794,9 @@ int au_opts_remount(struct super_block *sb, struct au_opts *opts)
SiMustWriteLock(sb);
+ err = 0;
dir = d_inode(sb->s_root);
sbinfo = au_sbi(sb);
- err = 0;
opt_xino = NULL;
opt = opts->opt;
while (err >= 0 && opt->type != Opt_tail) {
@@ -1795,10 +1812,14 @@ int au_opts_remount(struct super_block *sb, struct au_opts *opts)
AuTraceErr(err);
/* go on even err */
+ no_dreval = !!au_ftest_si(sbinfo, NO_DREVAL);
rerr = au_opts_verify(sb, opts->sb_flags, /*pending*/0);
if (unlikely(rerr && !err))
err = rerr;
+ if (no_dreval != !!au_ftest_si(sbinfo, NO_DREVAL))
+ au_fset_opts(opts->flags, REFRESH_IDOP);
+
if (au_ftest_opts(opts->flags, TRUNC_XIB)) {
rerr = au_xib_trunc(sb);
if (unlikely(rerr && !err))
@@ -1807,7 +1828,10 @@ int au_opts_remount(struct super_block *sb, struct au_opts *opts)
/* will be handled by the caller */
if (!au_ftest_opts(opts->flags, REFRESH)
- && (opts->given_udba || au_opt_test(sbinfo->si_mntflags, XINO)))
+ && (opts->given_udba
+ || au_opt_test(sbinfo->si_mntflags, XINO)
+ || au_ftest_opts(opts->flags, REFRESH_IDOP)
+ ))
au_fset_opts(opts->flags, REFRESH);
AuDbg("status 0x%x\n", opts->flags);
diff --git a/fs/aufs/opts.h b/fs/aufs/opts.h
index da552ff50..3899e9240 100644
--- a/fs/aufs/opts.h
+++ b/fs/aufs/opts.h
@@ -161,6 +161,7 @@ struct au_opt {
#define AuOpts_REFRESH (1 << 1)
#define AuOpts_TRUNC_XIB (1 << 2)
#define AuOpts_REFRESH_DYAOP (1 << 3)
+#define AuOpts_REFRESH_IDOP (1 << 4)
#define au_ftest_opts(flags, name) ((flags) & AuOpts_##name)
#define au_fset_opts(flags, name) \
do { (flags) |= AuOpts_##name; } while (0)
diff --git a/fs/aufs/sbinfo.c b/fs/aufs/sbinfo.c
index cb7ac83a1..8f2ec5573 100644
--- a/fs/aufs/sbinfo.c
+++ b/fs/aufs/sbinfo.c
@@ -113,6 +113,9 @@ int au_si_alloc(struct super_block *sb)
au_sphl_init(&sbinfo->si_files);
+ /* with getattr by default */
+ sbinfo->si_iop_array = aufs_iop;
+
/* leave other members for sysaufs and si_mnt. */
sbinfo->si_sb = sb;
sb->s_fs_info = sbinfo;
@@ -237,7 +240,10 @@ int aufs_read_lock(struct dentry *dentry, int flags)
if (au_ftest_lock(flags, GEN)) {
err = au_digen_test(dentry, au_sigen(sb));
- AuDebugOn(!err && au_dbrange_test(dentry));
+ if (!au_opt_test(au_mntflags(sb), UDBA_NONE))
+ AuDebugOn(!err && au_dbrange_test(dentry));
+ else if (!err)
+ err = au_dbrange_test(dentry);
if (unlikely(err))
aufs_read_unlock(dentry, flags);
}
@@ -278,7 +284,7 @@ int aufs_read_and_write_lock2(struct dentry *d1, struct dentry *d2, int flags)
if (unlikely(err))
goto out;
- di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIR));
+ di_write_lock2_child(d1, d2, au_ftest_lock(flags, DIRS));
if (au_ftest_lock(flags, GEN)) {
sigen = au_sigen(sb);
diff --git a/fs/aufs/super.c b/fs/aufs/super.c
index 7ae6ea12e..3fe10d35d 100644
--- a/fs/aufs/super.c
+++ b/fs/aufs/super.c
@@ -93,13 +93,13 @@ static int au_show_brs(struct seq_file *seq, struct super_block *sb)
err = au_seq_path(seq, &path);
if (!err) {
au_optstr_br_perm(&perm, br->br_perm);
- err = seq_printf(seq, "=%s", perm.a);
- if (err == -1)
- err = -E2BIG;
+ seq_printf(seq, "=%s", perm.a);
+ if (bindex != bend)
+ seq_putc(seq, ':');
}
- if (!err && bindex != bend)
- err = seq_putc(seq, ':');
}
+ if (unlikely(err || seq_has_overflowed(seq)))
+ err = -E2BIG;
return err;
}
@@ -466,7 +466,8 @@ void au_array_free(void *array)
}
}
-void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg)
+void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb,
+ struct super_block *sb, void *arg)
{
void *array;
unsigned long long n, sz;
@@ -491,7 +492,7 @@ void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg)
goto out;
}
- n = cb(array, *hint, arg);
+ n = cb(sb, array, *hint, arg);
AuDebugOn(n > *hint);
out:
@@ -499,7 +500,7 @@ out:
return array;
}
-static unsigned long long au_iarray_cb(void *a,
+static unsigned long long au_iarray_cb(struct super_block *sb, void *a,
unsigned long long max __maybe_unused,
void *arg)
{
@@ -510,7 +511,7 @@ static unsigned long long au_iarray_cb(void *a,
n = 0;
p = a;
head = arg;
- spin_lock(&inode_sb_list_lock);
+ spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(inode, head, i_sb_list) {
if (!is_bad_inode(inode)
&& au_ii(inode)->ii_bstart >= 0) {
@@ -524,7 +525,7 @@ static unsigned long long au_iarray_cb(void *a,
spin_unlock(&inode->i_lock);
}
}
- spin_unlock(&inode_sb_list_lock);
+ spin_unlock(&sb->s_inode_list_lock);
return n;
}
@@ -532,7 +533,7 @@ static unsigned long long au_iarray_cb(void *a,
struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max)
{
*max = atomic_long_read(&au_sbi(sb)->si_ninodes);
- return au_array_alloc(max, au_iarray_cb, &sb->s_inodes);
+ return au_array_alloc(max, au_iarray_cb, sb, &sb->s_inodes);
}
void au_iarray_free(struct inode **a, unsigned long long max)
@@ -568,7 +569,7 @@ static int au_do_refresh(struct dentry *dentry, unsigned int dir_flags,
static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen,
struct au_sbinfo *sbinfo,
- const unsigned int dir_flags)
+ const unsigned int dir_flags, unsigned int do_idop)
{
int err;
struct dentry *parent;
@@ -591,11 +592,17 @@ static int au_do_refresh_d(struct dentry *dentry, unsigned int sigen,
}
dput(parent);
+ if (!err) {
+ if (do_idop)
+ au_refresh_dop(dentry, /*force_reval*/0);
+ } else
+ au_refresh_dop(dentry, /*force_reval*/1);
+
AuTraceErr(err);
return err;
}
-static int au_refresh_d(struct super_block *sb)
+static int au_refresh_d(struct super_block *sb, unsigned int do_idop)
{
int err, i, j, ndentry, e;
unsigned int sigen;
@@ -606,6 +613,9 @@ static int au_refresh_d(struct super_block *sb)
struct dentry *root = sb->s_root;
const unsigned int dir_flags = au_hi_flags(d_inode(root), /*isdir*/1);
+ if (do_idop)
+ au_refresh_dop(root, /*force_reval*/0);
+
err = au_dpages_init(&dpages, GFP_NOFS);
if (unlikely(err))
goto out;
@@ -621,7 +631,8 @@ static int au_refresh_d(struct super_block *sb)
ndentry = dpage->ndentry;
for (j = 0; j < ndentry; j++) {
d = dentries[j];
- e = au_do_refresh_d(d, sigen, sbinfo, dir_flags);
+ e = au_do_refresh_d(d, sigen, sbinfo, dir_flags,
+ do_idop);
if (unlikely(e && !err))
err = e;
/* go on even err */
@@ -634,7 +645,7 @@ out:
return err;
}
-static int au_refresh_i(struct super_block *sb)
+static int au_refresh_i(struct super_block *sb, unsigned int do_idop)
{
int err, e;
unsigned int sigen;
@@ -652,17 +663,22 @@ static int au_refresh_i(struct super_block *sb)
inode = array[ull];
if (unlikely(!inode))
break;
+
+ e = 0;
+ ii_write_lock_child(inode);
if (au_iigen(inode, NULL) != sigen) {
- ii_write_lock_child(inode);
e = au_refresh_hinode_self(inode);
- ii_write_unlock(inode);
if (unlikely(e)) {
+ au_refresh_iop(inode, /*force_getattr*/1);
pr_err("error %d, i%lu\n", e, inode->i_ino);
if (!err)
err = e;
/* go on even if err */
}
}
+ if (!e && do_idop)
+ au_refresh_iop(inode, /*force_getattr*/0);
+ ii_write_unlock(inode);
}
au_iarray_free(array, max);
@@ -671,7 +687,7 @@ out:
return err;
}
-static void au_remount_refresh(struct super_block *sb)
+static void au_remount_refresh(struct super_block *sb, unsigned int do_idop)
{
int err, e;
unsigned int udba;
@@ -679,9 +695,11 @@ static void au_remount_refresh(struct super_block *sb)
struct dentry *root;
struct inode *inode;
struct au_branch *br;
+ struct au_sbinfo *sbi;
au_sigen_inc(sb);
- au_fclr_si(au_sbi(sb), FAILED_REFRESH_DIR);
+ sbi = au_sbi(sb);
+ au_fclr_si(sbi, FAILED_REFRESH_DIR);
root = sb->s_root;
DiMustNoWaiters(root);
@@ -700,9 +718,25 @@ static void au_remount_refresh(struct super_block *sb)
}
au_hn_reset(inode, au_hi_flags(inode, /*isdir*/1));
+ if (do_idop) {
+ if (au_ftest_si(sbi, NO_DREVAL)) {
+ AuDebugOn(sb->s_d_op == &aufs_dop_noreval);
+ sb->s_d_op = &aufs_dop_noreval;
+ AuDebugOn(sbi->si_iop_array == aufs_iop_nogetattr);
+ sbi->si_iop_array = aufs_iop_nogetattr;
+ } else {
+ AuDebugOn(sb->s_d_op == &aufs_dop);
+ sb->s_d_op = &aufs_dop;
+ AuDebugOn(sbi->si_iop_array == aufs_iop);
+ sbi->si_iop_array = aufs_iop;
+ }
+ pr_info("reset to %pf and %pf\n",
+ sb->s_d_op, sbi->si_iop_array);
+ }
+
di_write_unlock(root);
- err = au_refresh_d(sb);
- e = au_refresh_i(sb);
+ err = au_refresh_d(sb, do_idop);
+ e = au_refresh_i(sb, do_idop);
if (unlikely(e && !err))
err = e;
/* aufs_write_lock() calls ..._child() */
@@ -777,7 +811,7 @@ static int aufs_remount_fs(struct super_block *sb, int *flags, char *data)
au_opts_free(&opts);
if (au_ftest_opts(opts.flags, REFRESH))
- au_remount_refresh(sb);
+ au_remount_refresh(sb, au_ftest_opts(opts.flags, REFRESH_IDOP));
if (au_ftest_opts(opts.flags, REFRESH_DYAOP)) {
mntflags = au_mntflags(sb);
@@ -824,7 +858,7 @@ static int alloc_root(struct super_block *sb)
if (IS_ERR(inode))
goto out;
- inode->i_op = &aufs_dir_iop;
+ inode->i_op = aufs_iop + AuIop_DIR; /* with getattr by default */
inode->i_fop = &aufs_dir_fop;
inode->i_mode = S_IFDIR;
set_nlink(inode, 2);
@@ -853,6 +887,7 @@ static int aufs_fill_super(struct super_block *sb, void *raw_data,
{
int err;
struct au_opts opts;
+ struct au_sbinfo *sbinfo;
struct dentry *root;
struct inode *inode;
char *arg = raw_data;
@@ -874,6 +909,7 @@ static int aufs_fill_super(struct super_block *sb, void *raw_data,
err = au_si_alloc(sb);
if (unlikely(err))
goto out_opts;
+ sbinfo = au_sbi(sb);
/* all timestamps always follow the ones on the branch */
sb->s_flags |= MS_NOATIME | MS_NODIRATIME;
@@ -909,6 +945,13 @@ static int aufs_fill_super(struct super_block *sb, void *raw_data,
aufs_write_lock(root);
err = au_opts_mount(sb, &opts);
au_opts_free(&opts);
+ if (!err && au_ftest_si(sbinfo, NO_DREVAL)) {
+ sb->s_d_op = &aufs_dop_noreval;
+ pr_info("%pf\n", sb->s_d_op);
+ au_refresh_dop(root, /*force_reval*/0);
+ sbinfo->si_iop_array = aufs_iop_nogetattr;
+ au_refresh_iop(inode, /*force_getattr*/0);
+ }
aufs_write_unlock(root);
mutex_unlock(&inode->i_mutex);
if (!err)
@@ -918,8 +961,8 @@ out_root:
dput(root);
sb->s_root = NULL;
out_info:
- dbgaufs_si_fin(au_sbi(sb));
- kobject_put(&au_sbi(sb)->si_kobj);
+ dbgaufs_si_fin(sbinfo);
+ kobject_put(&sbinfo->si_kobj);
sb->s_fs_info = NULL;
out_opts:
free_page((unsigned long)opts.opt);
@@ -968,7 +1011,7 @@ static void aufs_kill_sb(struct super_block *sb)
sbinfo->si_wbr_create_ops->fin(sb);
if (au_opt_test(sbinfo->si_mntflags, UDBA_HNOTIFY)) {
au_opt_set_udba(sbinfo->si_mntflags, UDBA_NONE);
- au_remount_refresh(sb);
+ au_remount_refresh(sb, /*do_idop*/0);
}
if (au_opt_test(sbinfo->si_mntflags, PLINK))
au_plink_put(sb, /*verbose*/1);
diff --git a/fs/aufs/super.h b/fs/aufs/super.h
index 8ec54fa6e..e0f588ef2 100644
--- a/fs/aufs/super.h
+++ b/fs/aufs/super.h
@@ -178,6 +178,9 @@ struct au_sbinfo {
/* file list */
struct au_sphlhead si_files;
+ /* with/without getattr, brother of sb->s_d_op */
+ struct inode_operations *si_iop_array;
+
/*
* sysfs and lifetime management.
* this is not a small structure and it may be a waste of memory in case
@@ -209,8 +212,8 @@ struct au_sbinfo {
* if it is false, refreshing dirs at access time is unnecesary
*/
#define AuSi_FAILED_REFRESH_DIR 1
-
#define AuSi_FHSM (1 << 1) /* fhsm is active now */
+#define AuSi_NO_DREVAL (1 << 2) /* disable all d_revalidate */
#ifndef CONFIG_AUFS_FHSM
#undef AuSi_FHSM
@@ -246,7 +249,7 @@ static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi,
#define AuLock_IR (1 << 1) /* read-lock inode */
#define AuLock_IW (1 << 2) /* write-lock inode */
#define AuLock_FLUSH (1 << 3) /* wait for 'nowait' tasks */
-#define AuLock_DIR (1 << 4) /* target is a dir */
+#define AuLock_DIRS (1 << 4) /* target is a pair of dirs */
#define AuLock_NOPLM (1 << 5) /* return err in plm mode */
#define AuLock_NOPLMW (1 << 6) /* wait for plm mode ends */
#define AuLock_GEN (1 << 7) /* test digen/iigen */
@@ -261,10 +264,11 @@ static inline unsigned char au_do_ftest_si(struct au_sbinfo *sbi,
/* super.c */
extern struct file_system_type aufs_fs_type;
struct inode *au_iget_locked(struct super_block *sb, ino_t ino);
-typedef unsigned long long (*au_arraycb_t)(void *array, unsigned long long max,
- void *arg);
+typedef unsigned long long (*au_arraycb_t)(struct super_block *sb, void *array,
+ unsigned long long max, void *arg);
void au_array_free(void *array);
-void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb, void *arg);
+void *au_array_alloc(unsigned long long *hint, au_arraycb_t cb,
+ struct super_block *sb, void *arg);
struct inode **au_iarray_alloc(struct super_block *sb, unsigned long long *max);
void au_iarray_free(struct inode **a, unsigned long long max);
diff --git a/fs/aufs/sysfs.c b/fs/aufs/sysfs.c
index cc74d02b1..ec8df8f6c 100644
--- a/fs/aufs/sysfs.c
+++ b/fs/aufs/sysfs.c
@@ -64,15 +64,15 @@ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb,
err = au_seq_path(seq, &path);
if (!err) {
au_optstr_br_perm(&perm, br->br_perm);
- err = seq_printf(seq, "=%s\n", perm.a);
+ seq_printf(seq, "=%s\n", perm.a);
}
break;
case AuBrSysfs_BRID:
- err = seq_printf(seq, "%d\n", br->br_id);
+ seq_printf(seq, "%d\n", br->br_id);
break;
}
di_read_unlock(root, !AuLock_IR);
- if (err == -1)
+ if (unlikely(err || seq_has_overflowed(seq)))
err = -E2BIG;
return err;
@@ -229,8 +229,8 @@ static int au_brinfo(struct super_block *sb, union aufs_brinfo __user *arg)
err = au_seq_path(seq, &br->br_path);
if (unlikely(err))
break;
- err = seq_putc(seq, '\0');
- if (!err && seq->count <= sz) {
+ seq_putc(seq, '\0');
+ if (!seq_has_overflowed(seq)) {
err = copy_to_user(arg->path, seq->buf, seq->count);
seq->count = 0;
if (unlikely(err))
diff --git a/fs/aufs/sysrq.c b/fs/aufs/sysrq.c
index dcfa82462..3a0182b90 100644
--- a/fs/aufs/sysrq.c
+++ b/fs/aufs/sysrq.c
@@ -64,14 +64,14 @@ static void sysrq_sb(struct super_block *sb)
struct inode *i;
pr("isolated inode\n");
- spin_lock(&inode_sb_list_lock);
+ spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(i, &sb->s_inodes, i_sb_list) {
spin_lock(&i->i_lock);
if (1 || hlist_empty(&i->i_dentry))
au_dpri_inode(i);
spin_unlock(&i->i_lock);
}
- spin_unlock(&inode_sb_list_lock);
+ spin_unlock(&sb->s_inode_list_lock);
}
#endif
pr("files\n");
diff --git a/fs/aufs/vfsub.h b/fs/aufs/vfsub.h
index 541b7b160..4218da12e 100644
--- a/fs/aufs/vfsub.h
+++ b/fs/aufs/vfsub.h
@@ -19,7 +19,6 @@
/* copied from linux/fs/internal.h */
/* todo: BAD approach!! */
extern void __mnt_drop_write(struct vfsmount *);
-extern spinlock_t inode_sb_list_lock;
extern int open_check_o_direct(struct file *f);
/* ---------------------------------------------------------------------- */