Also clean up deep nesting/indentation. Signed-off-by: Erez Zadok <ezk@xxxxxxxxxxxxx> --- fs/unionfs/commonfops.c | 222 +++++++++++++++++++++++++++++----------------- fs/unionfs/union.h | 1 + 2 files changed, 141 insertions(+), 82 deletions(-) diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c index b7d3e38..2706194 100644 --- a/fs/unionfs/commonfops.c +++ b/fs/unionfs/commonfops.c @@ -299,6 +299,101 @@ out: } /* + * Helper function for unionfs_file_revalidate/locked. + * Expects dentry/parent to be locked already, and revalidated. + */ +static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry, + struct super_block *sb, int sbgen, + int dgen, bool willwrite) +{ + int fgen; + int bstart, bend, orig_brid; + int size; + int err = 0; + + fgen = atomic_read(&UNIONFS_F(file)->generation); + + /* + * There are two cases we are interested in. The first is if the + * generation is lower than the super-block. The second is if + * someone has copied up this file from underneath us, we also need + * to refresh things. + */ + if (d_deleted(dentry) || + (sbgen <= fgen && dbstart(dentry) == fbstart(file))) + goto out_may_copyup; + + /* save orig branch ID */ + orig_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; + + /* First we throw out the existing files. */ + cleanup_file(file); + + /* Now we reopen the file(s) as in unionfs_open. */ + bstart = fbstart(file) = dbstart(dentry); + bend = fbend(file) = dbend(dentry); + + size = sizeof(struct file *) * sbmax(sb); + UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); + if (unlikely(!UNIONFS_F(file)->lower_files)) { + err = -ENOMEM; + goto out; + } + size = sizeof(int) * sbmax(sb); + UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); + if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { + err = -ENOMEM; + goto out; + } + + if (S_ISDIR(dentry->d_inode->i_mode)) { + /* We need to open all the files. */ + err = open_all_files(file); + if (err) + goto out; + } else { + int new_brid; + /* We only open the highest priority branch. */ + err = open_highest_file(file, willwrite); + if (err) + goto out; + new_brid = UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; + if (unlikely(new_brid != orig_brid && sbgen > fgen)) { + /* + * If we re-opened the file on a different branch + * than the original one, and this was due to a new + * branch inserted, then update the mnt counts of + * the old and new branches accordingly. + */ + unionfs_mntget(dentry, bstart); + unionfs_mntput(sb->s_root, + branch_id_to_idx(sb, orig_brid)); + } + } + atomic_set(&UNIONFS_F(file)->generation, + atomic_read(&UNIONFS_I(dentry->d_inode)->generation)); + +out_may_copyup: + /* Copyup on the first write to a file on a readonly branch. */ + if (willwrite && IS_WRITE_FLAG(file->f_flags) && + !IS_WRITE_FLAG(unionfs_lower_file(file)->f_flags) && + is_robranch(dentry)) { + pr_debug("unionfs: do delay copyup of \"%s\"\n", + dentry->d_name.name); + err = do_delayed_copyup(file); + } + +out: + if (err) { + kfree(UNIONFS_F(file)->lower_files); + kfree(UNIONFS_F(file)->saved_branch_ids); + } else { + unionfs_check_file(file); + } + return err; +} + +/* * Revalidate the struct file * @file: file to revalidate * @willwrite: true if caller may cause changes to the file; false otherwise. @@ -308,29 +403,26 @@ int unionfs_file_revalidate(struct file *file, bool willwrite) { struct super_block *sb; struct dentry *dentry; - int sbgen, fgen, dgen; - int bstart, bend; - int size; + int sbgen, dgen; int err = 0; dentry = file->f_path.dentry; - verify_locked(dentry); sb = dentry->d_sb; + verify_locked(dentry); /* * First revalidate the dentry inside struct file, * but not unhashed dentries. */ reval_dentry: - if (unlikely(!d_deleted(dentry) && - !__unionfs_d_revalidate_chain(dentry, NULL, willwrite))) { + if (!d_deleted(dentry) && + !__unionfs_d_revalidate_chain(dentry, NULL, willwrite)) { err = -ESTALE; - goto out_nofree; + goto out; } sbgen = atomic_read(&UNIONFS_SB(sb)->generation); dgen = atomic_read(&UNIONFS_D(dentry)->generation); - fgen = atomic_read(&UNIONFS_F(file)->generation); if (unlikely(sbgen > dgen)) { pr_debug("unionfs: retry dentry revalidation\n"); @@ -339,86 +431,52 @@ reval_dentry: } BUG_ON(sbgen > dgen); - /* - * There are two cases we are interested in. The first is if the - * generation is lower than the super-block. The second is if - * someone has copied up this file from underneath us, we also need - * to refresh things. - */ - if (unlikely(!d_deleted(dentry) && - (sbgen > fgen || dbstart(dentry) != fbstart(file)))) { - /* save orig branch ID */ - int orig_brid = - UNIONFS_F(file)->saved_branch_ids[fbstart(file)]; - - /* First we throw out the existing files. */ - cleanup_file(file); - - /* Now we reopen the file(s) as in unionfs_open. */ - bstart = fbstart(file) = dbstart(dentry); - bend = fbend(file) = dbend(dentry); - - size = sizeof(struct file *) * sbmax(sb); - UNIONFS_F(file)->lower_files = kzalloc(size, GFP_KERNEL); - if (unlikely(!UNIONFS_F(file)->lower_files)) { - err = -ENOMEM; - goto out; - } - size = sizeof(int) * sbmax(sb); - UNIONFS_F(file)->saved_branch_ids = kzalloc(size, GFP_KERNEL); - if (unlikely(!UNIONFS_F(file)->saved_branch_ids)) { - err = -ENOMEM; - goto out; - } + err = __unionfs_file_revalidate(file, dentry, sb, + sbgen, dgen, willwrite); +out: + return err; +} - if (S_ISDIR(dentry->d_inode->i_mode)) { - /* We need to open all the files. */ - err = open_all_files(file); - if (err) - goto out; - } else { - int new_brid; - /* We only open the highest priority branch. */ - err = open_highest_file(file, willwrite); - if (err) - goto out; - new_brid = UNIONFS_F(file)-> - saved_branch_ids[fbstart(file)]; - if (unlikely(new_brid != orig_brid && sbgen > fgen)) { - /* - * If we re-opened the file on a different - * branch than the original one, and this - * was due to a new branch inserted, then - * update the mnt counts of the old and new - * branches accordingly. - */ - unionfs_mntget(dentry, bstart); - unionfs_mntput(sb->s_root, - branch_id_to_idx(sb, orig_brid)); - } - } - atomic_set(&UNIONFS_F(file)->generation, - atomic_read( - &UNIONFS_I(dentry->d_inode)->generation)); +/* same as unionfs_file_revalidate, but parent dentry must be locked too */ +int unionfs_file_revalidate_locked(struct file *file, bool willwrite) +{ + struct super_block *sb; + struct dentry *dentry; + int sbgen, dgen; + int err = 0, valid; + + dentry = file->f_path.dentry; + sb = dentry->d_sb; + verify_locked(dentry); + verify_locked(dentry->d_parent); + + /* first revalidate (locked) parent, then child */ + valid = __unionfs_d_revalidate_chain(dentry->d_parent, NULL, false); + if (unlikely(!valid)) { + err = -ESTALE; /* same as what real_lookup does */ + goto out; } - /* Copyup on the first write to a file on a readonly branch. */ - if (willwrite && IS_WRITE_FLAG(file->f_flags) && - !IS_WRITE_FLAG(unionfs_lower_file(file)->f_flags) && - is_robranch(dentry)) { - pr_debug("unionfs: do delay copyup of \"%s\"\n", - dentry->d_name.name); - err = do_delayed_copyup(file); +reval_dentry: + if (!d_deleted(dentry) && + !__unionfs_d_revalidate_one_locked(dentry, NULL, willwrite)) { + err = -ESTALE; + goto out; } -out: - if (err) { - kfree(UNIONFS_F(file)->lower_files); - kfree(UNIONFS_F(file)->saved_branch_ids); + sbgen = atomic_read(&UNIONFS_SB(sb)->generation); + dgen = atomic_read(&UNIONFS_D(dentry)->generation); + + if (unlikely(sbgen > dgen)) { + pr_debug("unionfs: retry (locked) dentry revalidation\n"); + schedule(); + goto reval_dentry; } -out_nofree: - if (!err) - unionfs_check_file(file); + BUG_ON(sbgen > dgen); + + err = __unionfs_file_revalidate(file, dentry, sb, + sbgen, dgen, willwrite); +out: return err; } diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h index ab877ee..edd5685 100644 --- a/fs/unionfs/union.h +++ b/fs/unionfs/union.h @@ -358,6 +358,7 @@ extern int unionfs_getlk(struct file *file, struct file_lock *fl); /* Common file operations. */ extern int unionfs_file_revalidate(struct file *file, bool willwrite); +extern int unionfs_file_revalidate_locked(struct file *file, bool willwrite); extern int unionfs_open(struct inode *inode, struct file *file); extern int unionfs_file_release(struct inode *inode, struct file *file); extern int unionfs_flush(struct file *file, fl_owner_t id); -- 1.5.2.2 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html