* [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups
@ 2008-04-25 22:18 Erez Zadok
2008-04-25 22:18 ` [PATCH 01/12] Unionfs: minor code cleanups Erez Zadok
` (11 more replies)
0 siblings, 12 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:18 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch
The following is a series of patchsets related to Unionfs. It includes
fixes to a few races discovered since moving to vm_ops->fault, and one small
optimiztion (skip copyup for when a file is opened for writing but no
writing takes place).
These patches were tested (where appropriate) on v2.6.25-4569-gb69d398, MM
(mmotm 2008-04-19-01-17), as well as the backports to
2.6.{25,24,23,22,21,20,19,18,9} on ext2/3/4, xfs (limited testing -- xfs was
oopsing a lot), reiserfs, nfs2/3/4, jffs2, ramfs, tmpfs, cramfs, and
squashfs (where available). Also tested with LTP-full-20080229 and with a
continuous parallel kernel compile (while forcing cache flushing,
manipulating lower branches, etc.). See http://unionfs.filesystems.org/ to
download back-ported unionfs code.
Please pull from the 'master' branch of
git://git.kernel.org/pub/scm/linux/kernel/git/ezk/unionfs.git
to receive the following:
Erez Zadok (12):
Unionfs: minor code cleanups
Unionfs: prevent races in unionfs_fault
Unionfs: copy lower times in fsync/fasync only when needed
Unionfs: lock inode around calls to notify_change()
Unionfs: stop as soon as first writeable branch is found
Unionfs: don't dereference dentry without lower branches in d_release
Unionfs: set append offset correctly for copied-up files
Unionfs: copyup only if file is being written to
Unionfs: reorganize file_revalidate for un/locking callers
Unionfs: maintain one-open-file invariant for non-directories
Unionfs: set lower file to NULL in file_release
Unionfs: lock parent dentry branch config in write
commonfops.c | 248 ++++++++++++++++++++++++++++++++++++++---------------------
copyup.c | 2
dentry.c | 4
file.c | 32 +++----
inode.c | 43 ++++------
mmap.c | 21 +++-
union.h | 2
7 files changed, 216 insertions(+), 136 deletions(-)
Thanks.
---
Erez Zadok
ezk@cs.sunysb.edu
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 01/12] Unionfs: minor code cleanups
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
@ 2008-04-25 22:18 ` Erez Zadok
2008-04-25 22:18 ` [PATCH 02/12] Unionfs: prevent races in unionfs_fault Erez Zadok
` (10 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:18 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/commonfops.c | 2 --
fs/unionfs/file.c | 17 +++++------------
fs/unionfs/inode.c | 39 +++++++++++++++++----------------------
3 files changed, 22 insertions(+), 36 deletions(-)
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 50f4eda..0fc7963 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -250,7 +250,6 @@ static int do_delayed_copyup(struct file *file)
BUG_ON(!S_ISREG(dentry->d_inode->i_mode));
unionfs_check_file(file);
- unionfs_check_dentry(dentry);
for (bindex = bstart - 1; bindex >= 0; bindex--) {
if (!d_deleted(dentry))
err = copyup_file(parent_inode, file, bstart,
@@ -292,7 +291,6 @@ static int do_delayed_copyup(struct file *file)
out:
unionfs_check_file(file);
- unionfs_check_dentry(dentry);
return err;
}
diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
index 0a39387..1fe4c30 100644
--- a/fs/unionfs/file.c
+++ b/fs/unionfs/file.c
@@ -35,14 +35,14 @@ static ssize_t unionfs_read(struct file *file, char __user *buf,
err = vfs_read(lower_file, buf, count, ppos);
/* update our inode atime upon a successful lower read */
if (err >= 0) {
- fsstack_copy_attr_atime(file->f_path.dentry->d_inode,
+ fsstack_copy_attr_atime(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
unionfs_check_file(file);
}
out:
unionfs_unlock_dentry(dentry);
- unionfs_read_unlock(file->f_path.dentry->d_sb);
+ unionfs_read_unlock(dentry->d_sb);
return err;
}
@@ -63,32 +63,25 @@ static ssize_t unionfs_write(struct file *file, const char __user *buf,
err = vfs_write(lower_file, buf, count, ppos);
/* update our inode times+sizes upon a successful lower write */
if (err >= 0) {
- fsstack_copy_inode_size(file->f_path.dentry->d_inode,
+ fsstack_copy_inode_size(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
- fsstack_copy_attr_times(file->f_path.dentry->d_inode,
+ fsstack_copy_attr_times(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
unionfs_check_file(file);
}
out:
unionfs_unlock_dentry(dentry);
- unionfs_read_unlock(file->f_path.dentry->d_sb);
+ unionfs_read_unlock(dentry->d_sb);
return err;
}
-
static int unionfs_file_readdir(struct file *file, void *dirent,
filldir_t filldir)
{
return -ENOTDIR;
}
-int unionfs_readpage_dummy(struct file *file, struct page *page)
-{
- BUG();
- return -EINVAL;
-}
-
static int unionfs_mmap(struct file *file, struct vm_area_struct *vma)
{
int err = 0;
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 0dc07ec..1446124 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -396,28 +396,23 @@ docopyup:
bindex, old_dentry->d_name.name,
old_dentry->d_name.len, NULL,
i_size_read(old_dentry->d_inode));
- if (!err) {
- lower_new_dentry =
- create_parents(dir, new_dentry,
- new_dentry->d_name.name,
- bindex);
- lower_old_dentry =
- unionfs_lower_dentry(old_dentry);
- lower_dir_dentry =
- lock_parent(lower_new_dentry);
- /*
- * see
- * Documentation/filesystems/unionfs/issues.txt
- */
- lockdep_off();
- /* do vfs_link */
- err = vfs_link(lower_old_dentry,
- lower_dir_dentry->d_inode,
- lower_new_dentry);
- lockdep_on();
- unlock_dir(lower_dir_dentry);
- goto check_link;
- }
+ if (err)
+ continue;
+ lower_new_dentry =
+ create_parents(dir, new_dentry,
+ new_dentry->d_name.name,
+ bindex);
+ lower_old_dentry = unionfs_lower_dentry(old_dentry);
+ lower_dir_dentry = lock_parent(lower_new_dentry);
+ /* see Documentation/filesystems/unionfs/issues.txt */
+ lockdep_off();
+ /* do vfs_link */
+ err = vfs_link(lower_old_dentry,
+ lower_dir_dentry->d_inode,
+ lower_new_dentry);
+ lockdep_on();
+ unlock_dir(lower_dir_dentry);
+ goto check_link;
}
goto out;
}
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 02/12] Unionfs: prevent races in unionfs_fault
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
2008-04-25 22:18 ` [PATCH 01/12] Unionfs: minor code cleanups Erez Zadok
@ 2008-04-25 22:18 ` Erez Zadok
2008-04-25 22:18 ` [PATCH 03/12] Unionfs: copy lower times in fsync/fasync only when needed Erez Zadok
` (9 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:18 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
vm_ops->fault may be called in parallel. Because we have to resort to
temporarily changing the vma->vm_file to point to the lower file, a
concurrent invocation of unionfs_fault could see a different value. In this
workaround, we keep a different copy of the vma structure in our stack, so
we never expose a different value of the vma->vm_file called to us, even
temporarily. A better fix (already tested) would be to change the calling
semantics of ->fault to take an explicit file pointer.
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/mmap.c | 21 +++++++++++++--------
1 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/fs/unionfs/mmap.c b/fs/unionfs/mmap.c
index 07db5b0..febde7c 100644
--- a/fs/unionfs/mmap.c
+++ b/fs/unionfs/mmap.c
@@ -40,23 +40,28 @@ static int unionfs_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
int err;
struct file *file, *lower_file;
struct vm_operations_struct *lower_vm_ops;
+ struct vm_area_struct lower_vma;
BUG_ON(!vma);
- file = vma->vm_file;
+ memcpy(&lower_vma, vma, sizeof(struct vm_area_struct));
+ file = lower_vma.vm_file;
lower_vm_ops = UNIONFS_F(file)->lower_vm_ops;
BUG_ON(!lower_vm_ops);
lower_file = unionfs_lower_file(file);
BUG_ON(!lower_file);
/*
- * XXX: we set the vm_file to the lower_file, before calling the
- * lower ->fault op, then we restore the vm_file back to the upper
- * file. Need to change the ->fault prototype to take an explicit
- * struct file, and fix all users accordingly.
+ * XXX: vm_ops->fault may be called in parallel. Because we have to
+ * resort to temporarily changing the vma->vm_file to point to the
+ * lower file, a concurrent invocation of unionfs_fault could see a
+ * different value. In this workaround, we keep a different copy of
+ * the vma structure in our stack, so we never expose a different
+ * value of the vma->vm_file called to us, even temporarily. A
+ * better fix would be to change the calling semantics of ->fault to
+ * take an explicit file pointer.
*/
- vma->vm_file = lower_file;
- err = lower_vm_ops->fault(vma, vmf);
- vma->vm_file = file;
+ lower_vma.vm_file = lower_file;
+ err = lower_vm_ops->fault(&lower_vma, vmf);
return err;
}
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 03/12] Unionfs: copy lower times in fsync/fasync only when needed
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
2008-04-25 22:18 ` [PATCH 01/12] Unionfs: minor code cleanups Erez Zadok
2008-04-25 22:18 ` [PATCH 02/12] Unionfs: prevent races in unionfs_fault Erez Zadok
@ 2008-04-25 22:18 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 04/12] Unionfs: lock inode around calls to notify_change() Erez Zadok
` (8 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:18 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/file.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
index 1fe4c30..f14b38b 100644
--- a/fs/unionfs/file.c
+++ b/fs/unionfs/file.c
@@ -196,13 +196,13 @@ int unionfs_fsync(struct file *file, struct dentry *dentry, int datasync)
err = lower_inode->i_fop->fsync(lower_file,
lower_dentry,
datasync);
+ if (!err && bindex == bstart)
+ fsstack_copy_attr_times(inode, lower_inode);
mutex_unlock(&lower_inode->i_mutex);
if (err)
goto out;
}
- unionfs_copy_attr_times(inode);
-
out:
if (!err)
unionfs_check_file(file);
@@ -244,13 +244,13 @@ int unionfs_fasync(int fd, struct file *file, int flag)
lower_file = unionfs_lower_file_idx(file, bindex);
mutex_lock(&lower_inode->i_mutex);
err = lower_inode->i_fop->fasync(fd, lower_file, flag);
+ if (!err && bindex == bstart)
+ fsstack_copy_attr_times(inode, lower_inode);
mutex_unlock(&lower_inode->i_mutex);
if (err)
goto out;
}
- unionfs_copy_attr_times(inode);
-
out:
if (!err)
unionfs_check_file(file);
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 04/12] Unionfs: lock inode around calls to notify_change()
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (2 preceding siblings ...)
2008-04-25 22:18 ` [PATCH 03/12] Unionfs: copy lower times in fsync/fasync only when needed Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 05/12] Unionfs: stop as soon as first writeable branch is found Erez Zadok
` (7 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/copyup.c | 2 ++
fs/unionfs/inode.c | 2 ++
2 files changed, 4 insertions(+), 0 deletions(-)
diff --git a/fs/unionfs/copyup.c b/fs/unionfs/copyup.c
index f71bddf..6d1e461 100644
--- a/fs/unionfs/copyup.c
+++ b/fs/unionfs/copyup.c
@@ -135,6 +135,7 @@ static int copyup_permissions(struct super_block *sb,
newattrs.ia_valid = ATTR_CTIME | ATTR_ATIME | ATTR_MTIME |
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_FORCE |
ATTR_GID | ATTR_UID;
+ mutex_lock(&new_lower_dentry->d_inode->i_mutex);
err = notify_change(new_lower_dentry, &newattrs);
if (err)
goto out;
@@ -152,6 +153,7 @@ static int copyup_permissions(struct super_block *sb,
}
out:
+ mutex_unlock(&new_lower_dentry->d_inode->i_mutex);
return err;
}
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 1446124..582d08b 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -1040,7 +1040,9 @@ static int unionfs_setattr(struct dentry *dentry, struct iattr *ia)
}
/* notify the (possibly copied-up) lower inode */
+ mutex_lock(&lower_dentry->d_inode->i_mutex);
err = notify_change(lower_dentry, ia);
+ mutex_unlock(&lower_dentry->d_inode->i_mutex);
if (err)
goto out;
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 05/12] Unionfs: stop as soon as first writeable branch is found
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (3 preceding siblings ...)
2008-04-25 22:19 ` [PATCH 04/12] Unionfs: lock inode around calls to notify_change() Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 06/12] Unionfs: don't dereference dentry without lower branches in d_release Erez Zadok
` (6 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/inode.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/fs/unionfs/inode.c b/fs/unionfs/inode.c
index 582d08b..a1d7aaf 100644
--- a/fs/unionfs/inode.c
+++ b/fs/unionfs/inode.c
@@ -128,6 +128,8 @@ begin:
err = check_for_whiteout(dentry, lower_dentry);
if (err)
continue;
+ /* if get here, we can write to the branch */
+ break;
}
/*
* If istart wasn't already branch 0, and we got any error, then try
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 06/12] Unionfs: don't dereference dentry without lower branches in d_release
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (4 preceding siblings ...)
2008-04-25 22:19 ` [PATCH 05/12] Unionfs: stop as soon as first writeable branch is found Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 07/12] Unionfs: set append offset correctly for copied-up files Erez Zadok
` (5 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/dentry.c | 4 +++-
1 files changed, 3 insertions(+), 1 deletions(-)
diff --git a/fs/unionfs/dentry.c b/fs/unionfs/dentry.c
index ee0da4f..e5f894c 100644
--- a/fs/unionfs/dentry.c
+++ b/fs/unionfs/dentry.c
@@ -482,12 +482,14 @@ static void unionfs_d_release(struct dentry *dentry)
int bindex, bstart, bend;
unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_CHILD);
+ if (unlikely(!UNIONFS_D(dentry)))
+ goto out; /* skip if no lower branches */
/* must lock our branch configuration here */
unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
unionfs_check_dentry(dentry);
/* this could be a negative dentry, so check first */
- if (unlikely(!UNIONFS_D(dentry) || dbstart(dentry) < 0)) {
+ if (dbstart(dentry) < 0) {
unionfs_unlock_dentry(dentry);
goto out; /* due to a (normal) failed lookup */
}
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 07/12] Unionfs: set append offset correctly for copied-up files
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (5 preceding siblings ...)
2008-04-25 22:19 ` [PATCH 06/12] Unionfs: don't dereference dentry without lower branches in d_release Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 08/12] Unionfs: copyup only if file is being written to Erez Zadok
` (4 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/commonfops.c | 12 ++++++++++--
1 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 0fc7963..83034c2 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -258,9 +258,13 @@ static int do_delayed_copyup(struct file *file)
else
err = copyup_deleted_file(file, dentry, bstart,
bindex);
-
- if (!err)
+ /* if succeeded, set lower open-file flags and break */
+ if (!err) {
+ struct file *lower_file;
+ lower_file = unionfs_lower_file_idx(file, bindex);
+ lower_file->f_flags = file->f_flags;
break;
+ }
}
if (err || (bstart <= fbstart(file)))
goto out;
@@ -491,6 +495,10 @@ static int __open_file(struct inode *inode, struct file *file)
}
return err;
} else {
+ /*
+ * turn off writeable flags, to force delayed copyup
+ * by caller.
+ */
lower_flags &= ~(OPEN_WRITE_FLAGS);
}
}
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 08/12] Unionfs: copyup only if file is being written to
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (6 preceding siblings ...)
2008-04-25 22:19 ` [PATCH 07/12] Unionfs: set append offset correctly for copied-up files Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 09/12] Unionfs: reorganize file_revalidate for un/locking callers Erez Zadok
` (3 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Before, we used to copyup on an open(2) call which used flags implying
writing (O_RDWR, O_WRONLY, O_APPEND). This meant that a file being opened
for writing, then immediately closed (without actually writing to the file),
will still have been copied up. Now, we don't copyup such files in ->open,
but defer the copyup till later. [Bug #591].
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/commonfops.c | 4 ++--
fs/unionfs/file.c | 1 +
fs/unionfs/union.h | 1 +
3 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 83034c2..b7d3e38 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -635,7 +635,7 @@ int unionfs_file_release(struct inode *inode, struct file *file)
* This is important for open-but-unlinked files, as well as mmap
* support.
*/
- err = unionfs_file_revalidate(file, true);
+ err = unionfs_file_revalidate(file, UNIONFS_F(file)->wrote_to_file);
if (unlikely(err))
goto out;
unionfs_check_file(file);
@@ -819,7 +819,7 @@ int unionfs_flush(struct file *file, fl_owner_t id)
unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
- err = unionfs_file_revalidate(file, true);
+ err = unionfs_file_revalidate(file, UNIONFS_F(file)->wrote_to_file);
if (unlikely(err))
goto out;
unionfs_check_file(file);
diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
index f14b38b..09f594d 100644
--- a/fs/unionfs/file.c
+++ b/fs/unionfs/file.c
@@ -67,6 +67,7 @@ static ssize_t unionfs_write(struct file *file, const char __user *buf,
lower_file->f_path.dentry->d_inode);
fsstack_copy_attr_times(dentry->d_inode,
lower_file->f_path.dentry->d_inode);
+ UNIONFS_F(file)->wrote_to_file = true; /* for delayed copyup */
unionfs_check_file(file);
}
diff --git a/fs/unionfs/union.h b/fs/unionfs/union.h
index 4ca4e76..ab877ee 100644
--- a/fs/unionfs/union.h
+++ b/fs/unionfs/union.h
@@ -91,6 +91,7 @@ struct unionfs_file_info {
struct file **lower_files;
int *saved_branch_ids; /* IDs of branches when file was opened */
struct vm_operations_struct *lower_vm_ops;
+ bool wrote_to_file; /* for delayed copyup */
};
/* unionfs inode data in memory */
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 09/12] Unionfs: reorganize file_revalidate for un/locking callers
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (7 preceding siblings ...)
2008-04-25 22:19 ` [PATCH 08/12] Unionfs: copyup only if file is being written to Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 10/12] Unionfs: maintain one-open-file invariant for non-directories Erez Zadok
` (2 subsequent siblings)
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Also clean up deep nesting/indentation.
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
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
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 10/12] Unionfs: maintain one-open-file invariant for non-directories
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (8 preceding siblings ...)
2008-04-25 22:19 ` [PATCH 09/12] Unionfs: reorganize file_revalidate for un/locking callers Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 11/12] Unionfs: set lower file to NULL in file_release Erez Zadok
2008-04-25 22:19 ` [PATCH 12/12] Unionfs: lock parent dentry branch config in write Erez Zadok
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/commonfops.c | 9 ++++++++-
1 files changed, 8 insertions(+), 1 deletions(-)
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 2706194..82b0eea 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -320,7 +320,9 @@ static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry,
* to refresh things.
*/
if (d_deleted(dentry) ||
- (sbgen <= fgen && dbstart(dentry) == fbstart(file)))
+ (sbgen <= fgen &&
+ dbstart(dentry) == fbstart(file) &&
+ unionfs_lower_file(file)))
goto out_may_copyup;
/* save orig branch ID */
@@ -369,6 +371,8 @@ static int __unionfs_file_revalidate(struct file *file, struct dentry *dentry,
unionfs_mntput(sb->s_root,
branch_id_to_idx(sb, orig_brid));
}
+ /* regular files have only one open lower file */
+ fbend(file) = fbstart(file);
}
atomic_set(&UNIONFS_F(file)->generation,
atomic_read(&UNIONFS_I(dentry->d_inode)->generation));
@@ -381,6 +385,9 @@ out_may_copyup:
pr_debug("unionfs: do delay copyup of \"%s\"\n",
dentry->d_name.name);
err = do_delayed_copyup(file);
+ /* regular files have only one open lower file */
+ if (!err && !S_ISDIR(dentry->d_inode->i_mode))
+ fbend(file) = fbstart(file);
}
out:
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 11/12] Unionfs: set lower file to NULL in file_release
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (9 preceding siblings ...)
2008-04-25 22:19 ` [PATCH 10/12] Unionfs: maintain one-open-file invariant for non-directories Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
2008-04-25 22:19 ` [PATCH 12/12] Unionfs: lock parent dentry branch config in write Erez Zadok
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/commonfops.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/fs/unionfs/commonfops.c b/fs/unionfs/commonfops.c
index 82b0eea..631e081 100644
--- a/fs/unionfs/commonfops.c
+++ b/fs/unionfs/commonfops.c
@@ -717,6 +717,7 @@ int unionfs_file_release(struct inode *inode, struct file *file)
lower_file = unionfs_lower_file_idx(file, bindex);
if (lower_file) {
+ unionfs_set_lower_file_idx(file, bindex, NULL);
fput(lower_file);
branchput(sb, bindex);
}
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 12/12] Unionfs: lock parent dentry branch config in write
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
` (10 preceding siblings ...)
2008-04-25 22:19 ` [PATCH 11/12] Unionfs: set lower file to NULL in file_release Erez Zadok
@ 2008-04-25 22:19 ` Erez Zadok
11 siblings, 0 replies; 13+ messages in thread
From: Erez Zadok @ 2008-04-25 22:19 UTC (permalink / raw)
To: akpm; +Cc: linux-kernel, linux-fsdevel, viro, hch, Erez Zadok
Ensure that branch configuration is available to file_revalidate should a
copyup be required.
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
---
fs/unionfs/file.c | 6 +++++-
1 files changed, 5 insertions(+), 1 deletions(-)
diff --git a/fs/unionfs/file.c b/fs/unionfs/file.c
index 09f594d..9b5fc58 100644
--- a/fs/unionfs/file.c
+++ b/fs/unionfs/file.c
@@ -55,7 +55,9 @@ static ssize_t unionfs_write(struct file *file, const char __user *buf,
unionfs_read_lock(dentry->d_sb, UNIONFS_SMUTEX_PARENT);
unionfs_lock_dentry(dentry, UNIONFS_DMUTEX_CHILD);
- err = unionfs_file_revalidate(file, true);
+ if (dentry != dentry->d_parent)
+ unionfs_lock_dentry(dentry->d_parent, UNIONFS_DMUTEX_PARENT);
+ err = unionfs_file_revalidate_locked(file, true);
if (unlikely(err))
goto out;
@@ -72,6 +74,8 @@ static ssize_t unionfs_write(struct file *file, const char __user *buf,
}
out:
+ if (dentry != dentry->d_parent)
+ unionfs_unlock_dentry(dentry->d_parent);
unionfs_unlock_dentry(dentry);
unionfs_read_unlock(dentry->d_sb);
return err;
--
1.5.2.2
^ permalink raw reply related [flat|nested] 13+ messages in thread
end of thread, other threads:[~2008-04-25 22:20 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-04-25 22:18 [GIT PULL -mm] 00/12 Unionfs updates/fixes/cleanups Erez Zadok
2008-04-25 22:18 ` [PATCH 01/12] Unionfs: minor code cleanups Erez Zadok
2008-04-25 22:18 ` [PATCH 02/12] Unionfs: prevent races in unionfs_fault Erez Zadok
2008-04-25 22:18 ` [PATCH 03/12] Unionfs: copy lower times in fsync/fasync only when needed Erez Zadok
2008-04-25 22:19 ` [PATCH 04/12] Unionfs: lock inode around calls to notify_change() Erez Zadok
2008-04-25 22:19 ` [PATCH 05/12] Unionfs: stop as soon as first writeable branch is found Erez Zadok
2008-04-25 22:19 ` [PATCH 06/12] Unionfs: don't dereference dentry without lower branches in d_release Erez Zadok
2008-04-25 22:19 ` [PATCH 07/12] Unionfs: set append offset correctly for copied-up files Erez Zadok
2008-04-25 22:19 ` [PATCH 08/12] Unionfs: copyup only if file is being written to Erez Zadok
2008-04-25 22:19 ` [PATCH 09/12] Unionfs: reorganize file_revalidate for un/locking callers Erez Zadok
2008-04-25 22:19 ` [PATCH 10/12] Unionfs: maintain one-open-file invariant for non-directories Erez Zadok
2008-04-25 22:19 ` [PATCH 11/12] Unionfs: set lower file to NULL in file_release Erez Zadok
2008-04-25 22:19 ` [PATCH 12/12] Unionfs: lock parent dentry branch config in write Erez Zadok
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).