summaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2014-08-07 09:12:31 -0400
committerAl Viro <viro@zeniv.linux.org.uk>2014-08-07 14:40:09 -0400
commit3064c3563ba4c23e2c7a47254ec056ed9ba0098a (patch)
tree806bc1df85e925686cc1075b659a6d6fe3e14aec /kernel
parent8fa1f1c2bd86007beb4a4845e6087ac4a704dc80 (diff)
downloadlinux-3064c3563ba4c23e2c7a47254ec056ed9ba0098a.tar.gz
linux-3064c3563ba4c23e2c7a47254ec056ed9ba0098a.tar.bz2
linux-3064c3563ba4c23e2c7a47254ec056ed9ba0098a.zip
death to mnt_pinned
Rather than playing silly buggers with vfsmount refcounts, just have acct_on() ask fs/namespace.c for internal clone of file->f_path.mnt and replace it with said clone. Then attach the pin to original vfsmount. Voila - the clone will be alive until the file gets closed, making sure that underlying superblock remains active, etc., and we can drop the original vfsmount, so that it's not kept busy. If the file lives until the final mntput of the original vfsmount, we'll notice that there's an fs_pin (one in bsd_acct_struct that holds that file) and mnt_pin_kill() will take it out. Since ->kill() is synchronous, we won't proceed past that point until these files are closed (and private clones of our vfsmount are gone), so we get the same ordering warranties we used to get. mnt_pin()/mnt_unpin()/->mnt_pinned is gone now, and good riddance - it never became usable outside of kernel/acct.c (and racy wrt umount even there). Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/acct.c24
1 files changed, 19 insertions, 5 deletions
diff --git a/kernel/acct.c b/kernel/acct.c
index a7993a6cb604..2e6cf818021d 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -154,7 +154,6 @@ static void close_work(struct work_struct *work)
{
struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work);
struct file *file = acct->file;
- mnt_unpin(file->f_path.mnt);
if (file->f_op->flush)
file->f_op->flush(file, NULL);
__fput_sync(file);
@@ -196,9 +195,10 @@ static void acct_pin_kill(struct fs_pin *pin)
static int acct_on(struct filename *pathname)
{
struct file *file;
- struct vfsmount *mnt;
+ struct vfsmount *mnt, *internal;
struct pid_namespace *ns = task_active_pid_ns(current);
struct bsd_acct_struct *acct, *old;
+ int err;
acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL);
if (!acct)
@@ -222,6 +222,21 @@ static int acct_on(struct filename *pathname)
filp_close(file, NULL);
return -EIO;
}
+ internal = mnt_clone_internal(&file->f_path);
+ if (IS_ERR(internal)) {
+ kfree(acct);
+ filp_close(file, NULL);
+ return PTR_ERR(internal);
+ }
+ err = mnt_want_write(internal);
+ if (err) {
+ mntput(internal);
+ kfree(acct);
+ filp_close(file, NULL);
+ return err;
+ }
+ mnt = file->f_path.mnt;
+ file->f_path.mnt = internal;
atomic_long_set(&acct->pin.count, 1);
acct->pin.kill = acct_pin_kill;
@@ -229,8 +244,6 @@ static int acct_on(struct filename *pathname)
acct->needcheck = jiffies;
acct->ns = ns;
mutex_init(&acct->lock);
- mnt = file->f_path.mnt;
- mnt_pin(mnt);
mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */
pin_insert(&acct->pin, mnt);
@@ -240,7 +253,8 @@ static int acct_on(struct filename *pathname)
else
ns->bacct = acct;
mutex_unlock(&acct->lock);
- mntput(mnt); /* it's pinned, now give up active reference */
+ mnt_drop_write(mnt);
+ mntput(mnt);
return 0;
}