diff --git a/fs/namespace.c b/fs/namespace.c index 94d8bad6f6e9..017d222812d1 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -426,6 +426,7 @@ void __mnt_drop_write(struct vfsmount *mnt) mnt_dec_writers(real_mount(mnt)); preempt_enable(); } +EXPORT_SYMBOL_GPL(__mnt_drop_write); /** * mnt_drop_write - give up write access to a mount @@ -446,6 +447,7 @@ void __mnt_drop_write_file(struct file *file) { __mnt_drop_write(file->f_path.mnt); } +EXPORT_SYMBOL_GPL(__mnt_drop_write_file); void mnt_drop_write_file(struct file *file) { diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 57712ea9dce6..a6bb87dcbe3d 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -283,6 +283,7 @@ err: return ret; } +EXPORT_SYMBOL(fsnotify_add_mark); int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, int allow_dups) @@ -293,7 +294,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, mutex_unlock(&group->mark_mutex); return ret; } -EXPORT_SYMBOL(fsnotify_add_mark); /* * clear any marks in a group in which mark->flags & flags is true diff --git a/ubuntu/aufs/Kconfig b/ubuntu/aufs/Kconfig index 7a96cb5c26ba..8ff2bf78a869 100644 --- a/ubuntu/aufs/Kconfig +++ b/ubuntu/aufs/Kconfig @@ -100,29 +100,6 @@ config AUFS_RDU shows better performance in most cases. See detail in aufs.5. -config AUFS_PROC_MAP - bool "support for /proc/maps and lsof(1)" - depends on PROC_FS - help - When you issue mmap(2) in aufs, it is actually a direct mmap(2) - call to the file on the branch fs since the file in aufs is - purely virtual. And the file path printed in /proc/maps (and - others) will be the path on the branch fs. In most cases, it - does no harm. But some utilities like lsof(1) may confuse since - the utility or user may expect the file path in aufs to be - printed. - To address this issue, aufs provides a patch which introduces a - new member called vm_prfile into struct vm_are_struct. The patch - is meaningless without enabling this configuration since nobody - sets the new vm_prfile member. - If you don't apply the patch, then enabling this configuration - will cause a compile error. - This approach is fragile since if someone else make some changes - around vm_file, then vm_prfile may not work anymore. As a - workaround such case, aufs provides this configuration. If you - disable it, then lsof(1) may produce incorrect result but the - problem will be gone even if the aufs patch is applied (I hope). - config AUFS_SP_IATTR bool "Respect the attributes (mtime/ctime mainly) of special files" help diff --git a/ubuntu/aufs/Makefile b/ubuntu/aufs/Makefile index 7e60943ffd4b..13c83e4c806a 100644 --- a/ubuntu/aufs/Makefile +++ b/ubuntu/aufs/Makefile @@ -10,9 +10,9 @@ endif ccflags-y += -DDEBUG # sparse requires the full pathname ifdef M -ccflags-y += -include ${M}/../../ubuntu/include/linux/aufs_type.h +ccflags-y += -include ${M}/../../include/uapi/linux/aufs_type.h else -ccflags-y += -include ${srctree}/ubuntu/include/linux/aufs_type.h +ccflags-y += -include ${srctree}/include/uapi/linux/aufs_type.h endif obj-$(CONFIG_AUFS_FS) += aufs.o diff --git a/ubuntu/aufs/aufs.h b/ubuntu/aufs/aufs.h index 33963a85a68c..8adb4f2566c5 100644 --- a/ubuntu/aufs/aufs.h +++ b/ubuntu/aufs/aufs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/aufs_type.h b/ubuntu/aufs/aufs_type.h new file mode 100644 index 000000000000..3829eccd2f07 --- /dev/null +++ b/ubuntu/aufs/aufs_type.h @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2005-2013 Junjiro R. Okajima + * + * This program, aufs is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __AUFS_TYPE_H__ +#define __AUFS_TYPE_H__ + +#define AUFS_NAME "aufs" + +#ifdef __KERNEL__ +/* + * define it before including all other headers. + * sched.h may use pr_* macros before defining "current", so define the + * no-current version first, and re-define later. + */ +#define pr_fmt(fmt) AUFS_NAME " %s:%d: " fmt, __func__, __LINE__ +#include +#undef pr_fmt +#define pr_fmt(fmt) \ + AUFS_NAME " %s:%d:%.*s[%d]: " fmt, __func__, __LINE__, \ + (int)sizeof(current->comm), current->comm, current->pid +#else +#include +#include +#endif /* __KERNEL__ */ + +#include + +#define AUFS_VERSION "3.8-20131104" + +/* todo? move this to linux-2.6.19/include/magic.h */ +#define AUFS_SUPER_MAGIC ('a' << 24 | 'u' << 16 | 'f' << 8 | 's') + +/* ---------------------------------------------------------------------- */ + +#ifdef CONFIG_AUFS_BRANCH_MAX_127 +typedef int8_t aufs_bindex_t; +#define AUFS_BRANCH_MAX 127 +#else +typedef int16_t aufs_bindex_t; +#ifdef CONFIG_AUFS_BRANCH_MAX_511 +#define AUFS_BRANCH_MAX 511 +#elif defined(CONFIG_AUFS_BRANCH_MAX_1023) +#define AUFS_BRANCH_MAX 1023 +#elif defined(CONFIG_AUFS_BRANCH_MAX_32767) +#define AUFS_BRANCH_MAX 32767 +#endif +#endif + +#ifdef __KERNEL__ +#ifndef AUFS_BRANCH_MAX +#error unknown CONFIG_AUFS_BRANCH_MAX value +#endif +#endif /* __KERNEL__ */ + +/* ---------------------------------------------------------------------- */ + +#define AUFS_FSTYPE AUFS_NAME + +#define AUFS_ROOT_INO 2 +#define AUFS_FIRST_INO 11 + +#define AUFS_WH_PFX ".wh." +#define AUFS_WH_PFX_LEN ((int)sizeof(AUFS_WH_PFX) - 1) +#define AUFS_WH_TMP_LEN 4 +/* a limit for rmdir/rename a dir and copyup */ +#define AUFS_MAX_NAMELEN (NAME_MAX \ + - AUFS_WH_PFX_LEN * 2 /* doubly whiteouted */\ + - 1 /* dot */\ + - AUFS_WH_TMP_LEN) /* hex */ +#define AUFS_XINO_FNAME "." AUFS_NAME ".xino" +#define AUFS_XINO_DEFPATH "/tmp/" AUFS_XINO_FNAME +#define AUFS_XINO_DEF_SEC 30 /* seconds */ +#define AUFS_XINO_DEF_TRUNC 45 /* percentage */ +#define AUFS_DIRWH_DEF 3 +#define AUFS_RDCACHE_DEF 10 /* seconds */ +#define AUFS_RDCACHE_MAX 3600 /* seconds */ +#define AUFS_RDBLK_DEF 512 /* bytes */ +#define AUFS_RDHASH_DEF 32 +#define AUFS_WKQ_NAME AUFS_NAME "d" +#define AUFS_MFS_DEF_SEC 30 /* seconds */ +#define AUFS_MFS_MAX_SEC 3600 /* seconds */ +#define AUFS_PLINK_WARN 50 /* number of plinks in a single bucket */ + +/* pseudo-link maintenace under /proc */ +#define AUFS_PLINK_MAINT_NAME "plink_maint" +#define AUFS_PLINK_MAINT_DIR "fs/" AUFS_NAME +#define AUFS_PLINK_MAINT_PATH AUFS_PLINK_MAINT_DIR "/" AUFS_PLINK_MAINT_NAME + +#define AUFS_DIROPQ_NAME AUFS_WH_PFX ".opq" /* whiteouted doubly */ +#define AUFS_WH_DIROPQ AUFS_WH_PFX AUFS_DIROPQ_NAME + +#define AUFS_BASE_NAME AUFS_WH_PFX AUFS_NAME +#define AUFS_PLINKDIR_NAME AUFS_WH_PFX "plnk" +#define AUFS_ORPHDIR_NAME AUFS_WH_PFX "orph" + +/* doubly whiteouted */ +#define AUFS_WH_BASE AUFS_WH_PFX AUFS_BASE_NAME +#define AUFS_WH_PLINKDIR AUFS_WH_PFX AUFS_PLINKDIR_NAME +#define AUFS_WH_ORPHDIR AUFS_WH_PFX AUFS_ORPHDIR_NAME + +/* branch permissions and attributes */ +#define AUFS_BRPERM_RW "rw" +#define AUFS_BRPERM_RO "ro" +#define AUFS_BRPERM_RR "rr" +#define AUFS_BRRATTR_WH "wh" +#define AUFS_BRWATTR_NLWH "nolwh" +#define AUFS_BRATTR_UNPIN "unpin" + +/* ---------------------------------------------------------------------- */ + +/* ioctl */ +enum { + /* readdir in userspace */ + AuCtl_RDU, + AuCtl_RDU_INO, + + /* pathconf wrapper */ + AuCtl_WBR_FD, + + /* busy inode */ + AuCtl_IBUSY +}; + +/* borrowed from linux/include/linux/kernel.h */ +#ifndef ALIGN +#define ALIGN(x, a) __ALIGN_MASK(x, (typeof(x))(a)-1) +#define __ALIGN_MASK(x, mask) (((x)+(mask))&~(mask)) +#endif + +/* borrowed from linux/include/linux/compiler-gcc3.h */ +#ifndef __aligned +#define __aligned(x) __attribute__((aligned(x))) +#endif + +#ifdef __KERNEL__ +#ifndef __packed +#define __packed __attribute__((packed)) +#endif +#endif + +struct au_rdu_cookie { + uint64_t h_pos; + int16_t bindex; + uint8_t flags; + uint8_t pad; + uint32_t generation; +} __aligned(8); + +struct au_rdu_ent { + uint64_t ino; + int16_t bindex; + uint8_t type; + uint8_t nlen; + uint8_t wh; + char name[0]; +} __aligned(8); + +static inline int au_rdu_len(int nlen) +{ + /* include the terminating NULL */ + return ALIGN(sizeof(struct au_rdu_ent) + nlen + 1, + sizeof(uint64_t)); +} + +union au_rdu_ent_ul { + struct au_rdu_ent __user *e; + uint64_t ul; +}; + +enum { + AufsCtlRduV_SZ, + AufsCtlRduV_End +}; + +struct aufs_rdu { + /* input */ + union { + uint64_t sz; /* AuCtl_RDU */ + uint64_t nent; /* AuCtl_RDU_INO */ + }; + union au_rdu_ent_ul ent; + uint16_t verify[AufsCtlRduV_End]; + + /* input/output */ + uint32_t blk; + + /* output */ + union au_rdu_ent_ul tail; + /* number of entries which were added in a single call */ + uint64_t rent; + uint8_t full; + uint8_t shwh; + + struct au_rdu_cookie cookie; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +struct aufs_wbr_fd { + uint32_t oflags; + int16_t brid; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +struct aufs_ibusy { + uint64_t ino, h_ino; + int16_t bindex; +} __aligned(8); + +/* ---------------------------------------------------------------------- */ + +#define AuCtlType 'A' +#define AUFS_CTL_RDU _IOWR(AuCtlType, AuCtl_RDU, struct aufs_rdu) +#define AUFS_CTL_RDU_INO _IOWR(AuCtlType, AuCtl_RDU_INO, struct aufs_rdu) +#define AUFS_CTL_WBR_FD _IOW(AuCtlType, AuCtl_WBR_FD, \ + struct aufs_wbr_fd) +#define AUFS_CTL_IBUSY _IOWR(AuCtlType, AuCtl_IBUSY, struct aufs_ibusy) + +#endif /* __AUFS_TYPE_H__ */ diff --git a/ubuntu/aufs/branch.c b/ubuntu/aufs/branch.c index 5ffd4c41a718..b0d492658577 100644 --- a/ubuntu/aufs/branch.c +++ b/ubuntu/aufs/branch.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,34 @@ /* * free a single branch */ + +/* prohibit rmdir to the root of the branch */ +/* todo: another new flag? */ +static void au_br_dflags_force(struct au_branch *br) +{ + struct dentry *h_dentry; + + h_dentry = au_br_dentry(br); + spin_lock(&h_dentry->d_lock); + br->br_dflags = h_dentry->d_flags & DCACHE_MOUNTED; + h_dentry->d_flags |= DCACHE_MOUNTED; + spin_unlock(&h_dentry->d_lock); +} + +/* restore its d_flags */ +static void au_br_dflags_restore(struct au_branch *br) +{ + struct dentry *h_dentry; + + if (br->br_dflags) + return; + + h_dentry = au_br_dentry(br); + spin_lock(&h_dentry->d_lock); + h_dentry->d_flags &= ~DCACHE_MOUNTED; + spin_unlock(&h_dentry->d_lock); +} + static void au_br_do_free(struct au_branch *br) { int i; @@ -56,7 +84,12 @@ static void au_br_do_free(struct au_branch *br) else break; - mntput(br->br_mnt); + au_br_dflags_restore(br); + + /* recursive lock, s_umount of branch's */ + lockdep_off(); + path_put(&br->br_path); + lockdep_on(); kfree(wbr); kfree(br); } @@ -269,7 +302,7 @@ out: * initialize or clean the whiteouts for an adding branch */ static int au_br_init_wh(struct super_block *sb, struct au_branch *br, - int new_perm, struct dentry *h_root) + int new_perm) { int err, old_perm; aufs_bindex_t bindex; @@ -277,6 +310,10 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br, struct au_wbr *wbr; struct au_hinode *hdir; + err = vfsub_mnt_want_write(au_br_mnt(br)); + if (unlikely(err)) + goto out; + wbr = br->br_wbr; old_perm = br->br_perm; br->br_perm = new_perm; @@ -287,20 +324,21 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br, hdir = au_hi(sb->s_root->d_inode, bindex); au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); } else { - h_mtx = &h_root->d_inode->i_mutex; + h_mtx = &au_br_dentry(br)->d_inode->i_mutex; mutex_lock_nested(h_mtx, AuLsc_I_PARENT); } if (!wbr) - err = au_wh_init(h_root, br, sb); + err = au_wh_init(br, sb); else { wbr_wh_write_lock(wbr); - err = au_wh_init(h_root, br, sb); + err = au_wh_init(br, sb); wbr_wh_write_unlock(wbr); } if (hdir) au_hn_imtx_unlock(hdir); else mutex_unlock(h_mtx); + vfsub_mnt_drop_write(au_br_mnt(br)); br->br_perm = old_perm; if (!err && wbr && !au_br_writable(new_perm)) { @@ -308,16 +346,16 @@ static int au_br_init_wh(struct super_block *sb, struct au_branch *br, br->br_wbr = NULL; } +out: return err; } static int au_wbr_init(struct au_branch *br, struct super_block *sb, - int perm, struct path *path) + int perm) { int err; struct kstatfs kst; struct au_wbr *wbr; - struct dentry *h_dentry; wbr = br->br_wbr; au_rw_init(&wbr->wbr_wh_rwsem); @@ -327,19 +365,18 @@ static int au_wbr_init(struct au_branch *br, struct super_block *sb, /* * a limit for rmdir/rename a dir - * cf. AUFS_MAX_NAMELEN in include/linux/aufs_type.h + * cf. AUFS_MAX_NAMELEN in include/uapi/linux/aufs_type.h */ - err = vfs_statfs(path, &kst); + err = vfs_statfs(&br->br_path, &kst); if (unlikely(err)) goto out; err = -EINVAL; - h_dentry = path->dentry; if (kst.f_namelen >= NAME_MAX) - err = au_br_init_wh(sb, br, perm, h_dentry); + err = au_br_init_wh(sb, br, perm); else pr_err("%.*s(%s), unsupported namelen %ld\n", - AuDLNPair(h_dentry), au_sbtype(h_dentry->d_sb), - kst.f_namelen); + AuDLNPair(au_br_dentry(br)), + au_sbtype(au_br_dentry(br)->d_sb), kst.f_namelen); out: return err; @@ -355,17 +392,19 @@ static int au_br_init(struct au_branch *br, struct super_block *sb, memset(&br->br_xino, 0, sizeof(br->br_xino)); mutex_init(&br->br_xino.xi_nondir_mtx); br->br_perm = add->perm; - br->br_mnt = add->path.mnt; /* set first, mntget() later */ + BUILD_BUG_ON(sizeof(br->br_dflags) + != sizeof(br->br_path.dentry->d_flags)); + br->br_dflags = DCACHE_MOUNTED; + br->br_path = add->path; /* set first, path_get() later */ spin_lock_init(&br->br_dykey_lock); memset(br->br_dykey, 0, sizeof(br->br_dykey)); atomic_set(&br->br_count, 0); - br->br_xino_upper = AUFS_XINO_TRUNC_INIT; atomic_set(&br->br_xino_running, 0); br->br_id = au_new_br_id(sb); AuDebugOn(br->br_id < 0); if (au_br_writable(add->perm)) { - err = au_wbr_init(br, sb, add->perm, &add->path); + err = au_wbr_init(br, sb, add->perm); if (unlikely(err)) goto out_err; } @@ -380,11 +419,11 @@ static int au_br_init(struct au_branch *br, struct super_block *sb, } sysaufs_br_init(br); - mntget(add->path.mnt); + path_get(&br->br_path); goto out; /* success */ out_err: - br->br_mnt = NULL; + memset(&br->br_path, 0, sizeof(br->br_path)); out: return err; } @@ -436,17 +475,20 @@ static void au_br_do_add_hip(struct au_iinfo *iinfo, aufs_bindex_t bindex, iinfo->ii_bstart = 0; } -static void au_br_do_add(struct super_block *sb, struct dentry *h_dentry, - struct au_branch *br, aufs_bindex_t bindex) +static void au_br_do_add(struct super_block *sb, struct au_branch *br, + aufs_bindex_t bindex) { - struct dentry *root; + struct dentry *root, *h_dentry; struct inode *root_inode; aufs_bindex_t bend, amount; + au_br_dflags_force(br); + root = sb->s_root; root_inode = root->d_inode; bend = au_sbend(sb); amount = bend + 1 - bindex; + h_dentry = au_br_dentry(br); au_sbilist_lock(); au_br_do_add_brp(au_sbi(sb), bindex, br, bend, amount); au_br_do_add_hdp(au_di(root), bindex, bend, amount); @@ -489,15 +531,15 @@ int au_br_add(struct super_block *sb, struct au_opt_add *add, int remount) } add_bindex = add->bindex; - h_dentry = add->path.dentry; if (!remount) - au_br_do_add(sb, h_dentry, add_branch, add_bindex); + au_br_do_add(sb, add_branch, add_bindex); else { sysaufs_brs_del(sb, add_bindex); - au_br_do_add(sb, h_dentry, add_branch, add_bindex); + au_br_do_add(sb, add_branch, add_bindex); sysaufs_brs_add(sb, add_bindex); } + h_dentry = add->path.dentry; if (!add_bindex) { au_cpup_attr_all(root_inode, /*force*/1); sb->s_maxbytes = h_dentry->d_sb->s_maxbytes; @@ -632,7 +674,7 @@ static int test_inode_busy(struct super_block *sb, aufs_bindex_t bindex, continue; /* AuDbgInode(i); */ - if (au_iigen(i) == sigen) + if (au_iigen(i, NULL) == sigen) ii_read_lock_child(i); else { ii_write_lock_child(i); @@ -803,6 +845,7 @@ int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) goto out; } br = au_sbr(sb, bindex); + AuDebugOn(!path_equal(&br->br_path, &del->h_path)); i = atomic_read(&br->br_count); if (unlikely(i)) { AuVerbose(verbose, "%d file(s) opened\n", i); @@ -851,7 +894,7 @@ int au_br_del(struct super_block *sb, struct au_opt_del *del, int remount) out_wh: /* revert */ - rerr = au_br_init_wh(sb, br, br->br_perm, del->h_path.dentry); + rerr = au_br_init_wh(sb, br, br->br_perm); if (rerr) pr_warn("failed re-creating base whiteout, %s. (%d)\n", del->pathname, rerr); @@ -1071,8 +1114,8 @@ static int au_br_mod_files_ro(struct super_block *sb, aufs_bindex_t bindex) hf->f_mode &= ~FMODE_WRITE; spin_unlock(&hf->f_lock); if (!file_check_writeable(hf)) { + __mnt_drop_write(hf->f_path.mnt); file_release_write(hf); - vfsub_mnt_drop_write(hf->f_vfsmnt); } } @@ -1088,7 +1131,6 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, { int err, rerr; aufs_bindex_t bindex; - struct path path; struct dentry *root; struct au_branch *br; @@ -1108,12 +1150,13 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, goto out; br = au_sbr(sb, bindex); + AuDebugOn(mod->h_root != au_br_dentry(br)); if (br->br_perm == mod->perm) return 0; /* success */ if (au_br_writable(br->br_perm)) { /* remove whiteout base */ - err = au_br_init_wh(sb, br, mod->perm, mod->h_root); + err = au_br_init_wh(sb, br, mod->perm); if (unlikely(err)) goto out; @@ -1130,12 +1173,8 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, rerr = -ENOMEM; br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); - if (br->br_wbr) { - path.mnt = br->br_mnt; - path.dentry = mod->h_root; - rerr = au_wbr_init(br, sb, br->br_perm, - &path); - } + if (br->br_wbr) + rerr = au_wbr_init(br, sb, br->br_perm); if (unlikely(rerr)) { AuIOErr("nested error %d (%d)\n", rerr, err); @@ -1148,9 +1187,7 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, err = -ENOMEM; br->br_wbr = kmalloc(sizeof(*br->br_wbr), GFP_NOFS); if (br->br_wbr) { - path.mnt = br->br_mnt; - path.dentry = mod->h_root; - err = au_wbr_init(br, sb, mod->perm, &path); + err = au_wbr_init(br, sb, mod->perm); if (unlikely(err)) { kfree(br->br_wbr); br->br_wbr = NULL; @@ -1159,6 +1196,12 @@ int au_br_mod(struct super_block *sb, struct au_opt_mod *mod, int remount, } if (!err) { + if ((br->br_perm & AuBrAttr_UNPIN) + && !(mod->perm & AuBrAttr_UNPIN)) + au_br_dflags_force(br); + else if (!(br->br_perm & AuBrAttr_UNPIN) + && (mod->perm & AuBrAttr_UNPIN)) + au_br_dflags_restore(br); *do_refresh |= need_sigen_inc(br->br_perm, mod->perm); br->br_perm = mod->perm; } diff --git a/ubuntu/aufs/branch.h b/ubuntu/aufs/branch.h index 3870bc3f4bd6..2f8888d0da90 100644 --- a/ubuntu/aufs/branch.h +++ b/ubuntu/aufs/branch.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -61,6 +61,25 @@ struct au_wbr { /* ext2 has 3 types of operations at least, ext3 has 4 */ #define AuBrDynOp (AuDyLast * 4) +#ifdef CONFIG_AUFS_HFSNOTIFY +/* support for asynchronous destruction */ +struct au_br_hfsnotify { + struct fsnotify_group *hfsn_group; +}; +#endif + +/* sysfs entries */ +struct au_brsysfs { + char name[16]; + struct attribute attr; +}; + +enum { + AuBrSysfs_BR, + AuBrSysfs_BRID, + AuBrSysfs_Last +}; + /* protected by superblock rwsem */ struct au_branch { struct au_xino_file br_xino; @@ -68,7 +87,8 @@ struct au_branch { aufs_bindex_t br_id; int br_perm; - struct vfsmount *br_mnt; + unsigned int br_dflags; + struct path br_path; spinlock_t br_dykey_lock; struct au_dykey *br_dykey[AuBrDynOp]; atomic_t br_count; @@ -76,23 +96,35 @@ struct au_branch { struct au_wbr *br_wbr; /* xino truncation */ - blkcnt_t br_xino_upper; /* watermark in blocks */ atomic_t br_xino_running; #ifdef CONFIG_AUFS_HFSNOTIFY - struct fsnotify_group *br_hfsn_group; - struct fsnotify_ops br_hfsn_ops; + struct au_br_hfsnotify *br_hfsn; #endif #ifdef CONFIG_SYSFS - /* an entry under sysfs per mount-point */ - char br_name[8]; - struct attribute br_attr; + /* entries under sysfs per mount-point */ + struct au_brsysfs br_sysfs[AuBrSysfs_Last]; #endif }; /* ---------------------------------------------------------------------- */ +static inline struct vfsmount *au_br_mnt(struct au_branch *br) +{ + return br->br_path.mnt; +} + +static inline struct dentry *au_br_dentry(struct au_branch *br) +{ + return br->br_path.dentry; +} + +static inline struct super_block *au_br_sb(struct au_branch *br) +{ + return au_br_mnt(br)->mnt_sb; +} + /* branch permissions and attributes */ #define AuBrPerm_RW 1 /* writable, hardlinkable wh */ #define AuBrPerm_RO (1 << 1) /* readonly */ @@ -103,6 +135,9 @@ struct au_branch { #define AuBrWAttr_NoLinkWH (1 << 4) /* un-hardlinkable whiteouts */ +#define AuBrAttr_UNPIN (1 << 5) /* rename-able top dir of + branch */ + static inline int au_br_writable(int brperm) { return brperm & AuBrPerm_RW; @@ -120,7 +155,7 @@ static inline int au_br_wh_linkable(int brperm) static inline int au_br_rdonly(struct au_branch *br) { - return ((br->br_mnt->mnt_sb->s_flags & MS_RDONLY) + return ((au_br_sb(br)->s_flags & MS_RDONLY) || !au_br_writable(br->br_perm)) ? -EROFS : 0; } @@ -190,13 +225,13 @@ aufs_bindex_t au_sbr_id(struct super_block *sb, aufs_bindex_t bindex) static inline struct vfsmount *au_sbr_mnt(struct super_block *sb, aufs_bindex_t bindex) { - return au_sbr(sb, bindex)->br_mnt; + return au_br_mnt(au_sbr(sb, bindex)); } static inline struct super_block *au_sbr_sb(struct super_block *sb, aufs_bindex_t bindex) { - return au_sbr_mnt(sb, bindex)->mnt_sb; + return au_br_sb(au_sbr(sb, bindex)); } static inline void au_sbr_put(struct super_block *sb, aufs_bindex_t bindex) diff --git a/ubuntu/aufs/conf.mk b/ubuntu/aufs/conf.mk index 6c5108de1aaf..9217b48c1a3e 100644 --- a/ubuntu/aufs/conf.mk +++ b/ubuntu/aufs/conf.mk @@ -12,7 +12,6 @@ AuConfAll = BRANCH_MAX_127 BRANCH_MAX_511 BRANCH_MAX_1023 BRANCH_MAX_32767 \ HNOTIFY HFSNOTIFY \ EXPORT INO_T_64 \ RDU \ - PROC_MAP \ SP_IATTR \ SHWH \ BR_RAMFS \ diff --git a/ubuntu/aufs/conf.str b/ubuntu/aufs/conf.str new file mode 100644 index 000000000000..d44fb00fa388 --- /dev/null +++ b/ubuntu/aufs/conf.str @@ -0,0 +1,14 @@ +"CONFIG_AUFS_FS=m\n" +"CONFIG_AUFS_BRANCH_MAX_127=y\n" +"CONFIG_AUFS_SBILIST=y\n" +"CONFIG_AUFS_HNOTIFY=y\n" +"CONFIG_AUFS_HFSNOTIFY=y\n" +"CONFIG_AUFS_EXPORT=y\n" +"CONFIG_AUFS_RDU=y\n" +"CONFIG_AUFS_SP_IATTR=y\n" +"CONFIG_AUFS_SHWH=y\n" +"CONFIG_AUFS_BR_RAMFS=y\n" +"CONFIG_AUFS_BR_FUSE=y\n" +"CONFIG_AUFS_POLL=y\n" +"CONFIG_AUFS_BR_HFSPLUS=y\n" +"CONFIG_AUFS_BDEV_LOOP=y\n" diff --git a/ubuntu/aufs/conf.str.tmp b/ubuntu/aufs/conf.str.tmp new file mode 100644 index 000000000000..d44fb00fa388 --- /dev/null +++ b/ubuntu/aufs/conf.str.tmp @@ -0,0 +1,14 @@ +"CONFIG_AUFS_FS=m\n" +"CONFIG_AUFS_BRANCH_MAX_127=y\n" +"CONFIG_AUFS_SBILIST=y\n" +"CONFIG_AUFS_HNOTIFY=y\n" +"CONFIG_AUFS_HFSNOTIFY=y\n" +"CONFIG_AUFS_EXPORT=y\n" +"CONFIG_AUFS_RDU=y\n" +"CONFIG_AUFS_SP_IATTR=y\n" +"CONFIG_AUFS_SHWH=y\n" +"CONFIG_AUFS_BR_RAMFS=y\n" +"CONFIG_AUFS_BR_FUSE=y\n" +"CONFIG_AUFS_POLL=y\n" +"CONFIG_AUFS_BR_HFSPLUS=y\n" +"CONFIG_AUFS_BDEV_LOOP=y\n" diff --git a/ubuntu/aufs/cpup.c b/ubuntu/aufs/cpup.c index b1115ce2550f..dd516f4f275d 100644 --- a/ubuntu/aufs/cpup.c +++ b/ubuntu/aufs/cpup.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,12 +24,14 @@ #include #include "aufs.h" -void au_cpup_attr_flags(struct inode *dst, struct inode *src) +void au_cpup_attr_flags(struct inode *dst, unsigned int iflags) { const unsigned int mask = S_DEAD | S_SWAPFILE | S_PRIVATE - | S_NOATIME | S_NOCMTIME; + | S_NOATIME | S_NOCMTIME | S_AUTOMOUNT; - dst->i_flags |= src->i_flags & ~mask; + BUILD_BUG_ON(sizeof(iflags) != sizeof(dst->i_flags)); + + dst->i_flags |= iflags & ~mask; if (au_test_fs_notime(dst->i_sb)) dst->i_flags |= S_NOATIME | S_NOCMTIME; } @@ -88,7 +90,7 @@ void au_cpup_attr_changeable(struct inode *inode) inode->i_uid = h_inode->i_uid; inode->i_gid = h_inode->i_gid; au_cpup_attr_timesizes(inode); - au_cpup_attr_flags(inode, h_inode); + au_cpup_attr_flags(inode, h_inode->i_flags); } void au_cpup_igen(struct inode *inode, struct inode *h_inode) @@ -149,13 +151,22 @@ void au_dtime_revert(struct au_dtime *dt) /* ---------------------------------------------------------------------- */ +/* internal use only */ +struct au_cpup_reg_attr { + int valid; + struct kstat st; + unsigned int iflags; /* inode->i_flags */ +}; + static noinline_for_stack -int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src) +int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src, + struct au_cpup_reg_attr *h_src_attr) { int err, sbits; struct iattr ia; struct path h_path; struct inode *h_isrc, *h_idst; + struct kstat *h_st; h_path.dentry = au_h_dptr(dst, bindex); h_idst = h_path.dentry->d_inode; @@ -164,17 +175,32 @@ int cpup_iattr(struct dentry *dst, aufs_bindex_t bindex, struct dentry *h_src) ia.ia_valid = ATTR_FORCE | ATTR_UID | ATTR_GID | ATTR_ATIME | ATTR_MTIME | ATTR_ATIME_SET | ATTR_MTIME_SET; - ia.ia_uid = h_isrc->i_uid; - ia.ia_gid = h_isrc->i_gid; - ia.ia_atime = h_isrc->i_atime; - ia.ia_mtime = h_isrc->i_mtime; - if (h_idst->i_mode != h_isrc->i_mode - && !S_ISLNK(h_idst->i_mode)) { - ia.ia_valid |= ATTR_MODE; - ia.ia_mode = h_isrc->i_mode; + if (h_src_attr && h_src_attr->valid) { + h_st = &h_src_attr->st; + ia.ia_uid = h_st->uid; + ia.ia_gid = h_st->gid; + ia.ia_atime = h_st->atime; + ia.ia_mtime = h_st->mtime; + if (h_idst->i_mode != h_st->mode + && !S_ISLNK(h_idst->i_mode)) { + ia.ia_valid |= ATTR_MODE; + ia.ia_mode = h_st->mode; + } + sbits = !!(h_st->mode & (S_ISUID | S_ISGID)); + au_cpup_attr_flags(h_idst, h_src_attr->iflags); + } else { + ia.ia_uid = h_isrc->i_uid; + ia.ia_gid = h_isrc->i_gid; + ia.ia_atime = h_isrc->i_atime; + ia.ia_mtime = h_isrc->i_mtime; + if (h_idst->i_mode != h_isrc->i_mode + && !S_ISLNK(h_idst->i_mode)) { + ia.ia_valid |= ATTR_MODE; + ia.ia_mode = h_isrc->i_mode; + } + sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); + au_cpup_attr_flags(h_idst, h_isrc->i_flags); } - sbits = !!(h_isrc->i_mode & (S_ISUID | S_ISGID)); - au_cpup_attr_flags(h_idst, h_isrc); err = vfsub_notify_change(&h_path, &ia); /* is this nfs only? */ @@ -240,6 +266,8 @@ static int au_do_copy_file(struct file *dst, struct file *src, loff_t len, wbytes -= b; p += b; } + if (unlikely(err < 0)) + break; } else { loff_t res; @@ -323,8 +351,7 @@ out: * to support a sparse file which is opened with O_APPEND, * we need to close the file. */ -static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst, - aufs_bindex_t bsrc, loff_t len) +static int au_cp_regular(struct au_cp_generic *cpg) { int err, i; enum { SRC, DST }; @@ -336,14 +363,14 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst, void *label, *label_file; } *f, file[] = { { - .bindex = bsrc, + .bindex = cpg->bsrc, .flags = O_RDONLY | O_NOATIME | O_LARGEFILE, .file = NULL, .label = &&out, .label_file = &&out_src }, { - .bindex = bdst, + .bindex = cpg->bdst, .flags = O_WRONLY | O_NOATIME | O_LARGEFILE, .file = NULL, .label = &&out_src, @@ -353,11 +380,12 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst, struct super_block *sb; /* bsrc branch can be ro/rw. */ - sb = dentry->d_sb; + sb = cpg->dentry->d_sb; f = file; for (i = 0; i < 2; i++, f++) { - f->dentry = au_h_dptr(dentry, f->bindex); - f->file = au_h_open(dentry, f->bindex, f->flags, /*file*/NULL); + f->dentry = au_h_dptr(cpg->dentry, f->bindex); + f->file = au_h_open(cpg->dentry, f->bindex, f->flags, + /*file*/NULL); err = PTR_ERR(f->file); if (IS_ERR(f->file)) goto *f->label; @@ -368,7 +396,7 @@ static int au_cp_regular(struct dentry *dentry, aufs_bindex_t bdst, /* try stopping to update while we copyup */ IMustLock(file[SRC].dentry->d_inode); - err = au_copy_file(file[DST].file, file[SRC].file, len); + err = au_copy_file(file[DST].file, file[SRC].file, cpg->len); out_dst: fput(file[DST].file); @@ -380,27 +408,39 @@ out: return err; } -static int au_do_cpup_regular(struct dentry *dentry, aufs_bindex_t bdst, - aufs_bindex_t bsrc, loff_t len, - struct inode *h_dir, struct path *h_path) +static int au_do_cpup_regular(struct au_cp_generic *cpg, + struct au_cpup_reg_attr *h_src_attr) { int err, rerr; loff_t l; + struct dentry *h_src_dentry; + struct inode *h_src_inode; + struct vfsmount *h_src_mnt; err = 0; - l = i_size_read(au_h_iptr(dentry->d_inode, bsrc)); - if (len == -1 || l < len) - len = l; - if (len) - err = au_cp_regular(dentry, bdst, bsrc, len); - if (!err) - goto out; /* success */ + h_src_inode = au_h_iptr(cpg->dentry->d_inode, cpg->bsrc); + l = i_size_read(h_src_inode); + if (cpg->len == -1 || l < cpg->len) + cpg->len = l; + if (cpg->len) { + /* try stopping to update while we are referencing */ + mutex_lock_nested(&h_src_inode->i_mutex, AuLsc_I_CHILD); + au_pin_hdir_unlock(cpg->pin); - rerr = vfsub_unlink(h_dir, h_path, /*force*/0); - if (rerr) { - AuIOErr("failed unlinking cpup-ed %.*s(%d, %d)\n", - AuDLNPair(h_path->dentry), err, rerr); - err = -EIO; + h_src_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); + h_src_mnt = au_sbr_mnt(cpg->dentry->d_sb, cpg->bsrc); + h_src_attr->iflags = h_src_inode->i_flags; + err = vfs_getattr(h_src_mnt, h_src_dentry, &h_src_attr->st); + if (unlikely(err)) { + mutex_unlock(&h_src_inode->i_mutex); + goto out; + } + h_src_attr->valid = 1; + err = au_cp_regular(cpg); + mutex_unlock(&h_src_inode->i_mutex); + rerr = au_pin_hdir_relock(cpg->pin); + if (!err && rerr) + err = rerr; } out: @@ -443,17 +483,15 @@ out: return err; } -/* return with the lower dst inode is locked */ static noinline_for_stack -int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, - aufs_bindex_t bsrc, loff_t len, unsigned int flags, - struct dentry *dst_parent) +int cpup_entry(struct au_cp_generic *cpg, struct dentry *dst_parent, + struct au_cpup_reg_attr *h_src_attr) { int err; umode_t mode; unsigned int mnt_flags; unsigned char isdir; - const unsigned char do_dt = !!au_ftest_cpup(flags, DTIME); + const unsigned char do_dt = !!au_ftest_cpup(cpg->flags, DTIME); struct au_dtime dt; struct path h_path; struct dentry *h_src, *h_dst, *h_parent; @@ -461,19 +499,22 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, struct super_block *sb; /* bsrc branch can be ro/rw. */ - h_src = au_h_dptr(dentry, bsrc); + h_src = au_h_dptr(cpg->dentry, cpg->bsrc); h_inode = h_src->d_inode; - AuDebugOn(h_inode != au_h_iptr(dentry->d_inode, bsrc)); + AuDebugOn(h_inode != au_h_iptr(cpg->dentry->d_inode, cpg->bsrc)); /* try stopping to be referenced while we are creating */ - h_dst = au_h_dptr(dentry, bdst); + h_dst = au_h_dptr(cpg->dentry, cpg->bdst); + if (au_ftest_cpup(cpg->flags, RENAME)) + AuDebugOn(strncmp(h_dst->d_name.name, AUFS_WH_PFX, + AUFS_WH_PFX_LEN)); h_parent = h_dst->d_parent; /* dir inode is locked */ h_dir = h_parent->d_inode; IMustLock(h_dir); AuDebugOn(h_parent != h_dst->d_parent); - sb = dentry->d_sb; - h_path.mnt = au_sbr_mnt(sb, bdst); + sb = cpg->dentry->d_sb; + h_path.mnt = au_sbr_mnt(sb, cpg->bdst); if (do_dt) { h_path.dentry = h_parent; au_dtime_store(&dt, dst_parent, &h_path); @@ -484,14 +525,10 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, mode = h_inode->i_mode; switch (mode & S_IFMT) { case S_IFREG: - /* try stopping to update while we are referencing */ - IMustLock(h_inode); err = vfsub_create(h_dir, &h_path, mode | S_IWUSR, /*want_excl*/true); if (!err) - err = au_do_cpup_regular - (dentry, bdst, bsrc, len, - au_h_iptr(dst_parent->d_inode, bdst), &h_path); + err = au_do_cpup_regular(cpg, h_src_attr); break; case S_IFDIR: isdir = 1; @@ -501,10 +538,10 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, * strange behaviour from the users view, * particularry setattr case */ - if (au_ibstart(dst_parent->d_inode) == bdst) + if (au_ibstart(dst_parent->d_inode) == cpg->bdst) au_cpup_attr_nlink(dst_parent->d_inode, /*force*/1); - au_cpup_attr_nlink(dentry->d_inode, /*force*/1); + au_cpup_attr_nlink(cpg->dentry->d_inode, /*force*/1); } break; case S_IFLNK: @@ -529,10 +566,10 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, && au_opt_test(mnt_flags, XINO) && h_inode->i_nlink == 1 /* todo: unnecessary? */ - /* && dentry->d_inode->i_nlink == 1 */ - && bdst < bsrc - && !au_ftest_cpup(flags, KEEPLINO)) - au_xino_write(sb, bsrc, h_inode->i_ino, /*ino*/0); + /* && cpg->dentry->d_inode->i_nlink == 1 */ + && cpg->bdst < cpg->bsrc + && !au_ftest_cpup(cpg->flags, KEEPLINO)) + au_xino_write(sb, cpg->bsrc, h_inode->i_ino, /*ino*/0); /* ignore this error */ if (do_dt) @@ -540,160 +577,232 @@ int cpup_entry(struct dentry *dentry, aufs_bindex_t bdst, return err; } +static int au_do_ren_after_cpup(struct dentry *dentry, aufs_bindex_t bdst, + struct path *h_path) +{ + int err; + struct dentry *h_dentry, *h_parent; + struct inode *h_dir; + + h_dentry = dget(au_h_dptr(dentry, bdst)); + au_set_h_dptr(dentry, bdst, NULL); + err = au_lkup_neg(dentry, bdst, /*wh*/0); + if (unlikely(err)) { + au_set_h_dptr(dentry, bdst, h_dentry); + goto out; + } + + h_path->dentry = dget(au_h_dptr(dentry, bdst)); + au_set_h_dptr(dentry, bdst, h_dentry); + h_parent = h_dentry->d_parent; /* dir inode is locked */ + h_dir = h_parent->d_inode; + IMustLock(h_dir); + AuDbg("%.*s %.*s\n", AuDLNPair(h_dentry), AuDLNPair(h_path->dentry)); + err = vfsub_rename(h_dir, h_dentry, h_dir, h_path); + dput(h_path->dentry); + +out: + return err; +} + /* * copyup the @dentry from @bsrc to @bdst. * the caller must set the both of lower dentries. * @len is for truncating when it is -1 copyup the entire file. * in link/rename cases, @dst_parent may be different from the real one. */ -static int au_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, - aufs_bindex_t bsrc, loff_t len, unsigned int flags, - struct dentry *dst_parent) +static int au_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) { int err, rerr; aufs_bindex_t old_ibstart; unsigned char isdir, plink; - struct au_dtime dt; - struct path h_path; struct dentry *h_src, *h_dst, *h_parent; struct inode *dst_inode, *h_dir, *inode; struct super_block *sb; + struct au_branch *br; + /* to reuduce stack size */ + struct { + struct au_dtime dt; + struct path h_path; + struct au_cpup_reg_attr h_src_attr; + } *a; - AuDebugOn(bsrc <= bdst); + AuDebugOn(cpg->bsrc <= cpg->bdst); - sb = dentry->d_sb; - h_path.mnt = au_sbr_mnt(sb, bdst); - h_dst = au_h_dptr(dentry, bdst); + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + a->h_src_attr.valid = 0; + + sb = cpg->dentry->d_sb; + br = au_sbr(sb, cpg->bdst); + a->h_path.mnt = au_br_mnt(br); + h_dst = au_h_dptr(cpg->dentry, cpg->bdst); h_parent = h_dst->d_parent; /* dir inode is locked */ h_dir = h_parent->d_inode; IMustLock(h_dir); - h_src = au_h_dptr(dentry, bsrc); - inode = dentry->d_inode; + h_src = au_h_dptr(cpg->dentry, cpg->bsrc); + inode = cpg->dentry->d_inode; if (!dst_parent) - dst_parent = dget_parent(dentry); + dst_parent = dget_parent(cpg->dentry); else dget(dst_parent); plink = !!au_opt_test(au_mntflags(sb), PLINK); - dst_inode = au_h_iptr(inode, bdst); + dst_inode = au_h_iptr(inode, cpg->bdst); if (dst_inode) { if (unlikely(!plink)) { err = -EIO; AuIOErr("hi%lu(i%lu) exists on b%d " "but plink is disabled\n", - dst_inode->i_ino, inode->i_ino, bdst); - goto out; + dst_inode->i_ino, inode->i_ino, cpg->bdst); + goto out_parent; } if (dst_inode->i_nlink) { - const int do_dt = au_ftest_cpup(flags, DTIME); + const int do_dt = au_ftest_cpup(cpg->flags, DTIME); - h_src = au_plink_lkup(inode, bdst); + h_src = au_plink_lkup(inode, cpg->bdst); err = PTR_ERR(h_src); if (IS_ERR(h_src)) - goto out; + goto out_parent; if (unlikely(!h_src->d_inode)) { err = -EIO; AuIOErr("i%lu exists on a upper branch " "but not pseudo-linked\n", inode->i_ino); dput(h_src); - goto out; + goto out_parent; } if (do_dt) { - h_path.dentry = h_parent; - au_dtime_store(&dt, dst_parent, &h_path); + a->h_path.dentry = h_parent; + au_dtime_store(&a->dt, dst_parent, &a->h_path); } - h_path.dentry = h_dst; - err = vfsub_link(h_src, h_dir, &h_path); + + a->h_path.dentry = h_dst; + err = vfsub_link(h_src, h_dir, &a->h_path); + if (!err && au_ftest_cpup(cpg->flags, RENAME)) + err = au_do_ren_after_cpup + (cpg->dentry, cpg->bdst, &a->h_path); if (do_dt) - au_dtime_revert(&dt); + au_dtime_revert(&a->dt); dput(h_src); - goto out; + goto out_parent; } else /* todo: cpup_wh_file? */ /* udba work */ au_update_ibrange(inode, /*do_put_zero*/1); } + isdir = S_ISDIR(inode->i_mode); old_ibstart = au_ibstart(inode); - err = cpup_entry(dentry, bdst, bsrc, len, flags, dst_parent); + err = cpup_entry(cpg, dst_parent, &a->h_src_attr); if (unlikely(err)) - goto out; + goto out_rev; dst_inode = h_dst->d_inode; mutex_lock_nested(&dst_inode->i_mutex, AuLsc_I_CHILD2); + /* todo: necessary? */ + /* au_pin_hdir_unlock(cpg->pin); */ - err = cpup_iattr(dentry, bdst, h_src); - isdir = S_ISDIR(dst_inode->i_mode); - if (!err) { - if (bdst < old_ibstart) { - if (S_ISREG(inode->i_mode)) { - err = au_dy_iaop(inode, bdst, dst_inode); - if (unlikely(err)) - goto out_rev; - } - au_set_ibstart(inode, bdst); - } - au_set_h_iptr(inode, bdst, au_igrab(dst_inode), - au_hi_flags(inode, isdir)); + err = cpup_iattr(cpg->dentry, cpg->bdst, h_src, &a->h_src_attr); + if (unlikely(err)) { + /* todo: necessary? */ + /* au_pin_hdir_relock(cpg->pin); */ /* ignore an error */ mutex_unlock(&dst_inode->i_mutex); - if (!isdir - && h_src->d_inode->i_nlink > 1 - && plink) - au_plink_append(inode, bdst, h_dst); - goto out; /* success */ + goto out_rev; } + if (cpg->bdst < old_ibstart) { + if (S_ISREG(inode->i_mode)) { + err = au_dy_iaop(inode, cpg->bdst, dst_inode); + if (unlikely(err)) { + /* ignore an error */ + /* au_pin_hdir_relock(cpg->pin); */ + mutex_unlock(&dst_inode->i_mutex); + goto out_rev; + } + } + au_set_ibstart(inode, cpg->bdst); + } + au_set_h_iptr(inode, cpg->bdst, au_igrab(dst_inode), + au_hi_flags(inode, isdir)); + + /* todo: necessary? */ + /* err = au_pin_hdir_relock(cpg->pin); */ + mutex_unlock(&dst_inode->i_mutex); + if (unlikely(err)) + goto out_rev; + + if (!isdir + && h_src->d_inode->i_nlink > 1 + && plink) + au_plink_append(inode, cpg->bdst, h_dst); + + if (au_ftest_cpup(cpg->flags, RENAME)) { + a->h_path.dentry = h_dst; + err = au_do_ren_after_cpup(cpg->dentry, cpg->bdst, &a->h_path); + } + if (!err) + goto out_parent; /* success */ + /* revert */ out_rev: - h_path.dentry = h_parent; - mutex_unlock(&dst_inode->i_mutex); - au_dtime_store(&dt, dst_parent, &h_path); - h_path.dentry = h_dst; - if (!isdir) - rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); - else - rerr = vfsub_rmdir(h_dir, &h_path); - au_dtime_revert(&dt); + a->h_path.dentry = h_parent; + au_dtime_store(&a->dt, dst_parent, &a->h_path); + a->h_path.dentry = h_dst; + rerr = 0; + if (h_dst->d_inode) { + if (!isdir) + rerr = vfsub_unlink(h_dir, &a->h_path, /*force*/0); + else + rerr = vfsub_rmdir(h_dir, &a->h_path); + } + au_dtime_revert(&a->dt); if (rerr) { AuIOErr("failed removing broken entry(%d, %d)\n", err, rerr); err = -EIO; } - -out: +out_parent: dput(dst_parent); + kfree(a); +out: return err; } +#if 0 /* unused */ struct au_cpup_single_args { int *errp; - struct dentry *dentry; - aufs_bindex_t bdst, bsrc; - loff_t len; - unsigned int flags; + struct au_cp_generic *cpg; struct dentry *dst_parent; }; static void au_call_cpup_single(void *args) { struct au_cpup_single_args *a = args; - *a->errp = au_cpup_single(a->dentry, a->bdst, a->bsrc, a->len, - a->flags, a->dst_parent); + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_single(a->cpg, a->dst_parent); + au_pin_hdir_release(a->cpg->pin); } +#endif /* * prevent SIGXFSZ in copy-up. * testing CAP_MKNOD is for generic fs, * but CAP_FSETID is for xfs only, currently. */ -static int au_cpup_sio_test(struct super_block *sb, umode_t mode) +static int au_cpup_sio_test(struct au_pin *pin, umode_t mode) { int do_sio; + struct super_block *sb; + struct inode *h_dir; do_sio = 0; + sb = au_pinned_parent(pin)->d_sb; if (!au_wkq_test() && (!au_sbi(sb)->si_plink_maint_pid || au_plink_maint(sb, AuLock_NOPLM))) { @@ -710,30 +819,29 @@ static int au_cpup_sio_test(struct super_block *sb, umode_t mode) if (!do_sio) do_sio = ((mode & (S_ISUID | S_ISGID)) && !capable(CAP_FSETID)); + /* this workaround may be removed in the future */ + if (!do_sio) { + h_dir = au_pinned_h_dir(pin); + do_sio = h_dir->i_mode & S_ISVTX; + } } return do_sio; } -int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, - aufs_bindex_t bsrc, loff_t len, unsigned int flags, - struct dentry *dst_parent) +#if 0 /* unused */ +int au_sio_cpup_single(struct au_cp_generic *cpg, struct dentry *dst_parent) { int err, wkq_err; struct dentry *h_dentry; - h_dentry = au_h_dptr(dentry, bsrc); - if (!au_cpup_sio_test(dentry->d_sb, h_dentry->d_inode->i_mode)) - err = au_cpup_single(dentry, bdst, bsrc, len, flags, - dst_parent); + h_dentry = au_h_dptr(cpg->dentry, cpg->bsrc); + if (!au_cpup_sio_test(pin, h_dentry->d_inode->i_mode)) + err = au_cpup_single(cpg, dst_parent); else { struct au_cpup_single_args args = { .errp = &err, - .dentry = dentry, - .bdst = bdst, - .bsrc = bsrc, - .len = len, - .flags = flags, + .cpg = cpg, .dst_parent = dst_parent }; wkq_err = au_wkq_wait(au_call_cpup_single, &args); @@ -743,31 +851,47 @@ int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, return err; } +#endif /* * copyup the @dentry from the first active lower branch to @bdst, * using au_cpup_single(). */ -static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, - unsigned int flags) +static int au_cpup_simple(struct au_cp_generic *cpg) { int err; + unsigned int flags_orig; aufs_bindex_t bsrc, bend; + struct dentry *dentry, *h_dentry; + + dentry = cpg->dentry; + DiMustWriteLock(dentry); bend = au_dbend(dentry); - for (bsrc = bdst + 1; bsrc <= bend; bsrc++) - if (au_h_dptr(dentry, bsrc)) - break; + if (cpg->bsrc < 0) { + for (bsrc = cpg->bdst + 1; bsrc <= bend; bsrc++) { + h_dentry = au_h_dptr(dentry, bsrc); + if (h_dentry) { + AuDebugOn(!h_dentry->d_inode); + break; + } + } + AuDebugOn(bsrc > bend); + cpg->bsrc = bsrc; + } - err = au_lkup_neg(dentry, bdst); + err = au_lkup_neg(dentry, cpg->bdst, /*wh*/1); if (!err) { - err = au_cpup_single(dentry, bdst, bsrc, len, flags, NULL); + flags_orig = cpg->flags; + au_fset_cpup(cpg->flags, RENAME); + err = au_cpup_single(cpg, NULL); + cpg->flags = flags_orig; if (!err) return 0; /* success */ /* revert */ - au_set_h_dptr(dentry, bdst, NULL); - au_set_dbstart(dentry, bsrc); + au_set_h_dptr(dentry, cpg->bdst, NULL); + au_set_dbstart(dentry, cpg->bsrc); } return err; @@ -775,37 +899,44 @@ static int au_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, struct au_cpup_simple_args { int *errp; - struct dentry *dentry; - aufs_bindex_t bdst; - loff_t len; - unsigned int flags; + struct au_cp_generic *cpg; }; static void au_call_cpup_simple(void *args) { struct au_cpup_simple_args *a = args; - *a->errp = au_cpup_simple(a->dentry, a->bdst, a->len, a->flags); + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_simple(a->cpg); + au_pin_hdir_release(a->cpg->pin); } -int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, - unsigned int flags) +int au_sio_cpup_simple(struct au_cp_generic *cpg) { int err, wkq_err; - struct dentry *parent; + struct dentry *dentry, *parent; + struct file *h_file; struct inode *h_dir; + dentry = cpg->dentry; + h_file = NULL; + if (au_ftest_cpup(cpg->flags, HOPEN)) { + AuDebugOn(cpg->bsrc < 0); + h_file = au_h_open_pre(dentry, cpg->bsrc); + err = PTR_ERR(h_file); + if (IS_ERR(h_file)) + goto out; + } + parent = dget_parent(dentry); - h_dir = au_h_iptr(parent->d_inode, bdst); + h_dir = au_h_iptr(parent->d_inode, cpg->bdst); if (!au_test_h_perm_sio(h_dir, MAY_EXEC | MAY_WRITE) - && !au_cpup_sio_test(dentry->d_sb, dentry->d_inode->i_mode)) - err = au_cpup_simple(dentry, bdst, len, flags); + && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) + err = au_cpup_simple(cpg); else { struct au_cpup_simple_args args = { .errp = &err, - .dentry = dentry, - .bdst = bdst, - .len = len, - .flags = flags + .cpg = cpg }; wkq_err = au_wkq_wait(au_call_cpup_simple, &args); if (unlikely(wkq_err)) @@ -813,6 +944,10 @@ int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, } dput(parent); + if (h_file) + au_h_open_post(dentry, cpg->bsrc, h_file); + +out: return err; } @@ -821,50 +956,57 @@ int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, /* * copyup the deleted file for writing. */ -static int au_do_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, - struct dentry *wh_dentry, struct file *file, - loff_t len) +static int au_do_cpup_wh(struct au_cp_generic *cpg, struct dentry *wh_dentry, + struct file *file) { int err; - aufs_bindex_t bstart; - struct au_dinfo *dinfo; + unsigned int flags_orig; + aufs_bindex_t bsrc_orig; struct dentry *h_d_dst, *h_d_start; + struct au_dinfo *dinfo; struct au_hdentry *hdp; - dinfo = au_di(dentry); + dinfo = au_di(cpg->dentry); AuRwMustWriteLock(&dinfo->di_rwsem); - bstart = dinfo->di_bstart; + bsrc_orig = cpg->bsrc; + cpg->bsrc = dinfo->di_bstart; hdp = dinfo->di_hdentry; - h_d_dst = hdp[0 + bdst].hd_dentry; - dinfo->di_bstart = bdst; - hdp[0 + bdst].hd_dentry = wh_dentry; + h_d_dst = hdp[0 + cpg->bdst].hd_dentry; + dinfo->di_bstart = cpg->bdst; + hdp[0 + cpg->bdst].hd_dentry = wh_dentry; + h_d_start = NULL; if (file) { - h_d_start = hdp[0 + bstart].hd_dentry; - hdp[0 + bstart].hd_dentry = au_hf_top(file)->f_dentry; + h_d_start = hdp[0 + cpg->bsrc].hd_dentry; + hdp[0 + cpg->bsrc].hd_dentry = au_hf_top(file)->f_dentry; } - err = au_cpup_single(dentry, bdst, bstart, len, !AuCpup_DTIME, - /*h_parent*/NULL); + flags_orig = cpg->flags; + cpg->flags = !AuCpup_DTIME; + err = au_cpup_single(cpg, /*h_parent*/NULL); + cpg->flags = flags_orig; if (file) { if (!err) err = au_reopen_nondir(file); - hdp[0 + bstart].hd_dentry = h_d_start; + hdp[0 + cpg->bsrc].hd_dentry = h_d_start; } - hdp[0 + bdst].hd_dentry = h_d_dst; - dinfo->di_bstart = bstart; + hdp[0 + cpg->bdst].hd_dentry = h_d_dst; + dinfo->di_bstart = cpg->bsrc; + cpg->bsrc = bsrc_orig; return err; } -static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, - struct file *file) +static int au_cpup_wh(struct au_cp_generic *cpg, struct file *file) { int err; + aufs_bindex_t bdst; struct au_dtime dt; - struct dentry *parent, *h_parent, *wh_dentry; + struct dentry *dentry, *parent, *h_parent, *wh_dentry; struct au_branch *br; struct path h_path; + dentry = cpg->dentry; + bdst = cpg->bdst; br = au_sbr(dentry->d_sb, bdst); parent = dget_parent(dentry); h_parent = au_h_dptr(parent, bdst); @@ -874,9 +1016,9 @@ static int au_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, goto out; h_path.dentry = h_parent; - h_path.mnt = br->br_mnt; + h_path.mnt = au_br_mnt(br); au_dtime_store(&dt, parent, &h_path); - err = au_do_cpup_wh(dentry, bdst, wh_dentry, file, len); + err = au_do_cpup_wh(cpg, wh_dentry, file); if (unlikely(err)) goto out_wh; @@ -903,32 +1045,37 @@ out: struct au_cpup_wh_args { int *errp; - struct dentry *dentry; - aufs_bindex_t bdst; - loff_t len; + struct au_cp_generic *cpg; struct file *file; }; static void au_call_cpup_wh(void *args) { struct au_cpup_wh_args *a = args; - *a->errp = au_cpup_wh(a->dentry, a->bdst, a->len, a->file); + + au_pin_hdir_acquire_nest(a->cpg->pin); + *a->errp = au_cpup_wh(a->cpg, a->file); + au_pin_hdir_release(a->cpg->pin); } -int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, - struct file *file) +int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file) { int err, wkq_err; - struct dentry *parent, *h_orph, *h_parent, *h_dentry; - struct inode *dir, *h_dir, *h_tmpdir, *h_inode; + aufs_bindex_t bdst; + struct dentry *dentry, *parent, *h_orph, *h_parent, *h_dentry; + struct inode *dir, *h_dir, *h_tmpdir; struct au_wbr *wbr; + struct au_pin wh_pin, *pin_orig; + dentry = cpg->dentry; + bdst = cpg->bdst; parent = dget_parent(dentry); dir = parent->d_inode; h_orph = NULL; h_parent = NULL; h_dir = au_igrab(au_h_iptr(dir, bdst)); h_tmpdir = h_dir; + pin_orig = NULL; if (!h_dir->i_nlink) { wbr = au_sbr(dentry->d_sb, bdst)->br_wbr; h_orph = wbr->wbr_orph; @@ -938,28 +1085,26 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, h_tmpdir = h_orph->d_inode; au_set_h_iptr(dir, bdst, au_igrab(h_tmpdir), /*flags*/0); - /* this temporary unlock is safe */ if (file) h_dentry = au_hf_top(file)->f_dentry; else h_dentry = au_h_dptr(dentry, au_dbstart(dentry)); - h_inode = h_dentry->d_inode; - IMustLock(h_inode); - mutex_unlock(&h_inode->i_mutex); mutex_lock_nested(&h_tmpdir->i_mutex, AuLsc_I_PARENT3); - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); /* todo: au_h_open_pre()? */ + + pin_orig = cpg->pin; + au_pin_init(&wh_pin, dentry, bdst, AuLsc_DI_PARENT, + AuLsc_I_PARENT3, cpg->pin->udba, AuPin_DI_LOCKED); + cpg->pin = &wh_pin; } if (!au_test_h_perm_sio(h_tmpdir, MAY_EXEC | MAY_WRITE) - && !au_cpup_sio_test(dentry->d_sb, dentry->d_inode->i_mode)) - err = au_cpup_wh(dentry, bdst, len, file); + && !au_cpup_sio_test(cpg->pin, dentry->d_inode->i_mode)) + err = au_cpup_wh(cpg, file); else { struct au_cpup_wh_args args = { .errp = &err, - .dentry = dentry, - .bdst = bdst, - .len = len, + .cpg = cpg, .file = file }; wkq_err = au_wkq_wait(au_call_cpup_wh, &args); @@ -972,6 +1117,8 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, /* todo: au_h_open_post()? */ au_set_h_iptr(dir, bdst, au_igrab(h_dir), /*flags*/0); au_set_h_dptr(parent, bdst, h_parent); + AuDebugOn(!pin_orig); + cpg->pin = pin_orig; } iput(h_dir); dput(parent); @@ -987,6 +1134,7 @@ int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, /* cf. revalidate function in file.c */ int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, struct dentry *h_parent, void *arg), void *arg) { @@ -1032,7 +1180,7 @@ int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, au_pin_set_dentry(&pin, d); err = au_do_pin(&pin); if (!err) { - err = cp(d, bdst, h_parent, arg); + err = cp(d, bdst, &pin, h_parent, arg); au_unpin(&pin); } } @@ -1049,10 +1197,19 @@ out: } static int au_cpup_dir(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, struct dentry *h_parent __maybe_unused , void *arg __maybe_unused) { - return au_sio_cpup_simple(dentry, bdst, -1, AuCpup_DTIME); + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = bdst, + .bsrc = -1, + .len = 0, + .pin = pin, + .flags = AuCpup_DTIME + }; + return au_sio_cpup_simple(&cpg); } int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst) diff --git a/ubuntu/aufs/cpup.h b/ubuntu/aufs/cpup.h index 2e4fbe079a25..40eef8c49e36 100644 --- a/ubuntu/aufs/cpup.h +++ b/ubuntu/aufs/cpup.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -29,8 +29,9 @@ struct inode; struct file; +struct au_pin; -void au_cpup_attr_flags(struct inode *dst, struct inode *src); +void au_cpup_attr_flags(struct inode *dst, unsigned int iflags); void au_cpup_attr_timesizes(struct inode *inode); void au_cpup_attr_nlink(struct inode *inode, int force); void au_cpup_attr_changeable(struct inode *inode); @@ -39,10 +40,21 @@ void au_cpup_attr_all(struct inode *inode, int force); /* ---------------------------------------------------------------------- */ +struct au_cp_generic { + struct dentry *dentry; + aufs_bindex_t bdst, bsrc; + loff_t len; + struct au_pin *pin; + unsigned int flags; +}; + /* cpup flags */ #define AuCpup_DTIME 1 /* do dtime_store/revert */ #define AuCpup_KEEPLINO (1 << 1) /* do not clear the lower xino, for link(2) */ +#define AuCpup_RENAME (1 << 2) /* rename after cpup */ +#define AuCpup_HOPEN (1 << 3) /* call h_open_pre/post() in cpup */ + #define au_ftest_cpup(flags, name) ((flags) & AuCpup_##name) #define au_fset_cpup(flags, name) \ do { (flags) |= AuCpup_##name; } while (0) @@ -50,16 +62,12 @@ void au_cpup_attr_all(struct inode *inode, int force); do { (flags) &= ~AuCpup_##name; } while (0) int au_copy_file(struct file *dst, struct file *src, loff_t len); -int au_sio_cpup_single(struct dentry *dentry, aufs_bindex_t bdst, - aufs_bindex_t bsrc, loff_t len, unsigned int flags, - struct dentry *dst_parent); -int au_sio_cpup_simple(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, - unsigned int flags); -int au_sio_cpup_wh(struct dentry *dentry, aufs_bindex_t bdst, loff_t len, - struct file *file); +int au_sio_cpup_simple(struct au_cp_generic *cpg); +int au_sio_cpup_wh(struct au_cp_generic *cpg, struct file *file); int au_cp_dirs(struct dentry *dentry, aufs_bindex_t bdst, int (*cp)(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, struct dentry *h_parent, void *arg), void *arg); int au_cpup_dirs(struct dentry *dentry, aufs_bindex_t bdst); diff --git a/ubuntu/aufs/dbgaufs.c b/ubuntu/aufs/dbgaufs.c index d1dbfd2655ec..6ba07ec994cb 100644 --- a/ubuntu/aufs/dbgaufs.c +++ b/ubuntu/aufs/dbgaufs.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -96,6 +96,101 @@ static ssize_t dbgaufs_xi_read(struct file *file, char __user *buf, /* ---------------------------------------------------------------------- */ +struct dbgaufs_plink_arg { + int n; + char a[]; +}; + +static int dbgaufs_plink_release(struct inode *inode __maybe_unused, + struct file *file) +{ + free_page((unsigned long)file->private_data); + return 0; +} + +static int dbgaufs_plink_open(struct inode *inode, struct file *file) +{ + int err, i, limit; + unsigned long n, sum; + struct dbgaufs_plink_arg *p; + struct au_sbinfo *sbinfo; + struct super_block *sb; + struct au_sphlhead *sphl; + + err = -ENOMEM; + p = (void *)get_zeroed_page(GFP_NOFS); + if (unlikely(!p)) + goto out; + + err = -EFBIG; + sbinfo = inode->i_private; + sb = sbinfo->si_sb; + si_noflush_read_lock(sb); + if (au_opt_test(au_mntflags(sb), PLINK)) { + limit = PAGE_SIZE - sizeof(p->n); + + /* the number of buckets */ + n = snprintf(p->a + p->n, limit, "%d\n", AuPlink_NHASH); + p->n += n; + limit -= n; + + sum = 0; + for (i = 0, sphl = sbinfo->si_plink; + i < AuPlink_NHASH; + i++, sphl++) { + n = au_sphl_count(sphl); + sum += n; + + n = snprintf(p->a + p->n, limit, "%lu ", n); + p->n += n; + limit -= n; + if (unlikely(limit <= 0)) + goto out_free; + } + p->a[p->n - 1] = '\n'; + + /* the sum of plinks */ + n = snprintf(p->a + p->n, limit, "%lu\n", sum); + p->n += n; + limit -= n; + if (unlikely(limit <= 0)) + goto out_free; + } else { +#define str "1\n0\n0\n" + p->n = sizeof(str) - 1; + strcpy(p->a, str); +#undef str + } + si_read_unlock(sb); + + err = 0; + file->private_data = p; + goto out; /* success */ + +out_free: + free_page((unsigned long)p); +out: + return err; +} + +static ssize_t dbgaufs_plink_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct dbgaufs_plink_arg *p; + + p = file->private_data; + return simple_read_from_buffer(buf, count, ppos, p->a, p->n); +} + +static const struct file_operations dbgaufs_plink_fop = { + .owner = THIS_MODULE, + .open = dbgaufs_plink_open, + .release = dbgaufs_plink_release, + .read = dbgaufs_plink_read +}; + +/* ---------------------------------------------------------------------- */ + static int dbgaufs_xib_open(struct inode *inode, struct file *file) { int err; @@ -175,10 +270,8 @@ void dbgaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) for (; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); xi = &br->br_xino; - if (xi->xi_dbgaufs) { - debugfs_remove(xi->xi_dbgaufs); - xi->xi_dbgaufs = NULL; - } + debugfs_remove(xi->xi_dbgaufs); + xi->xi_dbgaufs = NULL; } } @@ -305,6 +398,12 @@ int dbgaufs_si_init(struct au_sbinfo *sbinfo) if (unlikely(!sbinfo->si_dbgaufs_xib)) goto out_dir; + sbinfo->si_dbgaufs_plink = debugfs_create_file + ("plink", dbgaufs_mode, sbinfo->si_dbgaufs, sbinfo, + &dbgaufs_plink_fop); + if (unlikely(!sbinfo->si_dbgaufs_plink)) + goto out_dir; + err = dbgaufs_xigen_init(sbinfo); if (!err) goto out; /* success */ diff --git a/ubuntu/aufs/dbgaufs.h b/ubuntu/aufs/dbgaufs.h index 8fe49742e558..f418c92d54a3 100644 --- a/ubuntu/aufs/dbgaufs.h +++ b/ubuntu/aufs/dbgaufs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/dcsub.c b/ubuntu/aufs/dcsub.c index 5e8321eca3d4..5b3d904ee625 100644 --- a/ubuntu/aufs/dcsub.c +++ b/ubuntu/aufs/dcsub.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/dcsub.h b/ubuntu/aufs/dcsub.h index 3d10731e2769..53dcbd78548a 100644 --- a/ubuntu/aufs/dcsub.h +++ b/ubuntu/aufs/dcsub.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/debug.c b/ubuntu/aufs/debug.c index b1b0d5723668..c0b0bf602cb8 100644 --- a/ubuntu/aufs/debug.c +++ b/ubuntu/aufs/debug.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -23,9 +23,39 @@ #include #include "aufs.h" -int aufs_debug; +/* Returns 0, or -errno. arg is in kp->arg. */ +static int param_atomic_t_set(const char *val, const struct kernel_param *kp) +{ + int err, n; + + err = kstrtoint(val, 0, &n); + if (!err) { + if (n > 0) + au_debug_on(); + else + au_debug_off(); + } + return err; +} + +/* Returns length written or -errno. Buffer is 4k (ie. be short!) */ +static int param_atomic_t_get(char *buffer, const struct kernel_param *kp) +{ + atomic_t *a; + + a = kp->arg; + return sprintf(buffer, "%d", atomic_read(a)); +} + +static struct kernel_param_ops param_ops_atomic_t = { + .set = param_atomic_t_set, + .get = param_atomic_t_get + /* void (*free)(void *arg) */ +}; + +atomic_t aufs_debug = ATOMIC_INIT(0); MODULE_PARM_DESC(debug, "debug print"); -module_param_named(debug, aufs_debug, int, S_IRUGO | S_IWUSR | S_IWGRP); +module_param_named(debug, aufs_debug, atomic_t, S_IRUGO | S_IWUSR | S_IWGRP); char *au_plevel = KERN_DEBUG; #define dpri(fmt, ...) do { \ @@ -88,7 +118,7 @@ static int do_pri_inode(aufs_bindex_t bindex, struct inode *inode, int hn, return -1; } - /* the type of i_blocks depends upon CONFIG_LSF */ + /* the type of i_blocks depends upon CONFIG_LBDAF */ BUILD_BUG_ON(sizeof(inode->i_blocks) != sizeof(unsigned long) && sizeof(inode->i_blocks) != sizeof(u64)); if (wh) { @@ -124,7 +154,7 @@ void au_dpri_inode(struct inode *inode) if (!iinfo) return; dpri("i-1: bstart %d, bend %d, gen %d\n", - iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode)); + iinfo->ii_bstart, iinfo->ii_bend, au_iigen(inode, NULL)); if (iinfo->ii_bstart < 0) return; hn = 0; @@ -257,7 +287,7 @@ static int do_pri_br(aufs_bindex_t bindex, struct au_branch *br) if (!br || IS_ERR(br)) goto out; - mnt = br->br_mnt; + mnt = au_br_mnt(br); if (!mnt || IS_ERR(mnt)) goto out; sb = mnt->mnt_sb; @@ -298,7 +328,7 @@ void au_dpri_sb(struct super_block *sb) a->mnt.mnt_sb = sb; a->fake.br_perm = 0; - a->fake.br_mnt = &a->mnt; + a->fake.br_path.mnt = &a->mnt; a->fake.br_xino.xi_file = NULL; atomic_set(&a->fake.br_count, 0); smp_mb(); /* atomic_set */ @@ -328,8 +358,11 @@ void au_dbg_sleep_jiffy(int jiffy) void au_dbg_iattr(struct iattr *ia) { -#define AuBit(name) if (ia->ia_valid & ATTR_ ## name) \ - dpri(#name "\n") +#define AuBit(name) \ + do { \ + if (ia->ia_valid & ATTR_ ## name) \ + dpri(#name "\n"); \ + } while (0) AuBit(MODE); AuBit(UID); AuBit(GID); @@ -377,14 +410,11 @@ void __au_dbg_verify_dinode(struct dentry *dentry, const char *func, int line) continue; h_inode = au_h_iptr(inode, bindex); if (unlikely(h_inode != h_dentry->d_inode)) { - int old = au_debug_test(); - if (!old) - au_debug(1); + au_debug_on(); AuDbg("b%d, %s:%d\n", bindex, func, line); AuDbgDentry(dentry); AuDbgInode(inode); - if (!old) - au_debug(0); + au_debug_off(); BUG(); } } diff --git a/ubuntu/aufs/debug.h b/ubuntu/aufs/debug.h index 0fa016948716..0b17cc64a1ad 100644 --- a/ubuntu/aufs/debug.h +++ b/ubuntu/aufs/debug.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -25,6 +25,7 @@ #ifdef __KERNEL__ +#include #include #include #include @@ -33,23 +34,29 @@ #define AuDebugOn(a) BUG_ON(a) /* module parameter */ -extern int aufs_debug; -static inline void au_debug(int n) +extern atomic_t aufs_debug; +static inline void au_debug_on(void) { - aufs_debug = n; - smp_mb(); + atomic_inc(&aufs_debug); +} +static inline void au_debug_off(void) +{ + atomic_dec_if_positive(&aufs_debug); } static inline int au_debug_test(void) { - return aufs_debug; + return atomic_read(&aufs_debug) > 0; } #else #define AuDebugOn(a) do {} while (0) -AuStubVoid(au_debug, int n) +AuStubVoid(au_debug_on, void) +AuStubVoid(au_debug_off, void) AuStubInt0(au_debug_test, void) #endif /* CONFIG_AUFS_DEBUG */ +#define param_check_atomic_t(name, p) __param_check(name, p, atomic_t) + /* ---------------------------------------------------------------------- */ /* debug print */ diff --git a/ubuntu/aufs/dentry.c b/ubuntu/aufs/dentry.c index d5518cf00804..0a80a843db21 100644 --- a/ubuntu/aufs/dentry.c +++ b/ubuntu/aufs/dentry.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,7 +33,6 @@ struct au_do_lookup_args { unsigned int flags; mode_t type; - unsigned int nd_flags; }; /* @@ -124,8 +123,7 @@ static int au_test_shwh(struct super_block *sb, const struct qstr *name) * otherwise an error. * can be called at unlinking with @type is zero. */ -int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, - unsigned int flags) +int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type) { int npositive, err; aufs_bindex_t bindex, btail, bdiropq; @@ -133,8 +131,7 @@ int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, struct qstr whname; struct au_do_lookup_args args = { .flags = 0, - .type = type, - .nd_flags = flags + .type = type }; const struct qstr *name = &dentry->d_name; struct dentry *parent; @@ -249,15 +246,19 @@ struct dentry *au_sio_lkup_one(struct qstr *name, struct dentry *parent, /* * lookup @dentry on @bindex which should be negative. */ -int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex) +int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex, int wh) { int err; struct dentry *parent, *h_parent, *h_dentry; + struct au_branch *br; parent = dget_parent(dentry); h_parent = au_h_dptr(parent, bindex); - h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent, - au_sbr(dentry->d_sb, bindex)); + br = au_sbr(dentry->d_sb, bindex); + if (wh) + h_dentry = au_whtmp_lkup(h_parent, br, &dentry->d_name); + else + h_dentry = au_sio_lkup_one(&dentry->d_name, h_parent, br); err = PTR_ERR(h_dentry); if (IS_ERR(h_dentry)) goto out; @@ -732,7 +733,7 @@ int au_refresh_dentry(struct dentry *dentry, struct dentry *parent) * if current working dir is removed, it returns an error. * but the dentry is legal. */ - err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0, /*flags*/0); + err = au_lkup_dentry(dentry, /*bstart*/0, /*type*/0); AuDbgDentry(dentry); au_di_swap(tmp, dinfo); if (err == -ENOENT) diff --git a/ubuntu/aufs/dentry.h b/ubuntu/aufs/dentry.h index 3e95ecf45ffe..175a8712cb9e 100644 --- a/ubuntu/aufs/dentry.h +++ b/ubuntu/aufs/dentry.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,9 +51,8 @@ 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, struct dentry *h_parent, struct au_branch *br); -int au_lkup_dentry(struct dentry *dentry, aufs_bindex_t bstart, mode_t type, - unsigned int flags); -int au_lkup_neg(struct dentry *dentry, aufs_bindex_t bindex); +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); diff --git a/ubuntu/aufs/dinfo.c b/ubuntu/aufs/dinfo.c index 24f22a91d21e..2a924871a723 100644 --- a/ubuntu/aufs/dinfo.c +++ b/ubuntu/aufs/dinfo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/dir.c b/ubuntu/aufs/dir.c index 818508664e0f..5989b0def451 100644 --- a/ubuntu/aufs/dir.c +++ b/ubuntu/aufs/dir.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +33,7 @@ void au_add_nlink(struct inode *dir, struct inode *h_dir) nlink += h_dir->i_nlink - 2; if (h_dir->i_nlink < 2) nlink += 2; + smp_mb(); /* 0 can happen in revaliding */ set_nlink(dir, nlink); } @@ -47,6 +48,7 @@ void au_sub_nlink(struct inode *dir, struct inode *h_dir) nlink -= h_dir->i_nlink - 2; if (h_dir->i_nlink < 2) nlink -= 2; + smp_mb(); /* nlink == 0 means the branch-fs is broken */ set_nlink(dir, nlink); } diff --git a/ubuntu/aufs/dir.h b/ubuntu/aufs/dir.h index dc52a6325c33..fb237ba326a9 100644 --- a/ubuntu/aufs/dir.h +++ b/ubuntu/aufs/dir.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/dynop.c b/ubuntu/aufs/dynop.c index 162d5b98db21..be3539916671 100644 --- a/ubuntu/aufs/dynop.c +++ b/ubuntu/aufs/dynop.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Junjiro R. Okajima + * Copyright (C) 2010-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -239,7 +239,7 @@ static struct au_dykey *dy_get(struct au_dynop *op, struct au_branch *br) key->dk_op.dy_hop = op->dy_hop; kref_init(&key->dk_kref); - p->set(key, op->dy_hop, br->br_mnt->mnt_sb); + p->set(key, op->dy_hop, au_br_sb(br)); old = dy_gadd(spl, key); if (old) { kfree(key); diff --git a/ubuntu/aufs/dynop.h b/ubuntu/aufs/dynop.h index 029b08cff025..3f2c9aaa8c50 100644 --- a/ubuntu/aufs/dynop.h +++ b/ubuntu/aufs/dynop.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Junjiro R. Okajima + * Copyright (C) 2010-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/export.c b/ubuntu/aufs/export.c index d0b679582986..c266d2e65812 100644 --- a/ubuntu/aufs/export.c +++ b/ubuntu/aufs/export.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -89,6 +89,21 @@ static int au_test_anon(struct dentry *dentry) return !!(dentry->d_flags & DCACHE_DISCONNECTED); } +int au_test_nfsd(void) +{ + int ret; + struct task_struct *tsk = current; + char comm[sizeof(tsk->comm)]; + + ret = 0; + if (tsk->flags & PF_KTHREAD) { + get_task_comm(comm, tsk); + ret = !strcmp(comm, "nfsd"); + } + + return ret; +} + /* ---------------------------------------------------------------------- */ /* inode generation external table */ @@ -222,7 +237,7 @@ static struct dentry *decode_by_ino(struct super_block *sb, ino_t ino, sigen = au_sigen(sb); if (unlikely(is_bad_inode(inode) || IS_DEADDIR(inode) - || sigen != au_iigen(inode))) + || sigen != au_iigen(inode, NULL))) goto out_iput; dentry = NULL; @@ -380,7 +395,8 @@ static struct dentry *au_lkup_by_ino(struct path *path, ino_t ino, dentry = ERR_PTR(err); if (unlikely(err)) goto out_name; - dentry = ERR_PTR(-ENOENT); + /* instead of ENOENT */ + dentry = ERR_PTR(-ESTALE); if (!arg.found) goto out_name; @@ -492,7 +508,7 @@ struct dentry *decode_by_path(struct super_block *sb, ino_t ino, __u32 *fh, struct path path; br = au_sbr(sb, nsi_lock->bindex); - h_mnt = br->br_mnt; + h_mnt = au_br_mnt(br); h_sb = h_mnt->mnt_sb; /* todo: call lower fh_to_dentry()? fh_to_parent()? */ h_parent = exportfs_decode_fh(h_mnt, (void *)(fh + Fh_tail), @@ -716,7 +732,7 @@ static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, err = -EPERM; br = au_sbr(sb, bindex); - h_sb = br->br_mnt->mnt_sb; + h_sb = au_br_sb(br); if (unlikely(!h_sb->s_export_op)) { AuErr1("%s branch is not exportable\n", au_sbtype(h_sb)); goto out_hparent; @@ -735,7 +751,7 @@ static int aufs_encode_fh(struct inode *inode, __u32 *fh, int *max_len, err = fh[Fh_h_type]; *max_len += Fh_tail; /* todo: macros? */ - if (err != 255) + if (err != FILEID_INVALID) err = 99; else AuWarn1("%s encode_fh failed\n", au_sbtype(h_sb)); @@ -749,7 +765,7 @@ out_unlock: si_read_unlock(sb); out: if (unlikely(err < 0)) - err = 255; + err = FILEID_INVALID; return err; } diff --git a/ubuntu/aufs/f_op.c b/ubuntu/aufs/f_op.c index 80ea935052ef..5c35454e4b23 100644 --- a/ubuntu/aufs/f_op.c +++ b/ubuntu/aufs/f_op.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/f_op_sp.c b/ubuntu/aufs/f_op_sp.c index ad8cf9621d8e..0bb7d44cf28d 100644 --- a/ubuntu/aufs/f_op_sp.c +++ b/ubuntu/aufs/f_op_sp.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -174,12 +174,19 @@ static void au_init_fop_sp(struct file *file) static int au_cpup_sp(struct dentry *dentry) { int err; - aufs_bindex_t bcpup; struct au_pin pin; struct au_wr_dir_args wr_dir_args = { .force_btgt = -1, .flags = 0 }; + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = -1, + .bsrc = -1, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME + }; AuDbg("%.*s\n", AuDLNPair(dentry)); @@ -188,15 +195,15 @@ static int au_cpup_sp(struct dentry *dentry) err = au_wr_dir(dentry, /*src_dentry*/NULL, &wr_dir_args); if (unlikely(err < 0)) goto out; - bcpup = err; + cpg.bdst = err; err = 0; - if (bcpup == au_dbstart(dentry)) + if (cpg.bdst == au_dbstart(dentry)) goto out; /* success */ - err = au_pin(&pin, dentry, bcpup, au_opt_udba(dentry->d_sb), + err = au_pin(&pin, dentry, cpg.bdst, au_opt_udba(dentry->d_sb), AuPin_MNT_WRITE); if (!err) { - err = au_sio_cpup_simple(dentry, bcpup, -1, AuCpup_DTIME); + err = au_sio_cpup_simple(&cpg); au_unpin(&pin); } diff --git a/ubuntu/aufs/file.c b/ubuntu/aufs/file.c index d2da66c9e988..1afd2e35f5fb 100644 --- a/ubuntu/aufs/file.c +++ b/ubuntu/aufs/file.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -67,7 +67,7 @@ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, br = au_sbr(sb, bindex); h_file = ERR_PTR(-EACCES); exec_flag = flags & __FMODE_EXEC; - if (exec_flag && (br->br_mnt->mnt_flags & MNT_NOEXEC)) + if (exec_flag && (au_br_mnt(br)->mnt_flags & MNT_NOEXEC)) goto out; /* drop flags for writing */ @@ -76,7 +76,7 @@ struct file *au_h_open(struct dentry *dentry, aufs_bindex_t bindex, int flags, flags &= ~O_CREAT; atomic_inc(&br->br_count); h_path.dentry = h_dentry; - h_path.mnt = br->br_mnt; + h_path.mnt = au_br_mnt(br); if (!au_special_file(h_inode->i_mode)) h_file = vfsub_dentry_open(&h_path, flags); else { @@ -154,13 +154,29 @@ int au_reopen_nondir(struct file *file) au_set_h_fptr(file, bstart, NULL); } AuDebugOn(au_fi(file)->fi_hdir); - AuDebugOn(au_fbstart(file) < bstart); + /* + * it can happen + * file exists on both of rw and ro + * open --> dbstart and fbstart are both 0 + * prepend a branch as rw, "rw" become ro + * remove rw/file + * delete the top branch, "rw" becomes rw again + * --> dbstart is 1, fbstart is still 0 + * write --> fbstart is 0 but dbstart is 1 + */ + /* AuDebugOn(au_fbstart(file) < bstart); */ h_file = au_h_open(dentry, bstart, vfsub_file_flags(file) & ~O_TRUNC, file); err = PTR_ERR(h_file); - if (IS_ERR(h_file)) + if (IS_ERR(h_file)) { + if (h_file_tmp) { + atomic_inc(&au_sbr(dentry->d_sb, bstart)->br_count); + au_set_h_fptr(file, bstart, h_file_tmp); + h_file_tmp = NULL; + } goto out; /* todo: close all? */ + } err = 0; au_set_fbstart(file, bstart); @@ -202,32 +218,39 @@ static int au_reopen_wh(struct file *file, aufs_bindex_t btgt, } static int au_ready_to_write_wh(struct file *file, loff_t len, - aufs_bindex_t bcpup) + aufs_bindex_t bcpup, struct au_pin *pin) { int err; struct inode *inode, *h_inode; - struct dentry *dentry, *h_dentry, *hi_wh; + struct dentry *h_dentry, *hi_wh; + struct au_cp_generic cpg = { + .dentry = file->f_dentry, + .bdst = bcpup, + .bsrc = -1, + .len = len, + .pin = pin + }; - dentry = file->f_dentry; - au_update_dbstart(dentry); - inode = dentry->d_inode; + au_update_dbstart(cpg.dentry); + inode = cpg.dentry->d_inode; h_inode = NULL; - if (au_dbstart(dentry) <= bcpup && au_dbend(dentry) >= bcpup) { - h_dentry = au_h_dptr(dentry, bcpup); + if (au_dbstart(cpg.dentry) <= bcpup + && au_dbend(cpg.dentry) >= bcpup) { + h_dentry = au_h_dptr(cpg.dentry, bcpup); if (h_dentry) h_inode = h_dentry->d_inode; } hi_wh = au_hi_wh(inode, bcpup); if (!hi_wh && !h_inode) - err = au_sio_cpup_wh(dentry, bcpup, len, file); + err = au_sio_cpup_wh(&cpg, file); else /* already copied-up after unlink */ err = au_reopen_wh(file, bcpup, hi_wh); if (!err && inode->i_nlink > 1 - && au_opt_test(au_mntflags(dentry->d_sb), PLINK)) - au_plink_append(inode, bcpup, au_h_dptr(dentry, bcpup)); + && au_opt_test(au_mntflags(cpg.dentry->d_sb), PLINK)) + au_plink_append(inode, bcpup, au_h_dptr(cpg.dentry, bcpup)); return err; } @@ -238,81 +261,80 @@ static int au_ready_to_write_wh(struct file *file, loff_t len, int au_ready_to_write(struct file *file, loff_t len, struct au_pin *pin) { int err; - aufs_bindex_t bstart, bcpup, dbstart; - struct dentry *dentry, *parent, *h_dentry; - struct inode *h_inode, *inode; + aufs_bindex_t dbstart; + struct dentry *parent, *h_dentry; + struct inode *inode; struct super_block *sb; struct file *h_file; + struct au_cp_generic cpg = { + .dentry = file->f_dentry, + .bdst = -1, + .bsrc = -1, + .len = len, + .pin = pin, + .flags = AuCpup_DTIME + }; - dentry = file->f_dentry; - sb = dentry->d_sb; - inode = dentry->d_inode; + sb = cpg.dentry->d_sb; + inode = cpg.dentry->d_inode; AuDebugOn(au_special_file(inode->i_mode)); - bstart = au_fbstart(file); - err = au_test_ro(sb, bstart, inode); + cpg.bsrc = au_fbstart(file); + err = au_test_ro(sb, cpg.bsrc, inode); if (!err && (au_hf_top(file)->f_mode & FMODE_WRITE)) { - err = au_pin(pin, dentry, bstart, AuOpt_UDBA_NONE, /*flags*/0); + err = au_pin(pin, cpg.dentry, cpg.bsrc, AuOpt_UDBA_NONE, + /*flags*/0); goto out; } /* need to cpup or reopen */ - parent = dget_parent(dentry); + parent = dget_parent(cpg.dentry); di_write_lock_parent(parent); - err = AuWbrCopyup(au_sbi(sb), dentry); - bcpup = err; + err = AuWbrCopyup(au_sbi(sb), cpg.dentry); + cpg.bdst = err; if (unlikely(err < 0)) goto out_dgrade; err = 0; - if (!d_unhashed(dentry) && !au_h_dptr(parent, bcpup)) { - err = au_cpup_dirs(dentry, bcpup); + if (!d_unhashed(cpg.dentry) && !au_h_dptr(parent, cpg.bdst)) { + err = au_cpup_dirs(cpg.dentry, cpg.bdst); if (unlikely(err)) goto out_dgrade; } - err = au_pin(pin, dentry, bcpup, AuOpt_UDBA_NONE, + err = au_pin(pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out_dgrade; h_dentry = au_hf_top(file)->f_dentry; - h_inode = h_dentry->d_inode; - dbstart = au_dbstart(dentry); - if (dbstart <= bcpup) { - h_dentry = au_h_dptr(dentry, bcpup); + dbstart = au_dbstart(cpg.dentry); + if (dbstart <= cpg.bdst) { + h_dentry = au_h_dptr(cpg.dentry, cpg.bdst); AuDebugOn(!h_dentry); - h_inode = h_dentry->d_inode; - AuDebugOn(!h_inode); - bstart = bcpup; + cpg.bsrc = cpg.bdst; } - if (dbstart <= bcpup /* just reopen */ - || !d_unhashed(dentry) /* copyup and reopen */ + if (dbstart <= cpg.bdst /* just reopen */ + || !d_unhashed(cpg.dentry) /* copyup and reopen */ ) { - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - h_file = au_h_open_pre(dentry, bstart); - if (IS_ERR(h_file)) { + h_file = au_h_open_pre(cpg.dentry, cpg.bsrc); + if (IS_ERR(h_file)) err = PTR_ERR(h_file); - h_file = NULL; - } else { + else { di_downgrade_lock(parent, AuLock_IR); - if (dbstart > bcpup) - err = au_sio_cpup_simple(dentry, bcpup, len, - AuCpup_DTIME); + if (dbstart > cpg.bdst) + err = au_sio_cpup_simple(&cpg); if (!err) err = au_reopen_nondir(file); + au_h_open_post(cpg.dentry, cpg.bsrc, h_file); } - mutex_unlock(&h_inode->i_mutex); - au_h_open_post(dentry, bstart, h_file); } else { /* copyup as wh and reopen */ /* * since writable hfsplus branch is not supported, * h_open_pre/post() are unnecessary. */ - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - err = au_ready_to_write_wh(file, len, bcpup); + err = au_ready_to_write_wh(file, len, cpg.bdst, pin); di_downgrade_lock(parent, AuLock_IR); - mutex_unlock(&h_inode->i_mutex); } if (!err) { @@ -363,29 +385,35 @@ int au_do_flush(struct file *file, fl_owner_t id, static int au_file_refresh_by_inode(struct file *file, int *need_reopen) { int err; - aufs_bindex_t bstart; struct au_pin pin; struct au_finfo *finfo; - struct dentry *dentry, *parent, *hi_wh; + struct dentry *parent, *hi_wh; struct inode *inode; struct super_block *sb; + struct au_cp_generic cpg = { + .dentry = file->f_dentry, + .bdst = -1, + .bsrc = -1, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME + }; FiMustWriteLock(file); err = 0; finfo = au_fi(file); - dentry = file->f_dentry; - sb = dentry->d_sb; - inode = dentry->d_inode; - bstart = au_ibstart(inode); - if (bstart == finfo->fi_btop || IS_ROOT(dentry)) + sb = cpg.dentry->d_sb; + inode = cpg.dentry->d_inode; + cpg.bdst = au_ibstart(inode); + if (cpg.bdst == finfo->fi_btop || IS_ROOT(cpg.dentry)) goto out; - parent = dget_parent(dentry); - if (au_test_ro(sb, bstart, inode)) { + parent = dget_parent(cpg.dentry); + if (au_test_ro(sb, cpg.bdst, inode)) { di_read_lock_parent(parent, !AuLock_IR); - err = AuWbrCopyup(au_sbi(sb), dentry); - bstart = err; + err = AuWbrCopyup(au_sbi(sb), cpg.dentry); + cpg.bdst = err; di_read_unlock(parent, !AuLock_IR); if (unlikely(err < 0)) goto out_parent; @@ -393,25 +421,26 @@ static int au_file_refresh_by_inode(struct file *file, int *need_reopen) } di_read_lock_parent(parent, AuLock_IR); - hi_wh = au_hi_wh(inode, bstart); + hi_wh = au_hi_wh(inode, cpg.bdst); if (!S_ISDIR(inode->i_mode) && au_opt_test(au_mntflags(sb), PLINK) && au_plink_test(inode) - && !d_unhashed(dentry)) { - err = au_test_and_cpup_dirs(dentry, bstart); + && !d_unhashed(cpg.dentry) + && cpg.bdst < au_dbstart(cpg.dentry)) { + err = au_test_and_cpup_dirs(cpg.dentry, cpg.bdst); if (unlikely(err)) goto out_unlock; /* always superio. */ - err = au_pin(&pin, dentry, bstart, AuOpt_UDBA_NONE, + err = au_pin(&pin, cpg.dentry, cpg.bdst, AuOpt_UDBA_NONE, AuPin_DI_LOCKED | AuPin_MNT_WRITE); - if (!err) - err = au_sio_cpup_simple(dentry, bstart, -1, - AuCpup_DTIME); - au_unpin(&pin); + if (!err) { + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } } else if (hi_wh) { /* already copied-up after unlink */ - err = au_reopen_wh(file, bstart, hi_wh); + err = au_reopen_wh(file, cpg.bdst, hi_wh); *need_reopen = 0; } diff --git a/ubuntu/aufs/file.h b/ubuntu/aufs/file.h index af51fe1dfafe..bc0a1ea3876b 100644 --- a/ubuntu/aufs/file.h +++ b/ubuntu/aufs/file.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -284,14 +284,12 @@ static inline void au_vm_file_reset(struct vm_area_struct *vma, static inline void au_vm_prfile_set(struct vm_area_struct *vma, struct file *file) { -#ifdef CONFIG_AUFS_PROC_MAP get_file(file); vma->vm_prfile = file; #ifndef CONFIG_MMU get_file(file); vma->vm_region->vm_prfile = file; #endif -#endif } #endif /* __KERNEL__ */ diff --git a/ubuntu/aufs/finfo.c b/ubuntu/aufs/finfo.c index 3208510c5ee9..2111355a7514 100644 --- a/ubuntu/aufs/finfo.c +++ b/ubuntu/aufs/finfo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -127,7 +127,7 @@ void au_fi_init_once(void *_finfo) int au_finfo_init(struct file *file, struct au_fidir *fidir) { - int err, lc_idx; + int err; struct au_finfo *finfo; struct dentry *dentry; @@ -139,10 +139,11 @@ int au_finfo_init(struct file *file, struct au_fidir *fidir) err = 0; au_nfiles_inc(dentry->d_sb); - lc_idx = AuLcNonDir_FIINFO; - if (fidir) - lc_idx = AuLcDir_FIINFO; - au_rw_class(&finfo->fi_rwsem, au_lc_key + lc_idx); + /* verbose coding for lock class name */ + if (!fidir) + au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcNonDir_FIINFO); + else + au_rw_class(&finfo->fi_rwsem, au_lc_key + AuLcDir_FIINFO); au_rw_write_lock(&finfo->fi_rwsem); finfo->fi_btop = -1; finfo->fi_hdir = fidir; diff --git a/ubuntu/aufs/fstype.h b/ubuntu/aufs/fstype.h index afcba6ea7d68..4d9ab6e2e3e2 100644 --- a/ubuntu/aufs/fstype.h +++ b/ubuntu/aufs/fstype.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -110,15 +110,6 @@ static inline int au_test_ecryptfs(struct super_block *sb __maybe_unused) #endif } -static inline int au_test_smbfs(struct super_block *sb __maybe_unused) -{ -#if defined(CONFIG_SMB_FS) || defined(CONFIG_SMB_FS_MODULE) - return sb->s_magic == SMB_SUPER_MAGIC; -#else - return 0; -#endif -} - static inline int au_test_ocfs2(struct super_block *sb __maybe_unused) { #if defined(CONFIG_OCFS2_FS) || defined(CONFIG_OCFS2_FS_MODULE) @@ -157,7 +148,7 @@ static inline int au_test_v9fs(struct super_block *sb __maybe_unused) static inline int au_test_ext4(struct super_block *sb __maybe_unused) { -#if defined(CONFIG_EXT4DEV_FS) || defined(CONFIG_EXT4DEV_FS_MODULE) +#if defined(CONFIG_EXT4_FS) || defined(CONFIG_EXT4_FS_MODULE) return sb->s_magic == EXT4_SUPER_MAGIC; #else return 0; @@ -365,7 +356,6 @@ static inline int au_test_fs_refresh_iattr(struct super_block *sb) { return au_test_nfs(sb) || au_test_fuse(sb) - /* || au_test_smbfs(sb) */ /* untested */ /* || au_test_ocfs2(sb) */ /* untested */ /* || au_test_btrfs(sb) */ /* untested */ /* || au_test_coda(sb) */ /* untested */ @@ -411,7 +401,6 @@ static inline int au_test_fs_no_limit_nlink(struct super_block *sb) || au_test_ramfs(sb) #endif || au_test_ubifs(sb) - || au_test_btrfs(sb) || au_test_hfsplus(sb); } diff --git a/ubuntu/aufs/hfsnotify.c b/ubuntu/aufs/hfsnotify.c index 6fab0f894883..af61967691a8 100644 --- a/ubuntu/aufs/hfsnotify.c +++ b/ubuntu/aufs/hfsnotify.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,12 +35,13 @@ static void au_hfsn_free_mark(struct fsnotify_mark *mark) AuDbg("here\n"); au_cache_free_hnotify(hn); smp_mb__before_atomic_dec(); - atomic64_dec(&au_hfsn_ifree); - wake_up(&au_hfsn_wq); + if (atomic64_dec_and_test(&au_hfsn_ifree)) + wake_up(&au_hfsn_wq); } static int au_hfsn_alloc(struct au_hinode *hinode) { + int err; struct au_hnotify *hn; struct super_block *sb; struct au_branch *br; @@ -51,6 +52,8 @@ static int au_hfsn_alloc(struct au_hinode *hinode) sb = hn->hn_aufs_inode->i_sb; bindex = au_br_index(sb, hinode->hi_id); br = au_sbr(sb, bindex); + AuDebugOn(!br->br_hfsn); + mark = &hn->hn_mark; fsnotify_init_mark(mark, au_hfsn_free_mark); mark->mask = AuHfsnMask; @@ -58,8 +61,12 @@ static int au_hfsn_alloc(struct au_hinode *hinode) * by udba rename or rmdir, aufs assign a new inode to the known * h_inode, so specify 1 to allow dups. */ - return fsnotify_add_mark(mark, br->br_hfsn_group, hinode->hi_inode, + err = fsnotify_add_mark(mark, br->br_hfsn->hfsn_group, hinode->hi_inode, /*mnt*/NULL, /*allow_dups*/1); + /* even if err */ + fsnotify_put_mark(mark); + + return err; } static int au_hfsn_free(struct au_hinode *hinode, struct au_hnotify *hn) @@ -109,8 +116,11 @@ static void au_hfsn_ctl(struct au_hinode *hinode, int do_set) static char *au_hfsn_name(u32 mask) { #ifdef CONFIG_AUFS_DEBUG -#define test_ret(flag) if (mask & flag) \ - return #flag; +#define test_ret(flag) \ + do { \ + if (mask & flag) \ + return #flag; \ + } while (0) test_ret(FS_ACCESS); test_ret(FS_MODIFY); test_ret(FS_ATTRIB); @@ -139,6 +149,14 @@ static char *au_hfsn_name(u32 mask) /* ---------------------------------------------------------------------- */ +static void au_hfsn_free_group(struct fsnotify_group *group) +{ + struct au_br_hfsnotify *hfsn = group->private; + + AuDbg("here\n"); + kfree(hfsn); +} + static int au_hfsn_handle_event(struct fsnotify_group *group, struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, @@ -162,7 +180,7 @@ static int au_hfsn_handle_event(struct fsnotify_group *group, h_dir = event->to_tell; h_inode = event->inode; #ifdef AuDbgHnotify - au_debug(1); + au_debug_on(); if (1 || h_child_qstr.len != sizeof(AUFS_XINO_FNAME) - 1 || strncmp(h_child_qstr.name, AUFS_XINO_FNAME, h_child_qstr.len)) { AuDbg("i%lu, mask 0x%x %s, hcname %.*s, hi%lu\n", @@ -170,7 +188,7 @@ static int au_hfsn_handle_event(struct fsnotify_group *group, AuLNPair(&h_child_qstr), h_inode ? h_inode->i_ino : 0); /* WARN_ON(1); */ } - au_debug(0); + au_debug_off(); #endif AuDebugOn(!inode_mark); @@ -196,22 +214,54 @@ static bool au_hfsn_should_send_event(struct fsnotify_group *group, static struct fsnotify_ops au_hfsn_ops = { .should_send_event = au_hfsn_should_send_event, - .handle_event = au_hfsn_handle_event + .handle_event = au_hfsn_handle_event, + .free_group_priv = au_hfsn_free_group }; /* ---------------------------------------------------------------------- */ static void au_hfsn_fin_br(struct au_branch *br) { - if (br->br_hfsn_group) - fsnotify_put_group(br->br_hfsn_group); + struct au_br_hfsnotify *hfsn; + + hfsn = br->br_hfsn; + if (hfsn) + fsnotify_put_group(hfsn->hfsn_group); } static int au_hfsn_init_br(struct au_branch *br, int perm) { - br->br_hfsn_group = NULL; - br->br_hfsn_ops = au_hfsn_ops; - return 0; + int err; + struct fsnotify_group *group; + struct au_br_hfsnotify *hfsn; + + err = 0; + br->br_hfsn = NULL; + if (!au_br_hnotifyable(perm)) + goto out; + + err = -ENOMEM; + hfsn = kmalloc(sizeof(*hfsn), GFP_NOFS); + if (unlikely(!hfsn)) + goto out; + + err = 0; + group = fsnotify_alloc_group(&au_hfsn_ops); + if (IS_ERR(group)) { + err = PTR_ERR(group); + pr_err("fsnotify_alloc_group() failed, %d\n", err); + goto out_hfsn; + } + + group->private = hfsn; + hfsn->hfsn_group = group; + br->br_hfsn = hfsn; + goto out; /* success */ + +out_hfsn: + kfree(hfsn); +out: + return err; } static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) @@ -219,25 +269,9 @@ static int au_hfsn_reset_br(unsigned int udba, struct au_branch *br, int perm) int err; err = 0; - if (udba != AuOpt_UDBA_HNOTIFY - || !au_br_hnotifyable(perm)) { - au_hfsn_fin_br(br); - br->br_hfsn_group = NULL; - goto out; - } + if (!br->br_hfsn) + err = au_hfsn_init_br(br, perm); - if (br->br_hfsn_group) - goto out; - - br->br_hfsn_group = fsnotify_alloc_group(&br->br_hfsn_ops); - if (IS_ERR(br->br_hfsn_group)) { - err = PTR_ERR(br->br_hfsn_group); - pr_err("fsnotify_alloc_group() failed, %d\n", err); - br->br_hfsn_group = NULL; - } - -out: - AuTraceErr(err); return err; } diff --git a/ubuntu/aufs/hfsplus.c b/ubuntu/aufs/hfsplus.c index 9941f76083c5..c77a2a3c4469 100644 --- a/ubuntu/aufs/hfsplus.c +++ b/ubuntu/aufs/hfsplus.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Junjiro R. Okajima + * Copyright (C) 2010-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -36,7 +36,6 @@ struct file *au_h_open_pre(struct dentry *dentry, aufs_bindex_t bindex) h_dentry = au_h_dptr(dentry, bindex); AuDebugOn(!h_dentry); AuDebugOn(!h_dentry->d_inode); - IMustLock(h_dentry->d_inode); h_file = NULL; if (au_test_hfsplus(h_dentry->d_sb) diff --git a/ubuntu/aufs/hnotify.c b/ubuntu/aufs/hnotify.c index 9a1ac48ac2c4..699c7e8c794b 100644 --- a/ubuntu/aufs/hnotify.c +++ b/ubuntu/aufs/hnotify.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/i_op.c b/ubuntu/aufs/i_op.c index fa6c5f6aaf06..802925b16aa6 100644 --- a/ubuntu/aufs/i_op.c +++ b/ubuntu/aufs/i_op.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -114,7 +114,7 @@ static int aufs_permission(struct inode *inode, int mask) err = 0; bindex = au_ibstart(inode); br = au_sbr(sb, bindex); - err = h_permission(h_inode, mask, br->br_mnt, br->br_perm); + err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); if (write_mask && !err && !special_file(h_inode->i_mode)) { @@ -140,7 +140,7 @@ static int aufs_permission(struct inode *inode, int mask) break; br = au_sbr(sb, bindex); - err = h_permission(h_inode, mask, br->br_mnt, + err = h_permission(h_inode, mask, au_br_mnt(br), br->br_perm); } } @@ -159,19 +159,25 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, struct dentry *ret, *parent; struct inode *inode; struct super_block *sb; - int err, npositive, lc_idx; + int err, npositive; IMustLock(dir); + /* todo: support rcu-walk? */ + ret = ERR_PTR(-ECHILD); + if (flags & LOOKUP_RCU) + goto out; + + ret = ERR_PTR(-ENAMETOOLONG); + if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) + goto out; + sb = dir->i_sb; err = si_read_lock(sb, AuLock_FLUSH | AuLock_NOPLM); ret = ERR_PTR(err); if (unlikely(err)) goto out; - ret = ERR_PTR(-ENAMETOOLONG); - if (unlikely(dentry->d_name.len > AUFS_MAX_NAMELEN)) - goto out_si; err = au_di_init(dentry); ret = ERR_PTR(err); if (unlikely(err)) @@ -186,7 +192,7 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, err = au_digen_test(parent, au_sigen(sb)); if (!err) { npositive = au_lkup_dentry(dentry, au_dbstart(parent), - /*type*/0, flags); + /*type*/0); err = npositive; } di_read_unlock(parent, AuLock_IR); @@ -204,6 +210,13 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, } ret = d_splice_alias(inode, dentry); +#if 0 + if (unlikely(d_need_lookup(dentry))) { + spin_lock(&dentry->d_lock); + dentry->d_flags &= ~DCACHE_NEED_LOOKUP; + spin_unlock(&dentry->d_lock); + } else +#endif if (unlikely(IS_ERR(ret) && inode)) { ii_write_unlock(inode); iput(inode); @@ -213,12 +226,16 @@ static struct dentry *aufs_lookup(struct inode *dir, struct dentry *dentry, out_unlock: di_write_unlock(dentry); if (inode) { - lc_idx = AuLcNonDir_DIINFO; - if (S_ISLNK(inode->i_mode)) - lc_idx = AuLcSymlink_DIINFO; - else if (S_ISDIR(inode->i_mode)) - lc_idx = AuLcDir_DIINFO; - au_rw_class(&au_di(dentry)->di_rwsem, au_lc_key + lc_idx); + /* verbose coding for lock class name */ + if (unlikely(S_ISLNK(inode->i_mode))) + au_rw_class(&au_di(dentry)->di_rwsem, + au_lc_key + AuLcSymlink_DIINFO); + else if (unlikely(S_ISDIR(inode->i_mode))) + au_rw_class(&au_di(dentry)->di_rwsem, + au_lc_key + AuLcDir_DIINFO); + else /* likely */ + au_rw_class(&au_di(dentry)->di_rwsem, + au_lc_key + AuLcNonDir_DIINFO); } out_si: si_read_unlock(sb); @@ -243,16 +260,19 @@ static int au_wr_dir_cpup(struct dentry *dentry, struct dentry *parent, err = 0; if (!au_h_dptr(parent, bcpup)) { - if (bstart < bcpup) + if (bstart > bcpup) + err = au_cpup_dirs(dentry, bcpup); + else if (bstart < bcpup) err = au_cpdown_dirs(dentry, bcpup); else - err = au_cpup_dirs(dentry, bcpup); + BUG(); } if (!err && add_entry) { h_parent = au_h_dptr(parent, bcpup); h_dir = h_parent->d_inode; mutex_lock_nested(&h_dir->i_mutex, AuLsc_I_PARENT); - err = au_lkup_neg(dentry, bcpup); + err = au_lkup_neg(dentry, bcpup, + au_ftest_wrdir(add_entry, TMP_WHENTRY)); /* todo: no unlock here */ mutex_unlock(&h_dir->i_mutex); @@ -282,9 +302,11 @@ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, struct au_wr_dir_args *args) { int err; + unsigned int flags; aufs_bindex_t bcpup, bstart, src_bstart; - const unsigned char add_entry = !!au_ftest_wrdir(args->flags, - ADD_ENTRY); + const unsigned char add_entry + = au_ftest_wrdir(args->flags, ADD_ENTRY) + | au_ftest_wrdir(args->flags, TMP_WHENTRY); struct super_block *sb; struct dentry *parent; struct au_sbinfo *sbinfo; @@ -300,8 +322,10 @@ int au_wr_dir(struct dentry *dentry, struct dentry *src_dentry, if (src_bstart < bstart) bcpup = src_bstart; } else if (add_entry) { - err = AuWbrCreate(sbinfo, dentry, - au_ftest_wrdir(args->flags, ISDIR)); + flags = 0; + if (au_ftest_wrdir(args->flags, ISDIR)) + au_fset_wbr(flags, DIR); + err = AuWbrCreate(sbinfo, dentry, flags); bcpup = err; } @@ -348,6 +372,85 @@ out: /* ---------------------------------------------------------------------- */ +void au_pin_hdir_unlock(struct au_pin *p) +{ + if (p->hdir) + au_hn_imtx_unlock(p->hdir); +} + +static 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 */ + au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); + + err = -EBUSY; + if (unlikely(p->hdir->hi_inode != p->h_parent->d_inode)) + goto out; + + err = 0; + if (p->h_dentry) + err = au_h_verify(p->h_dentry, p->udba, 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; + h_i = h_d[i]->d_inode; + if (h_i) + err = !h_i->i_nlink; + } + +out: + return err; +} + +void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task) +{ +#if defined(CONFIG_DEBUG_MUTEXES) || defined(CONFIG_SMP) + p->hdir->hi_inode->i_mutex.owner = task; +#endif +} + +void au_pin_hdir_acquire_nest(struct au_pin *p) +{ + if (p->hdir) { + mutex_acquire_nest(&p->hdir->hi_inode->i_mutex.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); + mutex_release(&p->hdir->hi_inode->i_mutex.dep_map, 1, _RET_IP_); + } +} + struct dentry *au_pinned_h_parent(struct au_pin *pin) { if (pin && pin->parent) @@ -357,12 +460,13 @@ struct dentry *au_pinned_h_parent(struct au_pin *pin) 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; - au_hn_imtx_unlock(p->hdir); if (!au_ftest_pin(p->flags, DI_LOCKED)) di_read_unlock(p->parent, AuLock_IR); iput(p->hdir->hi_inode); @@ -370,22 +474,21 @@ void au_unpin(struct au_pin *p) 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 dentry *h_dentry, *h_parent; - struct au_branch *br; struct inode *h_dir; err = 0; sb = p->dentry->d_sb; - br = au_sbr(sb, p->bindex); + p->br = au_sbr(sb, p->bindex); if (IS_ROOT(p->dentry)) { if (au_ftest_pin(p->flags, MNT_WRITE)) { - p->h_mnt = br->br_mnt; + 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); @@ -395,16 +498,16 @@ int au_do_pin(struct au_pin *p) goto out; } - h_dentry = NULL; + p->h_dentry = NULL; if (p->bindex <= au_dbend(p->dentry)) - h_dentry = au_h_dptr(p->dentry, p->bindex); + 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; - h_parent = au_h_dptr(p->parent, p->bindex); + p->h_parent = au_h_dptr(p->parent, p->bindex); p->hdir = au_hi(p->parent->d_inode, p->bindex); if (p->hdir) h_dir = p->hdir->hi_inode; @@ -414,7 +517,7 @@ int au_do_pin(struct au_pin *p) * if DI_LOCKED is not set, then p->parent may be different * and h_parent can be NULL. */ - if (unlikely(!p->hdir || !h_dir || !h_parent)) { + 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); @@ -423,33 +526,24 @@ int au_do_pin(struct au_pin *p) goto out_err; } - au_igrab(h_dir); - au_hn_imtx_lock_nested(p->hdir, p->lsc_hi); - - if (unlikely(p->hdir->hi_inode != h_parent->d_inode)) { - err = -EBUSY; - goto out_unpin; - } - if (h_dentry) { - err = au_h_verify(h_dentry, p->udba, h_dir, h_parent, br); - if (unlikely(err)) { - au_fclr_pin(p->flags, MNT_WRITE); - goto out_unpin; - } - } - if (au_ftest_pin(p->flags, MNT_WRITE)) { - p->h_mnt = br->br_mnt; + 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_unpin; + if (!au_ftest_pin(p->flags, DI_LOCKED)) + di_read_unlock(p->parent, AuLock_IR); + dput(p->parent); + p->parent = NULL; + goto out_err; } } - goto out; /* success */ -out_unpin: - au_unpin(p); + au_igrab(h_dir); + err = au_pin_hdir_lock(p); + if (!err) + goto out; /* success */ + out_err: pr_err("err %d\n", err); err = au_busy_or_stale(); @@ -471,6 +565,11 @@ void au_pin_init(struct au_pin *p, struct dentry *dentry, 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, @@ -536,7 +635,6 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, aufs_bindex_t bstart, ibstart; struct dentry *hi_wh, *parent; struct inode *inode; - struct file *h_file; struct au_wr_dir_args wr_dir_args = { .force_btgt = -1, .flags = 0 @@ -576,13 +674,20 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, sz = -1; if ((ia->ia_valid & ATTR_SIZE) && ia->ia_size < i_size_read(a->h_inode)) sz = ia->ia_size; + mutex_unlock(&a->h_inode->i_mutex); - h_file = NULL; hi_wh = NULL; if (au_ftest_icpup(a->flags, DID_CPUP) && d_unlinked(dentry)) { hi_wh = au_hi_wh(inode, a->btgt); if (!hi_wh) { - err = au_sio_cpup_wh(dentry, a->btgt, sz, /*file*/NULL); + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->btgt, + .bsrc = -1, + .len = sz, + .pin = &a->pin + }; + err = au_sio_cpup_wh(&cpg, /*file*/NULL); if (unlikely(err)) goto out_unlock; hi_wh = au_hi_wh(inode, a->btgt); @@ -600,13 +705,15 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, goto out; /* success */ if (!d_unhashed(dentry)) { - h_file = au_h_open_pre(dentry, bstart); - if (IS_ERR(h_file)) { - err = PTR_ERR(h_file); - h_file = NULL; - } else - err = au_sio_cpup_simple(dentry, a->btgt, sz, - AuCpup_DTIME); + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->btgt, + .bsrc = bstart, + .len = sz, + .pin = &a->pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + err = au_sio_cpup_simple(&cpg); if (!err) a->h_path.dentry = au_h_dptr(dentry, a->btgt); } else if (!hi_wh) @@ -615,14 +722,9 @@ static int au_pin_and_icpup(struct dentry *dentry, struct iattr *ia, a->h_path.dentry = hi_wh; /* do not dget here */ out_unlock: - mutex_unlock(&a->h_inode->i_mutex); - au_h_open_post(dentry, bstart, h_file); a->h_inode = a->h_path.dentry->d_inode; - if (!err) { - mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); + if (!err) goto out; /* success */ - } - au_unpin(&a->pin); out_parent: if (parent) { @@ -630,6 +732,8 @@ out_parent: dput(parent); } out: + if (!err) + mutex_lock_nested(&a->h_inode->i_mutex, AuLsc_I_CHILD); return err; } @@ -698,8 +802,7 @@ static int aufs_setattr(struct dentry *dentry, struct iattr *ia) goto out_unlock; } else if ((ia->ia_valid & (ATTR_UID | ATTR_GID)) && (ia->ia_valid & ATTR_CTIME)) { - err = security_path_chown(&a->h_path, vfsub_ia_uid(ia), - vfsub_ia_gid(ia)); + err = security_path_chown(&a->h_path, ia->ia_uid, ia->ia_gid); if (unlikely(err)) goto out_unlock; } @@ -749,8 +852,9 @@ static void au_refresh_iattr(struct inode *inode, struct kstat *st, unsigned int n; inode->i_mode = st->mode; - i_uid_write(inode, st->uid); - i_gid_write(inode, st->gid); + /* don't i_[ug]id_write() here */ + inode->i_uid = st->uid; + inode->i_gid = st->gid; inode->i_atime = st->atime; inode->i_mtime = st->mtime; inode->i_ctime = st->ctime; @@ -760,6 +864,7 @@ static void au_refresh_iattr(struct inode *inode, struct kstat *st, n = inode->i_nlink; n -= nlink; n += st->nlink; + smp_mb(); /* 0 can happen */ set_nlink(inode, n); } diff --git a/ubuntu/aufs/i_op_add.c b/ubuntu/aufs/i_op_add.c index 1c8823dff9d5..d9482a0ec970 100644 --- a/ubuntu/aufs/i_op_add.c +++ b/ubuntu/aufs/i_op_add.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -190,7 +190,7 @@ lock_hdir_lkup_wh(struct dentry *dentry, struct au_dtime *dt, if (dt) { struct path tmp = { .dentry = h_parent, - .mnt = br->br_mnt + .mnt = au_br_mnt(br) }; au_dtime_store(dt, au_pinned_parent(pin), &tmp); } @@ -234,47 +234,55 @@ static int add_simple(struct inode *dir, struct dentry *dentry, int err; aufs_bindex_t bstart; unsigned char created; - struct au_dtime dt; - struct au_pin pin; - struct path h_path; struct dentry *wh_dentry, *parent; struct inode *h_dir; - struct au_wr_dir_args wr_dir_args = { - .force_btgt = -1, - .flags = AuWrDir_ADD_ENTRY - }; + /* to reuduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + struct path h_path; + struct au_wr_dir_args wr_dir_args; + } *a; AuDbg("%.*s\n", AuDLNPair(dentry)); IMustLock(dir); + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + a->wr_dir_args.force_btgt = -1; + a->wr_dir_args.flags = AuWrDir_ADD_ENTRY; + parent = dentry->d_parent; /* dir inode is locked */ err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); if (unlikely(err)) - goto out; + goto out_free; err = au_d_may_add(dentry); if (unlikely(err)) goto out_unlock; di_write_lock_parent(parent); - wh_dentry = lock_hdir_lkup_wh(dentry, &dt, /*src_dentry*/NULL, &pin, - &wr_dir_args); + wh_dentry = lock_hdir_lkup_wh(dentry, &a->dt, /*src_dentry*/NULL, + &a->pin, &a->wr_dir_args); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; bstart = au_dbstart(dentry); - h_path.dentry = au_h_dptr(dentry, bstart); - h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); - h_dir = au_pinned_h_dir(&pin); + a->h_path.dentry = au_h_dptr(dentry, bstart); + a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); + h_dir = au_pinned_h_dir(&a->pin); switch (arg->type) { case Creat: - err = vfsub_create(h_dir, &h_path, arg->u.c.mode, + err = vfsub_create(h_dir, &a->h_path, arg->u.c.mode, arg->u.c.want_excl); break; case Symlink: - err = vfsub_symlink(h_dir, &h_path, arg->u.s.symname); + err = vfsub_symlink(h_dir, &a->h_path, arg->u.s.symname); break; case Mknod: - err = vfsub_mknod(h_dir, &h_path, arg->u.m.mode, arg->u.m.dev); + err = vfsub_mknod(h_dir, &a->h_path, arg->u.m.mode, + arg->u.m.dev); break; default: BUG(); @@ -284,18 +292,18 @@ static int add_simple(struct inode *dir, struct dentry *dentry, err = epilog(dir, bstart, wh_dentry, dentry); /* revert */ - if (unlikely(created && err && h_path.dentry->d_inode)) { + if (unlikely(created && err && a->h_path.dentry->d_inode)) { int rerr; - rerr = vfsub_unlink(h_dir, &h_path, /*force*/0); + rerr = vfsub_unlink(h_dir, &a->h_path, /*force*/0); if (rerr) { AuIOErr("%.*s revert failure(%d, %d)\n", AuDLNPair(dentry), err, rerr); err = -EIO; } - au_dtime_revert(&dt); + au_dtime_revert(&a->dt); } - au_unpin(&pin); + au_unpin(&a->pin); dput(wh_dentry); out_parent: @@ -306,6 +314,8 @@ out_unlock: d_drop(dentry); } aufs_read_unlock(dentry, AuLock_DW); +out_free: + kfree(a); out: return err; } @@ -359,8 +369,14 @@ static int au_cpup_before_link(struct dentry *src_dentry, { int err; struct dentry *h_src_dentry; - struct mutex *h_mtx; - struct file *h_file; + struct au_cp_generic cpg = { + .dentry = src_dentry, + .bdst = a->bdst, + .bsrc = a->bsrc, + .len = -1, + .pin = &a->pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN /* | AuCpup_KEEPLINO */ + }; di_read_lock_parent(a->src_parent, AuLock_IR); err = au_test_and_cpup_dirs(src_dentry, a->bdst); @@ -368,22 +384,13 @@ static int au_cpup_before_link(struct dentry *src_dentry, goto out; h_src_dentry = au_h_dptr(src_dentry, a->bsrc); - h_mtx = &h_src_dentry->d_inode->i_mutex; err = au_pin(&a->pin, src_dentry, a->bdst, au_opt_udba(src_dentry->d_sb), AuPin_DI_LOCKED | AuPin_MNT_WRITE); if (unlikely(err)) goto out; - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - h_file = au_h_open_pre(src_dentry, a->bsrc); - if (IS_ERR(h_file)) { - err = PTR_ERR(h_file); - h_file = NULL; - } else - err = au_sio_cpup_simple(src_dentry, a->bdst, -1, - AuCpup_DTIME /* | AuCpup_KEEPLINO */); - mutex_unlock(h_mtx); - au_h_open_post(src_dentry, a->bsrc, h_file); + + err = au_sio_cpup_simple(&cpg); au_unpin(&a->pin); out: @@ -391,12 +398,14 @@ out: return err; } -static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a) +static int au_cpup_or_link(struct dentry *src_dentry, struct dentry *dentry, + struct au_link_args *a) { int err; unsigned char plink; - struct inode *h_inode, *inode; + aufs_bindex_t bend; struct dentry *h_src_dentry; + struct inode *h_inode, *inode; struct super_block *sb; struct file *h_file; @@ -408,22 +417,38 @@ static int au_cpup_or_link(struct dentry *src_dentry, struct au_link_args *a) h_inode = au_h_iptr(inode, a->bdst); if (!h_inode || !h_inode->i_nlink) { /* copyup src_dentry as the name of dentry. */ - au_set_dbstart(src_dentry, a->bdst); - au_set_h_dptr(src_dentry, a->bdst, dget(a->h_path.dentry)); - h_inode = au_h_dptr(src_dentry, a->bsrc)->d_inode; - mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - h_file = au_h_open_pre(src_dentry, a->bsrc); - if (IS_ERR(h_file)) { + bend = au_dbend(dentry); + if (bend < a->bsrc) + au_set_dbend(dentry, a->bsrc); + au_set_h_dptr(dentry, a->bsrc, + dget(au_h_dptr(src_dentry, a->bsrc))); + dget(a->h_path.dentry); + au_set_h_dptr(dentry, a->bdst, NULL); + dentry->d_inode = src_dentry->d_inode; /* tmp */ + h_file = au_h_open_pre(dentry, a->bsrc); + if (IS_ERR(h_file)) err = PTR_ERR(h_file); - h_file = NULL; - } else - err = au_sio_cpup_single(src_dentry, a->bdst, a->bsrc, - -1, AuCpup_KEEPLINO, - a->parent); - mutex_unlock(&h_inode->i_mutex); - au_h_open_post(src_dentry, a->bsrc, h_file); - au_set_h_dptr(src_dentry, a->bdst, NULL); - au_set_dbstart(src_dentry, a->bsrc); + else { + struct au_cp_generic cpg = { + .dentry = dentry, + .bdst = a->bdst, + .bsrc = -1, + .len = -1, + .pin = &a->pin, + .flags = AuCpup_KEEPLINO + }; + err = au_sio_cpup_simple(&cpg); + au_h_open_post(dentry, a->bsrc, h_file); + if (!err) { + dput(a->h_path.dentry); + a->h_path.dentry = au_h_dptr(dentry, a->bdst); + } else + au_set_h_dptr(dentry, a->bdst, + a->h_path.dentry); + } + dentry->d_inode = NULL; /* restore */ + au_set_h_dptr(dentry, a->bsrc, NULL); + au_set_dbend(dentry, bend); } else { /* the inode of src_dentry already exists on a.bdst branch */ h_src_dentry = d_find_alias(h_inode); @@ -522,7 +547,7 @@ int aufs_link(struct dentry *src_dentry, struct inode *dir, if (au_opt_test(au_mntflags(sb), PLINK)) { if (a->bdst < a->bsrc /* && h_src_dentry->d_sb != a->h_path.dentry->d_sb */) - err = au_cpup_or_link(src_dentry, a); + err = au_cpup_or_link(src_dentry, dentry, a); else err = vfsub_link(h_src_dentry, au_pinned_h_dir(&a->pin), &a->h_path); @@ -601,6 +626,7 @@ out_unlock: out_kfree: kfree(a); out: + AuTraceErr(err); return err; } diff --git a/ubuntu/aufs/i_op_del.c b/ubuntu/aufs/i_op_del.c index 65e4f14f53e9..be506032cd2e 100644 --- a/ubuntu/aufs/i_op_del.c +++ b/ubuntu/aufs/i_op_del.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -68,8 +68,7 @@ int au_wr_dir_need_wh(struct dentry *dentry, int isdir, aufs_bindex_t *bcpup) au_di_cp(tmp, dinfo); au_di_swap(tmp, dinfo); /* returns the number of positive dentries */ - need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0, - /*flags*/0); + need_wh = au_lkup_dentry(dentry, bstart + 1, /*type*/0); au_di_swap(tmp, dinfo); au_rw_write_unlock(&tmp->di_rwsem); au_di_free(tmp); @@ -300,17 +299,25 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry) { int err; aufs_bindex_t bwh, bindex, bstart; - struct au_dtime dt; - struct au_pin pin; - struct path h_path; struct inode *inode, *h_dir; struct dentry *parent, *wh_dentry; + /* to reuduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + struct path h_path; + } *a; IMustLock(dir); + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + err = aufs_read_lock(dentry, AuLock_DW | AuLock_GEN); if (unlikely(err)) - goto out; + goto out_free; err = au_d_hashed_positive(dentry); if (unlikely(err)) goto out_unlock; @@ -325,17 +332,18 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry) bindex = -1; parent = dentry->d_parent; /* dir inode is locked */ di_write_lock_parent(parent); - wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &dt, &pin); + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/0, &bindex, &a->dt, + &a->pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; - h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); - h_path.dentry = au_h_dptr(dentry, bstart); - dget(h_path.dentry); + a->h_path.mnt = au_sbr_mnt(dentry->d_sb, bstart); + a->h_path.dentry = au_h_dptr(dentry, bstart); + dget(a->h_path.dentry); if (bindex == bstart) { - h_dir = au_pinned_h_dir(&pin); - err = vfsub_unlink(h_dir, &h_path, /*force*/0); + h_dir = au_pinned_h_dir(&a->pin); + err = vfsub_unlink(h_dir, &a->h_path, /*force*/0); } else { /* dir inode is locked */ h_dir = wh_dentry->d_parent->d_inode; @@ -349,8 +357,9 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry) /* update target timestamps */ if (bindex == bstart) { - vfsub_update_h_iattr(&h_path, /*did*/NULL); /*ignore*/ - inode->i_ctime = h_path.dentry->d_inode->i_ctime; + vfsub_update_h_iattr(&a->h_path, /*did*/NULL); + /*ignore*/ + inode->i_ctime = a->h_path.dentry->d_inode->i_ctime; } else /* todo: this timestamp may be reverted later */ inode->i_ctime = h_dir->i_ctime; @@ -361,19 +370,22 @@ int aufs_unlink(struct inode *dir, struct dentry *dentry) if (wh_dentry) { int rerr; - rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt); + rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, + &a->dt); if (rerr) err = rerr; } out_unpin: - au_unpin(&pin); + au_unpin(&a->pin); dput(wh_dentry); - dput(h_path.dentry); + dput(a->h_path.dentry); out_parent: di_write_unlock(parent); out_unlock: aufs_read_unlock(dentry, AuLock_DW); +out_free: + kfree(a); out: return err; } @@ -382,17 +394,25 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry) { int err, rmdir_later; aufs_bindex_t bwh, bindex, bstart; - struct au_dtime dt; - struct au_pin pin; struct inode *inode; struct dentry *parent, *wh_dentry, *h_dentry; struct au_whtmp_rmdir *args; + /* to reuduce stack size */ + struct { + struct au_dtime dt; + struct au_pin pin; + } *a; IMustLock(dir); + err = -ENOMEM; + a = kmalloc(sizeof(*a), GFP_NOFS); + if (unlikely(!a)) + goto out; + err = aufs_read_lock(dentry, AuLock_DW | AuLock_FLUSH | AuLock_GEN); if (unlikely(err)) - goto out; + goto out_free; err = au_alive_dir(dentry); if (unlikely(err)) goto out_unlock; @@ -416,7 +436,8 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry) bstart = au_dbstart(dentry); bwh = au_dbwh(dentry); bindex = -1; - wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &dt, &pin); + wh_dentry = lock_hdir_create_wh(dentry, /*isdir*/1, &bindex, &a->dt, + &a->pin); err = PTR_ERR(wh_dentry); if (IS_ERR(wh_dentry)) goto out_parent; @@ -457,13 +478,14 @@ int aufs_rmdir(struct inode *dir, struct dentry *dentry) if (wh_dentry) { int rerr; - rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, &dt); + rerr = do_revert(err, dir, bindex, bwh, wh_dentry, dentry, + &a->dt); if (rerr) err = rerr; } out_unpin: - au_unpin(&pin); + au_unpin(&a->pin); dput(wh_dentry); dput(h_dentry); out_parent: @@ -472,6 +494,8 @@ out_parent: au_whtmp_rmdir_free(args); out_unlock: aufs_read_unlock(dentry, AuLock_DW); +out_free: + kfree(a); out: AuTraceErr(err); return err; diff --git a/ubuntu/aufs/i_op_ren.c b/ubuntu/aufs/i_op_ren.c index 85b5d65d3219..5d8ea76f7c98 100644 --- a/ubuntu/aufs/i_op_ren.c +++ b/ubuntu/aufs/i_op_ren.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -208,32 +208,9 @@ static int au_ren_or_cpup(struct au_ren_args *a) AuDebugOn(au_dbstart(d) != a->btgt); err = vfsub_rename(a->src_h_dir, au_h_dptr(d, a->btgt), a->dst_h_dir, &a->h_path); - } else { - struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; - struct file *h_file; + } else + BUG(); - au_fset_ren(a->flags, CPUP); - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - au_set_dbstart(d, a->btgt); - au_set_h_dptr(d, a->btgt, dget(a->dst_h_dentry)); - h_file = au_h_open_pre(d, a->src_bstart); - if (IS_ERR(h_file)) { - err = PTR_ERR(h_file); - h_file = NULL; - } else - err = au_sio_cpup_single(d, a->btgt, a->src_bstart, -1, - !AuCpup_DTIME, a->dst_parent); - mutex_unlock(h_mtx); - au_h_open_post(d, a->src_bstart, h_file); - if (!err) { - d = a->dst_dentry; - au_set_h_dptr(d, a->btgt, NULL); - au_update_dbstart(d); - } else { - au_set_h_dptr(d, a->btgt, NULL); - au_set_dbstart(d, a->src_bstart); - } - } if (!err && a->h_dst) /* it will be set to dinfo later */ dget(a->h_dst); @@ -334,31 +311,13 @@ static int do_rename(struct au_ren_args *a) d = a->dst_dentry; au_set_h_dptr(d, a->btgt, NULL); - err = au_lkup_neg(d, a->btgt); + err = au_lkup_neg(d, a->btgt, /*wh*/0); if (unlikely(err)) goto out_whtmp; a->dst_h_dentry = au_h_dptr(d, a->btgt); } - /* cpup src */ - if (a->dst_h_dentry->d_inode && a->src_bstart != a->btgt) { - struct mutex *h_mtx = &a->src_h_dentry->d_inode->i_mutex; - struct file *h_file; - - mutex_lock_nested(h_mtx, AuLsc_I_CHILD); - AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); - h_file = au_h_open_pre(a->src_dentry, a->src_bstart); - if (IS_ERR(h_file)) { - err = PTR_ERR(h_file); - h_file = NULL; - } else - err = au_sio_cpup_simple(a->src_dentry, a->btgt, -1, - !AuCpup_DTIME); - mutex_unlock(h_mtx); - au_h_open_post(a->src_dentry, a->src_bstart, h_file); - if (unlikely(err)) - goto out_whtmp; - } + BUG_ON(a->dst_h_dentry->d_inode && a->src_bstart != a->btgt); /* rename by vfs_rename or cpup */ d = a->dst_dentry; @@ -610,13 +569,10 @@ out: */ static void au_ren_unlock(struct au_ren_args *a) { - struct super_block *sb; - - sb = a->dst_dentry->d_sb; - if (au_ftest_ren(a->flags, MNT_WRITE)) - vfsub_mnt_drop_write(a->br->br_mnt); vfsub_unlock_rename(a->src_h_parent, a->src_hdir, a->dst_h_parent, a->dst_hdir); + if (au_ftest_ren(a->flags, MNT_WRITE)) + vfsub_mnt_drop_write(au_br_mnt(a->br)); } static int au_ren_lock(struct au_ren_args *a) @@ -629,6 +585,11 @@ static int au_ren_lock(struct au_ren_args *a) a->src_hdir = au_hi(a->src_dir, a->btgt); a->dst_h_parent = au_h_dptr(a->dst_parent, a->btgt); a->dst_hdir = au_hi(a->dst_dir, a->btgt); + + err = vfsub_mnt_want_write(au_br_mnt(a->br)); + if (unlikely(err)) + goto out; + au_fset_ren(a->flags, MNT_WRITE); a->h_trap = vfsub_lock_rename(a->src_h_parent, a->src_hdir, a->dst_h_parent, a->dst_hdir); udba = au_opt_udba(a->src_dentry->d_sb); @@ -643,18 +604,12 @@ static int au_ren_lock(struct au_ren_args *a) err = au_h_verify(a->dst_h_dentry, udba, a->dst_h_parent->d_inode, a->dst_h_parent, a->br); - if (!err) { - err = vfsub_mnt_want_write(a->br->br_mnt); - if (unlikely(err)) - goto out_unlock; - au_fset_ren(a->flags, MNT_WRITE); + if (!err) goto out; /* success */ - } err = au_busy_or_stale(); - -out_unlock: au_ren_unlock(a); + out: return err; } @@ -924,7 +879,7 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, if (unlikely(err < 0)) goto out_parent; a->br = au_sbr(a->dst_dentry->d_sb, a->btgt); - a->h_path.mnt = a->br->br_mnt; + a->h_path.mnt = au_br_mnt(a->br); /* are they available to be renamed */ err = au_ren_may_dir(a); @@ -962,9 +917,37 @@ int aufs_rename(struct inode *_src_dir, struct dentry *_src_dentry, if (err) au_fset_ren(a->flags, WHSRC); + /* cpup src */ + if (a->src_bstart != a->btgt) { + struct au_pin pin; + + err = au_pin(&pin, a->src_dentry, a->btgt, + au_opt_udba(a->src_dentry->d_sb), + AuPin_DI_LOCKED | AuPin_MNT_WRITE); + if (!err) { + struct au_cp_generic cpg = { + .dentry = a->src_dentry, + .bdst = a->btgt, + .bsrc = a->src_bstart, + .len = -1, + .pin = &pin, + .flags = AuCpup_DTIME | AuCpup_HOPEN + }; + AuDebugOn(au_dbstart(a->src_dentry) != a->src_bstart); + err = au_sio_cpup_simple(&cpg); + au_unpin(&pin); + } + if (unlikely(err)) + goto out_children; + a->src_bstart = a->btgt; + a->src_h_dentry = au_h_dptr(a->src_dentry, a->btgt); + au_fset_ren(a->flags, WHSRC); + } + /* lock them all */ err = au_ren_lock(a); if (unlikely(err)) + /* leave the copied-up one */ goto out_children; if (!au_opt_test(au_mntflags(a->dst_dir->i_sb), UDBA_NONE)) diff --git a/ubuntu/aufs/iinfo.c b/ubuntu/aufs/iinfo.c index d9571c47609f..b82ebbfbb459 100644 --- a/ubuntu/aufs/iinfo.c +++ b/ubuntu/aufs/iinfo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -110,10 +110,22 @@ void au_set_hi_wh(struct inode *inode, aufs_bindex_t bindex, hinode->hi_whdentry = h_wh; } -void au_update_iigen(struct inode *inode) +void au_update_iigen(struct inode *inode, int half) { - atomic_set(&au_ii(inode)->ii_generation, au_sigen(inode->i_sb)); - /* smp_mb(); */ /* atomic_set */ + struct au_iinfo *iinfo; + struct au_iigen *iigen; + unsigned int sigen; + + sigen = au_sigen(inode->i_sb); + iinfo = au_ii(inode); + iigen = &iinfo->ii_generation; + spin_lock(&iinfo->ii_genspin); + iigen->ig_generation = sigen; + if (half) + au_ig_fset(iigen->ig_flags, HALF_REFRESHED); + else + au_ig_fclr(iigen->ig_flags, HALF_REFRESHED); + spin_unlock(&iinfo->ii_genspin); } /* it may be called at remount time, too */ @@ -164,6 +176,7 @@ void au_icntnr_init_once(void *_c) struct au_iinfo *iinfo = &c->iinfo; static struct lock_class_key aufs_ii; + spin_lock_init(&iinfo->ii_genspin); au_rw_init(&iinfo->ii_rwsem); au_rw_class(&iinfo->ii_rwsem, &aufs_ii); inode_init_once(&c->vfs_inode); @@ -186,8 +199,7 @@ int au_iinfo_init(struct inode *inode) for (i = 0; i < nbr; i++) iinfo->ii_hinode[i].hi_id = -1; - atomic_set(&iinfo->ii_generation, au_sigen(sb)); - /* smp_mb(); */ /* atomic_set */ + iinfo->ii_generation.ig_generation = au_sigen(sb); iinfo->ii_bstart = -1; iinfo->ii_bend = -1; iinfo->ii_vdir = NULL; diff --git a/ubuntu/aufs/inode.c b/ubuntu/aufs/inode.c index 5c2e48e599eb..9c86d9832208 100644 --- a/ubuntu/aufs/inode.c +++ b/ubuntu/aufs/inode.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -34,7 +34,7 @@ struct inode *au_igrab(struct inode *inode) static void au_refresh_hinode_attr(struct inode *inode, int do_version) { au_cpup_attr_all(inode, /*force*/0); - au_update_iigen(inode); + au_update_iigen(inode, /*half*/1); if (do_version) inode->i_version++; } @@ -253,6 +253,8 @@ out: static int reval_inode(struct inode *inode, struct dentry *dentry) { int err; + unsigned int gen; + struct au_iigen iigen; aufs_bindex_t bindex, bend; struct inode *h_inode, *h_dinode; @@ -271,12 +273,20 @@ static int reval_inode(struct inode *inode, struct dentry *dentry) bend = au_ibend(inode); for (bindex = au_ibstart(inode); bindex <= bend; bindex++) { h_inode = au_h_iptr(inode, bindex); - if (h_inode && h_inode == h_dinode) { - err = 0; - if (au_iigen_test(inode, au_digen(dentry))) - err = au_refresh_hinode(inode, dentry); + if (!h_inode || h_inode != h_dinode) + continue; + + err = 0; + gen = au_iigen(inode, &iigen); + if (gen == au_digen(dentry) + && !au_ig_ftest(iigen.ig_flags, HALF_REFRESHED)) break; - } + + /* fully refresh inode using dentry */ + err = au_refresh_hinode(inode, dentry); + if (!err) + au_update_iigen(inode, /*half*/0); + break; } if (unlikely(err)) @@ -326,7 +336,7 @@ struct inode *au_new_inode(struct dentry *dentry, int must_new) struct super_block *sb; struct mutex *mtx; ino_t h_ino, ino; - int err, lc_idx; + int err; aufs_bindex_t bstart; sb = dentry->d_sb; @@ -367,12 +377,16 @@ new_ino: AuDbg("%lx, new %d\n", inode->i_state, !!(inode->i_state & I_NEW)); if (inode->i_state & I_NEW) { - lc_idx = AuLcNonDir_IIINFO; - if (S_ISLNK(h_inode->i_mode)) - lc_idx = AuLcSymlink_IIINFO; - else if (S_ISDIR(h_inode->i_mode)) - lc_idx = AuLcDir_IIINFO; - au_rw_class(&au_ii(inode)->ii_rwsem, au_lc_key + lc_idx); + /* verbose coding for lock class name */ + if (unlikely(S_ISLNK(h_inode->i_mode))) + au_rw_class(&au_ii(inode)->ii_rwsem, + au_lc_key + AuLcSymlink_IIINFO); + else if (unlikely(S_ISDIR(h_inode->i_mode))) + au_rw_class(&au_ii(inode)->ii_rwsem, + au_lc_key + AuLcDir_IIINFO); + else /* likely */ + au_rw_class(&au_ii(inode)->ii_rwsem, + au_lc_key + AuLcNonDir_IIINFO); ii_write_lock_new_child(inode); err = set_inode(inode, dentry); diff --git a/ubuntu/aufs/inode.h b/ubuntu/aufs/inode.h index 2b3283f2f228..2a87c7662183 100644 --- a/ubuntu/aufs/inode.h +++ b/ubuntu/aufs/inode.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,9 +51,22 @@ struct au_hinode { struct dentry *hi_whdentry; }; +/* ig_flags */ +#define AuIG_HALF_REFRESHED 1 +#define au_ig_ftest(flags, name) ((flags) & AuIG_##name) +#define au_ig_fset(flags, name) \ + do { (flags) |= AuIG_##name; } while (0) +#define au_ig_fclr(flags, name) \ + do { (flags) &= ~AuIG_##name; } while (0) + +struct au_iigen { + __u32 ig_generation, ig_flags; +}; + struct au_vdir; struct au_iinfo { - atomic_t ii_generation; + spinlock_t ii_genspin; + struct au_iigen ii_generation; struct super_block *ii_hsb1; /* no get/put */ struct au_rwsem ii_rwsem; @@ -88,8 +101,19 @@ struct au_pin { 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_relock(struct au_pin *p); +void au_pin_hdir_set_owner(struct au_pin *p, struct task_struct *task); +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) @@ -131,7 +155,8 @@ extern struct inode_operations aufs_iop, aufs_symlink_iop, aufs_dir_iop; /* au_wr_dir flags */ #define AuWrDir_ADD_ENTRY 1 -#define AuWrDir_ISDIR (1 << 1) +#define AuWrDir_TMP_WHENTRY (1 << 1) +#define AuWrDir_ISDIR (1 << 2) #define au_ftest_wrdir(flags, name) ((flags) & AuWrDir_##name) #define au_fset_wrdir(flags, name) \ do { (flags) |= AuWrDir_##name; } while (0) @@ -202,7 +227,7 @@ unsigned int au_hi_flags(struct inode *inode, int isdir); void au_set_h_iptr(struct inode *inode, aufs_bindex_t bindex, struct inode *h_inode, unsigned int flags); -void au_update_iigen(struct inode *inode); +void au_update_iigen(struct inode *inode, int half); void au_update_ibrange(struct inode *inode, int do_put_zero); void au_icntnr_init_once(void *_c); @@ -310,9 +335,19 @@ static inline void au_icntnr_init(struct au_icntnr *c) #endif } -static inline unsigned int au_iigen(struct inode *inode) +static inline unsigned int au_iigen(struct inode *inode, struct au_iigen *iigen) { - return atomic_read(&au_ii(inode)->ii_generation); + unsigned int gen; + struct au_iinfo *iinfo; + + iinfo = au_ii(inode); + spin_lock(&iinfo->ii_genspin); + if (iigen) + *iigen = iinfo->ii_generation; + gen = iinfo->ii_generation.ig_generation; + spin_unlock(&iinfo->ii_genspin); + + return gen; } /* tiny test for inode number */ @@ -329,7 +364,12 @@ static inline int au_test_higen(struct inode *inode, struct inode *h_inode) static inline void au_iigen_dec(struct inode *inode) { - atomic_dec(&au_ii(inode)->ii_generation); + struct au_iinfo *iinfo; + + iinfo = au_ii(inode); + spin_lock(&iinfo->ii_genspin); + iinfo->ii_generation.ig_generation--; + spin_unlock(&iinfo->ii_genspin); } static inline int au_iigen_test(struct inode *inode, unsigned int sigen) @@ -337,7 +377,7 @@ static inline int au_iigen_test(struct inode *inode, unsigned int sigen) int err; err = 0; - if (unlikely(inode && au_iigen(inode) != sigen)) + if (unlikely(inode && au_iigen(inode, NULL) != sigen)) err = -EIO; return err; diff --git a/ubuntu/aufs/ioctl.c b/ubuntu/aufs/ioctl.c index 30a49dff8214..dcb837fde943 100644 --- a/ubuntu/aufs/ioctl.c +++ b/ubuntu/aufs/ioctl.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/loop.c b/ubuntu/aufs/loop.c index 016276a06249..ccae19c1c1ac 100644 --- a/ubuntu/aufs/loop.c +++ b/ubuntu/aufs/loop.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -51,12 +51,14 @@ int au_test_loopback_kthread(void) { int ret; struct task_struct *tsk = current; + char c, comm[sizeof(tsk->comm)]; ret = 0; if (tsk->flags & PF_KTHREAD) { - const char c = tsk->comm[4]; + get_task_comm(comm, tsk); + c = comm[4]; ret = ('0' <= c && c <= '9' - && !strncmp(tsk->comm, "loop", 4)); + && !strncmp(comm, "loop", 4)); } return ret; diff --git a/ubuntu/aufs/loop.h b/ubuntu/aufs/loop.h index b7af6a73cad8..88d019cc8986 100644 --- a/ubuntu/aufs/loop.h +++ b/ubuntu/aufs/loop.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/module.c b/ubuntu/aufs/module.c index b1147af3c87a..36013eedf839 100644 --- a/ubuntu/aufs/module.c +++ b/ubuntu/aufs/module.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/module.h b/ubuntu/aufs/module.h index fe7dca5a2b01..52bf4729d794 100644 --- a/ubuntu/aufs/module.h +++ b/ubuntu/aufs/module.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/opts.c b/ubuntu/aufs/opts.c index c79a38d30f5b..76a6f801fac0 100644 --- a/ubuntu/aufs/opts.c +++ b/ubuntu/aufs/opts.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -186,17 +186,16 @@ static match_table_t brperm = { {0, NULL} }; -static match_table_t brrattr = { +static match_table_t brattr = { + {AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN}, {AuBrRAttr_WH, AUFS_BRRATTR_WH}, - {0, NULL} -}; - -static match_table_t brwattr = { {AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH}, {0, NULL} }; -#define AuBrStr_LONGEST AUFS_BRPERM_RW "+" AUFS_BRWATTR_NLWH +#define AuBrStr_LONGEST AUFS_BRPERM_RW \ + "+" AUFS_BRATTR_UNPIN \ + "+" AUFS_BRWATTR_NLWH static int br_attr_val(char *str, match_table_t table, substring_t args[]) { @@ -227,7 +226,7 @@ static int br_attr_val(char *str, match_table_t table, substring_t args[]) static int noinline_for_stack br_perm_val(char *perm) { int val; - char *p; + char *p, *q; substring_t args[MAX_OPT_ARGS]; p = strchr(perm, '+'); @@ -244,13 +243,33 @@ static int noinline_for_stack br_perm_val(char *perm) if (!p) goto out; - switch (val) { + p++; + while (1) { + q = strchr(p, '+'); + if (q) + *q = 0; + val |= br_attr_val(p, brattr, args); + if (q) { + *q = '+'; + p = q + 1; + } else + break; + } + switch (val & AuBrPerm_Mask) { case AuBrPerm_RO: case AuBrPerm_RR: - val |= br_attr_val(p + 1, brrattr, args); + if (unlikely(val & AuBrWAttr_NoLinkWH)) { + pr_warn("ignored branch attribute %s\n", + AUFS_BRWATTR_NLWH); + val &= ~AuBrWAttr_NoLinkWH; + } break; case AuBrPerm_RW: - val |= br_attr_val(p + 1, brwattr, args); + if (unlikely(val & AuBrRAttr_WH)) { + pr_warn("ignored branch attribute %s\n", + AUFS_BRRATTR_WH); + val &= ~AuBrRAttr_WH; + } break; } @@ -293,6 +312,7 @@ char *au_optstr_br_perm(int brperm) AuDebugOn(1); } + AppendAttr(AuBrAttr_UNPIN, AUFS_BRATTR_UNPIN); AppendAttr(AuBrRAttr_WH, AUFS_BRRATTR_WH); AppendAttr(AuBrWAttr_NoLinkWH, AUFS_BRWATTR_NLWH); @@ -344,6 +364,8 @@ static match_table_t au_wbr_create_policy = { {AuWbrCreate_MFSRRV, "mfsrr:%d:%d"}, {AuWbrCreate_PMFS, "pmfs"}, {AuWbrCreate_PMFSV, "pmfs:%d"}, + {AuWbrCreate_PMFSRR, "pmfsrr:%d"}, + {AuWbrCreate_PMFSRRV, "pmfsrr:%d:%d"}, {-1, NULL} }; @@ -413,6 +435,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create) create->wbr_create = err; switch (err) { case AuWbrCreate_MFSRRV: + case AuWbrCreate_PMFSRRV: e = au_wbr_mfs_wmark(&args[0], str, create); if (!e) e = au_wbr_mfs_sec(&args[1], str, create); @@ -420,6 +443,7 @@ au_wbr_create_val(char *str, struct au_opt_wbr_create *create) err = e; break; case AuWbrCreate_MFSRR: + case AuWbrCreate_PMFSRR: e = au_wbr_mfs_wmark(&args[0], str, create); if (unlikely(e)) { err = e; @@ -636,6 +660,7 @@ static void dump_opts(struct au_opts *opts) u.create->mfsrr_watermark); break; case AuWbrCreate_MFSRRV: + case AuWbrCreate_PMFSRRV: AuDbg("%llu watermark, %d sec\n", u.create->mfsrr_watermark, u.create->mfs_second); @@ -1175,6 +1200,8 @@ static int au_opt_wbr_create(struct super_block *sb, switch (create->wbr_create) { case AuWbrCreate_MFSRRV: case AuWbrCreate_MFSRR: + case AuWbrCreate_PMFSRR: + case AuWbrCreate_PMFSRRV: sbinfo->si_wbr_mfs.mfsrr_watermark = create->mfsrr_watermark; /*FALLTHROUGH*/ case AuWbrCreate_MFS: @@ -1518,7 +1545,7 @@ int au_opts_verify(struct super_block *sb, unsigned long sb_flags, au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); if (wbr) wbr_wh_write_lock(wbr); - err = au_wh_init(au_h_dptr(root, bindex), br, sb); + err = au_wh_init(br, sb); if (wbr) wbr_wh_write_unlock(wbr); au_hn_imtx_unlock(hdir); diff --git a/ubuntu/aufs/opts.h b/ubuntu/aufs/opts.h index 958f5119b01e..182a46461ea9 100644 --- a/ubuntu/aufs/opts.h +++ b/ubuntu/aufs/opts.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -102,6 +102,8 @@ enum { AuWbrCreate_MFSRRV, /* mfs then rr with seconds */ AuWbrCreate_PMFS, /* parent and mfs */ AuWbrCreate_PMFSV, /* parent and mfs with seconds */ + AuWbrCreate_PMFSRR, /* parent, mfs and round-robin */ + AuWbrCreate_PMFSRRV, /* plus seconds */ AuWbrCreate_Def = AuWbrCreate_TDP }; diff --git a/ubuntu/aufs/plink.c b/ubuntu/aufs/plink.c index b0a880f15534..73fbeaeb8b38 100644 --- a/ubuntu/aufs/plink.c +++ b/ubuntu/aufs/plink.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -119,19 +119,13 @@ int au_plink_maint_enter(struct super_block *sb) /* ---------------------------------------------------------------------- */ -struct pseudo_link { - union { - struct list_head list; - struct rcu_head rcu; - }; - struct inode *inode; -}; - #ifdef CONFIG_AUFS_DEBUG void au_plink_list(struct super_block *sb) { + int i; struct au_sbinfo *sbinfo; - struct list_head *plink_list; + struct hlist_head *plink_hlist; + struct hlist_node *pos; struct pseudo_link *plink; SiMustAnyLock(sb); @@ -140,20 +134,23 @@ void au_plink_list(struct super_block *sb) AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); - plink_list = &sbinfo->si_plink.head; - rcu_read_lock(); - list_for_each_entry_rcu(plink, plink_list, list) - AuDbg("%lu\n", plink->inode->i_ino); - rcu_read_unlock(); + for (i = 0; i < AuPlink_NHASH; i++) { + plink_hlist = &sbinfo->si_plink[i].head; + rcu_read_lock(); + hlist_for_each_entry_rcu(plink, pos, plink_hlist, hlist) + AuDbg("%lu\n", plink->inode->i_ino); + rcu_read_unlock(); + } } #endif /* is the inode pseudo-linked? */ int au_plink_test(struct inode *inode) { - int found; + int found, i; struct au_sbinfo *sbinfo; - struct list_head *plink_list; + struct hlist_head *plink_hlist; + struct hlist_node *pos; struct pseudo_link *plink; sbinfo = au_sbi(inode->i_sb); @@ -162,9 +159,10 @@ int au_plink_test(struct inode *inode) AuDebugOn(au_plink_maint(inode->i_sb, AuLock_NOPLM)); found = 0; - plink_list = &sbinfo->si_plink.head; + i = au_plink_hash(inode->i_ino); + plink_hlist = &sbinfo->si_plink[i].head; rcu_read_lock(); - list_for_each_entry_rcu(plink, plink_list, list) + hlist_for_each_entry_rcu(plink, pos, plink_hlist, hlist) if (plink->inode == inode) { found = 1; break; @@ -260,7 +258,7 @@ static int do_whplink(struct qstr *tgt, struct dentry *h_parent, { int err; struct path h_path = { - .mnt = br->br_mnt + .mnt = au_br_mnt(br) }; struct inode *h_dir; @@ -343,7 +341,7 @@ static int whplink(struct dentry *h_dentry, struct inode *inode, static void do_put_plink(struct pseudo_link *plink, int do_del) { if (do_del) - list_del(&plink->list); + hlist_del(&plink->hlist); iput(plink->inode); kfree(plink); } @@ -366,30 +364,24 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex, { struct super_block *sb; struct au_sbinfo *sbinfo; - struct list_head *plink_list; + struct hlist_head *plink_hlist; + struct hlist_node *pos; struct pseudo_link *plink, *tmp; - int found, err, cnt; + struct au_sphlhead *sphl; + int found, err, cnt, i; sb = inode->i_sb; sbinfo = au_sbi(sb); AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); - cnt = 0; - found = 0; - plink_list = &sbinfo->si_plink.head; - rcu_read_lock(); - list_for_each_entry_rcu(plink, plink_list, list) { - cnt++; - if (plink->inode == inode) { - found = 1; - break; - } - } - rcu_read_unlock(); + found = au_plink_test(inode); if (found) return; + i = au_plink_hash(inode->i_ino); + sphl = sbinfo->si_plink + i; + plink_hlist = &sphl->head; tmp = kmalloc(sizeof(*plink), GFP_NOFS); if (tmp) tmp->inode = au_igrab(inode); @@ -398,20 +390,22 @@ void au_plink_append(struct inode *inode, aufs_bindex_t bindex, goto out; } - spin_lock(&sbinfo->si_plink.spin); - list_for_each_entry(plink, plink_list, list) { + spin_lock(&sphl->spin); + hlist_for_each_entry(plink, pos, plink_hlist, hlist) { if (plink->inode == inode) { found = 1; break; } } if (!found) - list_add_rcu(&tmp->list, plink_list); - spin_unlock(&sbinfo->si_plink.spin); + hlist_add_head_rcu(&tmp->hlist, plink_hlist); + spin_unlock(&sphl->spin); if (!found) { - cnt++; - WARN_ONCE(cnt > AUFS_PLINK_WARN, - "unexpectedly many pseudo links, %d\n", cnt); + cnt = au_sphl_count(sphl); +#define msg "unexpectedly unblanced or too many pseudo-links" + if (cnt > AUFS_PLINK_WARN) + AuWarn1(msg ", %d\n", cnt); +#undef msg err = whplink(h_dentry, inode, bindex, au_sbr(sb, bindex)); } else { do_put_plink(tmp, 0); @@ -422,7 +416,7 @@ out: if (unlikely(err)) { pr_warn("err %d, damaged pseudo link.\n", err); if (tmp) { - au_spl_del_rcu(&tmp->list, &sbinfo->si_plink); + au_sphl_del_rcu(&tmp->hlist, sphl); call_rcu(&tmp->rcu, do_put_plink_rcu); } } @@ -431,9 +425,11 @@ out: /* free all plinks */ void au_plink_put(struct super_block *sb, int verbose) { + int i, warned; struct au_sbinfo *sbinfo; - struct list_head *plink_list; - struct pseudo_link *plink, *tmp; + struct hlist_head *plink_hlist; + struct hlist_node *pos, *tmp; + struct pseudo_link *plink; SiMustWriteLock(sb); @@ -441,12 +437,18 @@ void au_plink_put(struct super_block *sb, int verbose) AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); - plink_list = &sbinfo->si_plink.head; /* no spin_lock since sbinfo is write-locked */ - WARN(verbose && !list_empty(plink_list), "pseudo-link is not flushed"); - list_for_each_entry_safe(plink, tmp, plink_list, list) - do_put_plink(plink, 0); - INIT_LIST_HEAD(plink_list); + warned = 0; + for (i = 0; i < AuPlink_NHASH; i++) { + plink_hlist = &sbinfo->si_plink[i].head; + if (!warned && verbose && !hlist_empty(plink_hlist)) { + pr_warn("pseudo-link is not flushed"); + warned = 1; + } + hlist_for_each_entry_safe(plink, pos, tmp, plink_hlist, hlist) + do_put_plink(plink, 0); + INIT_HLIST_HEAD(plink_hlist); + } } void au_plink_clean(struct super_block *sb, int verbose) @@ -460,15 +462,44 @@ void au_plink_clean(struct super_block *sb, int verbose) aufs_write_unlock(root); } +static int au_plink_do_half_refresh(struct inode *inode, aufs_bindex_t br_id) +{ + int do_put; + aufs_bindex_t bstart, bend, bindex; + + do_put = 0; + bstart = au_ibstart(inode); + bend = au_ibend(inode); + if (bstart >= 0) { + for (bindex = bstart; bindex <= bend; bindex++) { + if (!au_h_iptr(inode, bindex) + || au_ii_br_id(inode, bindex) != br_id) + continue; + au_set_h_iptr(inode, bindex, NULL, 0); + do_put = 1; + break; + } + if (do_put) + for (bindex = bstart; bindex <= bend; bindex++) + if (au_h_iptr(inode, bindex)) { + do_put = 0; + break; + } + } else + do_put = 1; + + return do_put; +} + /* free the plinks on a branch specified by @br_id */ void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) { struct au_sbinfo *sbinfo; - struct list_head *plink_list; - struct pseudo_link *plink, *tmp; + struct hlist_head *plink_hlist; + struct hlist_node *pos, *tmp; + struct pseudo_link *plink; struct inode *inode; - aufs_bindex_t bstart, bend, bindex; - unsigned char do_put; + int i, do_put; SiMustWriteLock(sb); @@ -476,36 +507,17 @@ void au_plink_half_refresh(struct super_block *sb, aufs_bindex_t br_id) AuDebugOn(!au_opt_test(au_mntflags(sb), PLINK)); AuDebugOn(au_plink_maint(sb, AuLock_NOPLM)); - plink_list = &sbinfo->si_plink.head; /* no spin_lock since sbinfo is write-locked */ - list_for_each_entry_safe(plink, tmp, plink_list, list) { - do_put = 0; - inode = au_igrab(plink->inode); - ii_write_lock_child(inode); - bstart = au_ibstart(inode); - bend = au_ibend(inode); - if (bstart >= 0) { - for (bindex = bstart; bindex <= bend; bindex++) { - if (!au_h_iptr(inode, bindex) - || au_ii_br_id(inode, bindex) != br_id) - continue; - au_set_h_iptr(inode, bindex, NULL, 0); - do_put = 1; - break; - } - } else - do_put_plink(plink, 1); - - if (do_put) { - for (bindex = bstart; bindex <= bend; bindex++) - if (au_h_iptr(inode, bindex)) { - do_put = 0; - break; - } + for (i = 0; i < AuPlink_NHASH; i++) { + plink_hlist = &sbinfo->si_plink[i].head; + hlist_for_each_entry_safe(plink, pos, tmp, plink_hlist, hlist) { + inode = au_igrab(plink->inode); + ii_write_lock_child(inode); + do_put = au_plink_do_half_refresh(inode, br_id); if (do_put) do_put_plink(plink, 1); + ii_write_unlock(inode); + iput(inode); } - ii_write_unlock(inode); - iput(inode); } } diff --git a/ubuntu/aufs/poll.c b/ubuntu/aufs/poll.c index 59d1a4b285ea..aa5e2aef483a 100644 --- a/ubuntu/aufs/poll.c +++ b/ubuntu/aufs/poll.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/procfs.c b/ubuntu/aufs/procfs.c index 8169782d5e3a..7201cdf7c377 100644 --- a/ubuntu/aufs/procfs.c +++ b/ubuntu/aufs/procfs.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010-2012 Junjiro R. Okajima + * Copyright (C) 2010-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/rdu.c b/ubuntu/aufs/rdu.c index 24dc75525296..0dce11e8caae 100644 --- a/ubuntu/aufs/rdu.c +++ b/ubuntu/aufs/rdu.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/rwsem.h b/ubuntu/aufs/rwsem.h index d6c1b3767308..a1eb04b7ac1f 100644 --- a/ubuntu/aufs/rwsem.h +++ b/ubuntu/aufs/rwsem.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/sbinfo.c b/ubuntu/aufs/sbinfo.c index 2448efe08014..5e8713aecbc8 100644 --- a/ubuntu/aufs/sbinfo.c +++ b/ubuntu/aufs/sbinfo.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,11 +27,13 @@ */ void au_si_free(struct kobject *kobj) { + int i; struct au_sbinfo *sbinfo; char *locked __maybe_unused; /* debug only */ sbinfo = container_of(kobj, struct au_sbinfo, si_kobj); - AuDebugOn(!list_empty(&sbinfo->si_plink.head)); + for (i = 0; i < AuPlink_NHASH; i++) + AuDebugOn(!hlist_empty(&sbinfo->si_plink[i].head)); AuDebugOn(atomic_read(&sbinfo->si_nowait.nw_len)); au_rw_write_lock(&sbinfo->si_rwsem); @@ -53,7 +55,7 @@ void au_si_free(struct kobject *kobj) int au_si_alloc(struct super_block *sb) { - int err; + int err, i; struct au_sbinfo *sbinfo; static struct lock_class_key aufs_si; @@ -89,6 +91,7 @@ int au_si_alloc(struct super_block *sb) atomic_long_set(&sbinfo->si_nfiles, 0); sbinfo->si_bend = -1; + sbinfo->si_last_br_id = AUFS_BRANCH_MAX / 2; sbinfo->si_wbr_copyup = AuWbrCopyup_Def; sbinfo->si_wbr_create = AuWbrCreate_Def; @@ -97,6 +100,9 @@ int au_si_alloc(struct super_block *sb) sbinfo->si_mntflags = au_opts_plink(AuOpt_Def); + sbinfo->si_xino_jiffy = jiffies; + sbinfo->si_xino_expire + = msecs_to_jiffies(AUFS_XINO_DEF_SEC * MSEC_PER_SEC); mutex_init(&sbinfo->si_xib_mtx); sbinfo->si_xino_brid = -1; /* leave si_xib_last_pindex and si_xib_next_bit */ @@ -106,7 +112,8 @@ int au_si_alloc(struct super_block *sb) sbinfo->si_rdhash = AUFS_RDHASH_DEF; sbinfo->si_dirwh = AUFS_DIRWH_DEF; - au_spl_init(&sbinfo->si_plink); + for (i = 0; i < AuPlink_NHASH; i++) + au_sphl_init(sbinfo->si_plink + i); init_waitqueue_head(&sbinfo->si_plink_wq); spin_lock_init(&sbinfo->si_plink_maint_lock); @@ -157,7 +164,7 @@ unsigned int au_sigen_inc(struct super_block *sb) gen = ++au_sbi(sb)->si_generation; au_update_digen(sb->s_root); - au_update_iigen(sb->s_root->d_inode); + au_update_iigen(sb->s_root->d_inode, /*half*/0); sb->s_root->d_inode->i_version++; return gen; } diff --git a/ubuntu/aufs/spl.h b/ubuntu/aufs/spl.h index 743a3076207c..2d53e8777d32 100644 --- a/ubuntu/aufs/spl.h +++ b/ubuntu/aufs/spl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -58,5 +58,55 @@ static inline void au_spl_del_rcu(struct list_head *list, spin_unlock(&spl->spin); } +/* ---------------------------------------------------------------------- */ + +struct au_sphlhead { + spinlock_t spin; + struct hlist_head head; +}; + +static inline void au_sphl_init(struct au_sphlhead *sphl) +{ + spin_lock_init(&sphl->spin); + INIT_HLIST_HEAD(&sphl->head); +} + +static inline void au_sphl_add(struct hlist_node *hlist, + struct au_sphlhead *sphl) +{ + spin_lock(&sphl->spin); + hlist_add_head(hlist, &sphl->head); + spin_unlock(&sphl->spin); +} + +static inline void au_sphl_del(struct hlist_node *hlist, + struct au_sphlhead *sphl) +{ + spin_lock(&sphl->spin); + hlist_del(hlist); + spin_unlock(&sphl->spin); +} + +static inline void au_sphl_del_rcu(struct hlist_node *hlist, + struct au_sphlhead *sphl) +{ + spin_lock(&sphl->spin); + hlist_del_rcu(hlist); + spin_unlock(&sphl->spin); +} + +static inline unsigned long au_sphl_count(struct au_sphlhead *sphl) +{ + unsigned long cnt; + struct hlist_node *pos; + + cnt = 0; + spin_lock(&sphl->spin); + hlist_for_each(pos, &sphl->head) + cnt++; + spin_unlock(&sphl->spin); + return cnt; +} + #endif /* __KERNEL__ */ #endif /* __AUFS_SPL_H__ */ diff --git a/ubuntu/aufs/super.c b/ubuntu/aufs/super.c index ccf9ac754108..dfbfe03a75ce 100644 --- a/ubuntu/aufs/super.c +++ b/ubuntu/aufs/super.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -104,7 +104,7 @@ static int au_show_brs(struct seq_file *seq, struct super_block *sb) hdp = au_di(sb->s_root)->di_hdentry; for (bindex = 0; !err && bindex <= bend; bindex++) { br = au_sbr(sb, bindex); - path.mnt = br->br_mnt; + path.mnt = au_br_mnt(br); path.dentry = hdp[bindex].hd_dentry; err = au_seq_path(seq, &path); if (err > 0) { @@ -131,14 +131,14 @@ static void au_show_wbr_create(struct seq_file *m, int v, AuRwMustAnyLock(&sbinfo->si_rwsem); - seq_printf(m, ",create="); + seq_puts(m, ",create="); pat = au_optstr_wbr_create(v); switch (v) { case AuWbrCreate_TDP: case AuWbrCreate_RR: case AuWbrCreate_MFS: case AuWbrCreate_PMFS: - seq_printf(m, pat); + seq_puts(m, pat); break; case AuWbrCreate_MFSV: seq_printf(m, /*pat*/"mfs:%lu", @@ -160,6 +160,16 @@ static void au_show_wbr_create(struct seq_file *m, int v, jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) / MSEC_PER_SEC); break; + case AuWbrCreate_PMFSRR: + seq_printf(m, /*pat*/"pmfsrr:%llu", + sbinfo->si_wbr_mfs.mfsrr_watermark); + break; + case AuWbrCreate_PMFSRRV: + seq_printf(m, /*pat*/"pmfsrr:%llu:%lu", + sbinfo->si_wbr_mfs.mfsrr_watermark, + jiffies_to_msecs(sbinfo->si_wbr_mfs.mfs_expire) + / MSEC_PER_SEC); + break; } } @@ -417,6 +427,36 @@ static int aufs_statfs(struct dentry *dentry, struct kstatfs *buf) /* ---------------------------------------------------------------------- */ +static int aufs_sync_fs(struct super_block *sb, int wait) +{ + int err, e; + aufs_bindex_t bend, bindex; + struct au_branch *br; + struct super_block *h_sb; + + err = 0; + si_noflush_read_lock(sb); + bend = au_sbend(sb); + for (bindex = 0; bindex <= bend; bindex++) { + br = au_sbr(sb, bindex); + if (!au_br_writable(br->br_perm)) + continue; + + h_sb = au_sbr_sb(sb, bindex); + if (h_sb->s_op->sync_fs) { + e = h_sb->s_op->sync_fs(h_sb, wait); + if (unlikely(e && !err)) + err = e; + /* go on even if an error happens */ + } + } + si_read_unlock(sb); + + return err; +} + +/* ---------------------------------------------------------------------- */ + /* final actions when unmounting a file system */ static void aufs_put_super(struct super_block *sb) { @@ -627,7 +667,7 @@ static int au_refresh_i(struct super_block *sb) sigen = au_sigen(sb); for (ull = 0; ull < max; ull++) { inode = array[ull]; - if (au_iigen(inode) != sigen) { + if (au_iigen(inode, NULL) != sigen) { ii_write_lock_child(inode); e = au_refresh_hinode_self(inode); ii_write_unlock(inode); @@ -780,6 +820,7 @@ static const struct super_operations aufs_sop = { .show_options = aufs_show_options, .statfs = aufs_statfs, .put_super = aufs_put_super, + .sync_fs = aufs_sync_fs, .remount_fs = aufs_remount_fs }; diff --git a/ubuntu/aufs/super.h b/ubuntu/aufs/super.h index 9967dee0a50e..14b1c079c90c 100644 --- a/ubuntu/aufs/super.h +++ b/ubuntu/aufs/super.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -39,8 +39,15 @@ struct au_wbr_copyup_operations { int (*copyup)(struct dentry *dentry); }; +#define AuWbr_DIR 1 /* target is a dir */ +#define AuWbr_PARENT (1 << 1) /* always require a parent */ + +#define au_ftest_wbr(flags, name) ((flags) & AuWbr_##name) +#define au_fset_wbr(flags, name) { (flags) |= AuWbr_##name; } +#define au_fclr_wbr(flags, name) { (flags) &= ~AuWbr_##name; } + struct au_wbr_create_operations { - int (*create)(struct dentry *dentry, int isdir); + int (*create)(struct dentry *dentry, unsigned int flags); int (*init)(struct super_block *sb); int (*fin)(struct super_block *sb); }; @@ -55,6 +62,20 @@ struct au_wbr_mfs { unsigned long long mfsrr_watermark; }; +struct pseudo_link { + union { + struct hlist_node hlist; + struct rcu_head rcu; + }; + struct inode *inode; +}; + +#define AuPlink_NHASH 100 +static inline int au_plink_hash(ino_t ino) +{ + return ino % AuPlink_NHASH; +} + struct au_branch; struct au_sbinfo { /* nowait tasks in the system-wide workqueue */ @@ -116,6 +137,8 @@ struct au_sbinfo { unsigned long si_xib_last_pindex; int si_xib_next_bit; aufs_bindex_t si_xino_brid; + unsigned long si_xino_jiffy; + unsigned long si_xino_expire; /* reserved for future use */ /* unsigned long long si_xib_limit; */ /* Max xib file size */ @@ -145,7 +168,7 @@ struct au_sbinfo { /* int si_rendir; */ /* pseudo_link list */ - struct au_splhead si_plink; + struct au_sphlhead si_plink[AuPlink_NHASH]; wait_queue_head_t si_plink_wq; spinlock_t si_plink_maint_lock; pid_t si_plink_maint_pid; @@ -158,7 +181,9 @@ struct au_sbinfo { */ struct kobject si_kobj; #ifdef CONFIG_DEBUG_FS - struct dentry *si_dbgaufs, *si_dbgaufs_xib; + struct dentry *si_dbgaufs; + struct dentry *si_dbgaufs_plink; + struct dentry *si_dbgaufs_xib; #ifdef CONFIG_AUFS_EXPORT struct dentry *si_dbgaufs_xigen; #endif @@ -266,16 +291,8 @@ static inline struct au_sbinfo *au_sbi(struct super_block *sb) /* ---------------------------------------------------------------------- */ #ifdef CONFIG_AUFS_EXPORT +int au_test_nfsd(void); void au_export_init(struct super_block *sb); - -static inline int au_test_nfsd(void) -{ - struct task_struct *tsk = current; - - return (tsk->flags & PF_KTHREAD) - && !strcmp(tsk->comm, "nfsd"); -} - void au_xigen_inc(struct inode *inode); int au_xigen_new(struct inode *inode); int au_xigen_set(struct super_block *sb, struct file *base); @@ -288,8 +305,8 @@ static inline int au_busy_or_stale(void) return -ESTALE; } #else -AuStubVoid(au_export_init, struct super_block *sb) AuStubInt0(au_test_nfsd, void) +AuStubVoid(au_export_init, struct super_block *sb) AuStubVoid(au_xigen_inc, struct inode *inode) AuStubInt0(au_xigen_new, struct inode *inode) AuStubInt0(au_xigen_set, struct super_block *sb, struct file *base) @@ -357,6 +374,7 @@ static inline void dbgaufs_si_null(struct au_sbinfo *sbinfo) /* AuRwMustWriteLock(&sbinfo->si_rwsem); */ #ifdef CONFIG_DEBUG_FS sbinfo->si_dbgaufs = NULL; + sbinfo->si_dbgaufs_plink = NULL; sbinfo->si_dbgaufs_xib = NULL; #ifdef CONFIG_AUFS_EXPORT sbinfo->si_dbgaufs_xigen = NULL; diff --git a/ubuntu/aufs/sysaufs.c b/ubuntu/aufs/sysaufs.c index a675324ca6f2..f68e84467e94 100644 --- a/ubuntu/aufs/sysaufs.c +++ b/ubuntu/aufs/sysaufs.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/sysaufs.h b/ubuntu/aufs/sysaufs.h index 5aaff3bb608b..2fc17d9764b0 100644 --- a/ubuntu/aufs/sysaufs.h +++ b/ubuntu/aufs/sysaufs.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/sysfs.c b/ubuntu/aufs/sysfs.c index a4e39f1fce1c..722152bc705e 100644 --- a/ubuntu/aufs/sysfs.c +++ b/ubuntu/aufs/sysfs.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -78,7 +78,7 @@ int sysaufs_si_xi_path(struct seq_file *seq, struct super_block *sb) * unlinked. */ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, - aufs_bindex_t bindex) + aufs_bindex_t bindex, int idx) { int err; struct path path; @@ -92,18 +92,30 @@ static int sysaufs_si_br(struct seq_file *seq, struct super_block *sb, root = sb->s_root; di_read_lock_parent(root, !AuLock_IR); br = au_sbr(sb, bindex); - path.mnt = br->br_mnt; - path.dentry = au_h_dptr(root, bindex); - au_seq_path(seq, &path); - di_read_unlock(root, !AuLock_IR); - perm = au_optstr_br_perm(br->br_perm); - if (perm) { - err = seq_printf(seq, "=%s\n", perm); - kfree(perm); + + switch (idx) { + case AuBrSysfs_BR: + path.mnt = au_br_mnt(br); + path.dentry = au_h_dptr(root, bindex); + au_seq_path(seq, &path); + di_read_unlock(root, !AuLock_IR); + perm = au_optstr_br_perm(br->br_perm); + if (perm) { + err = seq_printf(seq, "=%s\n", perm); + kfree(perm); + if (err == -1) + err = -E2BIG; + } else + err = -ENOMEM; + break; + case AuBrSysfs_BRID: + err = seq_printf(seq, "%d\n", br->br_id); + di_read_unlock(root, !AuLock_IR); if (err == -1) err = -E2BIG; - } else - err = -ENOMEM; + break; + } + return err; } @@ -125,13 +137,15 @@ static struct seq_file *au_seq(char *p, ssize_t len) return seq; } -#define SysaufsBr_PREFIX "br" +#define SysaufsBr_PREFIX "br" +#define SysaufsBrid_PREFIX "brid" /* todo: file size may exceed PAGE_SIZE */ ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, char *buf) { ssize_t err; + int idx; long l; aufs_bindex_t bend; struct au_sbinfo *sbinfo; @@ -173,19 +187,25 @@ ssize_t sysaufs_si_show(struct kobject *kobj, struct attribute *attr, cattr++; } - bend = au_sbend(sb); - if (!strncmp(name, SysaufsBr_PREFIX, sizeof(SysaufsBr_PREFIX) - 1)) { + if (!strncmp(name, SysaufsBrid_PREFIX, + sizeof(SysaufsBrid_PREFIX) - 1)) { + idx = AuBrSysfs_BRID; + name += sizeof(SysaufsBrid_PREFIX) - 1; + } else if (!strncmp(name, SysaufsBr_PREFIX, + sizeof(SysaufsBr_PREFIX) - 1)) { + idx = AuBrSysfs_BR; name += sizeof(SysaufsBr_PREFIX) - 1; - err = kstrtol(name, 10, &l); - if (!err) { - if (l <= bend) - err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l); - else - err = -ENOENT; - } - goto out_seq; + } else + BUG(); + + err = kstrtol(name, 10, &l); + if (!err) { + bend = au_sbend(sb); + if (l <= bend) + err = sysaufs_si_br(seq, sb, (aufs_bindex_t)l, idx); + else + err = -ENOENT; } - BUG(); out_seq: if (!err) { @@ -205,17 +225,26 @@ out: void sysaufs_br_init(struct au_branch *br) { - struct attribute *attr = &br->br_attr; + int i; + struct au_brsysfs *br_sysfs; + struct attribute *attr; - sysfs_attr_init(attr); - attr->name = br->br_name; - attr->mode = S_IRUGO; + br_sysfs = br->br_sysfs; + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + attr = &br_sysfs->attr; + sysfs_attr_init(attr); + attr->name = br_sysfs->name; + attr->mode = S_IRUGO; + br_sysfs++; + } } void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) { struct au_branch *br; struct kobject *kobj; + struct au_brsysfs *br_sysfs; + int i; aufs_bindex_t bend; dbgaufs_brs_del(sb, bindex); @@ -227,16 +256,21 @@ void sysaufs_brs_del(struct super_block *sb, aufs_bindex_t bindex) bend = au_sbend(sb); for (; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); - sysfs_remove_file(kobj, &br->br_attr); + br_sysfs = br->br_sysfs; + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + sysfs_remove_file(kobj, &br_sysfs->attr); + br_sysfs++; + } } } void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) { - int err; + int err, i; aufs_bindex_t bend; struct kobject *kobj; struct au_branch *br; + struct au_brsysfs *br_sysfs; dbgaufs_brs_add(sb, bindex); @@ -247,11 +281,17 @@ void sysaufs_brs_add(struct super_block *sb, aufs_bindex_t bindex) bend = au_sbend(sb); for (; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); - snprintf(br->br_name, sizeof(br->br_name), SysaufsBr_PREFIX - "%d", bindex); - err = sysfs_create_file(kobj, &br->br_attr); - if (unlikely(err)) - pr_warn("failed %s under sysfs(%d)\n", - br->br_name, err); + br_sysfs = br->br_sysfs; + snprintf(br_sysfs[AuBrSysfs_BR].name, sizeof(br_sysfs->name), + SysaufsBr_PREFIX "%d", bindex); + snprintf(br_sysfs[AuBrSysfs_BRID].name, sizeof(br_sysfs->name), + SysaufsBrid_PREFIX "%d", bindex); + for (i = 0; i < ARRAY_SIZE(br->br_sysfs); i++) { + err = sysfs_create_file(kobj, &br_sysfs->attr); + if (unlikely(err)) + pr_warn("failed %s under sysfs(%d)\n", + br_sysfs->name, err); + br_sysfs++; + } } } diff --git a/ubuntu/aufs/sysrq.c b/ubuntu/aufs/sysrq.c index 4872eb3ac44c..86ff12099eab 100644 --- a/ubuntu/aufs/sysrq.c +++ b/ubuntu/aufs/sysrq.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -35,16 +35,18 @@ static void sysrq_sb(struct super_block *sb) plevel = au_plevel; au_plevel = KERN_WARNING; - sbinfo = au_sbi(sb); /* since we define pr_fmt, call printk directly */ +#define pr(str) printk(KERN_WARNING AUFS_NAME ": " str) + + sbinfo = au_sbi(sb); printk(KERN_WARNING "si=%lx\n", sysaufs_si_id(sbinfo)); - printk(KERN_WARNING AUFS_NAME ": superblock\n"); + pr("superblock\n"); au_dpri_sb(sb); #if 0 - printk(KERN_WARNING AUFS_NAME ": root dentry\n"); + pr("root dentry\n"); au_dpri_dentry(sb->s_root); - printk(KERN_WARNING AUFS_NAME ": root inode\n"); + pr("root inode\n"); au_dpri_inode(sb->s_root->d_inode); #endif @@ -72,7 +74,7 @@ static void sysrq_sb(struct super_block *sb) #if 1 { struct inode *i; - printk(KERN_WARNING AUFS_NAME ": isolated inode\n"); + pr("isolated inode\n"); spin_lock(&inode_sb_list_lock); list_for_each_entry(i, &sb->s_inodes, i_sb_list) { spin_lock(&i->i_lock); @@ -83,7 +85,7 @@ static void sysrq_sb(struct super_block *sb) spin_unlock(&inode_sb_list_lock); } #endif - printk(KERN_WARNING AUFS_NAME ": files\n"); + pr("files\n"); lg_global_lock(&files_lglock); do_file_list_for_each_entry(sb, file) { umode_t mode; @@ -92,8 +94,9 @@ static void sysrq_sb(struct super_block *sb) au_dpri_file(file); } while_file_list_for_each_entry; lg_global_unlock(&files_lglock); - printk(KERN_WARNING AUFS_NAME ": done\n"); + pr("done\n"); +#undef pr au_plevel = plevel; } diff --git a/ubuntu/aufs/vdir.c b/ubuntu/aufs/vdir.c index 3f16d5153774..998acffe883e 100644 --- a/ubuntu/aufs/vdir.c +++ b/ubuntu/aufs/vdir.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -522,7 +522,7 @@ static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, AuDebugOn(!au_opt_test(au_mntflags(sb), SHWH)); err = -ENOMEM; - o = p = __getname_gfp(GFP_NOFS); + o = p = (void *)__get_free_page(GFP_NOFS); if (unlikely(!p)) goto out; @@ -542,7 +542,7 @@ static int au_handle_shwh(struct super_block *sb, struct au_vdir *vdir, } } - __putname(o); + free_page((unsigned long)o); out: AuTraceErr(err); diff --git a/ubuntu/aufs/vfsub.c b/ubuntu/aufs/vfsub.c index 050da9beaf2a..6d34b27c2129 100644 --- a/ubuntu/aufs/vfsub.c +++ b/ubuntu/aufs/vfsub.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -540,23 +540,18 @@ int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, { int err; struct inode *h_inode; + struct super_block *h_sb; - h_inode = h_path->dentry->d_inode; if (!h_file) { - err = vfsub_mnt_want_write(h_path->mnt); - if (err) - goto out; - err = inode_permission(h_inode, MAY_WRITE); - if (err) - goto out_mnt; - err = get_write_access(h_inode); - if (err) - goto out_mnt; - err = break_lease(h_inode, O_WRONLY); - if (err) - goto out_inode; + err = vfsub_truncate(h_path, length); + goto out; } + h_inode = h_path->dentry->d_inode; + h_sb = h_inode->i_sb; + lockdep_off(); + sb_start_write(h_sb); + lockdep_on(); err = locks_verify_truncate(h_inode, h_file, length); if (!err) err = security_path_truncate(h_path); @@ -565,13 +560,10 @@ int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, err = do_truncate(h_path->dentry, length, attr, h_file); lockdep_on(); } + lockdep_off(); + sb_end_write(h_sb); + lockdep_on(); -out_inode: - if (!h_file) - put_write_access(h_inode); -out_mnt: - if (!h_file) - vfsub_mnt_drop_write(h_path->mnt); out: return err; } diff --git a/ubuntu/aufs/vfsub.h b/ubuntu/aufs/vfsub.h index 471502116b6e..c5cfd3decbf5 100644 --- a/ubuntu/aufs/vfsub.h +++ b/ubuntu/aufs/vfsub.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -33,6 +33,7 @@ /* copied from linux/fs/internal.h */ /* todo: BAD approach!! */ extern struct lglock vfsmount_lock; +extern void __mnt_drop_write(struct vfsmount *); extern spinlock_t inode_sb_list_lock; /* copied from linux/fs/file_table.c */ @@ -102,19 +103,6 @@ static inline void vfsub_dead_dir(struct inode *inode) /* ---------------------------------------------------------------------- */ -/* cf. i_[ug]id_read() in linux/include/fs.h */ -static inline uid_t vfsub_ia_uid(struct iattr *ia) -{ - return from_kuid(&init_user_ns, ia->ia_uid); -} - -static inline gid_t vfsub_ia_gid(struct iattr *ia) -{ - return from_kgid(&init_user_ns, ia->ia_gid); -} - -/* ---------------------------------------------------------------------- */ - int vfsub_update_h_iattr(struct path *h_path, int *did); struct file *vfsub_dentry_open(struct path *path, int flags); struct file *vfsub_filp_open(const char *path, int oflags, int mode); @@ -155,6 +143,13 @@ static inline void vfsub_mnt_drop_write(struct vfsmount *mnt) lockdep_on(); } +static inline void vfsub_mnt_drop_write_file(struct file *file) +{ + lockdep_off(); + mnt_drop_write_file(file); + lockdep_on(); +} + /* ---------------------------------------------------------------------- */ struct au_hinode; @@ -228,6 +223,16 @@ long vfsub_splice_to(struct file *in, loff_t *ppos, unsigned int flags); long vfsub_splice_from(struct pipe_inode_info *pipe, struct file *out, loff_t *ppos, size_t len, unsigned int flags); + +static inline long vfsub_truncate(struct path *path, loff_t length) +{ + long err; + lockdep_off(); + err = vfs_truncate(path, length); + lockdep_on(); + return err; +} + int vfsub_trunc(struct path *h_path, loff_t length, unsigned int attr, struct file *h_file); int vfsub_fsync(struct file *file, struct path *path, int datasync); diff --git a/ubuntu/aufs/wbr_policy.c b/ubuntu/aufs/wbr_policy.c index 2bf2efaa33b4..638e860a13ca 100644 --- a/ubuntu/aufs/wbr_policy.c +++ b/ubuntu/aufs/wbr_policy.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ int au_cpdown_attr(struct path *h_path, struct dentry *h_src) ia.ia_uid = h_isrc->i_uid; ia.ia_gid = h_isrc->i_gid; sbits = !!(ia.ia_mode & (S_ISUID | S_ISGID)); - au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc); + au_cpup_attr_flags(h_path->dentry->d_inode, h_isrc->i_flags); err = vfsub_sio_notify_change(h_path, &ia); /* is this nfs only? */ @@ -60,13 +60,8 @@ int au_cpdown_attr(struct path *h_path, struct dentry *h_src) #define au_fclr_cpdown(flags, name) \ do { (flags) &= ~AuCpdown_##name; } while (0) -struct au_cpdown_dir_args { - struct dentry *parent; - unsigned int flags; -}; - static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, - struct au_cpdown_dir_args *a) + unsigned int *flags) { int err; struct dentry *opq_dentry; @@ -76,7 +71,7 @@ static int au_cpdown_dir_opq(struct dentry *dentry, aufs_bindex_t bdst, if (IS_ERR(opq_dentry)) goto out; dput(opq_dentry); - au_fset_cpdown(a->flags, DIROPQ); + au_fset_cpdown(*flags, DIROPQ); out: return err; @@ -97,7 +92,7 @@ static int au_cpdown_dir_wh(struct dentry *dentry, struct dentry *h_parent, err = 0; if (h_path.dentry->d_inode) { - h_path.mnt = br->br_mnt; + h_path.mnt = au_br_mnt(br); err = au_wh_unlink_dentry(au_h_iptr(dir, bdst), &h_path, dentry); } @@ -108,6 +103,7 @@ out: } static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, + struct au_pin *pin, struct dentry *h_parent, void *arg) { int err, rerr; @@ -115,7 +111,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, struct path h_path; struct dentry *parent; struct inode *h_dir, *h_inode, *inode, *dir; - struct au_cpdown_dir_args *args = arg; + unsigned int *flags = arg; bstart = au_dbstart(dentry); /* dentry is di-locked */ @@ -125,7 +121,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, AuDebugOn(h_dir != au_h_iptr(dir, bdst)); IMustLock(h_dir); - err = au_lkup_neg(dentry, bdst); + err = au_lkup_neg(dentry, bdst, /*wh*/0); if (unlikely(err < 0)) goto out; h_path.dentry = au_h_dptr(dentry, bdst); @@ -134,19 +130,19 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, S_IRWXU | S_IRUGO | S_IXUGO); if (unlikely(err)) goto out_put; - au_fset_cpdown(args->flags, MADE_DIR); + au_fset_cpdown(*flags, MADE_DIR); bopq = au_dbdiropq(dentry); - au_fclr_cpdown(args->flags, WHED); - au_fclr_cpdown(args->flags, DIROPQ); + au_fclr_cpdown(*flags, WHED); + au_fclr_cpdown(*flags, DIROPQ); if (au_dbwh(dentry) == bdst) - au_fset_cpdown(args->flags, WHED); - if (!au_ftest_cpdown(args->flags, PARENT_OPQ) && bopq <= bdst) - au_fset_cpdown(args->flags, PARENT_OPQ); + au_fset_cpdown(*flags, WHED); + if (!au_ftest_cpdown(*flags, PARENT_OPQ) && bopq <= bdst) + au_fset_cpdown(*flags, PARENT_OPQ); h_inode = h_path.dentry->d_inode; mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); - if (au_ftest_cpdown(args->flags, WHED)) { - err = au_cpdown_dir_opq(dentry, bdst, args); + if (au_ftest_cpdown(*flags, WHED)) { + err = au_cpdown_dir_opq(dentry, bdst, flags); if (unlikely(err)) { mutex_unlock(&h_inode->i_mutex); goto out_dir; @@ -158,7 +154,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, if (unlikely(err)) goto out_opq; - if (au_ftest_cpdown(args->flags, WHED)) { + if (au_ftest_cpdown(*flags, WHED)) { err = au_cpdown_dir_wh(dentry, h_parent, dir, bdst); if (unlikely(err)) goto out_opq; @@ -173,7 +169,7 @@ static int au_cpdown_dir(struct dentry *dentry, aufs_bindex_t bdst, /* revert */ out_opq: - if (au_ftest_cpdown(args->flags, DIROPQ)) { + if (au_ftest_cpdown(*flags, DIROPQ)) { mutex_lock_nested(&h_inode->i_mutex, AuLsc_I_CHILD); rerr = au_diropq_remove(dentry, bdst); mutex_unlock(&h_inode->i_mutex); @@ -185,7 +181,7 @@ out_opq: } } out_dir: - if (au_ftest_cpdown(args->flags, MADE_DIR)) { + if (au_ftest_cpdown(*flags, MADE_DIR)) { rerr = vfsub_sio_rmdir(au_h_iptr(dir, bdst), &h_path); if (unlikely(rerr)) { AuIOErr("failed removing %.*s b%d (%d)\n", @@ -205,13 +201,10 @@ out: int au_cpdown_dirs(struct dentry *dentry, aufs_bindex_t bdst) { int err; - struct au_cpdown_dir_args args = { - .parent = dget_parent(dentry), - .flags = 0 - }; + unsigned int flags; - err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &args); - dput(args.parent); + flags = 0; + err = au_cp_dirs(dentry, bdst, au_cpdown_dir, &flags); return err; } @@ -267,7 +260,8 @@ static int au_wbr_bu(struct super_block *sb, aufs_bindex_t bindex) } /* top down parent */ -static int au_wbr_create_tdp(struct dentry *dentry, int isdir __maybe_unused) +static int au_wbr_create_tdp(struct dentry *dentry, + unsigned int flags __maybe_unused) { int err; aufs_bindex_t bstart, bindex; @@ -356,7 +350,7 @@ static int au_wbr_create_init_rr(struct super_block *sb) return err; } -static int au_wbr_create_rr(struct dentry *dentry, int isdir) +static int au_wbr_create_rr(struct dentry *dentry, unsigned int flags) { int err, nbr; unsigned int u; @@ -373,7 +367,7 @@ static int au_wbr_create_rr(struct dentry *dentry, int isdir) bend = au_sbend(sb); nbr = bend + 1; for (bindex = 0; bindex <= bend; bindex++) { - if (!isdir) { + if (!au_ftest_wbr(flags, DIR)) { err = atomic_dec_return(next) + 1; /* modulo for 0 is meaningless */ if (unlikely(!err)) @@ -400,11 +394,12 @@ out: /* ---------------------------------------------------------------------- */ /* most free space */ -static void au_mfs(struct dentry *dentry) +static void au_mfs(struct dentry *dentry, struct dentry *parent) { struct super_block *sb; struct au_branch *br; struct au_wbr_mfs *mfs; + struct dentry *h_parent; aufs_bindex_t bindex, bend; int err; unsigned long long b, bavail; @@ -424,14 +419,26 @@ static void au_mfs(struct dentry *dentry) MtxMustLock(&mfs->mfs_lock); mfs->mfs_bindex = -EROFS; mfs->mfsrr_bytes = 0; - bend = au_sbend(sb); - for (bindex = 0; bindex <= bend; bindex++) { + if (!parent) { + bindex = 0; + bend = au_sbend(sb); + } else { + bindex = au_dbstart(parent); + bend = au_dbtaildir(parent); + } + + for (; bindex <= bend; bindex++) { + if (parent) { + h_parent = au_h_dptr(parent, bindex); + if (!h_parent || !h_parent->d_inode) + continue; + } br = au_sbr(sb, bindex); if (au_br_rdonly(br)) continue; /* sb->s_root for NFS is unreliable */ - h_path.mnt = br->br_mnt; + h_path.mnt = au_br_mnt(br); h_path.dentry = h_path.mnt->mnt_root; err = vfs_statfs(&h_path, st); if (unlikely(err)) { @@ -456,9 +463,10 @@ static void au_mfs(struct dentry *dentry) kfree(st); } -static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused) +static int au_wbr_create_mfs(struct dentry *dentry, unsigned int flags) { int err; + struct dentry *parent; struct super_block *sb; struct au_wbr_mfs *mfs; @@ -467,14 +475,18 @@ static int au_wbr_create_mfs(struct dentry *dentry, int isdir __maybe_unused) goto out; sb = dentry->d_sb; + parent = NULL; + if (au_ftest_wbr(flags, PARENT)) + parent = dget_parent(dentry); mfs = &au_sbi(sb)->si_wbr_mfs; mutex_lock(&mfs->mfs_lock); if (time_after(jiffies, mfs->mfs_jiffy + mfs->mfs_expire) || mfs->mfs_bindex < 0 || au_br_rdonly(au_sbr(sb, mfs->mfs_bindex))) - au_mfs(dentry); + au_mfs(dentry, parent); mutex_unlock(&mfs->mfs_lock); err = mfs->mfs_bindex; + dput(parent); if (err >= 0) err = au_wbr_nonopq(dentry, err); @@ -505,17 +517,17 @@ static int au_wbr_create_fin_mfs(struct super_block *sb __maybe_unused) /* ---------------------------------------------------------------------- */ /* most free space and then round robin */ -static int au_wbr_create_mfsrr(struct dentry *dentry, int isdir) +static int au_wbr_create_mfsrr(struct dentry *dentry, unsigned int flags) { int err; struct au_wbr_mfs *mfs; - err = au_wbr_create_mfs(dentry, isdir); + err = au_wbr_create_mfs(dentry, flags); if (err >= 0) { mfs = &au_sbi(dentry->d_sb)->si_wbr_mfs; mutex_lock(&mfs->mfs_lock); if (mfs->mfsrr_bytes < mfs->mfsrr_watermark) - err = au_wbr_create_rr(dentry, isdir); + err = au_wbr_create_rr(dentry, flags); mutex_unlock(&mfs->mfs_lock); } @@ -536,7 +548,7 @@ static int au_wbr_create_init_mfsrr(struct super_block *sb) /* ---------------------------------------------------------------------- */ /* top down parent and most free space */ -static int au_wbr_create_pmfs(struct dentry *dentry, int isdir) +static int au_wbr_create_pmfs(struct dentry *dentry, unsigned int flags) { int err, e2; unsigned long long b; @@ -545,7 +557,7 @@ static int au_wbr_create_pmfs(struct dentry *dentry, int isdir) struct dentry *parent, *h_parent; struct au_branch *br; - err = au_wbr_create_tdp(dentry, isdir); + err = au_wbr_create_tdp(dentry, flags); if (unlikely(err < 0)) goto out; parent = dget_parent(dentry); @@ -554,7 +566,7 @@ static int au_wbr_create_pmfs(struct dentry *dentry, int isdir) if (bstart == bend) goto out_parent; /* success */ - e2 = au_wbr_create_mfs(dentry, isdir); + e2 = au_wbr_create_mfs(dentry, flags); if (e2 < 0) goto out_parent; /* success */ @@ -589,12 +601,46 @@ out: /* ---------------------------------------------------------------------- */ +/* + * - top down parent + * - most free space with parent + * - most free space round-robin regardless parent + */ +static int au_wbr_create_pmfsrr(struct dentry *dentry, unsigned int flags) +{ + int err; + unsigned long long watermark; + struct super_block *sb; + struct au_branch *br; + struct au_wbr_mfs *mfs; + + err = au_wbr_create_pmfs(dentry, flags | AuWbr_PARENT); + if (unlikely(err < 0)) + goto out; + + sb = dentry->d_sb; + br = au_sbr(sb, err); + mfs = &au_sbi(sb)->si_wbr_mfs; + mutex_lock(&mfs->mfs_lock); + watermark = mfs->mfsrr_watermark; + mutex_unlock(&mfs->mfs_lock); + if (br->br_wbr->wbr_bytes < watermark) + /* regardless the parent dir */ + err = au_wbr_create_mfsrr(dentry, flags); + +out: + AuDbg("b%d\n", err); + return err; +} + +/* ---------------------------------------------------------------------- */ + /* policies for copyup */ /* top down parent */ static int au_wbr_copyup_tdp(struct dentry *dentry) { - return au_wbr_create_tdp(dentry, /*isdir, anything is ok*/0); + return au_wbr_create_tdp(dentry, /*flags, anything is ok*/0); } /* bottom up parent */ @@ -696,5 +742,15 @@ struct au_wbr_create_operations au_wbr_create_ops[] = { .create = au_wbr_create_pmfs, .init = au_wbr_create_init_mfs, .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSRR] = { + .create = au_wbr_create_pmfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs + }, + [AuWbrCreate_PMFSRRV] = { + .create = au_wbr_create_pmfsrr, + .init = au_wbr_create_init_mfsrr, + .fin = au_wbr_create_fin_mfs } }; diff --git a/ubuntu/aufs/whout.c b/ubuntu/aufs/whout.c index 2e1c33cba444..087f9f9e2207 100644 --- a/ubuntu/aufs/whout.c +++ b/ubuntu/aufs/whout.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -174,7 +174,7 @@ int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br) { int err; struct path h_path = { - .mnt = br->br_mnt + .mnt = au_br_mnt(br) }; struct inode *h_dir; struct dentry *h_parent; @@ -233,7 +233,7 @@ static int unlink_wh_name(struct dentry *h_parent, struct qstr *wh, { int err; struct path h_path = { - .mnt = br->br_mnt + .mnt = au_br_mnt(br) }; err = 0; @@ -263,14 +263,10 @@ static void au_wh_clean(struct inode *h_dir, struct path *whpath, if (!whpath->dentry->d_inode) return; - err = vfsub_mnt_want_write(whpath->mnt); - if (!err) { - if (isdir) - err = vfsub_rmdir(h_dir, whpath); - else - err = vfsub_unlink(h_dir, whpath, /*force*/0); - vfsub_mnt_drop_write(whpath->mnt); - } + if (isdir) + err = vfsub_rmdir(h_dir, whpath); + else + err = vfsub_unlink(h_dir, whpath, /*force*/0); if (unlikely(err)) pr_warn("failed removing %.*s (%d), ignored.\n", AuDLNPair(whpath->dentry), err); @@ -299,11 +295,7 @@ static int au_whdir(struct inode *h_dir, struct path *path) if (au_test_nfs(path->dentry->d_sb)) mode |= S_IXUGO; - err = vfsub_mnt_want_write(path->mnt); - if (!err) { - err = vfsub_mkdir(h_dir, path, mode); - vfsub_mnt_drop_write(path->mnt); - } + err = vfsub_mkdir(h_dir, path, mode); } else if (S_ISDIR(path->dentry->d_inode->i_mode)) err = 0; else @@ -397,13 +389,8 @@ static int au_wh_init_rw(struct dentry *h_root, struct au_wbr *wbr, err = -EEXIST; h_dir = h_root->d_inode; if (!base[AuBrWh_BASE].dentry->d_inode) { - err = vfsub_mnt_want_write(h_path->mnt); - if (!err) { - h_path->dentry = base[AuBrWh_BASE].dentry; - err = vfsub_create(h_dir, h_path, WH_MASK, - /*want_excl*/true); - vfsub_mnt_drop_write(h_path->mnt); - } + h_path->dentry = base[AuBrWh_BASE].dentry; + err = vfsub_create(h_dir, h_path, WH_MASK, /*want_excl*/true); } else if (S_ISREG(base[AuBrWh_BASE].dentry->d_inode->i_mode)) err = 0; else @@ -435,16 +422,14 @@ out: /* * initialize the whiteout base file/dir for @br. */ -int au_wh_init(struct dentry *h_root, struct au_branch *br, - struct super_block *sb) +int au_wh_init(struct au_branch *br, struct super_block *sb) { int err, i; const unsigned char do_plink = !!au_opt_test(au_mntflags(sb), PLINK); - struct path path = { - .mnt = br->br_mnt - }; struct inode *h_dir; + struct path path = br->br_path; + struct dentry *h_root = path.dentry; struct au_wbr *wbr = br->br_wbr; static const struct qstr base_name[] = { [AuBrWh_BASE] = QSTR_INIT(AUFS_BASE_NAME, @@ -558,19 +543,16 @@ static void reinit_br_wh(void *arg) dir = a->sb->s_root->d_inode; hdir = au_hi(dir, bindex); h_root = au_h_dptr(a->sb->s_root, bindex); + AuDebugOn(h_root != au_br_dentry(a->br)); au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); wbr_wh_write_lock(wbr); err = au_h_verify(wbr->wbr_whbase, au_opt_udba(a->sb), hdir->hi_inode, h_root, a->br); if (!err) { - err = vfsub_mnt_want_write(a->br->br_mnt); - if (!err) { - h_path.dentry = wbr->wbr_whbase; - h_path.mnt = a->br->br_mnt; - err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0); - vfsub_mnt_drop_write(a->br->br_mnt); - } + h_path.dentry = wbr->wbr_whbase; + h_path.mnt = au_br_mnt(a->br); + err = vfsub_unlink(hdir->hi_inode, &h_path, /*force*/0); } else { pr_warn("%.*s is moved, ignored\n", AuDLNPair(wbr->wbr_whbase)); @@ -579,7 +561,7 @@ static void reinit_br_wh(void *arg) dput(wbr->wbr_whbase); wbr->wbr_whbase = NULL; if (!err) - err = au_wh_init(h_root, a->br, a->sb); + err = au_wh_init(a->br, a->sb); wbr_wh_write_unlock(wbr); au_hn_imtx_unlock(hdir); di_read_unlock(a->sb->s_root, AuLock_IR); @@ -650,7 +632,7 @@ static int link_or_create_wh(struct super_block *sb, aufs_bindex_t bindex, IMustLock(h_dir); br = au_sbr(sb, bindex); - h_path.mnt = br->br_mnt; + h_path.mnt = au_br_mnt(br); wbr = br->br_wbr; wbr_wh_read_lock(wbr); if (wbr->wbr_whbase) { @@ -699,7 +681,7 @@ static struct dentry *do_diropq(struct dentry *dentry, aufs_bindex_t bindex, } else { struct path tmp = { .dentry = opq_dentry, - .mnt = br->br_mnt + .mnt = au_br_mnt(br) }; err = do_unlink_wh(au_h_iptr(dentry->d_inode, bindex), &tmp); if (!err) @@ -951,7 +933,7 @@ int au_whtmp_rmdir(struct inode *dir, aufs_bindex_t bindex, if (!err) { h_tmp.dentry = wh_dentry; - h_tmp.mnt = br->br_mnt; + h_tmp.mnt = au_br_mnt(br); err = vfsub_rmdir(h_dir, &h_tmp); } @@ -995,21 +977,20 @@ static void call_rmdir_whtmp(void *args) h_parent = dget_parent(a->wh_dentry); h_dir = h_parent->d_inode; hdir = au_hi(a->dir, bindex); + err = vfsub_mnt_want_write(au_br_mnt(a->br)); + if (unlikely(err)) + goto out_mnt; au_hn_imtx_lock_nested(hdir, AuLsc_I_PARENT); err = au_h_verify(a->wh_dentry, au_opt_udba(sb), h_dir, h_parent, a->br); - if (!err) { - err = vfsub_mnt_want_write(a->br->br_mnt); - if (!err) { - err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, - &a->whlist); - vfsub_mnt_drop_write(a->br->br_mnt); - } - } + if (!err) + err = au_whtmp_rmdir(a->dir, bindex, a->wh_dentry, &a->whlist); au_hn_imtx_unlock(hdir); + vfsub_mnt_drop_write(au_br_mnt(a->br)); + +out_mnt: dput(h_parent); ii_write_unlock(a->dir); - out: /* mutex_unlock(&a->dir->i_mutex); */ au_whtmp_rmdir_free(a); diff --git a/ubuntu/aufs/whout.h b/ubuntu/aufs/whout.h index f7c45f0998fa..8508560b4c4f 100644 --- a/ubuntu/aufs/whout.h +++ b/ubuntu/aufs/whout.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -38,8 +38,7 @@ struct dentry *au_whtmp_lkup(struct dentry *h_parent, struct au_branch *br, int au_whtmp_ren(struct dentry *h_dentry, struct au_branch *br); int au_wh_unlink_dentry(struct inode *h_dir, struct path *h_path, struct dentry *dentry); -int au_wh_init(struct dentry *h_parent, struct au_branch *br, - struct super_block *sb); +int au_wh_init(struct au_branch *br, struct super_block *sb); /* diropq flags */ #define AuDiropq_CREATE 1 diff --git a/ubuntu/aufs/wkq.c b/ubuntu/aufs/wkq.c index 07561caeb0f0..bafbedf0f5a5 100644 --- a/ubuntu/aufs/wkq.c +++ b/ubuntu/aufs/wkq.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -62,7 +62,7 @@ static void wkq_func(struct work_struct *wk) /* * Since struct completion is large, try allocating it dynamically. */ -#if defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) +#if 1 /* defined(CONFIG_4KSTACKS) || defined(AuTest4KSTACKS) */ #define AuWkqCompDeclare(name) struct completion *comp = NULL static int au_wkq_comp_alloc(struct au_wkinfo *wkinfo, struct completion **comp) diff --git a/ubuntu/aufs/wkq.h b/ubuntu/aufs/wkq.h index 0e44f73c2a06..c316b7fd6f3c 100644 --- a/ubuntu/aufs/wkq.h +++ b/ubuntu/aufs/wkq.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by diff --git a/ubuntu/aufs/xino.c b/ubuntu/aufs/xino.c index 7ee9d79ec2ea..1579d6bd384f 100644 --- a/ubuntu/aufs/xino.c +++ b/ubuntu/aufs/xino.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2005-2012 Junjiro R. Okajima + * Copyright (C) 2005-2013 Junjiro R. Okajima * * This program, aufs is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -21,6 +21,7 @@ */ #include +#include #include "aufs.h" /* todo: unnecessary to support mmap_sem since kernel-space? */ @@ -243,38 +244,56 @@ static void au_xino_unlock_dir(struct au_xino_lock_dir *ldir) int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) { int err; + unsigned long jiffy; + blkcnt_t blocks; aufs_bindex_t bi, bend; + struct kstatfs *st; struct au_branch *br; struct file *new_xino, *file; struct super_block *h_sb; struct au_xino_lock_dir ldir; + err = -ENOMEM; + st = kzalloc(sizeof(*st), GFP_NOFS); + if (unlikely(!st)) + goto out; + err = -EINVAL; bend = au_sbend(sb); if (unlikely(bindex < 0 || bend < bindex)) - goto out; + goto out_st; br = au_sbr(sb, bindex); file = br->br_xino.xi_file; if (!file) - goto out; + goto out_st; + + err = vfs_statfs(&file->f_path, st); + if (unlikely(err)) + AuErr1("statfs err %d, ignored\n", err); + jiffy = jiffies; + blocks = file->f_dentry->d_inode->i_blocks; + pr_info("begin truncating xino(b%d), ib%llu, %llu/%llu free blks\n", + bindex, (u64)blocks, st->f_bfree, st->f_blocks); au_xino_lock_dir(sb, file, &ldir); /* mnt_want_write() is unnecessary here */ new_xino = au_xino_create2(file, file); au_xino_unlock_dir(&ldir); err = PTR_ERR(new_xino); - if (IS_ERR(new_xino)) - goto out; + if (IS_ERR(new_xino)) { + pr_err("err %d, ignored\n", err); + goto out_st; + } err = 0; fput(file); br->br_xino.xi_file = new_xino; - h_sb = br->br_mnt->mnt_sb; + h_sb = au_br_sb(br); for (bi = 0; bi <= bend; bi++) { if (unlikely(bi == bindex)) continue; br = au_sbr(sb, bi); - if (br->br_mnt->mnt_sb != h_sb) + if (au_br_sb(br) != h_sb) continue; fput(br->br_xino.xi_file); @@ -282,6 +301,18 @@ int au_xino_trunc(struct super_block *sb, aufs_bindex_t bindex) get_file(new_xino); } + err = vfs_statfs(&new_xino->f_path, st); + if (!err) { + pr_info("end truncating xino(b%d), ib%llu, %llu/%llu free blks\n", + bindex, (u64)new_xino->f_dentry->d_inode->i_blocks, + st->f_bfree, st->f_blocks); + if (new_xino->f_dentry->d_inode->i_blocks < blocks) + au_sbi(sb)->si_xino_jiffy = jiffy; + } else + AuErr1("statfs err %d, ignored\n", err); + +out_st: + kfree(st); out: return err; } @@ -309,11 +340,6 @@ static void xino_do_trunc(void *_args) ii_read_lock_parent(dir); bindex = au_br_index(sb, br->br_id); err = au_xino_trunc(sb, bindex); - if (!err - && br->br_xino.xi_file->f_dentry->d_inode->i_blocks - >= br->br_xino_upper) - br->br_xino_upper += AUFS_XINO_TRUNC_STEP; - ii_read_unlock(dir); if (unlikely(err)) pr_warn("err b%d, (%d)\n", bindex, err); @@ -324,13 +350,36 @@ static void xino_do_trunc(void *_args) kfree(args); } +static int xino_trunc_test(struct super_block *sb, struct au_branch *br) +{ + int err; + struct kstatfs st; + struct au_sbinfo *sbinfo; + + /* todo: si_xino_expire and the ratio should be customizable */ + sbinfo = au_sbi(sb); + if (time_before(jiffies, + sbinfo->si_xino_jiffy + sbinfo->si_xino_expire)) + return 0; + + /* truncation border */ + err = vfs_statfs(&br->br_xino.xi_file->f_path, &st); + if (unlikely(err)) { + AuErr1("statfs err %d, ignored\n", err); + return 0; + } + if (div64_u64(st.f_bfree * 100, st.f_blocks) >= AUFS_XINO_DEF_TRUNC) + return 0; + + return 1; +} + static void xino_try_trunc(struct super_block *sb, struct au_branch *br) { struct xino_do_trunc_args *args; int wkq_err; - if (br->br_xino.xi_file->f_dentry->d_inode->i_blocks - < br->br_xino_upper) + if (!xino_trunc_test(sb, br)) return; if (atomic_inc_return(&br->br_xino_running) > 1) @@ -408,7 +457,7 @@ int au_xino_write(struct super_block *sb, aufs_bindex_t bindex, ino_t h_ino, h_ino, ino); if (!err) { if (au_opt_test(mnt_flags, TRUNC_XINO) - && au_test_fs_trunc_xino(br->br_mnt->mnt_sb)) + && au_test_fs_trunc_xino(au_br_sb(br))) xino_try_trunc(sb, br); return 0; /* success */ } @@ -561,7 +610,7 @@ void au_xino_delete_inode(struct inode *inode, const int unlinked) err = au_xino_do_write(xwrite, br->br_xino.xi_file, h_inode->i_ino, /*ino*/0); if (!err && try_trunc - && au_test_fs_trunc_xino(br->br_mnt->mnt_sb)) + && au_test_fs_trunc_xino(au_br_sb(br))) xino_try_trunc(sb, br); } } @@ -768,10 +817,10 @@ int au_xino_br(struct super_block *sb, struct au_branch *br, ino_t h_ino, shared_br = NULL; bend = au_sbend(sb); if (do_test) { - tgt_sb = br->br_mnt->mnt_sb; + tgt_sb = au_br_sb(br); for (bindex = 0; bindex <= bend; bindex++) { b = au_sbr(sb, bindex); - if (tgt_sb == b->br_mnt->mnt_sb) { + if (tgt_sb == au_br_sb(b)) { shared_br = b; break; } @@ -1200,7 +1249,7 @@ struct file *au_xino_def(struct super_block *sb) for (bindex = 0; bindex <= bend; bindex++) { br = au_sbr(sb, bindex); if (au_br_writable(br->br_perm) - && !au_test_fs_bad_xino(br->br_mnt->mnt_sb)) { + && !au_test_fs_bad_xino(au_br_sb(br))) { bwr = bindex; break; } @@ -1211,7 +1260,7 @@ struct file *au_xino_def(struct super_block *sb) page = (void *)__get_free_page(GFP_NOFS); if (unlikely(!page)) goto out; - path.mnt = br->br_mnt; + path.mnt = au_br_mnt(br); path.dentry = au_h_dptr(sb->s_root, bwr); p = d_path(&path, page, PATH_MAX - sizeof(AUFS_XINO_FNAME)); file = (void *)p;