2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* linux/fs/namespace.c
|
|
|
|
*
|
|
|
|
* (C) Copyright Al Viro 2000, 2001
|
|
|
|
* Released under GPL v2.
|
|
|
|
*
|
|
|
|
* Based on code from fs/super.c, copyright Linus Torvalds and others.
|
|
|
|
* Heavily rewritten.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/syscalls.h>
|
2011-12-08 02:06:11 +08:00
|
|
|
#include <linux/export.h>
|
2006-01-12 04:17:46 +08:00
|
|
|
#include <linux/capability.h>
|
2006-12-08 18:37:56 +08:00
|
|
|
#include <linux/mnt_namespace.h>
|
2012-07-27 12:08:32 +08:00
|
|
|
#include <linux/user_namespace.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
#include <linux/namei.h>
|
|
|
|
#include <linux/security.h>
|
2008-03-27 05:11:34 +08:00
|
|
|
#include <linux/idr.h>
|
2011-12-08 02:06:11 +08:00
|
|
|
#include <linux/acct.h> /* acct_auto_close_mnt */
|
|
|
|
#include <linux/ramfs.h> /* init_rootfs */
|
|
|
|
#include <linux/fs_struct.h> /* get_fs_root et.al. */
|
|
|
|
#include <linux/fsnotify.h> /* fsnotify_vfsmount_delete */
|
|
|
|
#include <linux/uaccess.h>
|
2013-04-12 08:50:06 +08:00
|
|
|
#include <linux/proc_ns.h>
|
2013-05-02 08:51:54 +08:00
|
|
|
#include <linux/magic.h>
|
2005-11-08 06:19:07 +08:00
|
|
|
#include "pnode.h"
|
2007-07-16 14:41:25 +08:00
|
|
|
#include "internal.h"
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-02-06 17:37:57 +08:00
|
|
|
#define HASH_SHIFT ilog2(PAGE_SIZE / sizeof(struct list_head))
|
|
|
|
#define HASH_SIZE (1UL << HASH_SHIFT)
|
|
|
|
|
2005-11-08 06:15:49 +08:00
|
|
|
static int event;
|
2008-03-27 05:11:34 +08:00
|
|
|
static DEFINE_IDA(mnt_id_ida);
|
2008-03-27 20:06:23 +08:00
|
|
|
static DEFINE_IDA(mnt_group_ida);
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
static DEFINE_SPINLOCK(mnt_id_lock);
|
2009-06-24 15:12:00 +08:00
|
|
|
static int mnt_id_start = 0;
|
|
|
|
static int mnt_group_start = 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-03-26 17:37:24 +08:00
|
|
|
static struct list_head *mount_hashtable __read_mostly;
|
2013-03-15 22:53:28 +08:00
|
|
|
static struct list_head *mountpoint_hashtable __read_mostly;
|
2006-12-07 12:33:20 +08:00
|
|
|
static struct kmem_cache *mnt_cache __read_mostly;
|
2005-11-08 06:17:51 +08:00
|
|
|
static struct rw_semaphore namespace_sem;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-01-17 14:14:23 +08:00
|
|
|
/* /sys/fs */
|
2007-10-30 04:17:23 +08:00
|
|
|
struct kobject *fs_kobj;
|
|
|
|
EXPORT_SYMBOL_GPL(fs_kobj);
|
2006-01-17 14:14:23 +08:00
|
|
|
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
/*
|
|
|
|
* vfsmount lock may be taken for read to prevent changes to the
|
|
|
|
* vfsmount hash, ie. during mountpoint lookups or walking back
|
|
|
|
* up the tree.
|
|
|
|
*
|
|
|
|
* It should be taken for write in all cases where the vfsmount
|
|
|
|
* tree or hash is modified or when a vfsmount structure is modified.
|
|
|
|
*/
|
|
|
|
DEFINE_BRLOCK(vfsmount_lock);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)
|
|
|
|
{
|
2005-11-08 06:16:09 +08:00
|
|
|
unsigned long tmp = ((unsigned long)mnt / L1_CACHE_BYTES);
|
|
|
|
tmp += ((unsigned long)dentry / L1_CACHE_BYTES);
|
2008-02-06 17:37:57 +08:00
|
|
|
tmp = tmp + (tmp >> HASH_SHIFT);
|
|
|
|
return tmp & (HASH_SIZE - 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
#define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16)
|
|
|
|
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
/*
|
|
|
|
* allocation is serialized by namespace_sem, but we need the spinlock to
|
|
|
|
* serialize with freeing.
|
|
|
|
*/
|
2011-11-25 09:38:33 +08:00
|
|
|
static int mnt_alloc_id(struct mount *mnt)
|
2008-03-27 05:11:34 +08:00
|
|
|
{
|
|
|
|
int res;
|
|
|
|
|
|
|
|
retry:
|
|
|
|
ida_pre_get(&mnt_id_ida, GFP_KERNEL);
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
spin_lock(&mnt_id_lock);
|
2011-11-25 13:50:41 +08:00
|
|
|
res = ida_get_new_above(&mnt_id_ida, mnt_id_start, &mnt->mnt_id);
|
2009-06-24 15:12:00 +08:00
|
|
|
if (!res)
|
2011-11-25 13:50:41 +08:00
|
|
|
mnt_id_start = mnt->mnt_id + 1;
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
spin_unlock(&mnt_id_lock);
|
2008-03-27 05:11:34 +08:00
|
|
|
if (res == -EAGAIN)
|
|
|
|
goto retry;
|
|
|
|
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2011-11-25 09:38:33 +08:00
|
|
|
static void mnt_free_id(struct mount *mnt)
|
2008-03-27 05:11:34 +08:00
|
|
|
{
|
2011-11-25 13:50:41 +08:00
|
|
|
int id = mnt->mnt_id;
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
spin_lock(&mnt_id_lock);
|
2009-06-24 15:12:00 +08:00
|
|
|
ida_remove(&mnt_id_ida, id);
|
|
|
|
if (mnt_id_start > id)
|
|
|
|
mnt_id_start = id;
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
spin_unlock(&mnt_id_lock);
|
2008-03-27 05:11:34 +08:00
|
|
|
}
|
|
|
|
|
2008-03-27 20:06:23 +08:00
|
|
|
/*
|
|
|
|
* Allocate a new peer group ID
|
|
|
|
*
|
|
|
|
* mnt_group_ida is protected by namespace_sem
|
|
|
|
*/
|
2011-11-25 08:54:23 +08:00
|
|
|
static int mnt_alloc_group_id(struct mount *mnt)
|
2008-03-27 20:06:23 +08:00
|
|
|
{
|
2009-06-24 15:12:00 +08:00
|
|
|
int res;
|
|
|
|
|
2008-03-27 20:06:23 +08:00
|
|
|
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2009-06-24 15:12:00 +08:00
|
|
|
res = ida_get_new_above(&mnt_group_ida,
|
|
|
|
mnt_group_start,
|
2011-11-25 13:50:41 +08:00
|
|
|
&mnt->mnt_group_id);
|
2009-06-24 15:12:00 +08:00
|
|
|
if (!res)
|
2011-11-25 13:50:41 +08:00
|
|
|
mnt_group_start = mnt->mnt_group_id + 1;
|
2009-06-24 15:12:00 +08:00
|
|
|
|
|
|
|
return res;
|
2008-03-27 20:06:23 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Release a peer group ID
|
|
|
|
*/
|
2011-11-25 08:54:23 +08:00
|
|
|
void mnt_release_group_id(struct mount *mnt)
|
2008-03-27 20:06:23 +08:00
|
|
|
{
|
2011-11-25 13:50:41 +08:00
|
|
|
int id = mnt->mnt_group_id;
|
2009-06-24 15:12:00 +08:00
|
|
|
ida_remove(&mnt_group_ida, id);
|
|
|
|
if (mnt_group_start > id)
|
|
|
|
mnt_group_start = id;
|
2011-11-25 13:50:41 +08:00
|
|
|
mnt->mnt_group_id = 0;
|
2008-03-27 20:06:23 +08:00
|
|
|
}
|
|
|
|
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
/*
|
|
|
|
* vfsmount lock must be held for read
|
|
|
|
*/
|
2011-11-25 11:37:54 +08:00
|
|
|
static inline void mnt_add_count(struct mount *mnt, int n)
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_SMP
|
2011-11-25 11:53:09 +08:00
|
|
|
this_cpu_add(mnt->mnt_pcp->mnt_count, n);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
#else
|
|
|
|
preempt_disable();
|
2011-11-25 11:53:09 +08:00
|
|
|
mnt->mnt_count += n;
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
preempt_enable();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* vfsmount lock must be held for write
|
|
|
|
*/
|
2011-11-25 11:37:54 +08:00
|
|
|
unsigned int mnt_get_count(struct mount *mnt)
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_SMP
|
2011-01-15 11:30:21 +08:00
|
|
|
unsigned int count = 0;
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
int cpu;
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
2011-11-25 11:53:09 +08:00
|
|
|
count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
#else
|
2011-11-25 11:53:09 +08:00
|
|
|
return mnt->mnt_count;
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2011-11-25 09:38:33 +08:00
|
|
|
static struct mount *alloc_vfsmnt(const char *name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-25 15:35:16 +08:00
|
|
|
struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
|
|
|
|
if (mnt) {
|
2008-03-27 05:11:34 +08:00
|
|
|
int err;
|
|
|
|
|
2011-11-25 15:35:16 +08:00
|
|
|
err = mnt_alloc_id(mnt);
|
2008-07-21 18:06:36 +08:00
|
|
|
if (err)
|
|
|
|
goto out_free_cache;
|
|
|
|
|
|
|
|
if (name) {
|
2011-11-25 15:35:16 +08:00
|
|
|
mnt->mnt_devname = kstrdup(name, GFP_KERNEL);
|
|
|
|
if (!mnt->mnt_devname)
|
2008-07-21 18:06:36 +08:00
|
|
|
goto out_free_id;
|
2008-03-27 05:11:34 +08:00
|
|
|
}
|
|
|
|
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
#ifdef CONFIG_SMP
|
2011-11-25 15:35:16 +08:00
|
|
|
mnt->mnt_pcp = alloc_percpu(struct mnt_pcp);
|
|
|
|
if (!mnt->mnt_pcp)
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
goto out_free_devname;
|
|
|
|
|
2011-11-25 15:35:16 +08:00
|
|
|
this_cpu_add(mnt->mnt_pcp->mnt_count, 1);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
#else
|
2011-11-25 15:35:16 +08:00
|
|
|
mnt->mnt_count = 1;
|
|
|
|
mnt->mnt_writers = 0;
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
#endif
|
|
|
|
|
2011-11-25 15:35:16 +08:00
|
|
|
INIT_LIST_HEAD(&mnt->mnt_hash);
|
|
|
|
INIT_LIST_HEAD(&mnt->mnt_child);
|
|
|
|
INIT_LIST_HEAD(&mnt->mnt_mounts);
|
|
|
|
INIT_LIST_HEAD(&mnt->mnt_list);
|
|
|
|
INIT_LIST_HEAD(&mnt->mnt_expire);
|
|
|
|
INIT_LIST_HEAD(&mnt->mnt_share);
|
|
|
|
INIT_LIST_HEAD(&mnt->mnt_slave_list);
|
|
|
|
INIT_LIST_HEAD(&mnt->mnt_slave);
|
2009-12-18 10:24:27 +08:00
|
|
|
#ifdef CONFIG_FSNOTIFY
|
|
|
|
INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
|
2009-04-26 18:25:54 +08:00
|
|
|
#endif
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-11-25 15:35:16 +08:00
|
|
|
return mnt;
|
2008-07-21 18:06:36 +08:00
|
|
|
|
2009-04-26 18:25:54 +08:00
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
out_free_devname:
|
2011-11-25 15:35:16 +08:00
|
|
|
kfree(mnt->mnt_devname);
|
2009-04-26 18:25:54 +08:00
|
|
|
#endif
|
2008-07-21 18:06:36 +08:00
|
|
|
out_free_id:
|
2011-11-25 15:35:16 +08:00
|
|
|
mnt_free_id(mnt);
|
2008-07-21 18:06:36 +08:00
|
|
|
out_free_cache:
|
2011-11-25 15:35:16 +08:00
|
|
|
kmem_cache_free(mnt_cache, mnt);
|
2008-07-21 18:06:36 +08:00
|
|
|
return NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
/*
|
|
|
|
* Most r/o checks on a fs are for operations that take
|
|
|
|
* discrete amounts of time, like a write() or unlink().
|
|
|
|
* We must keep track of when those operations start
|
|
|
|
* (for permission checks) and when they end, so that
|
|
|
|
* we can determine when writes are able to occur to
|
|
|
|
* a filesystem.
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* __mnt_is_readonly: check whether a mount is read-only
|
|
|
|
* @mnt: the mount to check for its write status
|
|
|
|
*
|
|
|
|
* This shouldn't be used directly ouside of the VFS.
|
|
|
|
* It does not guarantee that the filesystem will stay
|
|
|
|
* r/w, just that it is right *now*. This can not and
|
|
|
|
* should not be used in place of IS_RDONLY(inode).
|
|
|
|
* mnt_want/drop_write() will _keep_ the filesystem
|
|
|
|
* r/w.
|
|
|
|
*/
|
|
|
|
int __mnt_is_readonly(struct vfsmount *mnt)
|
|
|
|
{
|
2008-02-16 06:38:00 +08:00
|
|
|
if (mnt->mnt_flags & MNT_READONLY)
|
|
|
|
return 1;
|
|
|
|
if (mnt->mnt_sb->s_flags & MS_RDONLY)
|
|
|
|
return 1;
|
|
|
|
return 0;
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(__mnt_is_readonly);
|
|
|
|
|
2011-11-25 11:37:54 +08:00
|
|
|
static inline void mnt_inc_writers(struct mount *mnt)
|
2009-04-26 18:25:54 +08:00
|
|
|
{
|
|
|
|
#ifdef CONFIG_SMP
|
2011-11-25 11:53:09 +08:00
|
|
|
this_cpu_inc(mnt->mnt_pcp->mnt_writers);
|
2009-04-26 18:25:54 +08:00
|
|
|
#else
|
2011-11-25 11:53:09 +08:00
|
|
|
mnt->mnt_writers++;
|
2009-04-26 18:25:54 +08:00
|
|
|
#endif
|
|
|
|
}
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
|
2011-11-25 11:37:54 +08:00
|
|
|
static inline void mnt_dec_writers(struct mount *mnt)
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
{
|
2009-04-26 18:25:54 +08:00
|
|
|
#ifdef CONFIG_SMP
|
2011-11-25 11:53:09 +08:00
|
|
|
this_cpu_dec(mnt->mnt_pcp->mnt_writers);
|
2009-04-26 18:25:54 +08:00
|
|
|
#else
|
2011-11-25 11:53:09 +08:00
|
|
|
mnt->mnt_writers--;
|
2009-04-26 18:25:54 +08:00
|
|
|
#endif
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
}
|
|
|
|
|
2011-11-25 11:37:54 +08:00
|
|
|
static unsigned int mnt_get_writers(struct mount *mnt)
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
{
|
2009-04-26 18:25:54 +08:00
|
|
|
#ifdef CONFIG_SMP
|
|
|
|
unsigned int count = 0;
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
int cpu;
|
|
|
|
|
|
|
|
for_each_possible_cpu(cpu) {
|
2011-11-25 11:53:09 +08:00
|
|
|
count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_writers;
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
}
|
|
|
|
|
2009-04-26 18:25:54 +08:00
|
|
|
return count;
|
|
|
|
#else
|
|
|
|
return mnt->mnt_writers;
|
|
|
|
#endif
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
}
|
|
|
|
|
2011-11-21 19:11:31 +08:00
|
|
|
static int mnt_is_readonly(struct vfsmount *mnt)
|
|
|
|
{
|
|
|
|
if (mnt->mnt_sb->s_readonly_remount)
|
|
|
|
return 1;
|
|
|
|
/* Order wrt setting s_flags/s_readonly_remount in do_remount() */
|
|
|
|
smp_rmb();
|
|
|
|
return __mnt_is_readonly(mnt);
|
|
|
|
}
|
|
|
|
|
2008-02-16 06:37:30 +08:00
|
|
|
/*
|
2012-06-12 22:20:35 +08:00
|
|
|
* Most r/o & frozen checks on a fs are for operations that take discrete
|
|
|
|
* amounts of time, like a write() or unlink(). We must keep track of when
|
|
|
|
* those operations start (for permission checks) and when they end, so that we
|
|
|
|
* can determine when writes are able to occur to a filesystem.
|
2008-02-16 06:37:30 +08:00
|
|
|
*/
|
|
|
|
/**
|
2012-06-12 22:20:35 +08:00
|
|
|
* __mnt_want_write - get write access to a mount without freeze protection
|
2011-11-25 11:37:54 +08:00
|
|
|
* @m: the mount on which to take a write
|
2008-02-16 06:37:30 +08:00
|
|
|
*
|
2012-06-12 22:20:35 +08:00
|
|
|
* This tells the low-level filesystem that a write is about to be performed to
|
|
|
|
* it, and makes sure that writes are allowed (mnt it read-write) before
|
|
|
|
* returning success. This operation does not protect against filesystem being
|
|
|
|
* frozen. When the write operation is finished, __mnt_drop_write() must be
|
|
|
|
* called. This is effectively a refcount.
|
2008-02-16 06:37:30 +08:00
|
|
|
*/
|
2012-06-12 22:20:35 +08:00
|
|
|
int __mnt_want_write(struct vfsmount *m)
|
2008-02-16 06:37:30 +08:00
|
|
|
{
|
2011-11-25 11:37:54 +08:00
|
|
|
struct mount *mnt = real_mount(m);
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
int ret = 0;
|
|
|
|
|
2009-04-26 18:25:54 +08:00
|
|
|
preempt_disable();
|
2011-01-07 14:50:10 +08:00
|
|
|
mnt_inc_writers(mnt);
|
2009-04-26 18:25:54 +08:00
|
|
|
/*
|
2011-01-07 14:50:10 +08:00
|
|
|
* The store to mnt_inc_writers must be visible before we pass
|
2009-04-26 18:25:54 +08:00
|
|
|
* MNT_WRITE_HOLD loop below, so that the slowpath can see our
|
|
|
|
* incremented count after it has set MNT_WRITE_HOLD.
|
|
|
|
*/
|
|
|
|
smp_mb();
|
2012-11-16 17:23:50 +08:00
|
|
|
while (ACCESS_ONCE(mnt->mnt.mnt_flags) & MNT_WRITE_HOLD)
|
2009-04-26 18:25:54 +08:00
|
|
|
cpu_relax();
|
|
|
|
/*
|
|
|
|
* After the slowpath clears MNT_WRITE_HOLD, mnt_is_readonly will
|
|
|
|
* be set to match its requirements. So we must not load that until
|
|
|
|
* MNT_WRITE_HOLD is cleared.
|
|
|
|
*/
|
|
|
|
smp_rmb();
|
2011-11-21 19:11:31 +08:00
|
|
|
if (mnt_is_readonly(m)) {
|
2011-01-07 14:50:10 +08:00
|
|
|
mnt_dec_writers(mnt);
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
ret = -EROFS;
|
|
|
|
}
|
2009-04-26 18:25:54 +08:00
|
|
|
preempt_enable();
|
2012-06-12 22:20:35 +08:00
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* mnt_want_write - get write access to a mount
|
|
|
|
* @m: the mount on which to take a write
|
|
|
|
*
|
|
|
|
* This tells the low-level filesystem that a write is about to be performed to
|
|
|
|
* it, and makes sure that writes are allowed (mount is read-write, filesystem
|
|
|
|
* is not frozen) before returning success. When the write operation is
|
|
|
|
* finished, mnt_drop_write() must be called. This is effectively a refcount.
|
|
|
|
*/
|
|
|
|
int mnt_want_write(struct vfsmount *m)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sb_start_write(m->mnt_sb);
|
|
|
|
ret = __mnt_want_write(m);
|
|
|
|
if (ret)
|
|
|
|
sb_end_write(m->mnt_sb);
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
return ret;
|
2008-02-16 06:37:30 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mnt_want_write);
|
|
|
|
|
2009-04-26 18:25:55 +08:00
|
|
|
/**
|
|
|
|
* mnt_clone_write - get write access to a mount
|
|
|
|
* @mnt: the mount on which to take a write
|
|
|
|
*
|
|
|
|
* This is effectively like mnt_want_write, except
|
|
|
|
* it must only be used to take an extra write reference
|
|
|
|
* on a mountpoint that we already know has a write reference
|
|
|
|
* on it. This allows some optimisation.
|
|
|
|
*
|
|
|
|
* After finished, mnt_drop_write must be called as usual to
|
|
|
|
* drop the reference.
|
|
|
|
*/
|
|
|
|
int mnt_clone_write(struct vfsmount *mnt)
|
|
|
|
{
|
|
|
|
/* superblock may be r/o */
|
|
|
|
if (__mnt_is_readonly(mnt))
|
|
|
|
return -EROFS;
|
|
|
|
preempt_disable();
|
2011-11-25 11:37:54 +08:00
|
|
|
mnt_inc_writers(real_mount(mnt));
|
2009-04-26 18:25:55 +08:00
|
|
|
preempt_enable();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(mnt_clone_write);
|
|
|
|
|
|
|
|
/**
|
2012-06-12 22:20:35 +08:00
|
|
|
* __mnt_want_write_file - get write access to a file's mount
|
2009-04-26 18:25:55 +08:00
|
|
|
* @file: the file who's mount on which to take a write
|
|
|
|
*
|
2012-06-12 22:20:35 +08:00
|
|
|
* This is like __mnt_want_write, but it takes a file and can
|
2009-04-26 18:25:55 +08:00
|
|
|
* do some optimisations if the file is open for write already
|
|
|
|
*/
|
2012-06-12 22:20:35 +08:00
|
|
|
int __mnt_want_write_file(struct file *file)
|
2009-04-26 18:25:55 +08:00
|
|
|
{
|
2013-01-24 06:07:38 +08:00
|
|
|
struct inode *inode = file_inode(file);
|
2012-06-12 22:20:35 +08:00
|
|
|
|
2009-08-07 06:07:39 +08:00
|
|
|
if (!(file->f_mode & FMODE_WRITE) || special_file(inode->i_mode))
|
2012-06-12 22:20:35 +08:00
|
|
|
return __mnt_want_write(file->f_path.mnt);
|
2009-04-26 18:25:55 +08:00
|
|
|
else
|
|
|
|
return mnt_clone_write(file->f_path.mnt);
|
|
|
|
}
|
2012-06-12 22:20:35 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* mnt_want_write_file - get write access to a file's mount
|
|
|
|
* @file: the file who's mount on which to take a write
|
|
|
|
*
|
|
|
|
* This is like mnt_want_write, but it takes a file and can
|
|
|
|
* do some optimisations if the file is open for write already
|
|
|
|
*/
|
|
|
|
int mnt_want_write_file(struct file *file)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
sb_start_write(file->f_path.mnt->mnt_sb);
|
|
|
|
ret = __mnt_want_write_file(file);
|
|
|
|
if (ret)
|
|
|
|
sb_end_write(file->f_path.mnt->mnt_sb);
|
|
|
|
return ret;
|
|
|
|
}
|
2009-04-26 18:25:55 +08:00
|
|
|
EXPORT_SYMBOL_GPL(mnt_want_write_file);
|
|
|
|
|
2008-02-16 06:37:30 +08:00
|
|
|
/**
|
2012-06-12 22:20:35 +08:00
|
|
|
* __mnt_drop_write - give up write access to a mount
|
2008-02-16 06:37:30 +08:00
|
|
|
* @mnt: the mount on which to give up write access
|
|
|
|
*
|
|
|
|
* Tells the low-level filesystem that we are done
|
|
|
|
* performing writes to it. Must be matched with
|
2012-06-12 22:20:35 +08:00
|
|
|
* __mnt_want_write() call above.
|
2008-02-16 06:37:30 +08:00
|
|
|
*/
|
2012-06-12 22:20:35 +08:00
|
|
|
void __mnt_drop_write(struct vfsmount *mnt)
|
2008-02-16 06:37:30 +08:00
|
|
|
{
|
2009-04-26 18:25:54 +08:00
|
|
|
preempt_disable();
|
2011-11-25 11:37:54 +08:00
|
|
|
mnt_dec_writers(real_mount(mnt));
|
2009-04-26 18:25:54 +08:00
|
|
|
preempt_enable();
|
2008-02-16 06:37:30 +08:00
|
|
|
}
|
2012-06-12 22:20:35 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* mnt_drop_write - give up write access to a mount
|
|
|
|
* @mnt: the mount on which to give up write access
|
|
|
|
*
|
|
|
|
* Tells the low-level filesystem that we are done performing writes to it and
|
|
|
|
* also allows filesystem to be frozen again. Must be matched with
|
|
|
|
* mnt_want_write() call above.
|
|
|
|
*/
|
|
|
|
void mnt_drop_write(struct vfsmount *mnt)
|
|
|
|
{
|
|
|
|
__mnt_drop_write(mnt);
|
|
|
|
sb_end_write(mnt->mnt_sb);
|
|
|
|
}
|
2008-02-16 06:37:30 +08:00
|
|
|
EXPORT_SYMBOL_GPL(mnt_drop_write);
|
|
|
|
|
2012-06-12 22:20:35 +08:00
|
|
|
void __mnt_drop_write_file(struct file *file)
|
|
|
|
{
|
|
|
|
__mnt_drop_write(file->f_path.mnt);
|
|
|
|
}
|
|
|
|
|
2011-12-09 21:06:57 +08:00
|
|
|
void mnt_drop_write_file(struct file *file)
|
|
|
|
{
|
|
|
|
mnt_drop_write(file->f_path.mnt);
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mnt_drop_write_file);
|
|
|
|
|
2011-11-25 11:37:54 +08:00
|
|
|
static int mnt_make_readonly(struct mount *mnt)
|
2008-02-16 06:37:30 +08:00
|
|
|
{
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
int ret = 0;
|
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 11:37:54 +08:00
|
|
|
mnt->mnt.mnt_flags |= MNT_WRITE_HOLD;
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
/*
|
2009-04-26 18:25:54 +08:00
|
|
|
* After storing MNT_WRITE_HOLD, we'll read the counters. This store
|
|
|
|
* should be visible before we do.
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
*/
|
2009-04-26 18:25:54 +08:00
|
|
|
smp_mb();
|
|
|
|
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
/*
|
2009-04-26 18:25:54 +08:00
|
|
|
* With writers on hold, if this value is zero, then there are
|
|
|
|
* definitely no active writers (although held writers may subsequently
|
|
|
|
* increment the count, they'll have to wait, and decrement it after
|
|
|
|
* seeing MNT_READONLY).
|
|
|
|
*
|
|
|
|
* It is OK to have counter incremented on one CPU and decremented on
|
|
|
|
* another: the sum will add up correctly. The danger would be when we
|
|
|
|
* sum up each counter, if we read a counter before it is incremented,
|
|
|
|
* but then read another CPU's count which it has been subsequently
|
|
|
|
* decremented from -- we would see more decrements than we should.
|
|
|
|
* MNT_WRITE_HOLD protects against this scenario, because
|
|
|
|
* mnt_want_write first increments count, then smp_mb, then spins on
|
|
|
|
* MNT_WRITE_HOLD, so it can't be decremented by another CPU while
|
|
|
|
* we're counting up here.
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
*/
|
2011-01-07 14:50:10 +08:00
|
|
|
if (mnt_get_writers(mnt) > 0)
|
2009-04-26 18:25:54 +08:00
|
|
|
ret = -EBUSY;
|
|
|
|
else
|
2011-11-25 11:37:54 +08:00
|
|
|
mnt->mnt.mnt_flags |= MNT_READONLY;
|
2009-04-26 18:25:54 +08:00
|
|
|
/*
|
|
|
|
* MNT_READONLY must become visible before ~MNT_WRITE_HOLD, so writers
|
|
|
|
* that become unheld will see MNT_READONLY.
|
|
|
|
*/
|
|
|
|
smp_wmb();
|
2011-11-25 11:37:54 +08:00
|
|
|
mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
return ret;
|
2008-02-16 06:37:30 +08:00
|
|
|
}
|
|
|
|
|
2011-11-25 11:37:54 +08:00
|
|
|
static void __mnt_unmake_readonly(struct mount *mnt)
|
2008-02-16 06:38:00 +08:00
|
|
|
{
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 11:37:54 +08:00
|
|
|
mnt->mnt.mnt_flags &= ~MNT_READONLY;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2008-02-16 06:38:00 +08:00
|
|
|
}
|
|
|
|
|
2011-11-21 19:11:31 +08:00
|
|
|
int sb_prepare_remount_readonly(struct super_block *sb)
|
|
|
|
{
|
|
|
|
struct mount *mnt;
|
|
|
|
int err = 0;
|
|
|
|
|
2011-11-21 19:11:33 +08:00
|
|
|
/* Racy optimization. Recheck the counter under MNT_WRITE_HOLD */
|
|
|
|
if (atomic_long_read(&sb->s_remove_count))
|
|
|
|
return -EBUSY;
|
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-21 19:11:31 +08:00
|
|
|
list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
|
|
|
|
if (!(mnt->mnt.mnt_flags & MNT_READONLY)) {
|
|
|
|
mnt->mnt.mnt_flags |= MNT_WRITE_HOLD;
|
|
|
|
smp_mb();
|
|
|
|
if (mnt_get_writers(mnt) > 0) {
|
|
|
|
err = -EBUSY;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2011-11-21 19:11:33 +08:00
|
|
|
if (!err && atomic_long_read(&sb->s_remove_count))
|
|
|
|
err = -EBUSY;
|
|
|
|
|
2011-11-21 19:11:31 +08:00
|
|
|
if (!err) {
|
|
|
|
sb->s_readonly_remount = 1;
|
|
|
|
smp_wmb();
|
|
|
|
}
|
|
|
|
list_for_each_entry(mnt, &sb->s_mounts, mnt_instance) {
|
|
|
|
if (mnt->mnt.mnt_flags & MNT_WRITE_HOLD)
|
|
|
|
mnt->mnt.mnt_flags &= ~MNT_WRITE_HOLD;
|
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2011-11-21 19:11:31 +08:00
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-11-25 09:38:33 +08:00
|
|
|
static void free_vfsmnt(struct mount *mnt)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-25 15:25:17 +08:00
|
|
|
kfree(mnt->mnt_devname);
|
2008-03-27 05:11:34 +08:00
|
|
|
mnt_free_id(mnt);
|
2009-04-26 18:25:54 +08:00
|
|
|
#ifdef CONFIG_SMP
|
2011-11-25 11:53:09 +08:00
|
|
|
free_percpu(mnt->mnt_pcp);
|
2009-04-26 18:25:54 +08:00
|
|
|
#endif
|
2011-11-25 09:38:33 +08:00
|
|
|
kmem_cache_free(mnt_cache, mnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2005-11-08 06:20:17 +08:00
|
|
|
* find the first or last mount at @dentry on vfsmount @mnt depending on
|
|
|
|
* @dir. If @dir is set return the first mount else return the last mount.
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
* vfsmount_lock must be held for read or write.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2011-11-25 07:22:03 +08:00
|
|
|
struct mount *__lookup_mnt(struct vfsmount *mnt, struct dentry *dentry,
|
2005-11-08 06:20:17 +08:00
|
|
|
int dir)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-08 06:16:09 +08:00
|
|
|
struct list_head *head = mount_hashtable + hash(mnt, dentry);
|
|
|
|
struct list_head *tmp = head;
|
2011-11-25 07:22:03 +08:00
|
|
|
struct mount *p, *found = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
for (;;) {
|
2005-11-08 06:20:17 +08:00
|
|
|
tmp = dir ? tmp->next : tmp->prev;
|
2005-04-17 06:20:36 +08:00
|
|
|
p = NULL;
|
|
|
|
if (tmp == head)
|
|
|
|
break;
|
2011-11-25 10:01:32 +08:00
|
|
|
p = list_entry(tmp, struct mount, mnt_hash);
|
2011-11-25 11:25:07 +08:00
|
|
|
if (&p->mnt_parent->mnt == mnt && p->mnt_mountpoint == dentry) {
|
2005-11-08 06:20:17 +08:00
|
|
|
found = p;
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2005-11-08 06:20:17 +08:00
|
|
|
/*
|
2012-06-25 19:55:28 +08:00
|
|
|
* lookup_mnt - Return the first child mount mounted at path
|
|
|
|
*
|
|
|
|
* "First" means first mounted chronologically. If you create the
|
|
|
|
* following mounts:
|
|
|
|
*
|
|
|
|
* mount /dev/sda1 /mnt
|
|
|
|
* mount /dev/sda2 /mnt
|
|
|
|
* mount /dev/sda3 /mnt
|
|
|
|
*
|
|
|
|
* Then lookup_mnt() on the base /mnt dentry in the root mount will
|
|
|
|
* return successively the root dentry and vfsmount of /dev/sda1, then
|
|
|
|
* /dev/sda2, then /dev/sda3, then NULL.
|
|
|
|
*
|
|
|
|
* lookup_mnt takes a reference to the found vfsmount.
|
2005-11-08 06:20:17 +08:00
|
|
|
*/
|
2009-04-19 02:06:57 +08:00
|
|
|
struct vfsmount *lookup_mnt(struct path *path)
|
2005-11-08 06:20:17 +08:00
|
|
|
{
|
2011-11-25 07:22:03 +08:00
|
|
|
struct mount *child_mnt;
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_read_lock(&vfsmount_lock);
|
2011-11-25 07:22:03 +08:00
|
|
|
child_mnt = __lookup_mnt(path->mnt, path->dentry, 1);
|
|
|
|
if (child_mnt) {
|
|
|
|
mnt_add_count(child_mnt, 1);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_read_unlock(&vfsmount_lock);
|
2011-11-25 07:22:03 +08:00
|
|
|
return &child_mnt->mnt;
|
|
|
|
} else {
|
2012-05-08 12:02:02 +08:00
|
|
|
br_read_unlock(&vfsmount_lock);
|
2011-11-25 07:22:03 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2005-11-08 06:20:17 +08:00
|
|
|
}
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
static struct mountpoint *new_mountpoint(struct dentry *dentry)
|
|
|
|
{
|
|
|
|
struct list_head *chain = mountpoint_hashtable + hash(NULL, dentry);
|
|
|
|
struct mountpoint *mp;
|
|
|
|
|
|
|
|
list_for_each_entry(mp, chain, m_hash) {
|
|
|
|
if (mp->m_dentry == dentry) {
|
|
|
|
/* might be worth a WARN_ON() */
|
|
|
|
if (d_unlinked(dentry))
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
mp->m_count++;
|
|
|
|
return mp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mp = kmalloc(sizeof(struct mountpoint), GFP_KERNEL);
|
|
|
|
if (!mp)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
spin_lock(&dentry->d_lock);
|
|
|
|
if (d_unlinked(dentry)) {
|
|
|
|
spin_unlock(&dentry->d_lock);
|
|
|
|
kfree(mp);
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
}
|
|
|
|
dentry->d_flags |= DCACHE_MOUNTED;
|
|
|
|
spin_unlock(&dentry->d_lock);
|
|
|
|
mp->m_dentry = dentry;
|
|
|
|
mp->m_count = 1;
|
|
|
|
list_add(&mp->m_hash, chain);
|
|
|
|
return mp;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void put_mountpoint(struct mountpoint *mp)
|
|
|
|
{
|
|
|
|
if (!--mp->m_count) {
|
|
|
|
struct dentry *dentry = mp->m_dentry;
|
|
|
|
spin_lock(&dentry->d_lock);
|
|
|
|
dentry->d_flags &= ~DCACHE_MOUNTED;
|
|
|
|
spin_unlock(&dentry->d_lock);
|
|
|
|
list_del(&mp->m_hash);
|
|
|
|
kfree(mp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-25 13:46:35 +08:00
|
|
|
static inline int check_mnt(struct mount *mnt)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-12-08 18:37:56 +08:00
|
|
|
return mnt->mnt_ns == current->nsproxy->mnt_ns;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
/*
|
|
|
|
* vfsmount lock must be held for write
|
|
|
|
*/
|
2006-12-08 18:37:56 +08:00
|
|
|
static void touch_mnt_namespace(struct mnt_namespace *ns)
|
2005-11-08 06:15:49 +08:00
|
|
|
{
|
|
|
|
if (ns) {
|
|
|
|
ns->event = ++event;
|
|
|
|
wake_up_interruptible(&ns->poll);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
/*
|
|
|
|
* vfsmount lock must be held for write
|
|
|
|
*/
|
2006-12-08 18:37:56 +08:00
|
|
|
static void __touch_mnt_namespace(struct mnt_namespace *ns)
|
2005-11-08 06:15:49 +08:00
|
|
|
{
|
|
|
|
if (ns && ns->event != event) {
|
|
|
|
ns->event = event;
|
|
|
|
wake_up_interruptible(&ns->poll);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
/*
|
|
|
|
* vfsmount lock must be held for write
|
|
|
|
*/
|
2011-11-25 08:41:16 +08:00
|
|
|
static void detach_mnt(struct mount *mnt, struct path *old_path)
|
|
|
|
{
|
2011-11-25 11:25:07 +08:00
|
|
|
old_path->dentry = mnt->mnt_mountpoint;
|
2011-11-25 11:19:58 +08:00
|
|
|
old_path->mnt = &mnt->mnt_parent->mnt;
|
|
|
|
mnt->mnt_parent = mnt;
|
2011-11-25 11:25:07 +08:00
|
|
|
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
2011-11-25 12:24:33 +08:00
|
|
|
list_del_init(&mnt->mnt_child);
|
2011-11-25 10:01:32 +08:00
|
|
|
list_del_init(&mnt->mnt_hash);
|
2013-03-15 22:53:28 +08:00
|
|
|
put_mountpoint(mnt->mnt_mp);
|
|
|
|
mnt->mnt_mp = NULL;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
/*
|
|
|
|
* vfsmount lock must be held for write
|
|
|
|
*/
|
2013-03-15 22:53:28 +08:00
|
|
|
void mnt_set_mountpoint(struct mount *mnt,
|
|
|
|
struct mountpoint *mp,
|
2011-11-25 10:28:22 +08:00
|
|
|
struct mount *child_mnt)
|
2005-11-08 06:19:50 +08:00
|
|
|
{
|
2013-03-15 22:53:28 +08:00
|
|
|
mp->m_count++;
|
2011-11-25 16:19:09 +08:00
|
|
|
mnt_add_count(mnt, 1); /* essentially, that's mntget */
|
2013-03-15 22:53:28 +08:00
|
|
|
child_mnt->mnt_mountpoint = dget(mp->m_dentry);
|
2011-11-25 16:19:09 +08:00
|
|
|
child_mnt->mnt_parent = mnt;
|
2013-03-15 22:53:28 +08:00
|
|
|
child_mnt->mnt_mp = mp;
|
2005-11-08 06:19:50 +08:00
|
|
|
}
|
|
|
|
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
/*
|
|
|
|
* vfsmount lock must be held for write
|
|
|
|
*/
|
2013-03-15 22:53:28 +08:00
|
|
|
static void attach_mnt(struct mount *mnt,
|
|
|
|
struct mount *parent,
|
|
|
|
struct mountpoint *mp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-03-15 22:53:28 +08:00
|
|
|
mnt_set_mountpoint(parent, mp, mnt);
|
2011-11-25 10:01:32 +08:00
|
|
|
list_add_tail(&mnt->mnt_hash, mount_hashtable +
|
2013-03-15 22:53:28 +08:00
|
|
|
hash(&parent->mnt, mp->m_dentry));
|
|
|
|
list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
|
2005-11-08 06:19:50 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
* vfsmount lock must be held for write
|
2005-11-08 06:19:50 +08:00
|
|
|
*/
|
2011-11-25 08:47:15 +08:00
|
|
|
static void commit_tree(struct mount *mnt)
|
2005-11-08 06:19:50 +08:00
|
|
|
{
|
2011-11-25 11:19:58 +08:00
|
|
|
struct mount *parent = mnt->mnt_parent;
|
2011-11-25 11:37:54 +08:00
|
|
|
struct mount *m;
|
2005-11-08 06:19:50 +08:00
|
|
|
LIST_HEAD(head);
|
2011-11-25 13:46:35 +08:00
|
|
|
struct mnt_namespace *n = parent->mnt_ns;
|
2005-11-08 06:19:50 +08:00
|
|
|
|
2011-11-25 11:19:58 +08:00
|
|
|
BUG_ON(parent == mnt);
|
2005-11-08 06:19:50 +08:00
|
|
|
|
2011-11-25 15:19:55 +08:00
|
|
|
list_add_tail(&head, &mnt->mnt_list);
|
2012-06-09 12:59:08 +08:00
|
|
|
list_for_each_entry(m, &head, mnt_list)
|
2011-11-25 13:46:35 +08:00
|
|
|
m->mnt_ns = n;
|
2011-01-15 11:30:21 +08:00
|
|
|
|
2005-11-08 06:19:50 +08:00
|
|
|
list_splice(&head, n->list.prev);
|
|
|
|
|
2011-11-25 10:01:32 +08:00
|
|
|
list_add_tail(&mnt->mnt_hash, mount_hashtable +
|
2011-11-25 11:25:07 +08:00
|
|
|
hash(&parent->mnt, mnt->mnt_mountpoint));
|
2011-11-25 12:24:33 +08:00
|
|
|
list_add_tail(&mnt->mnt_child, &parent->mnt_mounts);
|
2006-12-08 18:37:56 +08:00
|
|
|
touch_mnt_namespace(n);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-11-25 16:06:56 +08:00
|
|
|
static struct mount *next_mnt(struct mount *p, struct mount *root)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-25 12:24:33 +08:00
|
|
|
struct list_head *next = p->mnt_mounts.next;
|
|
|
|
if (next == &p->mnt_mounts) {
|
2005-04-17 06:20:36 +08:00
|
|
|
while (1) {
|
2011-11-25 16:06:56 +08:00
|
|
|
if (p == root)
|
2005-04-17 06:20:36 +08:00
|
|
|
return NULL;
|
2011-11-25 12:24:33 +08:00
|
|
|
next = p->mnt_child.next;
|
|
|
|
if (next != &p->mnt_parent->mnt_mounts)
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
2011-11-25 11:19:58 +08:00
|
|
|
p = p->mnt_parent;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2011-11-25 12:24:33 +08:00
|
|
|
return list_entry(next, struct mount, mnt_child);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-11-25 07:57:30 +08:00
|
|
|
static struct mount *skip_mnt_tree(struct mount *p)
|
2005-11-08 06:21:20 +08:00
|
|
|
{
|
2011-11-25 12:24:33 +08:00
|
|
|
struct list_head *prev = p->mnt_mounts.prev;
|
|
|
|
while (prev != &p->mnt_mounts) {
|
|
|
|
p = list_entry(prev, struct mount, mnt_child);
|
|
|
|
prev = p->mnt_mounts.prev;
|
2005-11-08 06:21:20 +08:00
|
|
|
}
|
|
|
|
return p;
|
|
|
|
}
|
|
|
|
|
2011-03-18 10:08:28 +08:00
|
|
|
struct vfsmount *
|
|
|
|
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
|
|
|
|
{
|
2011-11-25 09:38:33 +08:00
|
|
|
struct mount *mnt;
|
2011-03-18 10:08:28 +08:00
|
|
|
struct dentry *root;
|
|
|
|
|
|
|
|
if (!type)
|
|
|
|
return ERR_PTR(-ENODEV);
|
|
|
|
|
|
|
|
mnt = alloc_vfsmnt(name);
|
|
|
|
if (!mnt)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
|
|
|
|
if (flags & MS_KERNMOUNT)
|
2011-11-25 09:38:33 +08:00
|
|
|
mnt->mnt.mnt_flags = MNT_INTERNAL;
|
2011-03-18 10:08:28 +08:00
|
|
|
|
|
|
|
root = mount_fs(type, flags, name, data);
|
|
|
|
if (IS_ERR(root)) {
|
|
|
|
free_vfsmnt(mnt);
|
|
|
|
return ERR_CAST(root);
|
|
|
|
}
|
|
|
|
|
2011-11-25 09:38:33 +08:00
|
|
|
mnt->mnt.mnt_root = root;
|
|
|
|
mnt->mnt.mnt_sb = root->d_sb;
|
2011-11-25 11:25:07 +08:00
|
|
|
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
2011-11-25 11:19:58 +08:00
|
|
|
mnt->mnt_parent = mnt;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-21 19:11:30 +08:00
|
|
|
list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2011-11-25 09:38:33 +08:00
|
|
|
return &mnt->mnt;
|
2011-03-18 10:08:28 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(vfs_kern_mount);
|
|
|
|
|
2011-11-25 10:24:27 +08:00
|
|
|
static struct mount *clone_mnt(struct mount *old, struct dentry *root,
|
2005-11-08 06:17:22 +08:00
|
|
|
int flag)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-25 10:24:27 +08:00
|
|
|
struct super_block *sb = old->mnt.mnt_sb;
|
2012-06-25 19:55:18 +08:00
|
|
|
struct mount *mnt;
|
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-06-25 19:55:18 +08:00
|
|
|
mnt = alloc_vfsmnt(old->mnt_devname);
|
|
|
|
if (!mnt)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2008-03-27 20:06:23 +08:00
|
|
|
|
2012-08-01 04:13:04 +08:00
|
|
|
if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE))
|
2012-06-25 19:55:18 +08:00
|
|
|
mnt->mnt_group_id = 0; /* not a peer of original */
|
|
|
|
else
|
|
|
|
mnt->mnt_group_id = old->mnt_group_id;
|
2005-11-08 06:19:50 +08:00
|
|
|
|
2012-06-25 19:55:18 +08:00
|
|
|
if ((flag & CL_MAKE_SHARED) && !mnt->mnt_group_id) {
|
|
|
|
err = mnt_alloc_group_id(mnt);
|
|
|
|
if (err)
|
|
|
|
goto out_free;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-06-25 19:55:18 +08:00
|
|
|
|
|
|
|
mnt->mnt.mnt_flags = old->mnt.mnt_flags & ~MNT_WRITE_HOLD;
|
2013-03-22 19:08:05 +08:00
|
|
|
/* Don't allow unprivileged users to change mount flags */
|
|
|
|
if ((flag & CL_UNPRIVILEGED) && (mnt->mnt.mnt_flags & MNT_READONLY))
|
|
|
|
mnt->mnt.mnt_flags |= MNT_LOCK_READONLY;
|
|
|
|
|
2012-06-25 19:55:18 +08:00
|
|
|
atomic_inc(&sb->s_active);
|
|
|
|
mnt->mnt.mnt_sb = sb;
|
|
|
|
mnt->mnt.mnt_root = dget(root);
|
|
|
|
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
|
|
|
mnt->mnt_parent = mnt;
|
|
|
|
br_write_lock(&vfsmount_lock);
|
|
|
|
list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
|
|
|
|
br_write_unlock(&vfsmount_lock);
|
|
|
|
|
2012-08-01 04:13:04 +08:00
|
|
|
if ((flag & CL_SLAVE) ||
|
|
|
|
((flag & CL_SHARED_TO_SLAVE) && IS_MNT_SHARED(old))) {
|
2012-06-25 19:55:18 +08:00
|
|
|
list_add(&mnt->mnt_slave, &old->mnt_slave_list);
|
|
|
|
mnt->mnt_master = old;
|
|
|
|
CLEAR_MNT_SHARED(mnt);
|
|
|
|
} else if (!(flag & CL_PRIVATE)) {
|
|
|
|
if ((flag & CL_MAKE_SHARED) || IS_MNT_SHARED(old))
|
|
|
|
list_add(&mnt->mnt_share, &old->mnt_share);
|
|
|
|
if (IS_MNT_SLAVE(old))
|
|
|
|
list_add(&mnt->mnt_slave, &old->mnt_slave);
|
|
|
|
mnt->mnt_master = old->mnt_master;
|
|
|
|
}
|
|
|
|
if (flag & CL_MAKE_SHARED)
|
|
|
|
set_mnt_shared(mnt);
|
|
|
|
|
|
|
|
/* stick the duplicate mount on the same expiry list
|
|
|
|
* as the original if that was on one */
|
|
|
|
if (flag & CL_EXPIRE) {
|
|
|
|
if (!list_empty(&old->mnt_expire))
|
|
|
|
list_add(&mnt->mnt_expire, &old->mnt_expire);
|
|
|
|
}
|
|
|
|
|
2011-11-25 09:55:08 +08:00
|
|
|
return mnt;
|
2008-03-27 20:06:23 +08:00
|
|
|
|
|
|
|
out_free:
|
|
|
|
free_vfsmnt(mnt);
|
2012-06-25 19:55:18 +08:00
|
|
|
return ERR_PTR(err);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-11-25 11:37:54 +08:00
|
|
|
static inline void mntfree(struct mount *mnt)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-25 11:37:54 +08:00
|
|
|
struct vfsmount *m = &mnt->mnt;
|
|
|
|
struct super_block *sb = m->mnt_sb;
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
|
[PATCH] r/o bind mounts: track numbers of writers to mounts
This is the real meat of the entire series. It actually
implements the tracking of the number of writers to a mount.
However, it causes scalability problems because there can be
hundreds of cpus doing open()/close() on files on the same mnt at
the same time. Even an atomic_t in the mnt has massive scalaing
problems because the cacheline gets so terribly contended.
This uses a statically-allocated percpu variable. All want/drop
operations are local to a cpu as long that cpu operates on the same
mount, and there are no writer count imbalances. Writer count
imbalances happen when a write is taken on one cpu, and released
on another, like when an open/close pair is performed on two
Upon a remount,ro request, all of the data from the percpu
variables is collected (expensive, but very rare) and we determine
if there are any outstanding writers to the mount.
I've written a little benchmark to sit in a loop for a couple of
seconds in several cpus in parallel doing open/write/close loops.
http://sr71.net/~dave/linux/openbench.c
The code in here is a a worst-possible case for this patch. It
does opens on a _pair_ of files in two different mounts in parallel.
This should cause my code to lose its "operate on the same mount"
optimization completely. This worst-case scenario causes a 3%
degredation in the benchmark.
I could probably get rid of even this 3%, but it would be more
complex than what I have here, and I think this is getting into
acceptable territory. In practice, I expect writing more than 3
bytes to a file, as well as disk I/O to mask any effects that this
has.
(To get rid of that 3%, we could have an #defined number of mounts
in the percpu variable. So, instead of a CPU getting operate only
on percpu data when it accesses only one mount, it could stay on
percpu data when it only accesses N or fewer mounts.)
[AV] merged fix for __clear_mnt_mount() stepping on freed vfsmount
Acked-by: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Christoph Hellwig <hch@infradead.org>
Signed-off-by: Dave Hansen <haveblue@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2008-02-16 06:37:59 +08:00
|
|
|
/*
|
|
|
|
* This probably indicates that somebody messed
|
|
|
|
* up a mnt_want/drop_write() pair. If this
|
|
|
|
* happens, the filesystem was probably unable
|
|
|
|
* to make r/w->r/o transitions.
|
|
|
|
*/
|
2009-04-26 18:25:54 +08:00
|
|
|
/*
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
* The locking used to deal with mnt_count decrement provides barriers,
|
|
|
|
* so mnt_get_writers() below is safe.
|
2009-04-26 18:25:54 +08:00
|
|
|
*/
|
2011-01-07 14:50:10 +08:00
|
|
|
WARN_ON(mnt_get_writers(mnt));
|
2011-11-25 11:37:54 +08:00
|
|
|
fsnotify_vfsmount_delete(m);
|
|
|
|
dput(m->mnt_root);
|
|
|
|
free_vfsmnt(mnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
deactivate_super(sb);
|
|
|
|
}
|
|
|
|
|
2011-11-25 13:33:11 +08:00
|
|
|
static void mntput_no_expire(struct mount *mnt)
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
{
|
|
|
|
put_again:
|
2011-01-15 11:30:21 +08:00
|
|
|
#ifdef CONFIG_SMP
|
2012-05-08 12:02:02 +08:00
|
|
|
br_read_lock(&vfsmount_lock);
|
2012-06-09 12:59:08 +08:00
|
|
|
if (likely(mnt->mnt_ns)) {
|
|
|
|
/* shouldn't be the last one */
|
2011-11-24 08:04:52 +08:00
|
|
|
mnt_add_count(mnt, -1);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_read_unlock(&vfsmount_lock);
|
2011-01-15 11:30:21 +08:00
|
|
|
return;
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
br_read_unlock(&vfsmount_lock);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-24 08:04:52 +08:00
|
|
|
mnt_add_count(mnt, -1);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
if (mnt_get_count(mnt)) {
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
return;
|
|
|
|
}
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
#else
|
2011-11-24 08:04:52 +08:00
|
|
|
mnt_add_count(mnt, -1);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
if (likely(mnt_get_count(mnt)))
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
return;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-01-15 11:30:21 +08:00
|
|
|
#endif
|
2011-11-25 13:57:42 +08:00
|
|
|
if (unlikely(mnt->mnt_pinned)) {
|
|
|
|
mnt_add_count(mnt, mnt->mnt_pinned + 1);
|
|
|
|
mnt->mnt_pinned = 0;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2011-11-25 13:33:11 +08:00
|
|
|
acct_auto_close_mnt(&mnt->mnt);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
goto put_again;
|
2005-11-08 06:13:39 +08:00
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
|
2011-11-21 19:11:30 +08:00
|
|
|
list_del(&mnt->mnt_instance);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
mntfree(mnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
void mntput(struct vfsmount *mnt)
|
|
|
|
{
|
|
|
|
if (mnt) {
|
2011-11-25 13:57:42 +08:00
|
|
|
struct mount *m = real_mount(mnt);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
/* avoid cacheline pingpong, hope gcc doesn't get "smart" */
|
2011-11-25 13:57:42 +08:00
|
|
|
if (unlikely(m->mnt_expiry_mark))
|
|
|
|
m->mnt_expiry_mark = 0;
|
|
|
|
mntput_no_expire(m);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mntput);
|
|
|
|
|
|
|
|
struct vfsmount *mntget(struct vfsmount *mnt)
|
|
|
|
{
|
|
|
|
if (mnt)
|
2011-11-25 11:37:54 +08:00
|
|
|
mnt_add_count(real_mount(mnt), 1);
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
return mnt;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mntget);
|
|
|
|
|
2005-11-08 06:13:39 +08:00
|
|
|
void mnt_pin(struct vfsmount *mnt)
|
|
|
|
{
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 13:57:42 +08:00
|
|
|
real_mount(mnt)->mnt_pinned++;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-11-08 06:13:39 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mnt_pin);
|
|
|
|
|
2011-11-25 13:57:42 +08:00
|
|
|
void mnt_unpin(struct vfsmount *m)
|
2005-11-08 06:13:39 +08:00
|
|
|
{
|
2011-11-25 13:57:42 +08:00
|
|
|
struct mount *mnt = real_mount(m);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2005-11-08 06:13:39 +08:00
|
|
|
if (mnt->mnt_pinned) {
|
2011-11-25 13:57:42 +08:00
|
|
|
mnt_add_count(mnt, 1);
|
2005-11-08 06:13:39 +08:00
|
|
|
mnt->mnt_pinned--;
|
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-11-08 06:13:39 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mnt_unpin);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-02-08 20:21:35 +08:00
|
|
|
static inline void mangle(struct seq_file *m, const char *s)
|
|
|
|
{
|
|
|
|
seq_escape(m, s, " \t\n\\");
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Simple .show_options callback for filesystems which don't want to
|
|
|
|
* implement more complex mount option showing.
|
|
|
|
*
|
|
|
|
* See also save_mount_options().
|
|
|
|
*/
|
2011-12-09 10:32:45 +08:00
|
|
|
int generic_show_options(struct seq_file *m, struct dentry *root)
|
2008-02-08 20:21:35 +08:00
|
|
|
{
|
2009-05-09 04:05:57 +08:00
|
|
|
const char *options;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
2011-12-09 10:32:45 +08:00
|
|
|
options = rcu_dereference(root->d_sb->s_options);
|
2008-02-08 20:21:35 +08:00
|
|
|
|
|
|
|
if (options != NULL && options[0]) {
|
|
|
|
seq_putc(m, ',');
|
|
|
|
mangle(m, options);
|
|
|
|
}
|
2009-05-09 04:05:57 +08:00
|
|
|
rcu_read_unlock();
|
2008-02-08 20:21:35 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(generic_show_options);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If filesystem uses generic_show_options(), this function should be
|
|
|
|
* called from the fill_super() callback.
|
|
|
|
*
|
|
|
|
* The .remount_fs callback usually needs to be handled in a special
|
|
|
|
* way, to make sure, that previous options are not overwritten if the
|
|
|
|
* remount fails.
|
|
|
|
*
|
|
|
|
* Also note, that if the filesystem's .remount_fs function doesn't
|
|
|
|
* reset all options to their default value, but changes only newly
|
|
|
|
* given options, then the displayed options will not reflect reality
|
|
|
|
* any more.
|
|
|
|
*/
|
|
|
|
void save_mount_options(struct super_block *sb, char *options)
|
|
|
|
{
|
2009-05-09 04:05:57 +08:00
|
|
|
BUG_ON(sb->s_options);
|
|
|
|
rcu_assign_pointer(sb->s_options, kstrdup(options, GFP_KERNEL));
|
2008-02-08 20:21:35 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(save_mount_options);
|
|
|
|
|
2009-05-09 04:05:57 +08:00
|
|
|
void replace_mount_options(struct super_block *sb, char *options)
|
|
|
|
{
|
|
|
|
char *old = sb->s_options;
|
|
|
|
rcu_assign_pointer(sb->s_options, options);
|
|
|
|
if (old) {
|
|
|
|
synchronize_rcu();
|
|
|
|
kfree(old);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(replace_mount_options);
|
|
|
|
|
2008-03-27 20:06:24 +08:00
|
|
|
#ifdef CONFIG_PROC_FS
|
2011-12-07 01:21:54 +08:00
|
|
|
/* iterator; we want it to have access to namespace_sem, thus here... */
|
2005-04-17 06:20:36 +08:00
|
|
|
static void *m_start(struct seq_file *m, loff_t *pos)
|
|
|
|
{
|
2012-06-09 13:16:59 +08:00
|
|
|
struct proc_mounts *p = proc_mounts(m);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-08 06:17:51 +08:00
|
|
|
down_read(&namespace_sem);
|
2008-03-27 20:06:24 +08:00
|
|
|
return seq_list_start(&p->ns->list, *pos);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void *m_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
|
{
|
2012-06-09 13:16:59 +08:00
|
|
|
struct proc_mounts *p = proc_mounts(m);
|
2007-07-16 14:39:55 +08:00
|
|
|
|
2008-03-27 20:06:24 +08:00
|
|
|
return seq_list_next(v, &p->ns->list, pos);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void m_stop(struct seq_file *m, void *v)
|
|
|
|
{
|
2005-11-08 06:17:51 +08:00
|
|
|
up_read(&namespace_sem);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-12-07 01:21:54 +08:00
|
|
|
static int m_show(struct seq_file *m, void *v)
|
2008-03-27 20:06:25 +08:00
|
|
|
{
|
2012-06-09 13:16:59 +08:00
|
|
|
struct proc_mounts *p = proc_mounts(m);
|
2011-11-25 15:19:55 +08:00
|
|
|
struct mount *r = list_entry(v, struct mount, mnt_list);
|
2011-12-07 01:21:54 +08:00
|
|
|
return p->show(m, &r->mnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2008-03-27 20:06:24 +08:00
|
|
|
const struct seq_operations mounts_op = {
|
2005-04-17 06:20:36 +08:00
|
|
|
.start = m_start,
|
|
|
|
.next = m_next,
|
|
|
|
.stop = m_stop,
|
2011-12-07 01:21:54 +08:00
|
|
|
.show = m_show,
|
2006-03-21 02:44:12 +08:00
|
|
|
};
|
2008-03-27 20:06:24 +08:00
|
|
|
#endif /* CONFIG_PROC_FS */
|
2006-03-21 02:44:12 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/**
|
|
|
|
* may_umount_tree - check if a mount tree is busy
|
|
|
|
* @mnt: root of mount tree
|
|
|
|
*
|
|
|
|
* This is called to check if a tree of mounts has any
|
|
|
|
* open files, pwds, chroots or sub mounts that are
|
|
|
|
* busy.
|
|
|
|
*/
|
2011-11-25 16:06:56 +08:00
|
|
|
int may_umount_tree(struct vfsmount *m)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-25 16:06:56 +08:00
|
|
|
struct mount *mnt = real_mount(m);
|
2005-11-08 06:17:22 +08:00
|
|
|
int actual_refs = 0;
|
|
|
|
int minimum_refs = 0;
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *p;
|
2011-11-25 16:06:56 +08:00
|
|
|
BUG_ON(!m);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
/* write lock needed for mnt_get_count */
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 16:06:56 +08:00
|
|
|
for (p = mnt; p; p = next_mnt(p, mnt)) {
|
2011-11-25 11:37:54 +08:00
|
|
|
actual_refs += mnt_get_count(p);
|
2005-04-17 06:20:36 +08:00
|
|
|
minimum_refs += 2;
|
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (actual_refs > minimum_refs)
|
2006-03-27 17:14:51 +08:00
|
|
|
return 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-03-27 17:14:51 +08:00
|
|
|
return 1;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(may_umount_tree);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* may_umount - check if a mount point is busy
|
|
|
|
* @mnt: root of mount
|
|
|
|
*
|
|
|
|
* This is called to check if a mount point has any
|
|
|
|
* open files, pwds, chroots or sub mounts. If the
|
|
|
|
* mount has sub mounts this will return busy
|
|
|
|
* regardless of whether the sub mounts are busy.
|
|
|
|
*
|
|
|
|
* Doesn't take quota and stuff into account. IOW, in some cases it will
|
|
|
|
* give false negatives. The main reason why it's here is that we need
|
|
|
|
* a non-destructive way to look for easily umountable filesystems.
|
|
|
|
*/
|
|
|
|
int may_umount(struct vfsmount *mnt)
|
|
|
|
{
|
2006-03-27 17:14:51 +08:00
|
|
|
int ret = 1;
|
2010-01-17 01:56:08 +08:00
|
|
|
down_read(&namespace_sem);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 10:35:16 +08:00
|
|
|
if (propagate_mount_busy(real_mount(mnt), 2))
|
2006-03-27 17:14:51 +08:00
|
|
|
ret = 0;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2010-01-17 01:56:08 +08:00
|
|
|
up_read(&namespace_sem);
|
2005-11-08 06:20:17 +08:00
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(may_umount);
|
|
|
|
|
2013-03-17 02:35:16 +08:00
|
|
|
static LIST_HEAD(unmounted); /* protected by namespace_sem */
|
|
|
|
|
2013-03-17 03:12:40 +08:00
|
|
|
static void namespace_unlock(void)
|
2005-11-08 06:17:04 +08:00
|
|
|
{
|
2011-11-25 09:58:57 +08:00
|
|
|
struct mount *mnt;
|
2013-03-17 03:12:40 +08:00
|
|
|
LIST_HEAD(head);
|
|
|
|
|
|
|
|
if (likely(list_empty(&unmounted))) {
|
|
|
|
up_write(&namespace_sem);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
list_splice_init(&unmounted, &head);
|
|
|
|
up_write(&namespace_sem);
|
|
|
|
|
|
|
|
while (!list_empty(&head)) {
|
|
|
|
mnt = list_first_entry(&head, struct mount, mnt_hash);
|
2011-11-25 10:01:32 +08:00
|
|
|
list_del_init(&mnt->mnt_hash);
|
2011-11-25 10:47:05 +08:00
|
|
|
if (mnt_has_parent(mnt)) {
|
2005-11-08 06:17:04 +08:00
|
|
|
struct dentry *dentry;
|
2011-11-25 13:57:42 +08:00
|
|
|
struct mount *m;
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 11:25:07 +08:00
|
|
|
dentry = mnt->mnt_mountpoint;
|
2011-11-25 13:57:42 +08:00
|
|
|
m = mnt->mnt_parent;
|
2011-11-25 11:25:07 +08:00
|
|
|
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
|
2011-11-25 11:19:58 +08:00
|
|
|
mnt->mnt_parent = mnt;
|
2008-03-22 11:59:49 +08:00
|
|
|
m->mnt_ghosts--;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-11-08 06:17:04 +08:00
|
|
|
dput(dentry);
|
2011-11-25 13:57:42 +08:00
|
|
|
mntput(&m->mnt);
|
2005-11-08 06:17:04 +08:00
|
|
|
}
|
2011-11-25 09:58:57 +08:00
|
|
|
mntput(&mnt->mnt);
|
2005-11-08 06:17:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-03-17 03:12:40 +08:00
|
|
|
static inline void namespace_lock(void)
|
2013-03-17 02:35:16 +08:00
|
|
|
{
|
2013-03-17 03:12:40 +08:00
|
|
|
down_write(&namespace_sem);
|
2013-03-17 02:35:16 +08:00
|
|
|
}
|
|
|
|
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
/*
|
|
|
|
* vfsmount lock must be held for write
|
|
|
|
* namespace_sem must be held for write
|
|
|
|
*/
|
2013-03-17 02:49:45 +08:00
|
|
|
void umount_tree(struct mount *mnt, int propagate)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-01-16 09:08:44 +08:00
|
|
|
LIST_HEAD(tmp_list);
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *p;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-25 16:06:56 +08:00
|
|
|
for (p = mnt; p; p = next_mnt(p, mnt))
|
2011-11-25 10:01:32 +08:00
|
|
|
list_move(&p->mnt_hash, &tmp_list);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-08 06:20:17 +08:00
|
|
|
if (propagate)
|
2011-01-16 09:08:44 +08:00
|
|
|
propagate_umount(&tmp_list);
|
2005-11-08 06:20:17 +08:00
|
|
|
|
2011-11-25 10:01:32 +08:00
|
|
|
list_for_each_entry(p, &tmp_list, mnt_hash) {
|
2011-11-25 13:22:05 +08:00
|
|
|
list_del_init(&p->mnt_expire);
|
2011-11-25 15:19:55 +08:00
|
|
|
list_del_init(&p->mnt_list);
|
2011-11-25 13:46:35 +08:00
|
|
|
__touch_mnt_namespace(p->mnt_ns);
|
|
|
|
p->mnt_ns = NULL;
|
2011-11-25 12:24:33 +08:00
|
|
|
list_del_init(&p->mnt_child);
|
2011-11-25 10:47:05 +08:00
|
|
|
if (mnt_has_parent(p)) {
|
2011-11-25 13:57:42 +08:00
|
|
|
p->mnt_parent->mnt_ghosts++;
|
2013-03-15 22:53:28 +08:00
|
|
|
put_mountpoint(p->mnt_mp);
|
|
|
|
p->mnt_mp = NULL;
|
2008-03-22 11:59:49 +08:00
|
|
|
}
|
2011-11-25 09:43:10 +08:00
|
|
|
change_mnt_propagation(p, MS_PRIVATE);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-03-17 02:49:45 +08:00
|
|
|
list_splice(&tmp_list, &unmounted);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2013-03-17 02:39:34 +08:00
|
|
|
static void shrink_submounts(struct mount *mnt);
|
2008-03-22 12:46:23 +08:00
|
|
|
|
2011-11-25 10:35:16 +08:00
|
|
|
static int do_umount(struct mount *mnt, int flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-25 10:35:16 +08:00
|
|
|
struct super_block *sb = mnt->mnt.mnt_sb;
|
2005-04-17 06:20:36 +08:00
|
|
|
int retval;
|
|
|
|
|
2011-11-25 10:35:16 +08:00
|
|
|
retval = security_sb_umount(&mnt->mnt, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Allow userspace to request a mountpoint be expired rather than
|
|
|
|
* unmounting unconditionally. Unmount only happens if:
|
|
|
|
* (1) the mark is already set (the mark is cleared by mntput())
|
|
|
|
* (2) the usage count == 1 [parent vfsmount] + 1 [sys_umount]
|
|
|
|
*/
|
|
|
|
if (flags & MNT_EXPIRE) {
|
2011-11-25 10:35:16 +08:00
|
|
|
if (&mnt->mnt == current->fs->root.mnt ||
|
2005-04-17 06:20:36 +08:00
|
|
|
flags & (MNT_FORCE | MNT_DETACH))
|
|
|
|
return -EINVAL;
|
|
|
|
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
/*
|
|
|
|
* probably don't strictly need the lock here if we examined
|
|
|
|
* all race cases, but it's a slowpath.
|
|
|
|
*/
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 11:37:54 +08:00
|
|
|
if (mnt_get_count(mnt) != 2) {
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EBUSY;
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-25 13:57:42 +08:00
|
|
|
if (!xchg(&mnt->mnt_expiry_mark, 1))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If we may have to abort operations to get out of this
|
|
|
|
* mount, and they will themselves hold resources we must
|
|
|
|
* allow the fs to do things. In the Unix tradition of
|
|
|
|
* 'Gee thats tricky lets do it in userspace' the umount_begin
|
|
|
|
* might fail to complete on the first run through as other tasks
|
|
|
|
* must return, and the like. Thats for the mount program to worry
|
|
|
|
* about for the moment.
|
|
|
|
*/
|
|
|
|
|
2008-04-24 19:21:56 +08:00
|
|
|
if (flags & MNT_FORCE && sb->s_op->umount_begin) {
|
|
|
|
sb->s_op->umount_begin(sb);
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* No sense to grab the lock for this test, but test itself looks
|
|
|
|
* somewhat bogus. Suggestions for better replacement?
|
|
|
|
* Ho-hum... In principle, we might treat that as umount + switch
|
|
|
|
* to rootfs. GC would eventually take care of the old vfsmount.
|
|
|
|
* Actually it makes sense, especially if rootfs would contain a
|
|
|
|
* /reboot - static binary that would close all descriptors and
|
|
|
|
* call reboot(9). Then init(8) could umount root and exec /reboot.
|
|
|
|
*/
|
2011-11-25 10:35:16 +08:00
|
|
|
if (&mnt->mnt == current->fs->root.mnt && !(flags & MNT_DETACH)) {
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Special case for "unmounting" root ...
|
|
|
|
* we just try to remount it readonly.
|
|
|
|
*/
|
|
|
|
down_write(&sb->s_umount);
|
2009-05-09 01:36:58 +08:00
|
|
|
if (!(sb->s_flags & MS_RDONLY))
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = do_remount_sb(sb, MS_RDONLY, NULL, 0);
|
|
|
|
up_write(&sb->s_umount);
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2005-11-08 06:15:49 +08:00
|
|
|
event++;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-03-22 12:46:23 +08:00
|
|
|
if (!(flags & MNT_DETACH))
|
2013-03-17 02:39:34 +08:00
|
|
|
shrink_submounts(mnt);
|
2008-03-22 12:46:23 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -EBUSY;
|
2005-11-08 06:20:17 +08:00
|
|
|
if (flags & MNT_DETACH || !propagate_mount_busy(mnt, 2)) {
|
2011-11-25 15:19:55 +08:00
|
|
|
if (!list_empty(&mnt->mnt_list))
|
2013-03-17 02:49:45 +08:00
|
|
|
umount_tree(mnt, 1);
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = 0;
|
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2013-03-17 02:35:16 +08:00
|
|
|
namespace_unlock();
|
2005-04-17 06:20:36 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2013-02-23 11:45:42 +08:00
|
|
|
/*
|
|
|
|
* Is the caller allowed to modify his namespace?
|
|
|
|
*/
|
|
|
|
static inline bool may_mount(void)
|
|
|
|
{
|
|
|
|
return ns_capable(current->nsproxy->mnt_ns->user_ns, CAP_SYS_ADMIN);
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Now umount can handle mount points as well as block devices.
|
|
|
|
* This is important for filesystems which use unnamed block devices.
|
|
|
|
*
|
|
|
|
* We now support a flag for forced unmount like the other 'big iron'
|
|
|
|
* unixes. Our API is identical to OSF/1 to avoid making a mess of AMD
|
|
|
|
*/
|
|
|
|
|
2009-01-14 21:14:12 +08:00
|
|
|
SYSCALL_DEFINE2(umount, char __user *, name, int, flags)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-22 21:59:21 +08:00
|
|
|
struct path path;
|
2011-11-25 13:33:11 +08:00
|
|
|
struct mount *mnt;
|
2005-04-17 06:20:36 +08:00
|
|
|
int retval;
|
2010-02-10 19:15:53 +08:00
|
|
|
int lookup_flags = 0;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-02-10 19:15:53 +08:00
|
|
|
if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW))
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-02-23 11:45:42 +08:00
|
|
|
if (!may_mount())
|
|
|
|
return -EPERM;
|
|
|
|
|
2010-02-10 19:15:53 +08:00
|
|
|
if (!(flags & UMOUNT_NOFOLLOW))
|
|
|
|
lookup_flags |= LOOKUP_FOLLOW;
|
|
|
|
|
|
|
|
retval = user_path_at(AT_FDCWD, name, lookup_flags, &path);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (retval)
|
|
|
|
goto out;
|
2011-11-25 13:33:11 +08:00
|
|
|
mnt = real_mount(path.mnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
retval = -EINVAL;
|
2008-07-22 21:59:21 +08:00
|
|
|
if (path.dentry != path.mnt->mnt_root)
|
2005-04-17 06:20:36 +08:00
|
|
|
goto dput_and_out;
|
2011-11-25 13:46:35 +08:00
|
|
|
if (!check_mnt(mnt))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto dput_and_out;
|
|
|
|
|
2011-11-25 13:33:11 +08:00
|
|
|
retval = do_umount(mnt, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
dput_and_out:
|
2008-02-15 11:34:31 +08:00
|
|
|
/* we mustn't call path_put() as that would clear mnt_expiry_mark */
|
2008-07-22 21:59:21 +08:00
|
|
|
dput(path.dentry);
|
2011-11-25 13:33:11 +08:00
|
|
|
mntput_no_expire(mnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef __ARCH_WANT_SYS_OLDUMOUNT
|
|
|
|
|
|
|
|
/*
|
2005-11-08 06:16:09 +08:00
|
|
|
* The 2.0 compatible umount. No flags.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2009-01-14 21:14:12 +08:00
|
|
|
SYSCALL_DEFINE1(oldumount, char __user *, name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2005-11-08 06:16:09 +08:00
|
|
|
return sys_umount(name, 0);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
2010-03-08 10:49:36 +08:00
|
|
|
static bool mnt_ns_loop(struct path *path)
|
|
|
|
{
|
|
|
|
/* Could bind mounting the mount namespace inode cause a
|
|
|
|
* mount namespace loop?
|
|
|
|
*/
|
|
|
|
struct inode *inode = path->dentry->d_inode;
|
2013-04-12 08:50:06 +08:00
|
|
|
struct proc_ns *ei;
|
2010-03-08 10:49:36 +08:00
|
|
|
struct mnt_namespace *mnt_ns;
|
|
|
|
|
|
|
|
if (!proc_ns_inode(inode))
|
|
|
|
return false;
|
|
|
|
|
2013-04-12 08:50:06 +08:00
|
|
|
ei = get_proc_ns(inode);
|
2010-03-08 10:49:36 +08:00
|
|
|
if (ei->ns_ops != &mntns_operations)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
mnt_ns = ei->ns;
|
|
|
|
return current->nsproxy->mnt_ns->seq >= mnt_ns->seq;
|
|
|
|
}
|
|
|
|
|
2011-11-25 10:24:27 +08:00
|
|
|
struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
|
2005-11-08 06:17:22 +08:00
|
|
|
int flag)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-03-15 22:53:28 +08:00
|
|
|
struct mount *res, *p, *q, *r, *parent;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-25 14:05:37 +08:00
|
|
|
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(mnt))
|
2012-06-25 19:55:18 +08:00
|
|
|
return ERR_PTR(-EINVAL);
|
2005-11-08 06:21:20 +08:00
|
|
|
|
2005-11-08 06:17:22 +08:00
|
|
|
res = q = clone_mnt(mnt, dentry, flag);
|
2012-06-25 19:55:18 +08:00
|
|
|
if (IS_ERR(q))
|
|
|
|
return q;
|
|
|
|
|
2011-11-25 11:25:07 +08:00
|
|
|
q->mnt_mountpoint = mnt->mnt_mountpoint;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
p = mnt;
|
2011-11-25 12:24:33 +08:00
|
|
|
list_for_each_entry(r, &mnt->mnt_mounts, mnt_child) {
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *s;
|
2008-04-29 15:59:40 +08:00
|
|
|
if (!is_subdir(r->mnt_mountpoint, dentry))
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
|
|
|
|
2011-11-25 16:06:56 +08:00
|
|
|
for (s = r; s; s = next_mnt(s, r)) {
|
2011-11-25 14:05:37 +08:00
|
|
|
if (!(flag & CL_COPY_ALL) && IS_MNT_UNBINDABLE(s)) {
|
2005-11-08 06:21:20 +08:00
|
|
|
s = skip_mnt_tree(s);
|
|
|
|
continue;
|
|
|
|
}
|
2011-11-25 11:19:58 +08:00
|
|
|
while (p != s->mnt_parent) {
|
|
|
|
p = p->mnt_parent;
|
|
|
|
q = q->mnt_parent;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-11-25 10:24:27 +08:00
|
|
|
p = s;
|
2013-03-15 22:53:28 +08:00
|
|
|
parent = q;
|
2011-11-25 10:24:27 +08:00
|
|
|
q = clone_mnt(p, p->mnt.mnt_root, flag);
|
2012-06-25 19:55:18 +08:00
|
|
|
if (IS_ERR(q))
|
|
|
|
goto out;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 15:19:55 +08:00
|
|
|
list_add_tail(&q->mnt_list, &res->mnt_list);
|
2013-03-15 22:53:28 +08:00
|
|
|
attach_mnt(q, parent, p->mnt_mp);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
2012-06-25 19:55:18 +08:00
|
|
|
out:
|
2005-04-17 06:20:36 +08:00
|
|
|
if (res) {
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2013-03-17 02:49:45 +08:00
|
|
|
umount_tree(res, 0);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2012-06-25 19:55:18 +08:00
|
|
|
return q;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2012-06-25 19:55:18 +08:00
|
|
|
/* Caller should check returned pointer for errors */
|
|
|
|
|
2009-04-18 15:28:19 +08:00
|
|
|
struct vfsmount *collect_mounts(struct path *path)
|
2007-06-08 00:20:32 +08:00
|
|
|
{
|
2011-11-25 09:55:08 +08:00
|
|
|
struct mount *tree;
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2011-11-25 10:24:27 +08:00
|
|
|
tree = copy_tree(real_mount(path->mnt), path->dentry,
|
|
|
|
CL_COPY_ALL | CL_PRIVATE);
|
2013-03-17 02:49:45 +08:00
|
|
|
namespace_unlock();
|
2012-06-25 19:55:18 +08:00
|
|
|
if (IS_ERR(tree))
|
2013-08-14 17:44:39 +08:00
|
|
|
return ERR_CAST(tree);
|
2012-06-25 19:55:18 +08:00
|
|
|
return &tree->mnt;
|
2007-06-08 00:20:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void drop_collected_mounts(struct vfsmount *mnt)
|
|
|
|
{
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2013-03-17 02:49:45 +08:00
|
|
|
umount_tree(real_mount(mnt), 0);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2013-03-17 02:42:19 +08:00
|
|
|
namespace_unlock();
|
2007-06-08 00:20:32 +08:00
|
|
|
}
|
|
|
|
|
2010-01-31 11:51:25 +08:00
|
|
|
int iterate_mounts(int (*f)(struct vfsmount *, void *), void *arg,
|
|
|
|
struct vfsmount *root)
|
|
|
|
{
|
2011-11-25 15:19:55 +08:00
|
|
|
struct mount *mnt;
|
2010-01-31 11:51:25 +08:00
|
|
|
int res = f(root, arg);
|
|
|
|
if (res)
|
|
|
|
return res;
|
2011-11-25 15:19:55 +08:00
|
|
|
list_for_each_entry(mnt, &real_mount(root)->mnt_list, mnt_list) {
|
|
|
|
res = f(&mnt->mnt, arg);
|
2010-01-31 11:51:25 +08:00
|
|
|
if (res)
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-11-25 08:54:23 +08:00
|
|
|
static void cleanup_group_ids(struct mount *mnt, struct mount *end)
|
2008-03-27 20:06:23 +08:00
|
|
|
{
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *p;
|
2008-03-27 20:06:23 +08:00
|
|
|
|
2011-11-25 16:06:56 +08:00
|
|
|
for (p = mnt; p != end; p = next_mnt(p, mnt)) {
|
2011-11-25 14:05:37 +08:00
|
|
|
if (p->mnt_group_id && !IS_MNT_SHARED(p))
|
2011-11-25 08:54:23 +08:00
|
|
|
mnt_release_group_id(p);
|
2008-03-27 20:06:23 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-11-25 08:54:23 +08:00
|
|
|
static int invent_group_ids(struct mount *mnt, bool recurse)
|
2008-03-27 20:06:23 +08:00
|
|
|
{
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *p;
|
2008-03-27 20:06:23 +08:00
|
|
|
|
2011-11-25 16:06:56 +08:00
|
|
|
for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) {
|
2011-11-25 14:05:37 +08:00
|
|
|
if (!p->mnt_group_id && !IS_MNT_SHARED(p)) {
|
2011-11-25 08:54:23 +08:00
|
|
|
int err = mnt_alloc_group_id(p);
|
2008-03-27 20:06:23 +08:00
|
|
|
if (err) {
|
2011-11-25 08:54:23 +08:00
|
|
|
cleanup_group_ids(mnt, p);
|
2008-03-27 20:06:23 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-11-08 06:19:50 +08:00
|
|
|
/*
|
|
|
|
* @source_mnt : mount tree to be attached
|
2005-11-08 06:20:03 +08:00
|
|
|
* @nd : place the mount tree @source_mnt is attached
|
|
|
|
* @parent_nd : if non-null, detach the source_mnt from its parent and
|
|
|
|
* store the parent mount and mountpoint dentry.
|
|
|
|
* (done when source_mnt is moved)
|
2005-11-08 06:19:50 +08:00
|
|
|
*
|
|
|
|
* NOTE: in the table below explains the semantics when a source mount
|
|
|
|
* of a given type is attached to a destination mount of a given type.
|
2005-11-08 06:21:20 +08:00
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
* | BIND MOUNT OPERATION |
|
|
|
|
* |**************************************************************************
|
|
|
|
* | source-->| shared | private | slave | unbindable |
|
|
|
|
* | dest | | | | |
|
|
|
|
* | | | | | | |
|
|
|
|
* | v | | | | |
|
|
|
|
* |**************************************************************************
|
|
|
|
* | shared | shared (++) | shared (+) | shared(+++)| invalid |
|
|
|
|
* | | | | | |
|
|
|
|
* |non-shared| shared (+) | private | slave (*) | invalid |
|
|
|
|
* ***************************************************************************
|
2005-11-08 06:19:50 +08:00
|
|
|
* A bind operation clones the source mount and mounts the clone on the
|
|
|
|
* destination mount.
|
|
|
|
*
|
|
|
|
* (++) the cloned mount is propagated to all the mounts in the propagation
|
|
|
|
* tree of the destination mount and the cloned mount is added to
|
|
|
|
* the peer group of the source mount.
|
|
|
|
* (+) the cloned mount is created under the destination mount and is marked
|
|
|
|
* as shared. The cloned mount is added to the peer group of the source
|
|
|
|
* mount.
|
2005-11-08 06:21:01 +08:00
|
|
|
* (+++) the mount is propagated to all the mounts in the propagation tree
|
|
|
|
* of the destination mount and the cloned mount is made slave
|
|
|
|
* of the same master as that of the source mount. The cloned mount
|
|
|
|
* is marked as 'shared and slave'.
|
|
|
|
* (*) the cloned mount is made a slave of the same master as that of the
|
|
|
|
* source mount.
|
|
|
|
*
|
2005-11-08 06:21:20 +08:00
|
|
|
* ---------------------------------------------------------------------------
|
|
|
|
* | MOVE MOUNT OPERATION |
|
|
|
|
* |**************************************************************************
|
|
|
|
* | source-->| shared | private | slave | unbindable |
|
|
|
|
* | dest | | | | |
|
|
|
|
* | | | | | | |
|
|
|
|
* | v | | | | |
|
|
|
|
* |**************************************************************************
|
|
|
|
* | shared | shared (+) | shared (+) | shared(+++) | invalid |
|
|
|
|
* | | | | | |
|
|
|
|
* |non-shared| shared (+*) | private | slave (*) | unbindable |
|
|
|
|
* ***************************************************************************
|
2005-11-08 06:21:01 +08:00
|
|
|
*
|
|
|
|
* (+) the mount is moved to the destination. And is then propagated to
|
|
|
|
* all the mounts in the propagation tree of the destination mount.
|
2005-11-08 06:20:03 +08:00
|
|
|
* (+*) the mount is moved to the destination.
|
2005-11-08 06:21:01 +08:00
|
|
|
* (+++) the mount is moved to the destination and is then propagated to
|
|
|
|
* all the mounts belonging to the destination mount's propagation tree.
|
|
|
|
* the mount is marked as 'shared and slave'.
|
|
|
|
* (*) the mount continues to be a slave at the new location.
|
2005-11-08 06:19:50 +08:00
|
|
|
*
|
|
|
|
* if the source mount is a tree, the operations explained above is
|
|
|
|
* applied to each mount in the tree.
|
|
|
|
* Must be called without spinlocks held, since this function can sleep
|
|
|
|
* in allocations.
|
|
|
|
*/
|
2011-11-25 08:59:16 +08:00
|
|
|
static int attach_recursive_mnt(struct mount *source_mnt,
|
2013-03-15 22:53:28 +08:00
|
|
|
struct mount *dest_mnt,
|
|
|
|
struct mountpoint *dest_mp,
|
|
|
|
struct path *parent_path)
|
2005-11-08 06:19:50 +08:00
|
|
|
{
|
|
|
|
LIST_HEAD(tree_list);
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *child, *p;
|
2008-03-27 20:06:23 +08:00
|
|
|
int err;
|
2005-11-08 06:19:50 +08:00
|
|
|
|
2011-11-25 14:05:37 +08:00
|
|
|
if (IS_MNT_SHARED(dest_mnt)) {
|
2011-11-25 08:59:16 +08:00
|
|
|
err = invent_group_ids(source_mnt, true);
|
2008-03-27 20:06:23 +08:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
2013-03-15 22:53:28 +08:00
|
|
|
err = propagate_mnt(dest_mnt, dest_mp, source_mnt, &tree_list);
|
2008-03-27 20:06:23 +08:00
|
|
|
if (err)
|
|
|
|
goto out_cleanup_ids;
|
2005-11-08 06:19:50 +08:00
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2010-01-17 01:57:40 +08:00
|
|
|
|
2011-11-25 14:05:37 +08:00
|
|
|
if (IS_MNT_SHARED(dest_mnt)) {
|
2011-11-25 16:06:56 +08:00
|
|
|
for (p = source_mnt; p; p = next_mnt(p, source_mnt))
|
2011-11-25 09:43:10 +08:00
|
|
|
set_mnt_shared(p);
|
2005-11-08 06:19:50 +08:00
|
|
|
}
|
2008-03-22 08:48:19 +08:00
|
|
|
if (parent_path) {
|
2011-11-25 08:59:16 +08:00
|
|
|
detach_mnt(source_mnt, parent_path);
|
2013-03-15 22:53:28 +08:00
|
|
|
attach_mnt(source_mnt, dest_mnt, dest_mp);
|
2011-11-25 13:46:35 +08:00
|
|
|
touch_mnt_namespace(source_mnt->mnt_ns);
|
2005-11-08 06:20:03 +08:00
|
|
|
} else {
|
2013-03-15 22:53:28 +08:00
|
|
|
mnt_set_mountpoint(dest_mnt, dest_mp, source_mnt);
|
2011-11-25 08:59:16 +08:00
|
|
|
commit_tree(source_mnt);
|
2005-11-08 06:20:03 +08:00
|
|
|
}
|
2005-11-08 06:19:50 +08:00
|
|
|
|
2011-11-25 10:01:32 +08:00
|
|
|
list_for_each_entry_safe(child, p, &tree_list, mnt_hash) {
|
|
|
|
list_del_init(&child->mnt_hash);
|
2011-11-25 08:47:15 +08:00
|
|
|
commit_tree(child);
|
2005-11-08 06:19:50 +08:00
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
|
2005-11-08 06:19:50 +08:00
|
|
|
return 0;
|
2008-03-27 20:06:23 +08:00
|
|
|
|
|
|
|
out_cleanup_ids:
|
2011-11-25 14:05:37 +08:00
|
|
|
if (IS_MNT_SHARED(dest_mnt))
|
2011-11-25 08:59:16 +08:00
|
|
|
cleanup_group_ids(source_mnt, NULL);
|
2008-03-27 20:06:23 +08:00
|
|
|
out:
|
|
|
|
return err;
|
2005-11-08 06:19:50 +08:00
|
|
|
}
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
static struct mountpoint *lock_mount(struct path *path)
|
2011-03-18 20:55:38 +08:00
|
|
|
{
|
|
|
|
struct vfsmount *mnt;
|
2013-03-15 22:53:28 +08:00
|
|
|
struct dentry *dentry = path->dentry;
|
2011-03-18 20:55:38 +08:00
|
|
|
retry:
|
2013-03-15 22:53:28 +08:00
|
|
|
mutex_lock(&dentry->d_inode->i_mutex);
|
|
|
|
if (unlikely(cant_mount(dentry))) {
|
|
|
|
mutex_unlock(&dentry->d_inode->i_mutex);
|
|
|
|
return ERR_PTR(-ENOENT);
|
2011-03-18 20:55:38 +08:00
|
|
|
}
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2011-03-18 20:55:38 +08:00
|
|
|
mnt = lookup_mnt(path);
|
2013-03-15 22:53:28 +08:00
|
|
|
if (likely(!mnt)) {
|
|
|
|
struct mountpoint *mp = new_mountpoint(dentry);
|
|
|
|
if (IS_ERR(mp)) {
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_unlock();
|
2013-03-15 22:53:28 +08:00
|
|
|
mutex_unlock(&dentry->d_inode->i_mutex);
|
|
|
|
return mp;
|
|
|
|
}
|
|
|
|
return mp;
|
|
|
|
}
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_unlock();
|
2011-03-18 20:55:38 +08:00
|
|
|
mutex_unlock(&path->dentry->d_inode->i_mutex);
|
|
|
|
path_put(path);
|
|
|
|
path->mnt = mnt;
|
2013-03-15 22:53:28 +08:00
|
|
|
dentry = path->dentry = dget(mnt->mnt_root);
|
2011-03-18 20:55:38 +08:00
|
|
|
goto retry;
|
|
|
|
}
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
static void unlock_mount(struct mountpoint *where)
|
2011-03-18 20:55:38 +08:00
|
|
|
{
|
2013-03-15 22:53:28 +08:00
|
|
|
struct dentry *dentry = where->m_dentry;
|
|
|
|
put_mountpoint(where);
|
2013-03-17 02:49:45 +08:00
|
|
|
namespace_unlock();
|
2013-03-15 22:53:28 +08:00
|
|
|
mutex_unlock(&dentry->d_inode->i_mutex);
|
2011-03-18 20:55:38 +08:00
|
|
|
}
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
static int graft_tree(struct mount *mnt, struct mount *p, struct mountpoint *mp)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2011-11-25 13:30:56 +08:00
|
|
|
if (mnt->mnt.mnt_sb->s_flags & MS_NOUSER)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
if (S_ISDIR(mp->m_dentry->d_inode->i_mode) !=
|
2011-11-25 13:30:56 +08:00
|
|
|
S_ISDIR(mnt->mnt.mnt_root->d_inode->i_mode))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -ENOTDIR;
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
return attach_recursive_mnt(mnt, p, mp, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2010-08-27 02:07:22 +08:00
|
|
|
/*
|
|
|
|
* Sanity check the flags to change_mnt_propagation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int flags_to_propagation_type(int flags)
|
|
|
|
{
|
fs/namespace.c: bound mount propagation fix
This issue was discovered by users of busybox. And the bug is actual for
busybox users, I don't know how it affects others. Apparently, mount is
called with and without MS_SILENT, and this affects mount() behaviour.
But MS_SILENT is only supposed to affect kernel logging verbosity.
The following script was run in an empty test directory:
mkdir -p mount.dir mount.shared1 mount.shared2
touch mount.dir/a mount.dir/b
mount -vv --bind mount.shared1 mount.shared1
mount -vv --make-rshared mount.shared1
mount -vv --bind mount.shared2 mount.shared2
mount -vv --make-rshared mount.shared2
mount -vv --bind mount.shared2 mount.shared1
mount -vv --bind mount.dir mount.shared2
ls -R mount.dir mount.shared1 mount.shared2
umount mount.dir mount.shared1 mount.shared2 2>/dev/null
umount mount.dir mount.shared1 mount.shared2 2>/dev/null
umount mount.dir mount.shared1 mount.shared2 2>/dev/null
rm -f mount.dir/a mount.dir/b mount.dir/c
rmdir mount.dir mount.shared1 mount.shared2
mount -vv was used to show the mount() call arguments and result.
Output shows that flag argument has 0x00008000 = MS_SILENT bit:
mount: mount('mount.shared1','mount.shared1','(null)',0x00009000,'(null)'):0
mount: mount('','mount.shared1','',0x0010c000,''):0
mount: mount('mount.shared2','mount.shared2','(null)',0x00009000,'(null)'):0
mount: mount('','mount.shared2','',0x0010c000,''):0
mount: mount('mount.shared2','mount.shared1','(null)',0x00009000,'(null)'):0
mount: mount('mount.dir','mount.shared2','(null)',0x00009000,'(null)'):0
mount.dir:
a
b
mount.shared1:
mount.shared2:
a
b
After adding --loud option to remove MS_SILENT bit from just one mount cmd:
mkdir -p mount.dir mount.shared1 mount.shared2
touch mount.dir/a mount.dir/b
mount -vv --bind mount.shared1 mount.shared1 2>&1
mount -vv --make-rshared mount.shared1 2>&1
mount -vv --bind mount.shared2 mount.shared2 2>&1
mount -vv --loud --make-rshared mount.shared2 2>&1 # <-HERE
mount -vv --bind mount.shared2 mount.shared1 2>&1
mount -vv --bind mount.dir mount.shared2 2>&1
ls -R mount.dir mount.shared1 mount.shared2 2>&1
umount mount.dir mount.shared1 mount.shared2 2>/dev/null
umount mount.dir mount.shared1 mount.shared2 2>/dev/null
umount mount.dir mount.shared1 mount.shared2 2>/dev/null
rm -f mount.dir/a mount.dir/b mount.dir/c
rmdir mount.dir mount.shared1 mount.shared2
The result is different now - look closely at mount.shared1 directory listing.
Now it does show files 'a' and 'b':
mount: mount('mount.shared1','mount.shared1','(null)',0x00009000,'(null)'):0
mount: mount('','mount.shared1','',0x0010c000,''):0
mount: mount('mount.shared2','mount.shared2','(null)',0x00009000,'(null)'):0
mount: mount('','mount.shared2','',0x00104000,''):0
mount: mount('mount.shared2','mount.shared1','(null)',0x00009000,'(null)'):0
mount: mount('mount.dir','mount.shared2','(null)',0x00009000,'(null)'):0
mount.dir:
a
b
mount.shared1:
a
b
mount.shared2:
a
b
The analysis shows that MS_SILENT flag which is ON by default in any
busybox-> mount operations cames to flags_to_propagation_type function and
causes the error return while is_power_of_2 checking because the function
expects only one bit set. This doesn't allow to do busybox->mount with
any --make-[r]shared, --make-[r]private etc options.
Moreover, the recently added flags_to_propagation_type() function doesn't
allow us to do such operations as --make-[r]private --make-[r]shared etc.
when MS_SILENT is on. The idea or clearing the MS_SILENT flag came from
to Denys Vlasenko.
Signed-off-by: Roman Borisov <ext-roman.borisov@nokia.com>
Reported-by: Denys Vlasenko <vda.linux@googlemail.com>
Cc: Chuck Ebbert <cebbert@redhat.com>
Cc: Alexander Shishkin <virtuoso@slind.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@lst.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2011-05-26 07:26:48 +08:00
|
|
|
int type = flags & ~(MS_REC | MS_SILENT);
|
2010-08-27 02:07:22 +08:00
|
|
|
|
|
|
|
/* Fail if any non-propagation flags are set */
|
|
|
|
if (type & ~(MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
|
|
|
|
return 0;
|
|
|
|
/* Only one propagation flag should be set */
|
|
|
|
if (!is_power_of_2(type))
|
|
|
|
return 0;
|
|
|
|
return type;
|
|
|
|
}
|
|
|
|
|
2005-11-08 06:19:07 +08:00
|
|
|
/*
|
|
|
|
* recursively change the type of the mountpoint.
|
|
|
|
*/
|
2008-08-02 12:55:27 +08:00
|
|
|
static int do_change_type(struct path *path, int flag)
|
2005-11-08 06:19:07 +08:00
|
|
|
{
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *m;
|
2011-11-25 08:54:23 +08:00
|
|
|
struct mount *mnt = real_mount(path->mnt);
|
2005-11-08 06:19:07 +08:00
|
|
|
int recurse = flag & MS_REC;
|
2010-08-27 02:07:22 +08:00
|
|
|
int type;
|
2008-03-27 20:06:23 +08:00
|
|
|
int err = 0;
|
2005-11-08 06:19:07 +08:00
|
|
|
|
2008-08-02 12:51:11 +08:00
|
|
|
if (path->dentry != path->mnt->mnt_root)
|
2005-11-08 06:19:07 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2010-08-27 02:07:22 +08:00
|
|
|
type = flags_to_propagation_type(flag);
|
|
|
|
if (!type)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2008-03-27 20:06:23 +08:00
|
|
|
if (type == MS_SHARED) {
|
|
|
|
err = invent_group_ids(mnt, recurse);
|
|
|
|
if (err)
|
|
|
|
goto out_unlock;
|
|
|
|
}
|
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 16:06:56 +08:00
|
|
|
for (m = mnt; m; m = (recurse ? next_mnt(m, mnt) : NULL))
|
2011-11-25 09:43:10 +08:00
|
|
|
change_mnt_propagation(m, type);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2008-03-27 20:06:23 +08:00
|
|
|
|
|
|
|
out_unlock:
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_unlock();
|
2008-03-27 20:06:23 +08:00
|
|
|
return err;
|
2005-11-08 06:19:07 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* do loopback mount.
|
|
|
|
*/
|
2012-10-11 23:42:01 +08:00
|
|
|
static int do_loopback(struct path *path, const char *old_name,
|
2008-02-08 20:22:12 +08:00
|
|
|
int recurse)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-08-02 12:51:11 +08:00
|
|
|
struct path old_path;
|
2013-03-15 22:53:28 +08:00
|
|
|
struct mount *mnt = NULL, *old, *parent;
|
|
|
|
struct mountpoint *mp;
|
2013-02-23 11:49:10 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!old_name || !*old_name)
|
|
|
|
return -EINVAL;
|
2011-09-27 08:36:09 +08:00
|
|
|
err = kern_path(old_name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &old_path);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2010-03-08 10:49:36 +08:00
|
|
|
err = -EINVAL;
|
|
|
|
if (mnt_ns_loop(&old_path))
|
|
|
|
goto out;
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
mp = lock_mount(path);
|
|
|
|
err = PTR_ERR(mp);
|
|
|
|
if (IS_ERR(mp))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out;
|
|
|
|
|
2011-11-25 10:24:27 +08:00
|
|
|
old = real_mount(old_path.mnt);
|
2013-03-15 22:53:28 +08:00
|
|
|
parent = real_mount(path->mnt);
|
2011-11-25 10:24:27 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
2011-11-25 14:05:37 +08:00
|
|
|
if (IS_MNT_UNBINDABLE(old))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out2;
|
2005-11-08 06:21:20 +08:00
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
if (!check_mnt(parent) || !check_mnt(old))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out2;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-08 06:15:04 +08:00
|
|
|
if (recurse)
|
2011-11-25 10:24:27 +08:00
|
|
|
mnt = copy_tree(old, old_path.dentry, 0);
|
2005-11-08 06:15:04 +08:00
|
|
|
else
|
2011-11-25 10:24:27 +08:00
|
|
|
mnt = clone_mnt(old, old_path.dentry, 0);
|
2005-11-08 06:15:04 +08:00
|
|
|
|
2012-06-25 19:55:18 +08:00
|
|
|
if (IS_ERR(mnt)) {
|
|
|
|
err = PTR_ERR(mnt);
|
2013-04-09 21:33:29 +08:00
|
|
|
goto out2;
|
2012-06-25 19:55:18 +08:00
|
|
|
}
|
2005-11-08 06:15:04 +08:00
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
err = graft_tree(mnt, parent, mp);
|
2005-11-08 06:15:04 +08:00
|
|
|
if (err) {
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2013-03-17 02:49:45 +08:00
|
|
|
umount_tree(mnt, 0);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-11-08 06:16:29 +08:00
|
|
|
}
|
2011-03-18 20:55:38 +08:00
|
|
|
out2:
|
2013-03-15 22:53:28 +08:00
|
|
|
unlock_mount(mp);
|
2005-11-08 06:15:04 +08:00
|
|
|
out:
|
2008-08-02 12:51:11 +08:00
|
|
|
path_put(&old_path);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2008-02-16 06:38:00 +08:00
|
|
|
static int change_mount_flags(struct vfsmount *mnt, int ms_flags)
|
|
|
|
{
|
|
|
|
int error = 0;
|
|
|
|
int readonly_request = 0;
|
|
|
|
|
|
|
|
if (ms_flags & MS_RDONLY)
|
|
|
|
readonly_request = 1;
|
|
|
|
if (readonly_request == __mnt_is_readonly(mnt))
|
|
|
|
return 0;
|
|
|
|
|
2013-03-22 18:10:15 +08:00
|
|
|
if (mnt->mnt_flags & MNT_LOCK_READONLY)
|
|
|
|
return -EPERM;
|
|
|
|
|
2008-02-16 06:38:00 +08:00
|
|
|
if (readonly_request)
|
2011-11-25 11:37:54 +08:00
|
|
|
error = mnt_make_readonly(real_mount(mnt));
|
2008-02-16 06:38:00 +08:00
|
|
|
else
|
2011-11-25 11:37:54 +08:00
|
|
|
__mnt_unmake_readonly(real_mount(mnt));
|
2008-02-16 06:38:00 +08:00
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* change filesystem flags. dir should be a physical root of filesystem.
|
|
|
|
* If you've mounted a non-root directory somewhere and want to do remount
|
|
|
|
* on it - tough luck.
|
|
|
|
*/
|
2008-08-02 12:55:27 +08:00
|
|
|
static int do_remount(struct path *path, int flags, int mnt_flags,
|
2005-04-17 06:20:36 +08:00
|
|
|
void *data)
|
|
|
|
{
|
|
|
|
int err;
|
2008-08-02 12:51:11 +08:00
|
|
|
struct super_block *sb = path->mnt->mnt_sb;
|
2011-11-25 13:46:35 +08:00
|
|
|
struct mount *mnt = real_mount(path->mnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-25 13:46:35 +08:00
|
|
|
if (!check_mnt(mnt))
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2008-08-02 12:51:11 +08:00
|
|
|
if (path->dentry != path->mnt->mnt_root)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2011-03-04 05:09:14 +08:00
|
|
|
err = security_sb_remount(sb, data);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
down_write(&sb->s_umount);
|
2008-02-16 06:38:00 +08:00
|
|
|
if (flags & MS_BIND)
|
2008-08-02 12:51:11 +08:00
|
|
|
err = change_mount_flags(path->mnt, flags);
|
2013-02-23 11:49:10 +08:00
|
|
|
else if (!capable(CAP_SYS_ADMIN))
|
|
|
|
err = -EPERM;
|
2009-05-09 01:36:58 +08:00
|
|
|
else
|
2008-02-16 06:38:00 +08:00
|
|
|
err = do_remount_sb(sb, flags, data, 0);
|
2010-01-17 02:01:26 +08:00
|
|
|
if (!err) {
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 13:46:35 +08:00
|
|
|
mnt_flags |= mnt->mnt.mnt_flags & MNT_PROPAGATION_MASK;
|
|
|
|
mnt->mnt.mnt_flags = mnt_flags;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2010-01-17 02:01:26 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
up_write(&sb->s_umount);
|
2008-09-27 10:01:20 +08:00
|
|
|
if (!err) {
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 13:46:35 +08:00
|
|
|
touch_mnt_namespace(mnt->mnt_ns);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2008-09-27 10:01:20 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-11-25 09:01:19 +08:00
|
|
|
static inline int tree_contains_unbindable(struct mount *mnt)
|
2005-11-08 06:21:20 +08:00
|
|
|
{
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *p;
|
2011-11-25 16:06:56 +08:00
|
|
|
for (p = mnt; p; p = next_mnt(p, mnt)) {
|
2011-11-25 14:05:37 +08:00
|
|
|
if (IS_MNT_UNBINDABLE(p))
|
2005-11-08 06:21:20 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2012-10-11 23:42:01 +08:00
|
|
|
static int do_move_mount(struct path *path, const char *old_name)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-08-02 12:51:11 +08:00
|
|
|
struct path old_path, parent_path;
|
2011-11-25 10:47:05 +08:00
|
|
|
struct mount *p;
|
2011-11-25 08:59:16 +08:00
|
|
|
struct mount *old;
|
2013-03-15 22:53:28 +08:00
|
|
|
struct mountpoint *mp;
|
2013-02-23 11:49:10 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (!old_name || !*old_name)
|
|
|
|
return -EINVAL;
|
2008-08-02 12:51:11 +08:00
|
|
|
err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
mp = lock_mount(path);
|
|
|
|
err = PTR_ERR(mp);
|
|
|
|
if (IS_ERR(mp))
|
Add a dentry op to allow processes to be held during pathwalk transit
Add a dentry op (d_manage) to permit a filesystem to hold a process and make it
sleep when it tries to transit away from one of that filesystem's directories
during a pathwalk. The operation is keyed off a new dentry flag
(DCACHE_MANAGE_TRANSIT).
The filesystem is allowed to be selective about which processes it holds and
which it permits to continue on or prohibits from transiting from each flagged
directory. This will allow autofs to hold up client processes whilst letting
its userspace daemon through to maintain the directory or the stuff behind it
or mounted upon it.
The ->d_manage() dentry operation:
int (*d_manage)(struct path *path, bool mounting_here);
takes a pointer to the directory about to be transited away from and a flag
indicating whether the transit is undertaken by do_add_mount() or
do_move_mount() skipping through a pile of filesystems mounted on a mountpoint.
It should return 0 if successful and to let the process continue on its way;
-EISDIR to prohibit the caller from skipping to overmounted filesystems or
automounting, and to use this directory; or some other error code to return to
the user.
->d_manage() is called with namespace_sem writelocked if mounting_here is true
and no other locks held, so it may sleep. However, if mounting_here is true,
it may not initiate or wait for a mount or unmount upon the parameter
directory, even if the act is actually performed by userspace.
Within fs/namei.c, follow_managed() is extended to check with d_manage() first
on each managed directory, before transiting away from it or attempting to
automount upon it.
follow_down() is renamed follow_down_one() and should only be used where the
filesystem deliberately intends to avoid management steps (e.g. autofs).
A new follow_down() is added that incorporates the loop done by all other
callers of follow_down() (do_add/move_mount(), autofs and NFSD; whilst AFS, NFS
and CIFS do use it, their use is removed by converting them to use
d_automount()). The new follow_down() calls d_manage() as appropriate. It
also takes an extra parameter to indicate if it is being called from mount code
(with namespace_sem writelocked) which it passes to d_manage(). follow_down()
ignores automount points so that it can be used to mount on them.
__follow_mount_rcu() is made to abort rcu-walk mode if it hits a directory with
DCACHE_MANAGE_TRANSIT set on the basis that we're probably going to have to
sleep. It would be possible to enter d_manage() in rcu-walk mode too, and have
that determine whether to abort or not itself. That would allow the autofs
daemon to continue on in rcu-walk mode.
Note that DCACHE_MANAGE_TRANSIT on a directory should be cleared when it isn't
required as every tranist from that directory will cause d_manage() to be
invoked. It can always be set again when necessary.
==========================
WHAT THIS MEANS FOR AUTOFS
==========================
Autofs currently uses the lookup() inode op and the d_revalidate() dentry op to
trigger the automounting of indirect mounts, and both of these can be called
with i_mutex held.
autofs knows that the i_mutex will be held by the caller in lookup(), and so
can drop it before invoking the daemon - but this isn't so for d_revalidate(),
since the lock is only held on _some_ of the code paths that call it. This
means that autofs can't risk dropping i_mutex from its d_revalidate() function
before it calls the daemon.
The bug could manifest itself as, for example, a process that's trying to
validate an automount dentry that gets made to wait because that dentry is
expired and needs cleaning up:
mkdir S ffffffff8014e05a 0 32580 24956
Call Trace:
[<ffffffff885371fd>] :autofs4:autofs4_wait+0x674/0x897
[<ffffffff80127f7d>] avc_has_perm+0x46/0x58
[<ffffffff8009fdcf>] autoremove_wake_function+0x0/0x2e
[<ffffffff88537be6>] :autofs4:autofs4_expire_wait+0x41/0x6b
[<ffffffff88535cfc>] :autofs4:autofs4_revalidate+0x91/0x149
[<ffffffff80036d96>] __lookup_hash+0xa0/0x12f
[<ffffffff80057a2f>] lookup_create+0x46/0x80
[<ffffffff800e6e31>] sys_mkdirat+0x56/0xe4
versus the automount daemon which wants to remove that dentry, but can't
because the normal process is holding the i_mutex lock:
automount D ffffffff8014e05a 0 32581 1 32561
Call Trace:
[<ffffffff80063c3f>] __mutex_lock_slowpath+0x60/0x9b
[<ffffffff8000ccf1>] do_path_lookup+0x2ca/0x2f1
[<ffffffff80063c89>] .text.lock.mutex+0xf/0x14
[<ffffffff800e6d55>] do_rmdir+0x77/0xde
[<ffffffff8005d229>] tracesys+0x71/0xe0
[<ffffffff8005d28d>] tracesys+0xd5/0xe0
which means that the system is deadlocked.
This patch allows autofs to hold up normal processes whilst the daemon goes
ahead and does things to the dentry tree behind the automouter point without
risking a deadlock as almost no locks are held in d_manage() and none in
d_automount().
Signed-off-by: David Howells <dhowells@redhat.com>
Was-Acked-by: Ian Kent <raven@themaw.net>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2011-01-15 02:45:26 +08:00
|
|
|
goto out;
|
|
|
|
|
2011-11-25 13:46:35 +08:00
|
|
|
old = real_mount(old_path.mnt);
|
2011-11-25 14:05:37 +08:00
|
|
|
p = real_mount(path->mnt);
|
2011-11-25 13:46:35 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -EINVAL;
|
2011-11-25 14:05:37 +08:00
|
|
|
if (!check_mnt(p) || !check_mnt(old))
|
2005-04-17 06:20:36 +08:00
|
|
|
goto out1;
|
|
|
|
|
|
|
|
err = -EINVAL;
|
2008-08-02 12:51:11 +08:00
|
|
|
if (old_path.dentry != old_path.mnt->mnt_root)
|
2005-11-08 06:20:03 +08:00
|
|
|
goto out1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-25 10:47:05 +08:00
|
|
|
if (!mnt_has_parent(old))
|
2005-11-08 06:20:03 +08:00
|
|
|
goto out1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2008-08-02 12:51:11 +08:00
|
|
|
if (S_ISDIR(path->dentry->d_inode->i_mode) !=
|
|
|
|
S_ISDIR(old_path.dentry->d_inode->i_mode))
|
2005-11-08 06:20:03 +08:00
|
|
|
goto out1;
|
|
|
|
/*
|
|
|
|
* Don't move a mount residing in a shared parent.
|
|
|
|
*/
|
2011-11-25 14:05:37 +08:00
|
|
|
if (IS_MNT_SHARED(old->mnt_parent))
|
2005-11-08 06:20:03 +08:00
|
|
|
goto out1;
|
2005-11-08 06:21:20 +08:00
|
|
|
/*
|
|
|
|
* Don't move a mount tree containing unbindable mounts to a destination
|
|
|
|
* mount which is shared.
|
|
|
|
*/
|
2011-11-25 14:05:37 +08:00
|
|
|
if (IS_MNT_SHARED(p) && tree_contains_unbindable(old))
|
2005-11-08 06:21:20 +08:00
|
|
|
goto out1;
|
2005-04-17 06:20:36 +08:00
|
|
|
err = -ELOOP;
|
2011-11-25 14:05:37 +08:00
|
|
|
for (; mnt_has_parent(p); p = p->mnt_parent)
|
2011-11-25 10:47:05 +08:00
|
|
|
if (p == old)
|
2005-11-08 06:20:03 +08:00
|
|
|
goto out1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
err = attach_recursive_mnt(old, real_mount(path->mnt), mp, &parent_path);
|
2008-02-15 11:34:32 +08:00
|
|
|
if (err)
|
2005-11-08 06:20:03 +08:00
|
|
|
goto out1;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* if the mount is moved, it should no longer be expire
|
|
|
|
* automatically */
|
2011-11-25 13:22:05 +08:00
|
|
|
list_del_init(&old->mnt_expire);
|
2005-04-17 06:20:36 +08:00
|
|
|
out1:
|
2013-03-15 22:53:28 +08:00
|
|
|
unlock_mount(mp);
|
2005-04-17 06:20:36 +08:00
|
|
|
out:
|
|
|
|
if (!err)
|
2008-03-22 08:48:19 +08:00
|
|
|
path_put(&parent_path);
|
2008-08-02 12:51:11 +08:00
|
|
|
path_put(&old_path);
|
2005-04-17 06:20:36 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-03-18 10:08:28 +08:00
|
|
|
static struct vfsmount *fs_set_subtype(struct vfsmount *mnt, const char *fstype)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
const char *subtype = strchr(fstype, '.');
|
|
|
|
if (subtype) {
|
|
|
|
subtype++;
|
|
|
|
err = -EINVAL;
|
|
|
|
if (!subtype[0])
|
|
|
|
goto err;
|
|
|
|
} else
|
|
|
|
subtype = "";
|
|
|
|
|
|
|
|
mnt->mnt_sb->s_subtype = kstrdup(subtype, GFP_KERNEL);
|
|
|
|
err = -ENOMEM;
|
|
|
|
if (!mnt->mnt_sb->s_subtype)
|
|
|
|
goto err;
|
|
|
|
return mnt;
|
|
|
|
|
|
|
|
err:
|
|
|
|
mntput(mnt);
|
|
|
|
return ERR_PTR(err);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* add a mount into a namespace's mount tree
|
|
|
|
*/
|
2011-11-25 13:30:56 +08:00
|
|
|
static int do_add_mount(struct mount *newmnt, struct path *path, int mnt_flags)
|
2011-03-18 10:08:28 +08:00
|
|
|
{
|
2013-03-15 22:53:28 +08:00
|
|
|
struct mountpoint *mp;
|
|
|
|
struct mount *parent;
|
2011-03-18 10:08:28 +08:00
|
|
|
int err;
|
|
|
|
|
|
|
|
mnt_flags &= ~(MNT_SHARED | MNT_WRITE_HOLD | MNT_INTERNAL);
|
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
mp = lock_mount(path);
|
|
|
|
if (IS_ERR(mp))
|
|
|
|
return PTR_ERR(mp);
|
2011-03-18 10:08:28 +08:00
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
parent = real_mount(path->mnt);
|
2011-03-18 10:08:28 +08:00
|
|
|
err = -EINVAL;
|
2013-03-15 22:53:28 +08:00
|
|
|
if (unlikely(!check_mnt(parent))) {
|
2012-09-21 20:19:02 +08:00
|
|
|
/* that's acceptable only for automounts done in private ns */
|
|
|
|
if (!(mnt_flags & MNT_SHRINKABLE))
|
|
|
|
goto unlock;
|
|
|
|
/* ... and for those we'd better have mountpoint still alive */
|
2013-03-15 22:53:28 +08:00
|
|
|
if (!parent->mnt_ns)
|
2012-09-21 20:19:02 +08:00
|
|
|
goto unlock;
|
|
|
|
}
|
2011-03-18 10:08:28 +08:00
|
|
|
|
|
|
|
/* Refuse the same filesystem on the same mount point */
|
|
|
|
err = -EBUSY;
|
2011-11-25 13:30:56 +08:00
|
|
|
if (path->mnt->mnt_sb == newmnt->mnt.mnt_sb &&
|
2011-03-18 10:08:28 +08:00
|
|
|
path->mnt->mnt_root == path->dentry)
|
|
|
|
goto unlock;
|
|
|
|
|
|
|
|
err = -EINVAL;
|
2011-11-25 13:30:56 +08:00
|
|
|
if (S_ISLNK(newmnt->mnt.mnt_root->d_inode->i_mode))
|
2011-03-18 10:08:28 +08:00
|
|
|
goto unlock;
|
|
|
|
|
2011-11-25 13:30:56 +08:00
|
|
|
newmnt->mnt.mnt_flags = mnt_flags;
|
2013-03-15 22:53:28 +08:00
|
|
|
err = graft_tree(newmnt, parent, mp);
|
2011-03-18 10:08:28 +08:00
|
|
|
|
|
|
|
unlock:
|
2013-03-15 22:53:28 +08:00
|
|
|
unlock_mount(mp);
|
2011-03-18 10:08:28 +08:00
|
|
|
return err;
|
|
|
|
}
|
2011-01-17 14:47:59 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* create a new mount for userspace and request it to be added into the
|
|
|
|
* namespace's tree
|
|
|
|
*/
|
2012-07-27 12:42:03 +08:00
|
|
|
static int do_new_mount(struct path *path, const char *fstype, int flags,
|
2012-10-11 23:42:01 +08:00
|
|
|
int mnt_flags, const char *name, void *data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2012-07-27 12:42:03 +08:00
|
|
|
struct file_system_type *type;
|
2013-02-23 11:45:42 +08:00
|
|
|
struct user_namespace *user_ns = current->nsproxy->mnt_ns->user_ns;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct vfsmount *mnt;
|
2011-01-17 14:41:58 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-07-27 12:42:03 +08:00
|
|
|
if (!fstype)
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
2012-07-27 12:42:03 +08:00
|
|
|
type = get_fs_type(fstype);
|
|
|
|
if (!type)
|
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
if (user_ns != &init_user_ns) {
|
|
|
|
if (!(type->fs_flags & FS_USERNS_MOUNT)) {
|
|
|
|
put_filesystem(type);
|
|
|
|
return -EPERM;
|
|
|
|
}
|
|
|
|
/* Only in special cases allow devices from mounts
|
|
|
|
* created outside the initial user namespace.
|
|
|
|
*/
|
|
|
|
if (!(type->fs_flags & FS_USERNS_DEV_MOUNT)) {
|
|
|
|
flags |= MS_NODEV;
|
|
|
|
mnt_flags |= MNT_NODEV;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mnt = vfs_kern_mount(type, flags, name, data);
|
|
|
|
if (!IS_ERR(mnt) && (type->fs_flags & FS_HAS_SUBTYPE) &&
|
|
|
|
!mnt->mnt_sb->s_subtype)
|
|
|
|
mnt = fs_set_subtype(mnt, fstype);
|
|
|
|
|
|
|
|
put_filesystem(type);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (IS_ERR(mnt))
|
|
|
|
return PTR_ERR(mnt);
|
|
|
|
|
2011-11-25 13:30:56 +08:00
|
|
|
err = do_add_mount(real_mount(mnt), path, mnt_flags);
|
2011-01-17 14:41:58 +08:00
|
|
|
if (err)
|
|
|
|
mntput(mnt);
|
|
|
|
return err;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-01-17 14:35:23 +08:00
|
|
|
int finish_automount(struct vfsmount *m, struct path *path)
|
|
|
|
{
|
2011-11-25 13:22:05 +08:00
|
|
|
struct mount *mnt = real_mount(m);
|
2011-01-17 14:35:23 +08:00
|
|
|
int err;
|
|
|
|
/* The new mount record should have at least 2 refs to prevent it being
|
|
|
|
* expired before we get a chance to add it
|
|
|
|
*/
|
2011-11-25 13:22:05 +08:00
|
|
|
BUG_ON(mnt_get_count(mnt) < 2);
|
2011-01-17 14:35:23 +08:00
|
|
|
|
|
|
|
if (m->mnt_sb == path->mnt->mnt_sb &&
|
|
|
|
m->mnt_root == path->dentry) {
|
2011-01-17 14:47:59 +08:00
|
|
|
err = -ELOOP;
|
|
|
|
goto fail;
|
2011-01-17 14:35:23 +08:00
|
|
|
}
|
|
|
|
|
2011-11-25 13:30:56 +08:00
|
|
|
err = do_add_mount(mnt, path, path->mnt->mnt_flags | MNT_SHRINKABLE);
|
2011-01-17 14:47:59 +08:00
|
|
|
if (!err)
|
|
|
|
return 0;
|
|
|
|
fail:
|
|
|
|
/* remove m from any expiration list it may be on */
|
2011-11-25 13:22:05 +08:00
|
|
|
if (!list_empty(&mnt->mnt_expire)) {
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 13:22:05 +08:00
|
|
|
list_del_init(&mnt->mnt_expire);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_unlock();
|
2011-01-17 14:35:23 +08:00
|
|
|
}
|
2011-01-17 14:47:59 +08:00
|
|
|
mntput(m);
|
|
|
|
mntput(m);
|
2011-01-17 14:35:23 +08:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2011-01-15 03:10:03 +08:00
|
|
|
/**
|
|
|
|
* mnt_set_expiry - Put a mount on an expiration list
|
|
|
|
* @mnt: The mount to list.
|
|
|
|
* @expiry_list: The list to add the mount to.
|
|
|
|
*/
|
|
|
|
void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list)
|
|
|
|
{
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-01-15 03:10:03 +08:00
|
|
|
|
2011-11-25 13:22:05 +08:00
|
|
|
list_add_tail(&real_mount(mnt)->mnt_expire, expiry_list);
|
2011-01-15 03:10:03 +08:00
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_unlock();
|
2011-01-15 03:10:03 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mnt_set_expiry);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* process a list of expirable mountpoints with the intent of discarding any
|
|
|
|
* mountpoints that aren't in use and haven't been touched since last we came
|
|
|
|
* here
|
|
|
|
*/
|
|
|
|
void mark_mounts_for_expiry(struct list_head *mounts)
|
|
|
|
{
|
2011-11-25 10:07:43 +08:00
|
|
|
struct mount *mnt, *next;
|
2005-04-17 06:20:36 +08:00
|
|
|
LIST_HEAD(graveyard);
|
|
|
|
|
|
|
|
if (list_empty(mounts))
|
|
|
|
return;
|
|
|
|
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* extract from the expiration list every vfsmount that matches the
|
|
|
|
* following criteria:
|
|
|
|
* - only referenced by its parent vfsmount
|
|
|
|
* - still marked for expiry (marked on the last call here; marks are
|
|
|
|
* cleared by mntput())
|
|
|
|
*/
|
2011-11-25 13:22:05 +08:00
|
|
|
list_for_each_entry_safe(mnt, next, mounts, mnt_expire) {
|
2011-11-25 13:57:42 +08:00
|
|
|
if (!xchg(&mnt->mnt_expiry_mark, 1) ||
|
2011-11-25 10:35:16 +08:00
|
|
|
propagate_mount_busy(mnt, 1))
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2011-11-25 13:22:05 +08:00
|
|
|
list_move(&mnt->mnt_expire, &graveyard);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2008-03-22 12:21:53 +08:00
|
|
|
while (!list_empty(&graveyard)) {
|
2011-11-25 13:22:05 +08:00
|
|
|
mnt = list_first_entry(&graveyard, struct mount, mnt_expire);
|
2011-11-25 13:46:35 +08:00
|
|
|
touch_mnt_namespace(mnt->mnt_ns);
|
2013-03-17 02:49:45 +08:00
|
|
|
umount_tree(mnt, 1);
|
2008-03-22 12:21:53 +08:00
|
|
|
}
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2013-03-17 02:42:19 +08:00
|
|
|
namespace_unlock();
|
2006-06-09 21:34:17 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(mark_mounts_for_expiry);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Ripoff of 'select_parent()'
|
|
|
|
*
|
|
|
|
* search the list of submounts for a given mountpoint, and move any
|
|
|
|
* shrinkable submounts to the 'graveyard' list.
|
|
|
|
*/
|
2011-11-25 10:15:14 +08:00
|
|
|
static int select_submounts(struct mount *parent, struct list_head *graveyard)
|
2006-06-09 21:34:17 +08:00
|
|
|
{
|
2011-11-25 10:15:14 +08:00
|
|
|
struct mount *this_parent = parent;
|
2006-06-09 21:34:17 +08:00
|
|
|
struct list_head *next;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
repeat:
|
2011-11-25 12:24:33 +08:00
|
|
|
next = this_parent->mnt_mounts.next;
|
2006-06-09 21:34:17 +08:00
|
|
|
resume:
|
2011-11-25 12:24:33 +08:00
|
|
|
while (next != &this_parent->mnt_mounts) {
|
2006-06-09 21:34:17 +08:00
|
|
|
struct list_head *tmp = next;
|
2011-11-25 12:24:33 +08:00
|
|
|
struct mount *mnt = list_entry(tmp, struct mount, mnt_child);
|
2006-06-09 21:34:17 +08:00
|
|
|
|
|
|
|
next = tmp->next;
|
2011-11-25 10:15:14 +08:00
|
|
|
if (!(mnt->mnt.mnt_flags & MNT_SHRINKABLE))
|
2005-04-17 06:20:36 +08:00
|
|
|
continue;
|
2006-06-09 21:34:17 +08:00
|
|
|
/*
|
|
|
|
* Descend a level if the d_mounts list is non-empty.
|
|
|
|
*/
|
2011-11-25 12:24:33 +08:00
|
|
|
if (!list_empty(&mnt->mnt_mounts)) {
|
2006-06-09 21:34:17 +08:00
|
|
|
this_parent = mnt;
|
|
|
|
goto repeat;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2011-11-25 10:35:16 +08:00
|
|
|
if (!propagate_mount_busy(mnt, 1)) {
|
2011-11-25 13:22:05 +08:00
|
|
|
list_move_tail(&mnt->mnt_expire, graveyard);
|
2006-06-09 21:34:17 +08:00
|
|
|
found++;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2006-06-09 21:34:17 +08:00
|
|
|
/*
|
|
|
|
* All done at this level ... ascend and resume the search
|
|
|
|
*/
|
|
|
|
if (this_parent != parent) {
|
2011-11-25 12:24:33 +08:00
|
|
|
next = this_parent->mnt_child.next;
|
2011-11-25 11:19:58 +08:00
|
|
|
this_parent = this_parent->mnt_parent;
|
2006-06-09 21:34:17 +08:00
|
|
|
goto resume;
|
|
|
|
}
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* process a list of expirable mountpoints with the intent of discarding any
|
|
|
|
* submounts of a specific parent mountpoint
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
*
|
|
|
|
* vfsmount_lock must be held for write
|
2006-06-09 21:34:17 +08:00
|
|
|
*/
|
2013-03-17 02:39:34 +08:00
|
|
|
static void shrink_submounts(struct mount *mnt)
|
2006-06-09 21:34:17 +08:00
|
|
|
{
|
|
|
|
LIST_HEAD(graveyard);
|
2011-11-25 10:07:43 +08:00
|
|
|
struct mount *m;
|
2006-06-09 21:34:17 +08:00
|
|
|
|
|
|
|
/* extract submounts of 'mountpoint' from the expiration list */
|
2008-03-22 12:46:23 +08:00
|
|
|
while (select_submounts(mnt, &graveyard)) {
|
2008-03-22 12:21:53 +08:00
|
|
|
while (!list_empty(&graveyard)) {
|
2011-11-25 10:07:43 +08:00
|
|
|
m = list_first_entry(&graveyard, struct mount,
|
2011-11-25 13:22:05 +08:00
|
|
|
mnt_expire);
|
2011-11-25 13:46:35 +08:00
|
|
|
touch_mnt_namespace(m->mnt_ns);
|
2013-03-17 02:49:45 +08:00
|
|
|
umount_tree(m, 1);
|
2008-03-22 12:21:53 +08:00
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Some copy_from_user() implementations do not return the exact number of
|
|
|
|
* bytes remaining to copy on a fault. But copy_mount_options() requires that.
|
|
|
|
* Note that this function differs from copy_from_user() in that it will oops
|
|
|
|
* on bad values of `to', rather than returning a short copy.
|
|
|
|
*/
|
2005-11-08 06:16:09 +08:00
|
|
|
static long exact_copy_from_user(void *to, const void __user * from,
|
|
|
|
unsigned long n)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
char *t = to;
|
|
|
|
const char __user *f = from;
|
|
|
|
char c;
|
|
|
|
|
|
|
|
if (!access_ok(VERIFY_READ, from, n))
|
|
|
|
return n;
|
|
|
|
|
|
|
|
while (n) {
|
|
|
|
if (__get_user(c, f)) {
|
|
|
|
memset(t, 0, n);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*t++ = c;
|
|
|
|
f++;
|
|
|
|
n--;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2005-11-08 06:16:09 +08:00
|
|
|
int copy_mount_options(const void __user * data, unsigned long *where)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned long page;
|
|
|
|
unsigned long size;
|
2005-11-08 06:16:09 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
*where = 0;
|
|
|
|
if (!data)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (!(page = __get_free_page(GFP_KERNEL)))
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
/* We only care that *some* data at the address the user
|
|
|
|
* gave us is valid. Just in case, we'll zero
|
|
|
|
* the remainder of the page.
|
|
|
|
*/
|
|
|
|
/* copy_from_user cannot cross TASK_SIZE ! */
|
|
|
|
size = TASK_SIZE - (unsigned long)data;
|
|
|
|
if (size > PAGE_SIZE)
|
|
|
|
size = PAGE_SIZE;
|
|
|
|
|
|
|
|
i = size - exact_copy_from_user((void *)page, data, size);
|
|
|
|
if (!i) {
|
2005-11-08 06:16:09 +08:00
|
|
|
free_page(page);
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EFAULT;
|
|
|
|
}
|
|
|
|
if (i != PAGE_SIZE)
|
|
|
|
memset((char *)page + i, 0, PAGE_SIZE - i);
|
|
|
|
*where = page;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
int copy_mount_string(const void __user *data, char **where)
|
|
|
|
{
|
|
|
|
char *tmp;
|
|
|
|
|
|
|
|
if (!data) {
|
|
|
|
*where = NULL;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = strndup_user(data, PAGE_SIZE);
|
|
|
|
if (IS_ERR(tmp))
|
|
|
|
return PTR_ERR(tmp);
|
|
|
|
|
|
|
|
*where = tmp;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* Flags is a 32-bit value that allows up to 31 non-fs dependent flags to
|
|
|
|
* be given to the mount() call (ie: read-only, no-dev, no-suid etc).
|
|
|
|
*
|
|
|
|
* data is a (void *) that can point to any structure up to
|
|
|
|
* PAGE_SIZE-1 bytes, which can contain arbitrary fs-dependent
|
|
|
|
* information (or be NULL).
|
|
|
|
*
|
|
|
|
* Pre-0.97 versions of mount() didn't have a flags word.
|
|
|
|
* When the flags word was introduced its top half was required
|
|
|
|
* to have the magic value 0xC0ED, and this remained so until 2.4.0-test9.
|
|
|
|
* Therefore, if this magic number is present, it carries no information
|
|
|
|
* and must be discarded.
|
|
|
|
*/
|
2012-10-11 23:42:01 +08:00
|
|
|
long do_mount(const char *dev_name, const char *dir_name,
|
|
|
|
const char *type_page, unsigned long flags, void *data_page)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-08-02 12:51:11 +08:00
|
|
|
struct path path;
|
2005-04-17 06:20:36 +08:00
|
|
|
int retval = 0;
|
|
|
|
int mnt_flags = 0;
|
|
|
|
|
|
|
|
/* Discard magic */
|
|
|
|
if ((flags & MS_MGC_MSK) == MS_MGC_VAL)
|
|
|
|
flags &= ~MS_MGC_MSK;
|
|
|
|
|
|
|
|
/* Basic sanity checks */
|
|
|
|
|
|
|
|
if (!dir_name || !*dir_name || !memchr(dir_name, 0, PAGE_SIZE))
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (data_page)
|
|
|
|
((char *)data_page)[PAGE_SIZE - 1] = 0;
|
|
|
|
|
2009-10-04 20:49:49 +08:00
|
|
|
/* ... and get the mountpoint */
|
|
|
|
retval = kern_path(dir_name, LOOKUP_FOLLOW, &path);
|
|
|
|
if (retval)
|
|
|
|
return retval;
|
|
|
|
|
|
|
|
retval = security_sb_mount(dev_name, &path,
|
|
|
|
type_page, flags, data_page);
|
2013-05-05 02:40:51 +08:00
|
|
|
if (!retval && !may_mount())
|
|
|
|
retval = -EPERM;
|
2009-10-04 20:49:49 +08:00
|
|
|
if (retval)
|
|
|
|
goto dput_out;
|
|
|
|
|
2009-04-20 00:40:43 +08:00
|
|
|
/* Default to relatime unless overriden */
|
|
|
|
if (!(flags & MS_NOATIME))
|
|
|
|
mnt_flags |= MNT_RELATIME;
|
2009-03-27 01:53:14 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/* Separate the per-mountpoint flags */
|
|
|
|
if (flags & MS_NOSUID)
|
|
|
|
mnt_flags |= MNT_NOSUID;
|
|
|
|
if (flags & MS_NODEV)
|
|
|
|
mnt_flags |= MNT_NODEV;
|
|
|
|
if (flags & MS_NOEXEC)
|
|
|
|
mnt_flags |= MNT_NOEXEC;
|
2006-01-10 12:52:17 +08:00
|
|
|
if (flags & MS_NOATIME)
|
|
|
|
mnt_flags |= MNT_NOATIME;
|
|
|
|
if (flags & MS_NODIRATIME)
|
|
|
|
mnt_flags |= MNT_NODIRATIME;
|
2009-03-27 01:49:56 +08:00
|
|
|
if (flags & MS_STRICTATIME)
|
|
|
|
mnt_flags &= ~(MNT_RELATIME | MNT_NOATIME);
|
2008-02-16 06:38:00 +08:00
|
|
|
if (flags & MS_RDONLY)
|
|
|
|
mnt_flags |= MNT_READONLY;
|
2006-01-10 12:52:17 +08:00
|
|
|
|
2010-08-10 00:05:43 +08:00
|
|
|
flags &= ~(MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_ACTIVE | MS_BORN |
|
2009-03-27 01:49:56 +08:00
|
|
|
MS_NOATIME | MS_NODIRATIME | MS_RELATIME| MS_KERNMOUNT |
|
|
|
|
MS_STRICTATIME);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (flags & MS_REMOUNT)
|
2008-08-02 12:51:11 +08:00
|
|
|
retval = do_remount(&path, flags & ~MS_REMOUNT, mnt_flags,
|
2005-04-17 06:20:36 +08:00
|
|
|
data_page);
|
|
|
|
else if (flags & MS_BIND)
|
2008-08-02 12:51:11 +08:00
|
|
|
retval = do_loopback(&path, dev_name, flags & MS_REC);
|
2005-11-08 06:21:20 +08:00
|
|
|
else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE))
|
2008-08-02 12:51:11 +08:00
|
|
|
retval = do_change_type(&path, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
else if (flags & MS_MOVE)
|
2008-08-02 12:51:11 +08:00
|
|
|
retval = do_move_mount(&path, dev_name);
|
2005-04-17 06:20:36 +08:00
|
|
|
else
|
2008-08-02 12:51:11 +08:00
|
|
|
retval = do_new_mount(&path, type_page, flags, mnt_flags,
|
2005-04-17 06:20:36 +08:00
|
|
|
dev_name, data_page);
|
|
|
|
dput_out:
|
2008-08-02 12:51:11 +08:00
|
|
|
path_put(&path);
|
2005-04-17 06:20:36 +08:00
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2012-07-27 12:08:32 +08:00
|
|
|
static void free_mnt_ns(struct mnt_namespace *ns)
|
|
|
|
{
|
2011-06-16 01:21:48 +08:00
|
|
|
proc_free_inum(ns->proc_inum);
|
2012-07-27 12:08:32 +08:00
|
|
|
put_user_ns(ns->user_ns);
|
|
|
|
kfree(ns);
|
|
|
|
}
|
|
|
|
|
2010-03-08 10:49:36 +08:00
|
|
|
/*
|
|
|
|
* Assign a sequence number so we can detect when we attempt to bind
|
|
|
|
* mount a reference to an older mount namespace into the current
|
|
|
|
* mount namespace, preventing reference counting loops. A 64bit
|
|
|
|
* number incrementing at 10Ghz will take 12,427 years to wrap which
|
|
|
|
* is effectively never, so we can ignore the possibility.
|
|
|
|
*/
|
|
|
|
static atomic64_t mnt_ns_seq = ATOMIC64_INIT(1);
|
|
|
|
|
2012-07-27 12:08:32 +08:00
|
|
|
static struct mnt_namespace *alloc_mnt_ns(struct user_namespace *user_ns)
|
2009-06-23 03:09:13 +08:00
|
|
|
{
|
|
|
|
struct mnt_namespace *new_ns;
|
2011-06-16 01:21:48 +08:00
|
|
|
int ret;
|
2009-06-23 03:09:13 +08:00
|
|
|
|
|
|
|
new_ns = kmalloc(sizeof(struct mnt_namespace), GFP_KERNEL);
|
|
|
|
if (!new_ns)
|
|
|
|
return ERR_PTR(-ENOMEM);
|
2011-06-16 01:21:48 +08:00
|
|
|
ret = proc_alloc_inum(&new_ns->proc_inum);
|
|
|
|
if (ret) {
|
|
|
|
kfree(new_ns);
|
|
|
|
return ERR_PTR(ret);
|
|
|
|
}
|
2010-03-08 10:49:36 +08:00
|
|
|
new_ns->seq = atomic64_add_return(1, &mnt_ns_seq);
|
2009-06-23 03:09:13 +08:00
|
|
|
atomic_set(&new_ns->count, 1);
|
|
|
|
new_ns->root = NULL;
|
|
|
|
INIT_LIST_HEAD(&new_ns->list);
|
|
|
|
init_waitqueue_head(&new_ns->poll);
|
|
|
|
new_ns->event = 0;
|
2012-07-27 12:08:32 +08:00
|
|
|
new_ns->user_ns = get_user_ns(user_ns);
|
2009-06-23 03:09:13 +08:00
|
|
|
return new_ns;
|
|
|
|
}
|
|
|
|
|
2006-02-08 04:59:00 +08:00
|
|
|
/*
|
|
|
|
* Allocate a new namespace structure and populate it with contents
|
|
|
|
* copied from the namespace of the passed in task structure.
|
|
|
|
*/
|
2007-05-08 15:25:21 +08:00
|
|
|
static struct mnt_namespace *dup_mnt_ns(struct mnt_namespace *mnt_ns,
|
2012-07-27 12:08:32 +08:00
|
|
|
struct user_namespace *user_ns, struct fs_struct *fs)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2006-12-08 18:37:56 +08:00
|
|
|
struct mnt_namespace *new_ns;
|
2008-05-11 08:44:54 +08:00
|
|
|
struct vfsmount *rootmnt = NULL, *pwdmnt = NULL;
|
2011-11-25 07:57:30 +08:00
|
|
|
struct mount *p, *q;
|
2011-12-07 02:32:36 +08:00
|
|
|
struct mount *old = mnt_ns->root;
|
2011-11-25 09:55:08 +08:00
|
|
|
struct mount *new;
|
2012-08-01 04:13:04 +08:00
|
|
|
int copy_flags;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-07-27 12:08:32 +08:00
|
|
|
new_ns = alloc_mnt_ns(user_ns);
|
2009-06-23 03:09:13 +08:00
|
|
|
if (IS_ERR(new_ns))
|
|
|
|
return new_ns;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2005-04-17 06:20:36 +08:00
|
|
|
/* First pass: copy the tree topology */
|
2012-08-01 04:13:04 +08:00
|
|
|
copy_flags = CL_COPY_ALL | CL_EXPIRE;
|
|
|
|
if (user_ns != mnt_ns->user_ns)
|
2013-03-22 19:08:05 +08:00
|
|
|
copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED;
|
2012-08-01 04:13:04 +08:00
|
|
|
new = copy_tree(old, old->mnt.mnt_root, copy_flags);
|
2012-06-25 19:55:18 +08:00
|
|
|
if (IS_ERR(new)) {
|
2013-03-17 02:49:45 +08:00
|
|
|
namespace_unlock();
|
2012-07-27 12:08:32 +08:00
|
|
|
free_mnt_ns(new_ns);
|
2012-06-25 19:55:18 +08:00
|
|
|
return ERR_CAST(new);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-12-07 02:32:36 +08:00
|
|
|
new_ns->root = new;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 15:19:55 +08:00
|
|
|
list_add_tail(&new_ns->list, &new->mnt_list);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Second pass: switch the tsk->fs->* elements and mark new vfsmounts
|
|
|
|
* as belonging to new namespace. We have already acquired a private
|
|
|
|
* fs_struct, so tsk->fs->lock is not needed.
|
|
|
|
*/
|
2011-11-25 16:06:56 +08:00
|
|
|
p = old;
|
2011-11-25 09:55:08 +08:00
|
|
|
q = new;
|
2005-04-17 06:20:36 +08:00
|
|
|
while (p) {
|
2011-11-25 13:46:35 +08:00
|
|
|
q->mnt_ns = new_ns;
|
2005-04-17 06:20:36 +08:00
|
|
|
if (fs) {
|
2011-11-25 07:57:30 +08:00
|
|
|
if (&p->mnt == fs->root.mnt) {
|
|
|
|
fs->root.mnt = mntget(&q->mnt);
|
|
|
|
rootmnt = &p->mnt;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-11-25 07:57:30 +08:00
|
|
|
if (&p->mnt == fs->pwd.mnt) {
|
|
|
|
fs->pwd.mnt = mntget(&q->mnt);
|
|
|
|
pwdmnt = &p->mnt;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
}
|
2011-11-25 16:06:56 +08:00
|
|
|
p = next_mnt(p, old);
|
|
|
|
q = next_mnt(q, new);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2013-03-17 02:49:45 +08:00
|
|
|
namespace_unlock();
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
if (rootmnt)
|
2011-01-15 11:30:21 +08:00
|
|
|
mntput(rootmnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (pwdmnt)
|
2011-01-15 11:30:21 +08:00
|
|
|
mntput(pwdmnt);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2006-02-08 04:59:00 +08:00
|
|
|
return new_ns;
|
|
|
|
}
|
|
|
|
|
2007-07-16 14:41:15 +08:00
|
|
|
struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
|
2012-07-27 12:08:32 +08:00
|
|
|
struct user_namespace *user_ns, struct fs_struct *new_fs)
|
2006-02-08 04:59:00 +08:00
|
|
|
{
|
2006-12-08 18:37:56 +08:00
|
|
|
struct mnt_namespace *new_ns;
|
2006-02-08 04:59:00 +08:00
|
|
|
|
2007-05-08 15:25:21 +08:00
|
|
|
BUG_ON(!ns);
|
2006-12-08 18:37:56 +08:00
|
|
|
get_mnt_ns(ns);
|
2006-02-08 04:59:00 +08:00
|
|
|
|
|
|
|
if (!(flags & CLONE_NEWNS))
|
2007-05-08 15:25:21 +08:00
|
|
|
return ns;
|
2006-02-08 04:59:00 +08:00
|
|
|
|
2012-07-27 12:08:32 +08:00
|
|
|
new_ns = dup_mnt_ns(ns, user_ns, new_fs);
|
2006-02-08 04:59:00 +08:00
|
|
|
|
2006-12-08 18:37:56 +08:00
|
|
|
put_mnt_ns(ns);
|
2007-05-08 15:25:21 +08:00
|
|
|
return new_ns;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2009-06-23 03:09:13 +08:00
|
|
|
/**
|
|
|
|
* create_mnt_ns - creates a private namespace and adds a root filesystem
|
|
|
|
* @mnt: pointer to the new root filesystem mountpoint
|
|
|
|
*/
|
2011-11-25 15:19:55 +08:00
|
|
|
static struct mnt_namespace *create_mnt_ns(struct vfsmount *m)
|
2009-06-23 03:09:13 +08:00
|
|
|
{
|
2012-07-27 12:08:32 +08:00
|
|
|
struct mnt_namespace *new_ns = alloc_mnt_ns(&init_user_ns);
|
2009-06-23 03:09:13 +08:00
|
|
|
if (!IS_ERR(new_ns)) {
|
2011-11-25 15:19:55 +08:00
|
|
|
struct mount *mnt = real_mount(m);
|
|
|
|
mnt->mnt_ns = new_ns;
|
2011-12-07 02:32:36 +08:00
|
|
|
new_ns->root = mnt;
|
2013-05-05 03:18:53 +08:00
|
|
|
list_add(&mnt->mnt_list, &new_ns->list);
|
2011-11-17 05:12:14 +08:00
|
|
|
} else {
|
2011-11-25 15:19:55 +08:00
|
|
|
mntput(m);
|
2009-06-23 03:09:13 +08:00
|
|
|
}
|
|
|
|
return new_ns;
|
|
|
|
}
|
|
|
|
|
2011-11-17 10:43:59 +08:00
|
|
|
struct dentry *mount_subtree(struct vfsmount *mnt, const char *name)
|
|
|
|
{
|
|
|
|
struct mnt_namespace *ns;
|
2011-11-23 01:31:21 +08:00
|
|
|
struct super_block *s;
|
2011-11-17 10:43:59 +08:00
|
|
|
struct path path;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
ns = create_mnt_ns(mnt);
|
|
|
|
if (IS_ERR(ns))
|
|
|
|
return ERR_CAST(ns);
|
|
|
|
|
|
|
|
err = vfs_path_lookup(mnt->mnt_root, mnt,
|
|
|
|
name, LOOKUP_FOLLOW|LOOKUP_AUTOMOUNT, &path);
|
|
|
|
|
|
|
|
put_mnt_ns(ns);
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
return ERR_PTR(err);
|
|
|
|
|
|
|
|
/* trade a vfsmount reference for active sb one */
|
2011-11-23 01:31:21 +08:00
|
|
|
s = path.mnt->mnt_sb;
|
|
|
|
atomic_inc(&s->s_active);
|
2011-11-17 10:43:59 +08:00
|
|
|
mntput(path.mnt);
|
|
|
|
/* lock the sucker */
|
2011-11-23 01:31:21 +08:00
|
|
|
down_write(&s->s_umount);
|
2011-11-17 10:43:59 +08:00
|
|
|
/* ... and return the root of (sub)tree on it */
|
|
|
|
return path.dentry;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(mount_subtree);
|
|
|
|
|
2009-01-14 21:14:12 +08:00
|
|
|
SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
|
|
|
|
char __user *, type, unsigned long, flags, void __user *, data)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
int ret;
|
|
|
|
char *kernel_type;
|
2012-10-11 03:25:28 +08:00
|
|
|
struct filename *kernel_dir;
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
char *kernel_dev;
|
2005-04-17 06:20:36 +08:00
|
|
|
unsigned long data_page;
|
|
|
|
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
ret = copy_mount_string(type, &kernel_type);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_type;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
kernel_dir = getname(dir_name);
|
|
|
|
if (IS_ERR(kernel_dir)) {
|
|
|
|
ret = PTR_ERR(kernel_dir);
|
|
|
|
goto out_dir;
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
ret = copy_mount_string(dev_name, &kernel_dev);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_dev;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
ret = copy_mount_options(data, &data_page);
|
|
|
|
if (ret < 0)
|
|
|
|
goto out_data;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-10-11 03:25:28 +08:00
|
|
|
ret = do_mount(kernel_dev, kernel_dir->name, kernel_type, flags,
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
(void *) data_page);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
fs: fix overflow in sys_mount() for in-kernel calls
sys_mount() reads/copies a whole page for its "type" parameter. When
do_mount_root() passes a kernel address that points to an object which is
smaller than a whole page, copy_mount_options() will happily go past this
memory object, possibly dereferencing "wild" pointers that could be in any
state (hence the kmemcheck warning, which shows that parts of the next
page are not even allocated).
(The likelihood of something going wrong here is pretty low -- first of
all this only applies to kernel calls to sys_mount(), which are mostly
found in the boot code. Secondly, I guess if the page was not mapped,
exact_copy_from_user() _would_ in fact handle it correctly because of its
access_ok(), etc. checks.)
But it is much nicer to avoid the dubious reads altogether, by stopping as
soon as we find a NUL byte. Is there a good reason why we can't do
something like this, using the already existing strndup_from_user()?
[akpm@linux-foundation.org: make copy_mount_string() static]
[AV: fix compat mount breakage, which involves undoing akpm's change above]
Reported-by: Ingo Molnar <mingo@elte.hu>
Signed-off-by: Vegard Nossum <vegard.nossum@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Pekka Enberg <penberg@cs.helsinki.fi>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: al <al@dizzy.pdmi.ras.ru>
2009-09-19 04:05:45 +08:00
|
|
|
free_page(data_page);
|
|
|
|
out_data:
|
|
|
|
kfree(kernel_dev);
|
|
|
|
out_dev:
|
|
|
|
putname(kernel_dir);
|
|
|
|
out_dir:
|
|
|
|
kfree(kernel_type);
|
|
|
|
out_type:
|
|
|
|
return ret;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2011-11-24 08:34:49 +08:00
|
|
|
/*
|
|
|
|
* Return true if path is reachable from root
|
|
|
|
*
|
|
|
|
* namespace_sem or vfsmount_lock is held
|
|
|
|
*/
|
2011-11-25 11:00:28 +08:00
|
|
|
bool is_path_reachable(struct mount *mnt, struct dentry *dentry,
|
2011-11-24 08:34:49 +08:00
|
|
|
const struct path *root)
|
|
|
|
{
|
2011-11-25 11:00:28 +08:00
|
|
|
while (&mnt->mnt != root->mnt && mnt_has_parent(mnt)) {
|
2011-11-25 11:25:07 +08:00
|
|
|
dentry = mnt->mnt_mountpoint;
|
2011-11-25 11:19:58 +08:00
|
|
|
mnt = mnt->mnt_parent;
|
2011-11-24 08:34:49 +08:00
|
|
|
}
|
2011-11-25 11:00:28 +08:00
|
|
|
return &mnt->mnt == root->mnt && is_subdir(dentry, root->dentry);
|
2011-11-24 08:34:49 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
int path_is_under(struct path *path1, struct path *path2)
|
|
|
|
{
|
|
|
|
int res;
|
2012-05-08 12:02:02 +08:00
|
|
|
br_read_lock(&vfsmount_lock);
|
2011-11-25 11:00:28 +08:00
|
|
|
res = is_path_reachable(real_mount(path1->mnt), path1->dentry, path2);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_read_unlock(&vfsmount_lock);
|
2011-11-24 08:34:49 +08:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(path_is_under);
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* pivot_root Semantics:
|
|
|
|
* Moves the root file system of the current process to the directory put_old,
|
|
|
|
* makes new_root as the new root file system of the current process, and sets
|
|
|
|
* root/cwd of all processes which had them on the current root to new_root.
|
|
|
|
*
|
|
|
|
* Restrictions:
|
|
|
|
* The new_root and put_old must be directories, and must not be on the
|
|
|
|
* same file system as the current process root. The put_old must be
|
|
|
|
* underneath new_root, i.e. adding a non-zero number of /.. to the string
|
|
|
|
* pointed to by put_old must yield the same directory as new_root. No other
|
|
|
|
* file system may be mounted on put_old. After all, new_root is a mountpoint.
|
|
|
|
*
|
2006-01-08 17:03:18 +08:00
|
|
|
* Also, the current root cannot be on the 'rootfs' (initial ramfs) filesystem.
|
|
|
|
* See Documentation/filesystems/ramfs-rootfs-initramfs.txt for alternatives
|
|
|
|
* in this situation.
|
|
|
|
*
|
2005-04-17 06:20:36 +08:00
|
|
|
* Notes:
|
|
|
|
* - we don't move root/cwd if they are not at the root (reason: if something
|
|
|
|
* cared enough to change them, it's probably wrong to force them elsewhere)
|
|
|
|
* - it's okay to pick a root that isn't the root of a file system, e.g.
|
|
|
|
* /nfs/my_root where /nfs is the mount point. It must be a mountpoint,
|
|
|
|
* though, so you may need to say mount --bind /nfs/my_root /nfs/my_root
|
|
|
|
* first.
|
|
|
|
*/
|
2009-01-14 21:14:16 +08:00
|
|
|
SYSCALL_DEFINE2(pivot_root, const char __user *, new_root,
|
|
|
|
const char __user *, put_old)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-07-22 21:59:21 +08:00
|
|
|
struct path new, old, parent_path, root_parent, root;
|
2013-03-15 22:53:28 +08:00
|
|
|
struct mount *new_mnt, *root_mnt, *old_mnt;
|
|
|
|
struct mountpoint *old_mp, *root_mp;
|
2005-04-17 06:20:36 +08:00
|
|
|
int error;
|
|
|
|
|
2013-02-23 11:45:42 +08:00
|
|
|
if (!may_mount())
|
2005-04-17 06:20:36 +08:00
|
|
|
return -EPERM;
|
|
|
|
|
2008-07-22 21:59:21 +08:00
|
|
|
error = user_path_dir(new_root, &new);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (error)
|
|
|
|
goto out0;
|
|
|
|
|
2008-07-22 21:59:21 +08:00
|
|
|
error = user_path_dir(put_old, &old);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (error)
|
|
|
|
goto out1;
|
|
|
|
|
2008-07-22 21:59:21 +08:00
|
|
|
error = security_sb_pivotroot(&old, &new);
|
2011-03-18 20:55:38 +08:00
|
|
|
if (error)
|
|
|
|
goto out2;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-10 17:41:36 +08:00
|
|
|
get_fs_root(current->fs, &root);
|
2013-03-15 22:53:28 +08:00
|
|
|
old_mp = lock_mount(&old);
|
|
|
|
error = PTR_ERR(old_mp);
|
|
|
|
if (IS_ERR(old_mp))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out3;
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
error = -EINVAL;
|
2011-11-25 08:41:16 +08:00
|
|
|
new_mnt = real_mount(new.mnt);
|
|
|
|
root_mnt = real_mount(root.mnt);
|
2013-03-15 22:53:28 +08:00
|
|
|
old_mnt = real_mount(old.mnt);
|
|
|
|
if (IS_MNT_SHARED(old_mnt) ||
|
2011-11-25 14:05:37 +08:00
|
|
|
IS_MNT_SHARED(new_mnt->mnt_parent) ||
|
|
|
|
IS_MNT_SHARED(root_mnt->mnt_parent))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4;
|
2011-11-25 13:46:35 +08:00
|
|
|
if (!check_mnt(root_mnt) || !check_mnt(new_mnt))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4;
|
2005-04-17 06:20:36 +08:00
|
|
|
error = -ENOENT;
|
2009-05-04 07:32:03 +08:00
|
|
|
if (d_unlinked(new.dentry))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4;
|
2005-04-17 06:20:36 +08:00
|
|
|
error = -EBUSY;
|
2013-03-15 22:53:28 +08:00
|
|
|
if (new_mnt == root_mnt || old_mnt == root_mnt)
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4; /* loop, on the same file system */
|
2005-04-17 06:20:36 +08:00
|
|
|
error = -EINVAL;
|
2008-03-23 06:00:39 +08:00
|
|
|
if (root.mnt->mnt_root != root.dentry)
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4; /* not a mountpoint */
|
2011-11-25 10:47:05 +08:00
|
|
|
if (!mnt_has_parent(root_mnt))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4; /* not attached */
|
2013-03-15 22:53:28 +08:00
|
|
|
root_mp = root_mnt->mnt_mp;
|
2008-07-22 21:59:21 +08:00
|
|
|
if (new.mnt->mnt_root != new.dentry)
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4; /* not a mountpoint */
|
2011-11-25 10:47:05 +08:00
|
|
|
if (!mnt_has_parent(new_mnt))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4; /* not attached */
|
2008-02-15 11:34:32 +08:00
|
|
|
/* make sure we can reach put_old from new_root */
|
2013-03-15 22:53:28 +08:00
|
|
|
if (!is_path_reachable(old_mnt, old.dentry, &new))
|
2011-03-18 20:55:38 +08:00
|
|
|
goto out4;
|
2013-03-15 22:53:28 +08:00
|
|
|
root_mp->m_count++; /* pin it so it won't go away */
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2011-11-25 08:41:16 +08:00
|
|
|
detach_mnt(new_mnt, &parent_path);
|
|
|
|
detach_mnt(root_mnt, &root_parent);
|
2008-02-15 11:34:32 +08:00
|
|
|
/* mount old root on put_old */
|
2013-03-15 22:53:28 +08:00
|
|
|
attach_mnt(root_mnt, old_mnt, old_mp);
|
2008-02-15 11:34:32 +08:00
|
|
|
/* mount new_root on / */
|
2013-03-15 22:53:28 +08:00
|
|
|
attach_mnt(new_mnt, real_mount(root_parent.mnt), root_mp);
|
2006-12-08 18:37:56 +08:00
|
|
|
touch_mnt_namespace(current->nsproxy->mnt_ns);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2008-07-22 21:59:21 +08:00
|
|
|
chroot_fs_refs(&root, &new);
|
2013-03-15 22:53:28 +08:00
|
|
|
put_mountpoint(root_mp);
|
2005-04-17 06:20:36 +08:00
|
|
|
error = 0;
|
2011-03-18 20:55:38 +08:00
|
|
|
out4:
|
2013-03-15 22:53:28 +08:00
|
|
|
unlock_mount(old_mp);
|
2011-03-18 20:55:38 +08:00
|
|
|
if (!error) {
|
|
|
|
path_put(&root_parent);
|
|
|
|
path_put(&parent_path);
|
|
|
|
}
|
|
|
|
out3:
|
2008-03-23 06:00:39 +08:00
|
|
|
path_put(&root);
|
2011-03-18 20:55:38 +08:00
|
|
|
out2:
|
2008-07-22 21:59:21 +08:00
|
|
|
path_put(&old);
|
2005-04-17 06:20:36 +08:00
|
|
|
out1:
|
2008-07-22 21:59:21 +08:00
|
|
|
path_put(&new);
|
2005-04-17 06:20:36 +08:00
|
|
|
out0:
|
|
|
|
return error;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void __init init_mount_tree(void)
|
|
|
|
{
|
|
|
|
struct vfsmount *mnt;
|
2006-12-08 18:37:56 +08:00
|
|
|
struct mnt_namespace *ns;
|
2008-02-15 11:34:39 +08:00
|
|
|
struct path root;
|
2012-07-27 12:42:03 +08:00
|
|
|
struct file_system_type *type;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-07-27 12:42:03 +08:00
|
|
|
type = get_fs_type("rootfs");
|
|
|
|
if (!type)
|
|
|
|
panic("Can't find rootfs type");
|
|
|
|
mnt = vfs_kern_mount(type, 0, "rootfs", NULL);
|
|
|
|
put_filesystem(type);
|
2005-04-17 06:20:36 +08:00
|
|
|
if (IS_ERR(mnt))
|
|
|
|
panic("Can't create rootfs");
|
fs: scale mntget/mntput
The problem that this patch aims to fix is vfsmount refcounting scalability.
We need to take a reference on the vfsmount for every successful path lookup,
which often go to the same mount point.
The fundamental difficulty is that a "simple" reference count can never be made
scalable, because any time a reference is dropped, we must check whether that
was the last reference. To do that requires communication with all other CPUs
that may have taken a reference count.
We can make refcounts more scalable in a couple of ways, involving keeping
distributed counters, and checking for the global-zero condition less
frequently.
- check the global sum once every interval (this will delay zero detection
for some interval, so it's probably a showstopper for vfsmounts).
- keep a local count and only taking the global sum when local reaches 0 (this
is difficult for vfsmounts, because we can't hold preempt off for the life of
a reference, so a counter would need to be per-thread or tied strongly to a
particular CPU which requires more locking).
- keep a local difference of increments and decrements, which allows us to sum
the total difference and hence find the refcount when summing all CPUs. Then,
keep a single integer "long" refcount for slow and long lasting references,
and only take the global sum of local counters when the long refcount is 0.
This last scheme is what I implemented here. Attached mounts and process root
and working directory references are "long" references, and everything else is
a short reference.
This allows scalable vfsmount references during path walking over mounted
subtrees and unattached (lazy umounted) mounts with processes still running
in them.
This results in one fewer atomic op in the fastpath: mntget is now just a
per-CPU inc, rather than an atomic inc; and mntput just requires a spinlock
and non-atomic decrement in the common case. However code is otherwise bigger
and heavier, so single threaded performance is basically a wash.
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
2011-01-07 14:50:11 +08:00
|
|
|
|
2009-06-24 05:29:49 +08:00
|
|
|
ns = create_mnt_ns(mnt);
|
|
|
|
if (IS_ERR(ns))
|
2005-04-17 06:20:36 +08:00
|
|
|
panic("Can't allocate initial namespace");
|
2006-12-08 18:37:56 +08:00
|
|
|
|
|
|
|
init_task.nsproxy->mnt_ns = ns;
|
|
|
|
get_mnt_ns(ns);
|
|
|
|
|
2011-12-07 02:32:36 +08:00
|
|
|
root.mnt = mnt;
|
|
|
|
root.dentry = mnt->mnt_root;
|
2008-02-15 11:34:39 +08:00
|
|
|
|
|
|
|
set_fs_pwd(current->fs, &root);
|
|
|
|
set_fs_root(current->fs, &root);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
2007-10-17 14:26:30 +08:00
|
|
|
void __init mnt_init(void)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2008-02-06 17:37:57 +08:00
|
|
|
unsigned u;
|
2006-09-29 16:58:57 +08:00
|
|
|
int err;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-08 06:17:51 +08:00
|
|
|
init_rwsem(&namespace_sem);
|
|
|
|
|
2011-11-24 01:14:10 +08:00
|
|
|
mnt_cache = kmem_cache_create("mnt_cache", sizeof(struct mount),
|
2007-07-20 09:11:58 +08:00
|
|
|
0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2005-11-08 06:16:09 +08:00
|
|
|
mount_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);
|
2013-03-15 22:53:28 +08:00
|
|
|
mountpoint_hashtable = (struct list_head *)__get_free_page(GFP_ATOMIC);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2013-03-15 22:53:28 +08:00
|
|
|
if (!mount_hashtable || !mountpoint_hashtable)
|
2005-04-17 06:20:36 +08:00
|
|
|
panic("Failed to allocate mount hash table\n");
|
|
|
|
|
2011-03-23 07:33:54 +08:00
|
|
|
printk(KERN_INFO "Mount-cache hash table entries: %lu\n", HASH_SIZE);
|
2008-02-06 17:37:57 +08:00
|
|
|
|
|
|
|
for (u = 0; u < HASH_SIZE; u++)
|
|
|
|
INIT_LIST_HEAD(&mount_hashtable[u]);
|
2013-03-15 22:53:28 +08:00
|
|
|
for (u = 0; u < HASH_SIZE; u++)
|
|
|
|
INIT_LIST_HEAD(&mountpoint_hashtable[u]);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2012-05-08 12:02:02 +08:00
|
|
|
br_lock_init(&vfsmount_lock);
|
fs: brlock vfsmount_lock
fs: brlock vfsmount_lock
Use a brlock for the vfsmount lock. It must be taken for write whenever
modifying the mount hash or associated fields, and may be taken for read when
performing mount hash lookups.
A new lock is added for the mnt-id allocator, so it doesn't need to take
the heavy vfsmount write-lock.
The number of atomics should remain the same for fastpath rlock cases, though
code would be slightly slower due to per-cpu access. Scalability is not not be
much improved in common cases yet, due to other locks (ie. dcache_lock) getting
in the way. However path lookups crossing mountpoints should be one case where
scalability is improved (currently requiring the global lock).
The slowpath is slower due to use of brlock. On a 64 core, 64 socket, 32 node
Altix system (high latency to remote nodes), a simple umount microbenchmark
(mount --bind mnt mnt2 ; umount mnt2 loop 1000 times), before this patch it
took 6.8s, afterwards took 7.1s, about 5% slower.
Cc: Al Viro <viro@ZenIV.linux.org.uk>
Signed-off-by: Nick Piggin <npiggin@kernel.dk>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
2010-08-18 02:37:39 +08:00
|
|
|
|
2006-09-29 16:58:57 +08:00
|
|
|
err = sysfs_init();
|
|
|
|
if (err)
|
|
|
|
printk(KERN_WARNING "%s: sysfs_init error: %d\n",
|
2008-04-30 15:55:09 +08:00
|
|
|
__func__, err);
|
2007-10-30 04:17:23 +08:00
|
|
|
fs_kobj = kobject_create_and_add("fs", NULL);
|
|
|
|
if (!fs_kobj)
|
2008-04-30 15:55:09 +08:00
|
|
|
printk(KERN_WARNING "%s: kobj create error\n", __func__);
|
2005-04-17 06:20:36 +08:00
|
|
|
init_rootfs();
|
|
|
|
init_mount_tree();
|
|
|
|
}
|
|
|
|
|
2009-06-23 03:09:13 +08:00
|
|
|
void put_mnt_ns(struct mnt_namespace *ns)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2010-02-05 15:21:06 +08:00
|
|
|
if (!atomic_dec_and_test(&ns->count))
|
2009-06-23 03:09:13 +08:00
|
|
|
return;
|
2013-03-17 03:12:40 +08:00
|
|
|
namespace_lock();
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
2013-03-17 02:49:45 +08:00
|
|
|
umount_tree(ns->root, 0);
|
2012-05-08 12:02:02 +08:00
|
|
|
br_write_unlock(&vfsmount_lock);
|
2013-03-17 02:42:19 +08:00
|
|
|
namespace_unlock();
|
2012-07-27 12:08:32 +08:00
|
|
|
free_mnt_ns(ns);
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
2011-03-18 10:08:28 +08:00
|
|
|
|
|
|
|
struct vfsmount *kern_mount_data(struct file_system_type *type, void *data)
|
|
|
|
{
|
2011-07-20 00:32:38 +08:00
|
|
|
struct vfsmount *mnt;
|
|
|
|
mnt = vfs_kern_mount(type, MS_KERNMOUNT, type->name, data);
|
|
|
|
if (!IS_ERR(mnt)) {
|
|
|
|
/*
|
|
|
|
* it is a longterm mount, don't release mnt until
|
|
|
|
* we unmount before file sys is unregistered
|
|
|
|
*/
|
2012-06-09 12:59:08 +08:00
|
|
|
real_mount(mnt)->mnt_ns = MNT_NS_INTERNAL;
|
2011-07-20 00:32:38 +08:00
|
|
|
}
|
|
|
|
return mnt;
|
2011-03-18 10:08:28 +08:00
|
|
|
}
|
|
|
|
EXPORT_SYMBOL_GPL(kern_mount_data);
|
2011-07-20 00:32:38 +08:00
|
|
|
|
|
|
|
void kern_unmount(struct vfsmount *mnt)
|
|
|
|
{
|
|
|
|
/* release long term mount so mount point can be released */
|
|
|
|
if (!IS_ERR_OR_NULL(mnt)) {
|
2012-06-09 12:59:08 +08:00
|
|
|
br_write_lock(&vfsmount_lock);
|
|
|
|
real_mount(mnt)->mnt_ns = NULL;
|
|
|
|
br_write_unlock(&vfsmount_lock);
|
2011-07-20 00:32:38 +08:00
|
|
|
mntput(mnt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
EXPORT_SYMBOL(kern_unmount);
|
fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API
__d_path() API is asking for trouble and in case of apparmor d_namespace_path()
getting just that. The root cause is that when __d_path() misses the root
it had been told to look for, it stores the location of the most remote ancestor
in *root. Without grabbing references. Sure, at the moment of call it had
been pinned down by what we have in *path. And if we raced with umount -l, we
could have very well stopped at vfsmount/dentry that got freed as soon as
prepend_path() dropped vfsmount_lock.
It is safe to compare these pointers with pre-existing (and known to be still
alive) vfsmount and dentry, as long as all we are asking is "is it the same
address?". Dereferencing is not safe and apparmor ended up stepping into
that. d_namespace_path() really wants to examine the place where we stopped,
even if it's not connected to our namespace. As the result, it looked
at ->d_sb->s_magic of a dentry that might've been already freed by that point.
All other callers had been careful enough to avoid that, but it's really
a bad interface - it invites that kind of trouble.
The fix is fairly straightforward, even though it's bigger than I'd like:
* prepend_path() root argument becomes const.
* __d_path() is never called with NULL/NULL root. It was a kludge
to start with. Instead, we have an explicit function - d_absolute_root().
Same as __d_path(), except that it doesn't get root passed and stops where
it stops. apparmor and tomoyo are using it.
* __d_path() returns NULL on path outside of root. The main
caller is show_mountinfo() and that's precisely what we pass root for - to
skip those outside chroot jail. Those who don't want that can (and do)
use d_path().
* __d_path() root argument becomes const. Everyone agrees, I hope.
* apparmor does *NOT* try to use __d_path() or any of its variants
when it sees that path->mnt is an internal vfsmount. In that case it's
definitely not mounted anywhere and dentry_path() is exactly what we want
there. Handling of sysctl()-triggered weirdness is moved to that place.
* if apparmor is asked to do pathname relative to chroot jail
and __d_path() tells it we it's not in that jail, the sucker just calls
d_absolute_path() instead. That's the other remaining caller of __d_path(),
BTW.
* seq_path_root() does _NOT_ return -ENAMETOOLONG (it's stupid anyway -
the normal seq_file logics will take care of growing the buffer and redoing
the call of ->show() just fine). However, if it gets path not reachable
from root, it returns SEQ_SKIP. The only caller adjusted (i.e. stopped
ignoring the return value as it used to do).
Reviewed-by: John Johansen <john.johansen@canonical.com>
ACKed-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: stable@vger.kernel.org
2011-12-05 21:43:34 +08:00
|
|
|
|
|
|
|
bool our_mnt(struct vfsmount *mnt)
|
|
|
|
{
|
2011-11-25 13:46:35 +08:00
|
|
|
return check_mnt(real_mount(mnt));
|
fix apparmor dereferencing potentially freed dentry, sanitize __d_path() API
__d_path() API is asking for trouble and in case of apparmor d_namespace_path()
getting just that. The root cause is that when __d_path() misses the root
it had been told to look for, it stores the location of the most remote ancestor
in *root. Without grabbing references. Sure, at the moment of call it had
been pinned down by what we have in *path. And if we raced with umount -l, we
could have very well stopped at vfsmount/dentry that got freed as soon as
prepend_path() dropped vfsmount_lock.
It is safe to compare these pointers with pre-existing (and known to be still
alive) vfsmount and dentry, as long as all we are asking is "is it the same
address?". Dereferencing is not safe and apparmor ended up stepping into
that. d_namespace_path() really wants to examine the place where we stopped,
even if it's not connected to our namespace. As the result, it looked
at ->d_sb->s_magic of a dentry that might've been already freed by that point.
All other callers had been careful enough to avoid that, but it's really
a bad interface - it invites that kind of trouble.
The fix is fairly straightforward, even though it's bigger than I'd like:
* prepend_path() root argument becomes const.
* __d_path() is never called with NULL/NULL root. It was a kludge
to start with. Instead, we have an explicit function - d_absolute_root().
Same as __d_path(), except that it doesn't get root passed and stops where
it stops. apparmor and tomoyo are using it.
* __d_path() returns NULL on path outside of root. The main
caller is show_mountinfo() and that's precisely what we pass root for - to
skip those outside chroot jail. Those who don't want that can (and do)
use d_path().
* __d_path() root argument becomes const. Everyone agrees, I hope.
* apparmor does *NOT* try to use __d_path() or any of its variants
when it sees that path->mnt is an internal vfsmount. In that case it's
definitely not mounted anywhere and dentry_path() is exactly what we want
there. Handling of sysctl()-triggered weirdness is moved to that place.
* if apparmor is asked to do pathname relative to chroot jail
and __d_path() tells it we it's not in that jail, the sucker just calls
d_absolute_path() instead. That's the other remaining caller of __d_path(),
BTW.
* seq_path_root() does _NOT_ return -ENAMETOOLONG (it's stupid anyway -
the normal seq_file logics will take care of growing the buffer and redoing
the call of ->show() just fine). However, if it gets path not reachable
from root, it returns SEQ_SKIP. The only caller adjusted (i.e. stopped
ignoring the return value as it used to do).
Reviewed-by: John Johansen <john.johansen@canonical.com>
ACKed-by: John Johansen <john.johansen@canonical.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: stable@vger.kernel.org
2011-12-05 21:43:34 +08:00
|
|
|
}
|
2010-03-08 10:49:36 +08:00
|
|
|
|
2013-03-15 16:45:51 +08:00
|
|
|
bool current_chrooted(void)
|
|
|
|
{
|
|
|
|
/* Does the current process have a non-standard root */
|
|
|
|
struct path ns_root;
|
|
|
|
struct path fs_root;
|
|
|
|
bool chrooted;
|
|
|
|
|
|
|
|
/* Find the namespace root */
|
|
|
|
ns_root.mnt = ¤t->nsproxy->mnt_ns->root->mnt;
|
|
|
|
ns_root.dentry = ns_root.mnt->mnt_root;
|
|
|
|
path_get(&ns_root);
|
|
|
|
while (d_mountpoint(ns_root.dentry) && follow_down_one(&ns_root))
|
|
|
|
;
|
|
|
|
|
|
|
|
get_fs_root(current->fs, &fs_root);
|
|
|
|
|
|
|
|
chrooted = !path_equal(&fs_root, &ns_root);
|
|
|
|
|
|
|
|
path_put(&fs_root);
|
|
|
|
path_put(&ns_root);
|
|
|
|
|
|
|
|
return chrooted;
|
|
|
|
}
|
|
|
|
|
2013-03-25 05:28:27 +08:00
|
|
|
void update_mnt_policy(struct user_namespace *userns)
|
|
|
|
{
|
|
|
|
struct mnt_namespace *ns = current->nsproxy->mnt_ns;
|
|
|
|
struct mount *mnt;
|
|
|
|
|
|
|
|
down_read(&namespace_sem);
|
|
|
|
list_for_each_entry(mnt, &ns->list, mnt_list) {
|
|
|
|
switch (mnt->mnt.mnt_sb->s_magic) {
|
|
|
|
case SYSFS_MAGIC:
|
|
|
|
userns->may_mount_sysfs = true;
|
|
|
|
break;
|
|
|
|
case PROC_SUPER_MAGIC:
|
|
|
|
userns->may_mount_proc = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (userns->may_mount_sysfs && userns->may_mount_proc)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
up_read(&namespace_sem);
|
|
|
|
}
|
|
|
|
|
2010-03-08 10:49:36 +08:00
|
|
|
static void *mntns_get(struct task_struct *task)
|
|
|
|
{
|
|
|
|
struct mnt_namespace *ns = NULL;
|
|
|
|
struct nsproxy *nsproxy;
|
|
|
|
|
|
|
|
rcu_read_lock();
|
|
|
|
nsproxy = task_nsproxy(task);
|
|
|
|
if (nsproxy) {
|
|
|
|
ns = nsproxy->mnt_ns;
|
|
|
|
get_mnt_ns(ns);
|
|
|
|
}
|
|
|
|
rcu_read_unlock();
|
|
|
|
|
|
|
|
return ns;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void mntns_put(void *ns)
|
|
|
|
{
|
|
|
|
put_mnt_ns(ns);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int mntns_install(struct nsproxy *nsproxy, void *ns)
|
|
|
|
{
|
|
|
|
struct fs_struct *fs = current->fs;
|
|
|
|
struct mnt_namespace *mnt_ns = ns;
|
|
|
|
struct path root;
|
|
|
|
|
2012-07-27 12:42:03 +08:00
|
|
|
if (!ns_capable(mnt_ns->user_ns, CAP_SYS_ADMIN) ||
|
2012-12-14 23:55:36 +08:00
|
|
|
!nsown_capable(CAP_SYS_CHROOT) ||
|
|
|
|
!nsown_capable(CAP_SYS_ADMIN))
|
2012-09-13 16:38:03 +08:00
|
|
|
return -EPERM;
|
2010-03-08 10:49:36 +08:00
|
|
|
|
|
|
|
if (fs->users != 1)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
get_mnt_ns(mnt_ns);
|
|
|
|
put_mnt_ns(nsproxy->mnt_ns);
|
|
|
|
nsproxy->mnt_ns = mnt_ns;
|
|
|
|
|
|
|
|
/* Find the root */
|
|
|
|
root.mnt = &mnt_ns->root->mnt;
|
|
|
|
root.dentry = mnt_ns->root->mnt.mnt_root;
|
|
|
|
path_get(&root);
|
|
|
|
while(d_mountpoint(root.dentry) && follow_down_one(&root))
|
|
|
|
;
|
|
|
|
|
|
|
|
/* Update the pwd and root */
|
|
|
|
set_fs_pwd(fs, &root);
|
|
|
|
set_fs_root(fs, &root);
|
|
|
|
|
|
|
|
path_put(&root);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2011-06-16 01:21:48 +08:00
|
|
|
static unsigned int mntns_inum(void *ns)
|
|
|
|
{
|
|
|
|
struct mnt_namespace *mnt_ns = ns;
|
|
|
|
return mnt_ns->proc_inum;
|
|
|
|
}
|
|
|
|
|
2010-03-08 10:49:36 +08:00
|
|
|
const struct proc_ns_operations mntns_operations = {
|
|
|
|
.name = "mnt",
|
|
|
|
.type = CLONE_NEWNS,
|
|
|
|
.get = mntns_get,
|
|
|
|
.put = mntns_put,
|
|
|
|
.install = mntns_install,
|
2011-06-16 01:21:48 +08:00
|
|
|
.inum = mntns_inum,
|
2010-03-08 10:49:36 +08:00
|
|
|
};
|