[PATCH 09/12] Unionfs: reorganize file_revalidate for un/locking callers

From: Erez Zadok
Date: Fri Apr 25 2008 - 18:21:30 EST


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-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/