Commit 9f923257 authored by J. R. Okajima's avatar J. R. Okajima
Browse files

aufs: pin or lock the parent dir and the child on a branch



To create/delete/rename files including copy-up, aufs acquires several
locks on the branch fs internally. These lock/unlock operations are
consolidated into struct au_pin in this commit.
au_pin handles
- LOCKDEP class
- re-validate/verify
- suspend/resume HNOTIFY

See also lookup.txt in later commit.
Signed-off-by: default avatarJ. R. Okajima <hooanon05g@gmail.com>
parent 629cbe32
...@@ -13,8 +13,8 @@ obj-$(CONFIG_AUFS_FS) += aufs.o ...@@ -13,8 +13,8 @@ obj-$(CONFIG_AUFS_FS) += aufs.o
aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \ aufs-y := module.o sbinfo.o super.o branch.o xino.o sysaufs.o opts.o \
wkq.o vfsub.o dcsub.o \ wkq.o vfsub.o dcsub.o \
cpup.o whout.o \ cpup.o whout.o \
dinfo.o \ dinfo.o dentry.o \
iinfo.o inode.o iinfo.o inode.o i_op.o
# all are boolean # all are boolean
aufs-$(CONFIG_PROC_FS) += procfs.o plink.o aufs-$(CONFIG_PROC_FS) += procfs.o plink.o
......
...@@ -80,6 +80,9 @@ AuStubInt0(au_debug_test, void) ...@@ -80,6 +80,9 @@ AuStubInt0(au_debug_test, void)
AuDbg("err %ld\n", PTR_ERR(p)); \ AuDbg("err %ld\n", PTR_ERR(p)); \
} while (0) } while (0)
/* dirty macros for debug print, use with "%.*s" and caution */
#define AuLNPair(qstr) (qstr)->len, (qstr)->name
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
struct dentry; struct dentry;
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* lookup and dentry operations
*/
#include <linux/iversion.h>
#include <linux/namei.h>
#include "aufs.h"
/* subset of struct inode */
struct au_iattr {
unsigned long i_ino;
/* unsigned int i_nlink; */
kuid_t i_uid;
kgid_t i_gid;
u64 i_version;
/*
loff_t i_size;
blkcnt_t i_blocks;
*/
umode_t i_mode;
};
static void au_iattr_save(struct au_iattr *ia, struct inode *h_inode)
{
ia->i_ino = h_inode->i_ino;
/* ia->i_nlink = h_inode->i_nlink; */
ia->i_uid = h_inode->i_uid;
ia->i_gid = h_inode->i_gid;
ia->i_version = inode_query_iversion(h_inode);
/*
ia->i_size = h_inode->i_size;
ia->i_blocks = h_inode->i_blocks;
*/
ia->i_mode = (h_inode->i_mode & S_IFMT);
}
static int au_iattr_test(struct au_iattr *ia, struct inode *h_inode)
{
return ia->i_ino != h_inode->i_ino
/* || ia->i_nlink != h_inode->i_nlink */
|| !uid_eq(ia->i_uid, h_inode->i_uid)
|| !gid_eq(ia->i_gid, h_inode->i_gid)
|| !inode_eq_iversion(h_inode, ia->i_version)
/*
|| ia->i_size != h_inode->i_size
|| ia->i_blocks != h_inode->i_blocks
*/
|| ia->i_mode != (h_inode->i_mode & S_IFMT);
}
static int au_h_verify_dentry(struct dentry *h_dentry, struct dentry *h_parent,
struct au_branch *br)
{
int err;
struct au_iattr ia;
struct inode *h_inode;
struct dentry *h_d;
struct super_block *h_sb;
err = 0;
memset(&ia, -1, sizeof(ia));
h_sb = h_dentry->d_sb;
h_inode = NULL;
if (d_is_positive(h_dentry)) {
h_inode = d_inode(h_dentry);
au_iattr_save(&ia, h_inode);
} else if (au_test_nfs(h_sb))
/* nfs d_revalidate may return 0 for negative dentry */
goto out;
/* main purpose is namei.c:cached_lookup() and d_revalidate */
h_d = vfsub_lkup_one(&h_dentry->d_name, h_parent);
err = PTR_ERR(h_d);
if (IS_ERR(h_d))
goto out;
err = 0;
if (unlikely(h_d != h_dentry
|| d_inode(h_d) != h_inode
|| (h_inode && au_iattr_test(&ia, h_inode))))
err = -EBUSY;
dput(h_d);
out:
AuTraceErr(err);
return err;
}
int au_h_verify(struct dentry *h_dentry, struct inode *h_dir,
struct dentry *h_parent, struct au_branch *br)
{
int err;
err = 0;
if (!au_test_fs_remote(h_dentry->d_sb)) {
IMustLock(h_dir);
err = (d_inode(h_dentry->d_parent) != h_dir);
} else
err = au_h_verify_dentry(h_dentry, h_parent, br);
return err;
}
...@@ -31,6 +31,11 @@ struct au_dinfo { ...@@ -31,6 +31,11 @@ struct au_dinfo {
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
/* dentry.c */
struct au_branch;
int au_h_verify(struct dentry *h_dentry, struct inode *h_dir,
struct dentry *h_parent, struct au_branch *br);
/* dinfo.c */ /* dinfo.c */
void au_di_init_once(void *_di); void au_di_init_once(void *_di);
struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc); struct au_dinfo *au_di_alloc(struct super_block *sb, unsigned int lsc);
......
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2005-2019 Junjiro R. Okajima
*/
/*
* inode operations (except add/del/rename)
*/
#include "aufs.h"
void au_pin_hdir_unlock(struct au_pin *p)
{
if (p->hdir)
inode_unlock(p->hdir->hi_inode);
}
int au_pin_hdir_lock(struct au_pin *p)
{
int err;
err = 0;
if (!p->hdir)
goto out;
/* even if an error happens later, keep this lock */
inode_lock_nested(p->hdir->hi_inode, p->lsc_hi);
err = -EBUSY;
if (unlikely(p->hdir->hi_inode != d_inode(p->h_parent)))
goto out;
err = 0;
if (p->h_dentry)
err = au_h_verify(p->h_dentry, p->hdir->hi_inode, p->h_parent,
p->br);
out:
return err;
}
int au_pin_hdir_relock(struct au_pin *p)
{
int err, i;
struct inode *h_i;
struct dentry *h_d[] = {
p->h_dentry,
p->h_parent
};
err = au_pin_hdir_lock(p);
if (unlikely(err))
goto out;
for (i = 0; !err && i < sizeof(h_d)/sizeof(*h_d); i++) {
if (!h_d[i])
continue;
if (d_is_positive(h_d[i])) {
h_i = d_inode(h_d[i]);
err = !h_i->i_nlink;
}
}
out:
return err;
}
static void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task)
{
#if !defined(CONFIG_RWSEM_GENERIC_SPINLOCK) && defined(CONFIG_RWSEM_SPIN_ON_OWNER)
p->hdir->hi_inode->i_rwsem.owner = task;
#endif
}
void au_pin_hdir_acquire_nest(struct au_pin *p)
{
if (p->hdir) {
rwsem_acquire_nest(&p->hdir->hi_inode->i_rwsem.dep_map,
p->lsc_hi, 0, NULL, _RET_IP_);
au_pin_hdir_set_owner(p, current);
}
}
void au_pin_hdir_release(struct au_pin *p)
{
if (p->hdir) {
au_pin_hdir_set_owner(p, p->task);
rwsem_release(&p->hdir->hi_inode->i_rwsem.dep_map, 1, _RET_IP_);
}
}
struct dentry *au_pinned_h_parent(struct au_pin *pin)
{
if (pin && pin->parent)
return au_h_dptr(pin->parent, pin->bindex);
return NULL;
}
void au_unpin(struct au_pin *p)
{
if (p->hdir)
au_pin_hdir_unlock(p);
if (p->h_mnt && au_ftest_pin(p->flags, MNT_WRITE))
vfsub_mnt_drop_write(p->h_mnt);
if (!p->hdir)
return;
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_unlock(p->parent, AuLock_IR);
iput(p->hdir->hi_inode);
dput(p->parent);
p->parent = NULL;
p->hdir = NULL;
p->h_mnt = NULL;
/* do not clear p->task */
}
int au_do_pin(struct au_pin *p)
{
int err;
struct super_block *sb;
struct inode *h_dir;
err = 0;
sb = p->dentry->d_sb;
p->br = au_sbr(sb, p->bindex);
if (IS_ROOT(p->dentry)) {
if (au_ftest_pin(p->flags, MNT_WRITE)) {
p->h_mnt = au_br_mnt(p->br);
err = vfsub_mnt_want_write(p->h_mnt);
if (unlikely(err)) {
au_fclr_pin(p->flags, MNT_WRITE);
goto out_err;
}
}
goto out;
}
p->h_dentry = NULL;
if (p->bindex <= au_dbbot(p->dentry))
p->h_dentry = au_h_dptr(p->dentry, p->bindex);
p->parent = dget_parent(p->dentry);
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_lock(p->parent, AuLock_IR, p->lsc_di);
h_dir = NULL;
p->h_parent = au_h_dptr(p->parent, p->bindex);
p->hdir = au_hi(d_inode(p->parent), p->bindex);
if (p->hdir)
h_dir = p->hdir->hi_inode;
/*
* udba case, or
* if DI_LOCKED is not set, then p->parent may be different
* and h_parent can be NULL.
*/
if (unlikely(!p->hdir || !h_dir || !p->h_parent)) {
err = -EBUSY;
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_unlock(p->parent, AuLock_IR);
dput(p->parent);
p->parent = NULL;
goto out_err;
}
if (au_ftest_pin(p->flags, MNT_WRITE)) {
p->h_mnt = au_br_mnt(p->br);
err = vfsub_mnt_want_write(p->h_mnt);
if (unlikely(err)) {
au_fclr_pin(p->flags, MNT_WRITE);
if (!au_ftest_pin(p->flags, DI_LOCKED))
di_read_unlock(p->parent, AuLock_IR);
dput(p->parent);
p->parent = NULL;
goto out_err;
}
}
au_igrab(h_dir);
err = au_pin_hdir_lock(p);
if (!err)
goto out; /* success */
au_unpin(p);
out_err:
pr_err("err %d\n", err);
err = -EBUSY;
out:
return err;
}
void au_pin_init(struct au_pin *p, struct dentry *dentry,
aufs_bindex_t bindex, int lsc_di, int lsc_hi,
unsigned char flags)
{
p->dentry = dentry;
p->lsc_di = lsc_di;
p->lsc_hi = lsc_hi;
p->flags = flags;
p->bindex = bindex;
p->parent = NULL;
p->hdir = NULL;
p->h_mnt = NULL;
p->h_dentry = NULL;
p->h_parent = NULL;
p->br = NULL;
p->task = current;
}
int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
unsigned char flags)
{
au_pin_init(pin, dentry, bindex, AuLsc_DI_PARENT, AuLsc_I_PARENT2,
flags);
return au_do_pin(pin);
}
...@@ -15,6 +15,8 @@ ...@@ -15,6 +15,8 @@
#include <linux/fs.h> #include <linux/fs.h>
#include "rwsem.h" #include "rwsem.h"
struct vfsmount;
struct au_hinode { struct au_hinode {
struct inode *hi_inode; struct inode *hi_inode;
aufs_bindex_t hi_id; aufs_bindex_t hi_id;
...@@ -42,6 +44,38 @@ struct au_icntnr { ...@@ -42,6 +44,38 @@ struct au_icntnr {
struct rcu_head rcu; struct rcu_head rcu;
} ____cacheline_aligned_in_smp; } ____cacheline_aligned_in_smp;
/* au_pin flags */
#define AuPin_DI_LOCKED 1
#define AuPin_MNT_WRITE (1 << 1)
#define au_ftest_pin(flags, name) ((flags) & AuPin_##name)
#define au_fset_pin(flags, name) \
do { (flags) |= AuPin_##name; } while (0)
#define au_fclr_pin(flags, name) \
do { (flags) &= ~AuPin_##name; } while (0)
struct au_pin {
/* input */
struct dentry *dentry;
unsigned char lsc_di, lsc_hi, flags;
aufs_bindex_t bindex;
/* output */
struct dentry *parent;
struct au_hinode *hdir;
struct vfsmount *h_mnt;
/* temporary unlock/relock for copyup */
struct dentry *h_dentry, *h_parent;
struct au_branch *br;
struct task_struct *task;
};
void au_pin_hdir_unlock(struct au_pin *p);
int au_pin_hdir_lock(struct au_pin *p);
int au_pin_hdir_relock(struct au_pin *p);
void au_pin_hdir_acquire_nest(struct au_pin *p);
void au_pin_hdir_release(struct au_pin *p);
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static inline struct au_iinfo *au_ii(struct inode *inode) static inline struct au_iinfo *au_ii(struct inode *inode)
...@@ -57,6 +91,16 @@ struct inode *au_igrab(struct inode *inode); ...@@ -57,6 +91,16 @@ struct inode *au_igrab(struct inode *inode);
int au_test_h_perm(struct inode *h_inode, int mask); int au_test_h_perm(struct inode *h_inode, int mask);
int au_test_h_perm_sio(struct inode *h_inode, int mask); int au_test_h_perm_sio(struct inode *h_inode, int mask);
/* i_op.c */
struct dentry *au_pinned_h_parent(struct au_pin *pin);
void au_pin_init(struct au_pin *pin, struct dentry *dentry,
aufs_bindex_t bindex, int lsc_di, int lsc_hi,
unsigned char flags);
int au_pin(struct au_pin *pin, struct dentry *dentry, aufs_bindex_t bindex,
unsigned char flags) __must_check;
int au_do_pin(struct au_pin *pin) __must_check;
void au_unpin(struct au_pin *pin);
/* iinfo.c */ /* iinfo.c */
struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex); struct inode *au_h_iptr(struct inode *inode, aufs_bindex_t bindex);
void au_hiput(struct au_hinode *hinode); void au_hiput(struct au_hinode *hinode);
...@@ -279,5 +323,55 @@ static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex) ...@@ -279,5 +323,55 @@ static inline struct au_hinode *au_hi(struct inode *inode, aufs_bindex_t bindex)
return au_hinode(au_ii(inode), bindex); return au_hinode(au_ii(inode), bindex);
} }
/* ---------------------------------------------------------------------- */
static inline struct dentry *au_pinned_parent(struct au_pin *pin)
{
if (pin)
return pin->parent;
return NULL;
}
static inline struct inode *au_pinned_h_dir(struct au_pin *pin)
{
if (pin && pin->hdir)
return pin->hdir->hi_inode;
return NULL;
}
static inline struct au_hinode *au_pinned_hdir(struct au_pin *pin)
{
if (pin)
return pin->hdir;
return NULL;
}
static inline void au_pin_set_dentry(struct au_pin *pin, struct dentry *dentry)
{
if (pin)
pin->dentry = dentry;
}
static inline void au_pin_set_parent_lflag(struct au_pin *pin,
unsigned char lflag)
{
if (pin) {
if (lflag)
au_fset_pin(pin->flags, DI_LOCKED);
else
au_fclr_pin(pin->flags, DI_LOCKED);
}
}
#if 0 /* reserved */
static inline void au_pin_set_parent(struct au_pin *pin, struct dentry *parent)
{
if (pin) {
dput(pin->parent);
pin->parent = dget(parent);
}
}
#endif
#endif /* __KERNEL__ */ #endif /* __KERNEL__ */
#endif /* __AUFS_INODE_H__ */ #endif /* __AUFS_INODE_H__ */
...@@ -66,6 +66,12 @@ out: ...@@ -66,6 +66,12 @@ out:
return path.dentry; return path.dentry;
} }
void vfsub_call_lkup_one(void *args)
{
struct vfsub_lkup_one_args *a = args;
*a->errp = vfsub_lkup_one(a->name, a->parent);
}
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl) int vfsub_create(struct inode *dir, struct path *path, int mode, bool want_excl)
......
...@@ -44,12 +44,20 @@ int vfsub_kern_path(const char *name, unsigned int flags, struct path *path); ...@@ -44,12 +44,20 @@ int vfsub_kern_path(const char *name, unsigned int flags, struct path *path);
struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent, struct dentry *vfsub_lookup_one_len(const char *name, struct dentry *parent,
int len); int len);
struct vfsub_lkup_one_args {
struct dentry **errp;
struct qstr *name;
struct dentry *parent;
};
static inline struct dentry *vfsub_lkup_one(struct qstr *name, static inline struct dentry *vfsub_lkup_one(struct qstr *name,
struct dentry *parent) struct dentry *parent)
{ {
return vfsub_lookup_one_len(name->name, parent, name->len); return vfsub_lookup_one_len(name->name, parent, name->len);
} }
void vfsub_call_lkup_one(void *args);
/* ---------------------------------------------------------------------- */ /* ---------------------------------------------------------------------- */
static inline int vfsub_mnt_want_write(struct vfsmount *mnt) static inline int vfsub_mnt_want_write(struct vfsmount *mnt)
......
...@@ -446,9 +446,7 @@ static void reinit_br_wh(void *arg) ...@@ -446,9 +446,7 @@ static void reinit_br_wh(void *arg)
inode_lock_nested(hdir->hi_inode, AuLsc_I_PARENT); inode_lock_nested(hdir->hi_inode, AuLsc_I_PARENT);
wbr_wh_write_lock(wbr); wbr_wh_write_lock(wbr);
/* re-commit later */ err = au_h_verify(wbr->wbr_whbase, hdir->hi_inode, h_root, a->br);
/* err = au_h_verify(wbr->wbr_whbase, hdir->hi_inode, h_root, a->br); */
err = 0;
if (!err) { if (!err) {
h_path.dentry = wbr->wbr_whbase; h_path.dentry = wbr->wbr_whbase;
h_path.mnt = au_br_mnt(a->br); h_path.mnt = au_br_mnt(a->br);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment