Newer
Older
/*
* 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>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/percpu.h>
#include <linux/kernel.h>
#include <linux/cpumask.h>
#include <linux/mnt_namespace.h>
#include <linux/security.h>
#include <linux/mount.h>

David Howells
committed
#include <linux/ramfs.h>
#include <linux/fsnotify.h>
#define HASH_SHIFT ilog2(PAGE_SIZE / sizeof(struct list_head))
#define HASH_SIZE (1UL << HASH_SHIFT)
static DEFINE_IDA(mnt_group_ida);
static int mnt_id_start = 0;
static int mnt_group_start = 1;
static struct list_head *mount_hashtable __read_mostly;
static struct kmem_cache *mnt_cache __read_mostly;
struct kobject *fs_kobj;
EXPORT_SYMBOL_GPL(fs_kobj);
/*
* 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);
static inline unsigned long hash(struct vfsmount *mnt, struct dentry *dentry)
{
unsigned long tmp = ((unsigned long)mnt / L1_CACHE_BYTES);
tmp += ((unsigned long)dentry / L1_CACHE_BYTES);
tmp = tmp + (tmp >> HASH_SHIFT);
return tmp & (HASH_SIZE - 1);
#define MNT_WRITER_UNDERFLOW_LIMIT -(1<<16)
/*
* allocation is serialized by namespace_sem, but we need the spinlock to
* serialize with freeing.
*/
static int mnt_alloc_id(struct vfsmount *mnt)
{
int res;
retry:
ida_pre_get(&mnt_id_ida, GFP_KERNEL);
res = ida_get_new_above(&mnt_id_ida, mnt_id_start, &mnt->mnt_id);
if (!res)
mnt_id_start = mnt->mnt_id + 1;
if (res == -EAGAIN)
goto retry;
return res;
}
static void mnt_free_id(struct vfsmount *mnt)
{
ida_remove(&mnt_id_ida, id);
if (mnt_id_start > id)
mnt_id_start = id;
/*
* Allocate a new peer group ID
*
* mnt_group_ida is protected by namespace_sem
*/
static int mnt_alloc_group_id(struct vfsmount *mnt)
{
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
res = ida_get_new_above(&mnt_group_ida,
mnt_group_start,
&mnt->mnt_group_id);
if (!res)
mnt_group_start = mnt->mnt_group_id + 1;
return res;
}
/*
* Release a peer group ID
*/
void mnt_release_group_id(struct vfsmount *mnt)
{
int id = mnt->mnt_group_id;
ida_remove(&mnt_group_ida, id);
if (mnt_group_start > id)
mnt_group_start = id;
mnt->mnt_group_id = 0;
}
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
* vfsmount lock must be held for read
*/
static inline void mnt_add_count(struct vfsmount *mnt, int n)
{
#ifdef CONFIG_SMP
this_cpu_add(mnt->mnt_pcp->mnt_count, n);
#else
preempt_disable();
mnt->mnt_count += n;
preempt_enable();
#endif
}
static inline void mnt_set_count(struct vfsmount *mnt, int n)
{
#ifdef CONFIG_SMP
this_cpu_write(mnt->mnt_pcp->mnt_count, n);
#else
mnt->mnt_count = n;
#endif
}
/*
* vfsmount lock must be held for read
*/
static inline void mnt_inc_count(struct vfsmount *mnt)
{
mnt_add_count(mnt, 1);
}
/*
* vfsmount lock must be held for read
*/
static inline void mnt_dec_count(struct vfsmount *mnt)
{
mnt_add_count(mnt, -1);
}
/*
* vfsmount lock must be held for write
*/
unsigned int mnt_get_count(struct vfsmount *mnt)
{
#ifdef CONFIG_SMP
int cpu;
for_each_possible_cpu(cpu) {
count += per_cpu_ptr(mnt->mnt_pcp, cpu)->mnt_count;
}
return count;
#else
return mnt->mnt_count;
#endif
}
static struct vfsmount *alloc_vfsmnt(const char *name)
Loading
Loading full blame...