Commit 50652963 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs

Pull misc VFS updates from Al Viro:
 "This cycle a lot of stuff sits on topical branches, so I'll be sending
  more or less one pull request per branch.

  This is the first pile; more to follow in a few.  In this one are
  several misc commits from early in the cycle (before I went for
  separate branches), plus the rework of mntput/dput ordering on umount,
  switching to use of fs_pin instead of convoluted games in
  namespace_unlock()"

* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs:
  switch the IO-triggering parts of umount to fs_pin
  new fs_pin killing logics
  allow attaching fs_pin to a group not associated with some superblock
  get rid of the second argument of acct_kill()
  take count and rcu_head out of fs_pin
  dcache: let the dentry count go down to zero without taking d_lock
  pull bumping refcount into ->kill()
  kill pin_put()
  mode_t whack-a-mole: chelsio
  file->f_path.dentry is pinned down for as long as the file is open...
  get rid of lustre_dump_dentry()
  gut proc_register() a bit
  kill d_validate()
  ncpfs: get rid of d_validate() nonsense
  selinuxfs: don't open-code d_genocide()
parents e2b74f23 87b95ce0
...@@ -55,7 +55,7 @@ static const struct file_operations name##_debugfs_fops = { \ ...@@ -55,7 +55,7 @@ static const struct file_operations name##_debugfs_fops = { \
struct t4_debugfs_entry { struct t4_debugfs_entry {
const char *name; const char *name;
const struct file_operations *ops; const struct file_operations *ops;
mode_t mode; umode_t mode;
unsigned char data; unsigned char data;
}; };
......
...@@ -263,14 +263,6 @@ void ll_invalidate_aliases(struct inode *inode) ...@@ -263,14 +263,6 @@ void ll_invalidate_aliases(struct inode *inode)
dentry, dentry, dentry->d_parent, dentry, dentry, dentry->d_parent,
dentry->d_inode, dentry->d_flags); dentry->d_inode, dentry->d_flags);
if (unlikely(dentry == dentry->d_sb->s_root)) {
CERROR("%s: called on root dentry=%p, fid="DFID"\n",
ll_get_fsname(dentry->d_sb, NULL, 0),
dentry, PFID(ll_inode2fid(inode)));
lustre_dump_dentry(dentry, 1);
dump_stack();
}
d_lustre_invalidate(dentry, 0); d_lustre_invalidate(dentry, 0);
} }
ll_unlock_dcache(inode); ll_unlock_dcache(inode);
......
...@@ -816,7 +816,6 @@ int ll_show_options(struct seq_file *seq, struct dentry *dentry); ...@@ -816,7 +816,6 @@ int ll_show_options(struct seq_file *seq, struct dentry *dentry);
void ll_dirty_page_discard_warn(struct page *page, int ioret); void ll_dirty_page_discard_warn(struct page *page, int ioret);
int ll_prep_inode(struct inode **inode, struct ptlrpc_request *req, int ll_prep_inode(struct inode **inode, struct ptlrpc_request *req,
struct super_block *, struct lookup_intent *); struct super_block *, struct lookup_intent *);
void lustre_dump_dentry(struct dentry *, int recur);
int ll_obd_statfs(struct inode *inode, void *arg); int ll_obd_statfs(struct inode *inode, void *arg);
int ll_get_max_mdsize(struct ll_sb_info *sbi, int *max_mdsize); int ll_get_max_mdsize(struct ll_sb_info *sbi, int *max_mdsize);
int ll_get_default_mdsize(struct ll_sb_info *sbi, int *default_mdsize); int ll_get_default_mdsize(struct ll_sb_info *sbi, int *default_mdsize);
......
...@@ -665,48 +665,6 @@ int ll_get_default_cookiesize(struct ll_sb_info *sbi, int *lmmsize) ...@@ -665,48 +665,6 @@ int ll_get_default_cookiesize(struct ll_sb_info *sbi, int *lmmsize)
return rc; return rc;
} }
static void ll_dump_inode(struct inode *inode)
{
struct ll_d_hlist_node *tmp;
int dentry_count = 0;
LASSERT(inode != NULL);
ll_d_hlist_for_each(tmp, &inode->i_dentry)
dentry_count++;
CERROR("inode %p dump: dev=%s ino=%lu mode=%o count=%u, %d dentries\n",
inode, ll_i2mdexp(inode)->exp_obd->obd_name, inode->i_ino,
inode->i_mode, atomic_read(&inode->i_count), dentry_count);
}
void lustre_dump_dentry(struct dentry *dentry, int recur)
{
struct list_head *tmp;
int subdirs = 0;
LASSERT(dentry != NULL);
list_for_each(tmp, &dentry->d_subdirs)
subdirs++;
CERROR("dentry %p dump: name=%pd parent=%pd (%p), inode=%p, count=%u, flags=0x%x, fsdata=%p, %d subdirs\n",
dentry, dentry, dentry->d_parent, dentry->d_parent,
dentry->d_inode, d_count(dentry),
dentry->d_flags, dentry->d_fsdata, subdirs);
if (dentry->d_inode != NULL)
ll_dump_inode(dentry->d_inode);
if (recur == 0)
return;
list_for_each(tmp, &dentry->d_subdirs) {
struct dentry *d = list_entry(tmp, struct dentry, d_child);
lustre_dump_dentry(d, recur - 1);
}
}
static void client_common_put_super(struct super_block *sb) static void client_common_put_super(struct super_block *sb)
{ {
struct ll_sb_info *sbi = ll_s2sbi(sb); struct ll_sb_info *sbi = ll_s2sbi(sb);
......
...@@ -511,7 +511,7 @@ static void __dentry_kill(struct dentry *dentry) ...@@ -511,7 +511,7 @@ static void __dentry_kill(struct dentry *dentry)
* dentry_iput drops the locks, at which point nobody (except * dentry_iput drops the locks, at which point nobody (except
* transient RCU lookups) can reach this dentry. * transient RCU lookups) can reach this dentry.
*/ */
BUG_ON((int)dentry->d_lockref.count > 0); BUG_ON(dentry->d_lockref.count > 0);
this_cpu_dec(nr_dentry); this_cpu_dec(nr_dentry);
if (dentry->d_op && dentry->d_op->d_release) if (dentry->d_op && dentry->d_op->d_release)
dentry->d_op->d_release(dentry); dentry->d_op->d_release(dentry);
...@@ -564,7 +564,7 @@ static inline struct dentry *lock_parent(struct dentry *dentry) ...@@ -564,7 +564,7 @@ static inline struct dentry *lock_parent(struct dentry *dentry)
struct dentry *parent = dentry->d_parent; struct dentry *parent = dentry->d_parent;
if (IS_ROOT(dentry)) if (IS_ROOT(dentry))
return NULL; return NULL;
if (unlikely((int)dentry->d_lockref.count < 0)) if (unlikely(dentry->d_lockref.count < 0))
return NULL; return NULL;
if (likely(spin_trylock(&parent->d_lock))) if (likely(spin_trylock(&parent->d_lock)))
return parent; return parent;
...@@ -593,6 +593,110 @@ static inline struct dentry *lock_parent(struct dentry *dentry) ...@@ -593,6 +593,110 @@ static inline struct dentry *lock_parent(struct dentry *dentry)
return parent; return parent;
} }
/*
* Try to do a lockless dput(), and return whether that was successful.
*
* If unsuccessful, we return false, having already taken the dentry lock.
*
* The caller needs to hold the RCU read lock, so that the dentry is
* guaranteed to stay around even if the refcount goes down to zero!
*/
static inline bool fast_dput(struct dentry *dentry)
{
int ret;
unsigned int d_flags;
/*
* If we have a d_op->d_delete() operation, we sould not
* let the dentry count go to zero, so use "put__or_lock".
*/
if (unlikely(dentry->d_flags & DCACHE_OP_DELETE))
return lockref_put_or_lock(&dentry->d_lockref);
/*
* .. otherwise, we can try to just decrement the
* lockref optimistically.
*/
ret = lockref_put_return(&dentry->d_lockref);
/*
* If the lockref_put_return() failed due to the lock being held
* by somebody else, the fast path has failed. We will need to
* get the lock, and then check the count again.
*/
if (unlikely(ret < 0)) {
spin_lock(&dentry->d_lock);
if (dentry->d_lockref.count > 1) {
dentry->d_lockref.count--;
spin_unlock(&dentry->d_lock);
return 1;
}
return 0;
}
/*
* If we weren't the last ref, we're done.
*/
if (ret)
return 1;
/*
* Careful, careful. The reference count went down
* to zero, but we don't hold the dentry lock, so
* somebody else could get it again, and do another
* dput(), and we need to not race with that.
*
* However, there is a very special and common case
* where we don't care, because there is nothing to
* do: the dentry is still hashed, it does not have
* a 'delete' op, and it's referenced and already on
* the LRU list.
*
* NOTE! Since we aren't locked, these values are
* not "stable". However, it is sufficient that at
* some point after we dropped the reference the
* dentry was hashed and the flags had the proper
* value. Other dentry users may have re-gotten
* a reference to the dentry and change that, but
* our work is done - we can leave the dentry
* around with a zero refcount.
*/
smp_rmb();
d_flags = ACCESS_ONCE(dentry->d_flags);
d_flags &= DCACHE_REFERENCED | DCACHE_LRU_LIST;
/* Nothing to do? Dropping the reference was all we needed? */
if (d_flags == (DCACHE_REFERENCED | DCACHE_LRU_LIST) && !d_unhashed(dentry))
return 1;
/*
* Not the fast normal case? Get the lock. We've already decremented
* the refcount, but we'll need to re-check the situation after
* getting the lock.
*/
spin_lock(&dentry->d_lock);
/*
* Did somebody else grab a reference to it in the meantime, and
* we're no longer the last user after all? Alternatively, somebody
* else could have killed it and marked it dead. Either way, we
* don't need to do anything else.
*/
if (dentry->d_lockref.count) {
spin_unlock(&dentry->d_lock);
return 1;
}
/*
* Re-get the reference we optimistically dropped. We hold the
* lock, and we just tested that it was zero, so we can just
* set it to 1.
*/
dentry->d_lockref.count = 1;
return 0;
}
/* /*
* This is dput * This is dput
* *
...@@ -625,8 +729,14 @@ void dput(struct dentry *dentry) ...@@ -625,8 +729,14 @@ void dput(struct dentry *dentry)
return; return;
repeat: repeat:
if (lockref_put_or_lock(&dentry->d_lockref)) rcu_read_lock();
if (likely(fast_dput(dentry))) {
rcu_read_unlock();
return; return;
}
/* Slow case: now with the dentry lock held */
rcu_read_unlock();
/* Unreachable? Get rid of it */ /* Unreachable? Get rid of it */
if (unlikely(d_unhashed(dentry))) if (unlikely(d_unhashed(dentry)))
...@@ -813,7 +923,7 @@ static void shrink_dentry_list(struct list_head *list) ...@@ -813,7 +923,7 @@ static void shrink_dentry_list(struct list_head *list)
* We found an inuse dentry which was not removed from * We found an inuse dentry which was not removed from
* the LRU because of laziness during lookup. Do not free it. * the LRU because of laziness during lookup. Do not free it.
*/ */
if ((int)dentry->d_lockref.count > 0) { if (dentry->d_lockref.count > 0) {
spin_unlock(&dentry->d_lock); spin_unlock(&dentry->d_lock);
if (parent) if (parent)
spin_unlock(&parent->d_lock); spin_unlock(&parent->d_lock);
...@@ -2191,37 +2301,6 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name) ...@@ -2191,37 +2301,6 @@ struct dentry *d_hash_and_lookup(struct dentry *dir, struct qstr *name)
} }
EXPORT_SYMBOL(d_hash_and_lookup); EXPORT_SYMBOL(d_hash_and_lookup);
/**
* d_validate - verify dentry provided from insecure source (deprecated)
* @dentry: The dentry alleged to be valid child of @dparent
* @dparent: The parent dentry (known to be valid)
*
* An insecure source has sent us a dentry, here we verify it and dget() it.
* This is used by ncpfs in its readdir implementation.
* Zero is returned in the dentry is invalid.
*
* This function is slow for big directories, and deprecated, do not use it.
*/
int d_validate(struct dentry *dentry, struct dentry *dparent)
{
struct dentry *child;
spin_lock(&dparent->d_lock);
list_for_each_entry(child, &dparent->d_subdirs, d_child) {
if (dentry == child) {
spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED);
__dget_dlock(dentry);
spin_unlock(&dentry->d_lock);
spin_unlock(&dparent->d_lock);
return 1;
}
}
spin_unlock(&dparent->d_lock);
return 0;
}
EXPORT_SYMBOL(d_validate);
/* /*
* When a file is deleted, we have two options: * When a file is deleted, we have two options:
* - turn this dentry into a negative dentry * - turn this dentry into a negative dentry
......
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/sched.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/fs_pin.h>
#include "internal.h" #include "internal.h"
#include "mount.h" #include "mount.h"
static void pin_free_rcu(struct rcu_head *head)
{
kfree(container_of(head, struct fs_pin, rcu));
}
static DEFINE_SPINLOCK(pin_lock); static DEFINE_SPINLOCK(pin_lock);
void pin_put(struct fs_pin *p)
{
if (atomic_long_dec_and_test(&p->count))
call_rcu(&p->rcu, pin_free_rcu);
}
void pin_remove(struct fs_pin *pin) void pin_remove(struct fs_pin *pin)
{ {
spin_lock(&pin_lock); spin_lock(&pin_lock);
hlist_del(&pin->m_list); hlist_del(&pin->m_list);
hlist_del(&pin->s_list); hlist_del(&pin->s_list);
spin_unlock(&pin_lock); spin_unlock(&pin_lock);
spin_lock_irq(&pin->wait.lock);
pin->done = 1;
wake_up_locked(&pin->wait);
spin_unlock_irq(&pin->wait.lock);
} }
void pin_insert(struct fs_pin *pin, struct vfsmount *m) void pin_insert_group(struct fs_pin *pin, struct vfsmount *m, struct hlist_head *p)
{ {
spin_lock(&pin_lock); spin_lock(&pin_lock);
hlist_add_head(&pin->s_list, &m->mnt_sb->s_pins); if (p)
hlist_add_head(&pin->s_list, p);
hlist_add_head(&pin->m_list, &real_mount(m)->mnt_pins); hlist_add_head(&pin->m_list, &real_mount(m)->mnt_pins);
spin_unlock(&pin_lock); spin_unlock(&pin_lock);
} }
void pin_insert(struct fs_pin *pin, struct vfsmount *m)
{
pin_insert_group(pin, m, &m->mnt_sb->s_pins);
}
void pin_kill(struct fs_pin *p)
{
wait_queue_t wait;
if (!p) {
rcu_read_unlock();
return;
}
init_wait(&wait);
spin_lock_irq(&p->wait.lock);
if (likely(!p->done)) {
p->done = -1;
spin_unlock_irq(&p->wait.lock);
rcu_read_unlock();
p->kill(p);
return;
}
if (p->done > 0) {
spin_unlock_irq(&p->wait.lock);
rcu_read_unlock();
return;
}
__add_wait_queue(&p->wait, &wait);
while (1) {
set_current_state(TASK_UNINTERRUPTIBLE);
spin_unlock_irq(&p->wait.lock);
rcu_read_unlock();
schedule();
rcu_read_lock();
if (likely(list_empty(&wait.task_list)))
break;
/* OK, we know p couldn't have been freed yet */
spin_lock_irq(&p->wait.lock);
if (p->done > 0) {
spin_unlock_irq(&p->wait.lock);
break;
}
}
rcu_read_unlock();
}
void mnt_pin_kill(struct mount *m) void mnt_pin_kill(struct mount *m)
{ {
while (1) { while (1) {
struct hlist_node *p; struct hlist_node *p;
struct fs_pin *pin;
rcu_read_lock(); rcu_read_lock();
p = ACCESS_ONCE(m->mnt_pins.first); p = ACCESS_ONCE(m->mnt_pins.first);
if (!p) { if (!p) {
rcu_read_unlock(); rcu_read_unlock();
break; break;
} }
pin = hlist_entry(p, struct fs_pin, m_list); pin_kill(hlist_entry(p, struct fs_pin, m_list));
if (!atomic_long_inc_not_zero(&pin->count)) {
rcu_read_unlock();
cpu_relax();
continue;
}
rcu_read_unlock();
pin->kill(pin);
} }
} }
void sb_pin_kill(struct super_block *sb) void group_pin_kill(struct hlist_head *p)
{ {
while (1) { while (1) {
struct hlist_node *p; struct hlist_node *q;
struct fs_pin *pin;
rcu_read_lock(); rcu_read_lock();
p = ACCESS_ONCE(sb->s_pins.first); q = ACCESS_ONCE(p->first);
if (!p) { if (!q) {
rcu_read_unlock(); rcu_read_unlock();
break; break;
} }
pin = hlist_entry(p, struct fs_pin, s_list); pin_kill(hlist_entry(q, struct fs_pin, s_list));
if (!atomic_long_inc_not_zero(&pin->count)) {
rcu_read_unlock();
cpu_relax();
continue;
}
rcu_read_unlock();
pin->kill(pin);
} }
} }
...@@ -144,7 +144,7 @@ extern const struct file_operations pipefifo_fops; ...@@ -144,7 +144,7 @@ extern const struct file_operations pipefifo_fops;
/* /*
* fs_pin.c * fs_pin.c
*/ */
extern void sb_pin_kill(struct super_block *sb); extern void group_pin_kill(struct hlist_head *p);
extern void mnt_pin_kill(struct mount *m); extern void mnt_pin_kill(struct mount *m);
/* /*
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#include <linux/seq_file.h> #include <linux/seq_file.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/ns_common.h> #include <linux/ns_common.h>
#include <linux/fs_pin.h>
struct mnt_namespace { struct mnt_namespace {
atomic_t count; atomic_t count;
...@@ -62,7 +63,8 @@ struct mount { ...@@ -62,7 +63,8 @@ struct mount {
int mnt_group_id; /* peer group identifier */ int mnt_group_id; /* peer group identifier */
int mnt_expiry_mark; /* true if marked for expiry */ int mnt_expiry_mark; /* true if marked for expiry */
struct hlist_head mnt_pins; struct hlist_head mnt_pins;
struct path mnt_ex_mountpoint; struct fs_pin mnt_umount;
struct dentry *mnt_ex_mountpoint;
}; };
#define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */ #define MNT_NS_INTERNAL ERR_PTR(-EINVAL) /* distinct from any mnt_namespace */
......
...@@ -190,6 +190,14 @@ unsigned int mnt_get_count(struct mount *mnt) ...@@ -190,6 +190,14 @@ unsigned int mnt_get_count(struct mount *mnt)
#endif #endif
} }
static void drop_mountpoint(struct fs_pin *p)
{
struct mount *m = container_of(p, struct mount, mnt_umount);
dput(m->mnt_ex_mountpoint);
pin_remove(p);
mntput(&m->mnt);
}
static struct mount *alloc_vfsmnt(const char *name) static struct mount *alloc_vfsmnt(const char *name)
{ {
struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL); struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
...@@ -229,6 +237,7 @@ static struct mount *alloc_vfsmnt(const char *name) ...@@ -229,6 +237,7 @@ static struct mount *alloc_vfsmnt(const char *name)
#ifdef CONFIG_FSNOTIFY #ifdef CONFIG_FSNOTIFY
INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks); INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks);
#endif #endif
init_fs_pin(&mnt->mnt_umount, drop_mountpoint);
} }
return mnt; return mnt;
...@@ -1289,7 +1298,6 @@ static HLIST_HEAD(unmounted); /* protected by namespace_sem */ ...@@ -1289,7 +1298,6 @@ static HLIST_HEAD(unmounted); /* protected by namespace_sem */
static void namespace_unlock(void) static void namespace_unlock(void)
{ {
struct mount *mnt;
struct hlist_head head = unmounted; struct hlist_head head = unmounted;
if (likely(hlist_empty(&head))) { if (likely(hlist_empty(&head))) {
...@@ -1299,23 +1307,11 @@ static void namespace_unlock(void) ...@@ -1299,23 +1307,11 @@ static void namespace_unlock(void)
head.first->pprev = &head.first; head.first->pprev = &head.first;
INIT_HLIST_HEAD(&unmounted); INIT_HLIST_HEAD(&unmounted);
/* undo decrements we'd done in umount_tree() */
hlist_for_each_entry(mnt, &head, mnt_hash)
if (mnt->mnt_ex_mountpoint.mnt)
mntget(mnt->mnt_ex_mountpoint.mnt);
up_write(&namespace_sem); up_write(&namespace_sem);
synchronize_rcu(); synchronize_rcu();
while (!hlist_empty(&head)) { group_pin_kill(&head);
mnt = hlist_entry(head.first, struct mount, mnt_hash);
hlist_del_init(&mnt->mnt_hash);
if (mnt->mnt_ex_mountpoint.mnt)
path_put(&mnt->mnt_ex_mountpoint);
mntput(&mnt->mnt);
}
} }
static inline void namespace_lock(void) static inline void namespace_lock(void)
...@@ -1334,7 +1330,6 @@ void umount_tree(struct mount *mnt, int how) ...@@ -1334,7 +1330,6 @@ void umount_tree(struct mount *mnt, int how)
{ {
HLIST_HEAD(tmp_list); HLIST_HEAD(tmp_list);
struct mount *p; struct mount *p;
struct mount *last = NULL;
for (p = mnt; p; p = next_mnt(p, mnt)) { for (p = mnt; p; p = next_mnt(p, mnt)) {
hlist_del_init_rcu(&p->mnt_hash); hlist_del_init_rcu(&p->mnt_hash);
...@@ -1347,33 +1342,28 @@ void umount_tree(struct mount *mnt, int how) ...@@ -1347,33 +1342,28 @@ void umount_tree(struct mount *mnt, int how)
if (how) if (how)
propagate_umount(&tmp_list); propagate_umount(&tmp_list);
hlist_for_each_entry(p, &tmp_list, mnt_hash) { while (!hlist_empty(&tmp_list)) {
p = hlist_entry(tmp_list.first, struct mount, mnt_hash);
hlist_del_init_rcu(&p->mnt_hash);
list_del_init(&p->mnt_expire); list_del_init(&p->mnt_expire);
list_del_init(&p->mnt_list); list_del_init(&p->mnt_list);
__touch_mnt_namespace(p->mnt_ns); __touch_mnt_namespace(p->mnt_ns);
p->mnt_ns = NULL; p->mnt_ns = NULL;
if (how < 2) if (how < 2)
p->mnt.mnt_flags |= MNT_SYNC_UMOUNT; p->mnt.mnt_flags |= MNT_SYNC_UMOUNT;
pin_insert_group(&p->mnt_umount, &p->mnt_parent->mnt, &unmounted);
if (mnt_has_parent(p)) { if (mnt_has_parent(p)) {
hlist_del_init(&p->mnt_mp_list); hlist_del_init(&p->mnt_mp_list);
put_mountpoint(p->mnt_mp); put_mountpoint(p->mnt_mp);
mnt_add_count(p->mnt_parent, -1); mnt_add_count(p->mnt_parent, -1);
/* move the reference to mountpoint into ->mnt_ex_mountpoint */ /* old mountpoint will be dropped when we can do that */
p->mnt_ex_mountpoint.dentry = p->mnt_mountpoint; p->mnt_ex_mountpoint = p->mnt_mountpoint;
p->mnt_ex_mountpoint.mnt = &p->mnt_parent->mnt;
p->mnt_mountpoint = p->mnt.mnt_root; p->mnt_mountpoint = p->mnt.mnt_root;
p->mnt_parent = p; p->mnt_parent = p;
p->mnt_mp = NULL; p->mnt_mp = NULL;
} }
change_mnt_propagation(p, MS_PRIVATE); change_mnt_propagation(p, MS_PRIVATE);
last = p;
}
if (last) {
last->mnt_hash.next = unmounted.first;
if (unmounted.first)
unmounted.first->pprev = &last->mnt_hash.next;
unmounted.first = tmp_list.first;
unmounted.first->pprev = &unmounted.first;
} }
} }
......
...@@ -77,6 +77,7 @@ static int ncp_hash_dentry(const struct dentry *, struct qstr *); ...@@ -77,6 +77,7 @@ static int ncp_hash_dentry(const struct dentry *, struct qstr *);
static int ncp_compare_dentry(const struct dentry *, const struct dentry *, static int ncp_compare_dentry(const struct dentry *, const struct dentry *,
unsigned int, const char *, const struct qstr *); unsigned int, const char *, const struct qstr *);
static int ncp_delete_dentry(const struct dentry *); static int ncp_delete_dentry(const struct dentry *);
static void ncp_d_prune(struct dentry *dentry);
const struct dentry_operations ncp_dentry_operations = const struct dentry_operations ncp_dentry_operations =
{ {
...@@ -84,6 +85,7 @@ const struct dentry_operations ncp_dentry_operations = ...@@ -84,6 +85,7 @@ const struct dentry_operations ncp_dentry_operations =
.d_hash = ncp_hash_dentry, .d_hash = ncp_hash_dentry,
.d_compare = ncp_compare_dentry, .d_compare = ncp_compare_dentry,
.d_delete = ncp_delete_dentry, .d_delete = ncp_delete_dentry,
.d_prune = ncp_d_prune,
}; };
#define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber]) #define ncp_namespace(i) (NCP_SERVER(i)->name_space[NCP_FINFO(i)->volNumber])
...@@ -384,42 +386,6 @@ ncp_lookup_validate(struct dentry *dentry, unsigned int flags) ...@@ -384,42 +386,6 @@ ncp_lookup_validate(struct dentry *dentry, unsigned int flags)
return val; return val;
} }
static struct dentry *
ncp_dget_fpos(struct dentry *dentry, struct dentry *parent, unsigned long fpos)
{
struct dentry *dent = dentry;
if (d_validate(dent, parent)) {
if (dent->d_name.len <= NCP_MAXPATHLEN &&
(unsigned long)dent->d_fsdata == fpos) {
if (!dent->d_inode) {
dput(dent);
dent = NULL;
}
return dent;
}
dput(dent);
}
/* If a pointer is invalid, we search the dentry. */
spin_lock(&parent->d_lock);
list_for_each_entry(dent, &parent->d_subdirs, d_child) {
if ((unsigned long)dent->d_fsdata == fpos) {
if (dent->d_inode)
dget(dent);
else
dent = NULL;
spin_unlock(&parent->d_lock);
goto out;
}
}
spin_unlock(&parent->d_lock);
return NULL;
out:
return dent;
}
static time_t ncp_obtain_mtime(struct dentry *dentry) static time_t ncp_obtain_mtime(struct dentry *dentry)
{ {
struct inode *inode = dentry->d_inode; struct inode *inode = dentry->d_inode;
...@@ -435,6 +401,20 @@ static time_t ncp_obtain_mtime(struct dentry *dentry) ...@@ -435,6 +401,20 @@ static time_t ncp_obtain_mtime(struct dentry *dentry)
return ncp_date_dos2unix(i.modifyTime, i.modifyDate); return ncp_date_dos2unix(i.modifyTime, i.modifyDate);
} }
static inline void
ncp_invalidate_dircache_entries(struct dentry *parent)
{
struct ncp_server *server = NCP_SERVER(parent->d_inode);
struct dentry *dentry;
spin_lock(&parent->d_lock);
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
dentry->d_fsdata = NULL;
ncp_age_dentry(server, dentry);
}
spin_unlock(&parent->d_lock);
}
static int ncp_readdir(struct file *file, struct dir_context *ctx) static int ncp_readdir(struct file *file, struct dir_context *ctx)
{ {
struct dentry *dentry = file->f_path.dentry; struct dentry *dentry = file->f_path.dentry;
...@@ -500,10 +480,21 @@ static int ncp_readdir(struct file *file, struct dir_context *ctx) ...@@ -500,10 +480,21 @@ static int ncp_readdir(struct file *file, struct dir_context *ctx)
struct dentry *dent; struct dentry *dent;
bool over; bool over;
dent = ncp_dget_fpos(ctl.cache->dentry[ctl.idx], spin_lock(&dentry->d_lock);
dentry, ctx->pos); if (!(NCP_FINFO(inode)->flags & NCPI_DIR_CACHE)) {
if (!dent) spin_unlock(&dentry->d_lock);
goto invalid_cache;
}
dent = ctl.cache->dentry[ctl.idx];
if (unlikely(!lockref_get_not_dead(&dent->d_lockref))) {
spin_unlock(&dentry->d_lock);
goto invalid_cache;
}
spin_unlock(&dentry->d_lock);
if (!dent->d_inode) {
dput(dent);
goto invalid_cache; goto invalid_cache;
}
over = !dir_emit(ctx, dent->d_name.name, over = !dir_emit(ctx, dent->d_name.name,
dent->d_name.len, dent->d_name.len,
dent->d_inode->i_ino, DT_UNKNOWN); dent->d_inode->i_ino, DT_UNKNOWN);
...@@ -548,6 +539,9 @@ static int ncp_readdir(struct file *file, struct dir_context *ctx) ...@@ -548,6 +539,9 @@ static int ncp_readdir(struct file *file, struct dir_context *ctx)
ctl.filled = 0; ctl.filled = 0;
ctl.valid = 1; ctl.valid = 1;
read_really: read_really:
spin_lock(&dentry->d_lock);
NCP_FINFO(inode)->flags |= NCPI_DIR_CACHE;
spin_unlock(&dentry->d_lock);
if (ncp_is_server_root(inode)) { if (ncp_is_server_root(inode)) {
ncp_read_volume_list(file, ctx, &ctl); ncp_read_volume_list(file, ctx, &ctl);
} else { } else {
...@@ -573,6 +567,13 @@ static int ncp_readdir(struct file *file, struct dir_context *ctx) ...@@ -573,6 +567,13 @@ static int ncp_readdir(struct file *file, struct dir_context *ctx)
return result; return result;
} }
static void ncp_d_prune(struct dentry *dentry)
{
if (!dentry->d_fsdata) /* not referenced from page cache */
return;
NCP_FINFO(dentry->d_parent->d_inode)->flags &= ~NCPI_DIR_CACHE;
}
static int static int
ncp_fill_cache(struct file *file, struct dir_context *ctx, ncp_fill_cache(struct file *file, struct dir_context *ctx,
struct ncp_cache_control *ctrl, struct ncp_entry_info *entry, struct ncp_cache_control *ctrl, struct ncp_entry_info *entry,
...@@ -630,6 +631,10 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx, ...@@ -630,6 +631,10 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx,
d_instantiate(newdent, inode); d_instantiate(newdent, inode);
if (!hashed) if (!hashed)
d_rehash(newdent); d_rehash(newdent);
} else {
spin_lock(&dentry->d_lock);
NCP_FINFO(inode)->flags &= ~NCPI_DIR_CACHE;
spin_unlock(&dentry->d_lock);
} }
} else { } else {
struct inode *inode = newdent->d_inode; struct inode *inode = newdent->d_inode;
...@@ -639,12 +644,6 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx, ...@@ -639,12 +644,6 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx,
mutex_unlock(&inode->i_mutex); mutex_unlock(&inode->i_mutex);
} }
if (newdent->d_inode) {
ino = newdent->d_inode->i_ino;
newdent->d_fsdata = (void *) ctl.fpos;
ncp_new_dentry(newdent);
}
if (ctl.idx >= NCP_DIRCACHE_SIZE) { if (ctl.idx >= NCP_DIRCACHE_SIZE) {
if (ctl.page) { if (ctl.page) {
kunmap(ctl.page); kunmap(ctl.page);
...@@ -660,8 +659,13 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx, ...@@ -660,8 +659,13 @@ ncp_fill_cache(struct file *file, struct dir_context *ctx,
ctl.cache = kmap(ctl.page); ctl.cache = kmap(ctl.page);
} }
if (ctl.cache) { if (ctl.cache) {
ctl.cache->dentry[ctl.idx] = newdent; if (newdent->d_inode) {
valid = 1; newdent->d_fsdata = newdent;
ctl.cache->dentry[ctl.idx] = newdent;
ino = newdent->d_inode->i_ino;
ncp_new_dentry(newdent);
}
valid = 1;
} }
dput(newdent); dput(newdent);
end_advance: end_advance:
......
...@@ -22,6 +22,7 @@ struct ncp_inode_info { ...@@ -22,6 +22,7 @@ struct ncp_inode_info {
int access; int access;
int flags; int flags;
#define NCPI_KLUDGE_SYMLINK 0x0001 #define NCPI_KLUDGE_SYMLINK 0x0001
#define NCPI_DIR_CACHE 0x0002
__u8 file_handle[6]; __u8 file_handle[6];
struct inode vfs_inode; struct inode vfs_inode;
}; };
......
...@@ -184,36 +184,6 @@ ncp_new_dentry(struct dentry* dentry) ...@@ -184,36 +184,6 @@ ncp_new_dentry(struct dentry* dentry)
dentry->d_time = jiffies; dentry->d_time = jiffies;
} }
static inline void
ncp_renew_dentries(struct dentry *parent)
{
struct ncp_server *server = NCP_SERVER(parent->d_inode);
struct dentry *dentry;
spin_lock(&parent->d_lock);
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
if (dentry->d_fsdata == NULL)
ncp_age_dentry(server, dentry);
else
ncp_new_dentry(dentry);
}
spin_unlock(&parent->d_lock);
}
static inline void
ncp_invalidate_dircache_entries(struct dentry *parent)
{
struct ncp_server *server = NCP_SERVER(parent->d_inode);
struct dentry *dentry;
spin_lock(&parent->d_lock);
list_for_each_entry(dentry, &parent->d_subdirs, d_child) {
dentry->d_fsdata = NULL;
ncp_age_dentry(server, dentry);
}
spin_unlock(&parent->d_lock);
}
struct ncp_cache_head { struct ncp_cache_head {
time_t mtime; time_t mtime;
unsigned long time; /* cache age */ unsigned long time; /* cache age */
......
...@@ -350,29 +350,12 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp ...@@ -350,29 +350,12 @@ static int proc_register(struct proc_dir_entry * dir, struct proc_dir_entry * dp
if (ret) if (ret)
return ret; return ret;
if (S_ISDIR(dp->mode)) {
dp->proc_fops = &proc_dir_operations;
dp->proc_iops = &proc_dir_inode_operations;
dir->nlink++;
} else if (S_ISLNK(dp->mode)) {
dp->proc_iops = &proc_link_inode_operations;
} else if (S_ISREG(dp->mode)) {
BUG_ON(dp->proc_fops == NULL);
dp->proc_iops = &proc_file_inode_operations;
} else {
WARN_ON(1);
proc_free_inum(dp->low_ino);
return -EINVAL;
}
spin_lock(&proc_subdir_lock); spin_lock(&proc_subdir_lock);
dp->parent = dir; dp->parent = dir;
if (pde_subdir_insert(dir, dp) == false) { if (pde_subdir_insert(dir, dp) == false) {
WARN(1, "proc_dir_entry '%s/%s' already registered\n", WARN(1, "proc_dir_entry '%s/%s' already registered\n",
dir->name, dp->name); dir->name, dp->name);
spin_unlock(&proc_subdir_lock); spin_unlock(&proc_subdir_lock);
if (S_ISDIR(dp->mode))
dir->nlink--;
proc_free_inum(dp->low_ino); proc_free_inum(dp->low_ino);
return -EEXIST; return -EEXIST;
} }
...@@ -431,6 +414,7 @@ struct proc_dir_entry *proc_symlink(const char *name, ...@@ -431,6 +414,7 @@ struct proc_dir_entry *proc_symlink(const char *name,
ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL); ent->data = kmalloc((ent->size=strlen(dest))+1, GFP_KERNEL);
if (ent->data) { if (ent->data) {
strcpy((char*)ent->data,dest); strcpy((char*)ent->data,dest);
ent->proc_iops = &proc_link_inode_operations;
if (proc_register(parent, ent) < 0) { if (proc_register(parent, ent) < 0) {
kfree(ent->data); kfree(ent->data);
kfree(ent); kfree(ent);
...@@ -456,8 +440,12 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode, ...@@ -456,8 +440,12 @@ struct proc_dir_entry *proc_mkdir_data(const char *name, umode_t mode,
ent = __proc_create(&parent, name, S_IFDIR | mode, 2); ent = __proc_create(&parent, name, S_IFDIR | mode, 2);
if (ent) { if (ent) {
ent->data = data; ent->data = data;
ent->proc_fops = &proc_dir_operations;
ent->proc_iops = &proc_dir_inode_operations;
parent->nlink++;
if (proc_register(parent, ent) < 0) { if (proc_register(parent, ent) < 0) {
kfree(ent); kfree(ent);
parent->nlink--;
ent = NULL; ent = NULL;
} }
} }
...@@ -493,6 +481,8 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, ...@@ -493,6 +481,8 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
return NULL; return NULL;
} }
BUG_ON(proc_fops == NULL);
if ((mode & S_IALLUGO) == 0) if ((mode & S_IALLUGO) == 0)
mode |= S_IRUGO; mode |= S_IRUGO;
pde = __proc_create(&parent, name, mode, 1); pde = __proc_create(&parent, name, mode, 1);
...@@ -500,6 +490,7 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode, ...@@ -500,6 +490,7 @@ struct proc_dir_entry *proc_create_data(const char *name, umode_t mode,
goto out; goto out;
pde->proc_fops = proc_fops; pde->proc_fops = proc_fops;
pde->data = data; pde->data = data;
pde->proc_iops = &proc_file_inode_operations;
if (proc_register(parent, pde) < 0) if (proc_register(parent, pde) < 0)
goto out_free; goto out_free;
return pde; return pde;
......
...@@ -715,9 +715,9 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force) ...@@ -715,9 +715,9 @@ int do_remount_sb(struct super_block *sb, int flags, void *data, int force)
remount_ro = (flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY); remount_ro = (flags & MS_RDONLY) && !(sb->s_flags & MS_RDONLY);
if (remount_ro) { if (remount_ro) {
if (sb->s_pins.first) { if (!hlist_empty(&sb->s_pins)) {
up_write(&sb->s_umount); up_write(&sb->s_umount);
sb_pin_kill(sb); group_pin_kill(&sb->s_pins);
down_write(&sb->s_umount); down_write(&sb->s_umount);
if (!sb->s_root) if (!sb->s_root)
return 0; return 0;
......
...@@ -319,9 +319,6 @@ static inline unsigned d_count(const struct dentry *dentry) ...@@ -319,9 +319,6 @@ static inline unsigned d_count(const struct dentry *dentry)
return dentry->d_lockref.count; return dentry->d_lockref.count;
} }
/* validate "insecure" dentry pointer */
extern int d_validate(struct dentry *, struct dentry *);
/* /*
* helper function for dentry_operations.d_dname() members * helper function for dentry_operations.d_dname() members
*/ */
......
#include <linux/fs.h> #include <linux/wait.h>
struct fs_pin { struct fs_pin {
atomic_long_t count; wait_queue_head_t wait;
union { int done;
struct { struct hlist_node s_list;
struct hlist_node s_list; struct hlist_node m_list;
struct hlist_node m_list;
};
struct rcu_head rcu;
};
void (*kill)(struct fs_pin *); void (*kill)(struct fs_pin *);
}; };
void pin_put(struct fs_pin *); struct vfsmount;
static inline void init_fs_pin(struct fs_pin *p, void (*kill)(struct fs_pin *))
{
init_waitqueue_head(&p->wait);
p->kill = kill;
}
void pin_remove(struct fs_pin *); void pin_remove(struct fs_pin *);
void pin_insert_group(struct fs_pin *, struct vfsmount *, struct hlist_head *);
void pin_insert(struct fs_pin *, struct vfsmount *); void pin_insert(struct fs_pin *, struct vfsmount *);
void pin_kill(struct fs_pin *);
...@@ -28,12 +28,13 @@ struct lockref { ...@@ -28,12 +28,13 @@ struct lockref {
#endif #endif
struct { struct {
spinlock_t lock; spinlock_t lock;
unsigned int count; int count;
}; };
}; };
}; };
extern void lockref_get(struct lockref *); extern void lockref_get(struct lockref *);
extern int lockref_put_return(struct lockref *);
extern int lockref_get_not_zero(struct lockref *); extern int lockref_get_not_zero(struct lockref *);
extern int lockref_get_or_lock(struct lockref *); extern int lockref_get_or_lock(struct lockref *);
extern int lockref_put_or_lock(struct lockref *); extern int lockref_put_or_lock(struct lockref *);
......
...@@ -19,7 +19,7 @@ struct pidmap { ...@@ -19,7 +19,7 @@ struct pidmap {
#define BITS_PER_PAGE_MASK (BITS_PER_PAGE-1) #define BITS_PER_PAGE_MASK (BITS_PER_PAGE-1)
#define PIDMAP_ENTRIES ((PID_MAX_LIMIT+BITS_PER_PAGE-1)/BITS_PER_PAGE) #define PIDMAP_ENTRIES ((PID_MAX_LIMIT+BITS_PER_PAGE-1)/BITS_PER_PAGE)
struct bsd_acct_struct; struct fs_pin;
struct pid_namespace { struct pid_namespace {
struct kref kref; struct kref kref;
...@@ -37,7 +37,7 @@ struct pid_namespace { ...@@ -37,7 +37,7 @@ struct pid_namespace {
struct dentry *proc_thread_self; struct dentry *proc_thread_self;
#endif #endif
#ifdef CONFIG_BSD_PROCESS_ACCT #ifdef CONFIG_BSD_PROCESS_ACCT
struct bsd_acct_struct *bacct; struct fs_pin *bacct;
#endif #endif
struct user_namespace *user_ns; struct user_namespace *user_ns;
struct work_struct proc_work; struct work_struct proc_work;
......
...@@ -76,10 +76,11 @@ int acct_parm[3] = {4, 2, 30}; ...@@ -76,10 +76,11 @@ int acct_parm[3] = {4, 2, 30};
/* /*
* External references and all of the globals. * External references and all of the globals.
*/ */
static void do_acct_process(struct bsd_acct_struct *acct);
struct bsd_acct_struct { struct bsd_acct_struct {
struct fs_pin pin; struct fs_pin pin;
atomic_long_t count;
struct rcu_head rcu;
struct mutex lock; struct mutex lock;
int active; int active;
unsigned long needcheck; unsigned long needcheck;
...@@ -89,6 +90,8 @@ struct bsd_acct_struct { ...@@ -89,6 +90,8 @@ struct bsd_acct_struct {
struct completion done; struct completion done;
}; };
static void do_acct_process(struct bsd_acct_struct *acct);
/* /*
* Check the amount of free space and suspend/resume accordingly. * Check the amount of free space and suspend/resume accordingly.
*/ */
...@@ -124,32 +127,56 @@ static int check_free_space(struct bsd_acct_struct *acct) ...@@ -124,32 +127,56 @@ static int check_free_space(struct bsd_acct_struct *acct)
return acct->active; return acct->active;
} }
static void acct_put(struct bsd_acct_struct *p)
{
if (atomic_long_dec_and_test(&p->count))
kfree_rcu(p, rcu);
}
static inline struct bsd_acct_struct *to_acct(struct fs_pin *p)
{
return p ? container_of(p, struct bsd_acct_struct, pin) : NULL;
}
static struct bsd_acct_struct *acct_get(struct pid_namespace *ns) static struct bsd_acct_struct *acct_get(struct pid_namespace *ns)
{ {
struct bsd_acct_struct *res; struct bsd_acct_struct *res;
again: again:
smp_rmb(); smp_rmb();
rcu_read_lock(); rcu_read_lock();
res = ACCESS_ONCE(ns->bacct); res = to_acct(ACCESS_ONCE(ns->bacct));
if (!res) { if (!res) {
rcu_read_unlock(); rcu_read_unlock();
return NULL; return NULL;
} }
if (!atomic_long_inc_not_zero(&res->pin.count)) { if (!atomic_long_inc_not_zero(&res->count)) {
rcu_read_unlock(); rcu_read_unlock();
cpu_relax(); cpu_relax();
goto again; goto again;
} }
rcu_read_unlock(); rcu_read_unlock();
mutex_lock(&res->lock); mutex_lock(&res->lock);
if (!res->ns) { if (res != to_acct(ACCESS_ONCE(ns->bacct))) {
mutex_unlock(&res->lock); mutex_unlock(&res->lock);
pin_put(&res->pin); acct_put(res);
goto again; goto again;
} }
return res; return res;
} }
static void acct_pin_kill(struct fs_pin *pin)
{
struct bsd_acct_struct *acct = to_acct(pin);
mutex_lock(&acct->lock);
do_acct_process(acct);
schedule_work(&acct->work);
wait_for_completion(&acct->done);
cmpxchg(&acct->ns->bacct, pin, NULL);
mutex_unlock(&acct->lock);
pin_remove(pin);
acct_put(acct);
}
static void close_work(struct work_struct *work) static void close_work(struct work_struct *work)
{ {
struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work); struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work);
...@@ -160,44 +187,13 @@ static void close_work(struct work_struct *work) ...@@ -160,44 +187,13 @@ static void close_work(struct work_struct *work)
complete(&acct->done); complete(&acct->done);
} }
static void acct_kill(struct bsd_acct_struct *acct,
struct bsd_acct_struct *new)
{
if (acct) {
struct pid_namespace *ns = acct->ns;
do_acct_process(acct);
INIT_WORK(&acct->work, close_work);
init_completion(&acct->done);
schedule_work(&acct->work);
wait_for_completion(&acct->done);
pin_remove(&acct->pin);
ns->bacct = new;
acct->ns = NULL;
atomic_long_dec(&acct->pin.count);
mutex_unlock(&acct->lock);
pin_put(&acct->pin);
}
}
static void acct_pin_kill(struct fs_pin *pin)
{
struct bsd_acct_struct *acct;
acct = container_of(pin, struct bsd_acct_struct, pin);
mutex_lock(&acct->lock);
if (!acct->ns) {
mutex_unlock(&acct->lock);
pin_put(pin);
acct = NULL;
}
acct_kill(acct, NULL);
}
static int acct_on(struct filename *pathname) static int acct_on(struct filename *pathname)
{ {
struct file *file; struct file *file;
struct vfsmount *mnt, *internal; struct vfsmount *mnt, *internal;
struct pid_namespace *ns = task_active_pid_ns(current); struct pid_namespace *ns = task_active_pid_ns(current);
struct bsd_acct_struct *acct, *old; struct bsd_acct_struct *acct;
struct fs_pin *old;
int err; int err;
acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL); acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL);
...@@ -238,21 +234,21 @@ static int acct_on(struct filename *pathname) ...@@ -238,21 +234,21 @@ static int acct_on(struct filename *pathname)
mnt = file->f_path.mnt; mnt = file->f_path.mnt;
file->f_path.mnt = internal; file->f_path.mnt = internal;
atomic_long_set(&acct->pin.count, 1); atomic_long_set(&acct->count, 1);
acct->pin.kill = acct_pin_kill; init_fs_pin(&acct->pin, acct_pin_kill);
acct->file = file; acct->file = file;
acct->needcheck = jiffies; acct->needcheck = jiffies;
acct->ns = ns; acct->ns = ns;
mutex_init(&acct->lock); mutex_init(&acct->lock);
INIT_WORK(&acct->work, close_work);
init_completion(&acct->done);
mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */ mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */
pin_insert(&acct->pin, mnt); pin_insert(&acct->pin, mnt);
old = acct_get(ns); rcu_read_lock();
if (old) old = xchg(&ns->bacct, &acct->pin);
acct_kill(old, acct);
else
ns->bacct = acct;
mutex_unlock(&acct->lock); mutex_unlock(&acct->lock);
pin_kill(old);
mnt_drop_write(mnt); mnt_drop_write(mnt);
mntput(mnt); mntput(mnt);
return 0; return 0;
...@@ -288,7 +284,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name) ...@@ -288,7 +284,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name)
mutex_unlock(&acct_on_mutex); mutex_unlock(&acct_on_mutex);
putname(tmp); putname(tmp);
} else { } else {
acct_kill(acct_get(task_active_pid_ns(current)), NULL); rcu_read_lock();
pin_kill(task_active_pid_ns(current)->bacct);
} }
return error; return error;
...@@ -296,7 +293,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name) ...@@ -296,7 +293,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name)
void acct_exit_ns(struct pid_namespace *ns) void acct_exit_ns(struct pid_namespace *ns)
{ {
acct_kill(acct_get(ns), NULL); rcu_read_lock();
pin_kill(ns->bacct);
} }
/* /*
...@@ -576,7 +574,7 @@ static void slow_acct_process(struct pid_namespace *ns) ...@@ -576,7 +574,7 @@ static void slow_acct_process(struct pid_namespace *ns)
if (acct) { if (acct) {
do_acct_process(acct); do_acct_process(acct);
mutex_unlock(&acct->lock); mutex_unlock(&acct->lock);
pin_put(&acct->pin); acct_put(acct);
} }
} }
} }
......
...@@ -2405,7 +2405,6 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm, ...@@ -2405,7 +2405,6 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
struct audit_aux_data_bprm_fcaps *ax; struct audit_aux_data_bprm_fcaps *ax;
struct audit_context *context = current->audit_context; struct audit_context *context = current->audit_context;
struct cpu_vfs_cap_data vcaps; struct cpu_vfs_cap_data vcaps;
struct dentry *dentry;
ax = kmalloc(sizeof(*ax), GFP_KERNEL); ax = kmalloc(sizeof(*ax), GFP_KERNEL);
if (!ax) if (!ax)
...@@ -2415,9 +2414,7 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm, ...@@ -2415,9 +2414,7 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm,
ax->d.next = context->aux; ax->d.next = context->aux;
context->aux = (void *)ax; context->aux = (void *)ax;
dentry = dget(bprm->file->f_path.dentry); get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps);
get_vfs_caps_from_disk(dentry, &vcaps);
dput(dentry);
ax->fcap.permitted = vcaps.permitted; ax->fcap.permitted = vcaps.permitted;
ax->fcap.inheritable = vcaps.inheritable; ax->fcap.inheritable = vcaps.inheritable;
......
...@@ -60,7 +60,7 @@ void lockref_get(struct lockref *lockref) ...@@ -60,7 +60,7 @@ void lockref_get(struct lockref *lockref)
EXPORT_SYMBOL(lockref_get); EXPORT_SYMBOL(lockref_get);
/** /**
* lockref_get_not_zero - Increments count unless the count is 0 * lockref_get_not_zero - Increments count unless the count is 0 or dead
* @lockref: pointer to lockref structure * @lockref: pointer to lockref structure
* Return: 1 if count updated successfully or 0 if count was zero * Return: 1 if count updated successfully or 0 if count was zero
*/ */
...@@ -70,7 +70,7 @@ int lockref_get_not_zero(struct lockref *lockref) ...@@ -70,7 +70,7 @@ int lockref_get_not_zero(struct lockref *lockref)
CMPXCHG_LOOP( CMPXCHG_LOOP(
new.count++; new.count++;
if (!old.count) if (old.count <= 0)
return 0; return 0;
, ,
return 1; return 1;
...@@ -78,7 +78,7 @@ int lockref_get_not_zero(struct lockref *lockref) ...@@ -78,7 +78,7 @@ int lockref_get_not_zero(struct lockref *lockref)
spin_lock(&lockref->lock); spin_lock(&lockref->lock);
retval = 0; retval = 0;
if (lockref->count) { if (lockref->count > 0) {
lockref->count++; lockref->count++;
retval = 1; retval = 1;
} }
...@@ -88,7 +88,7 @@ int lockref_get_not_zero(struct lockref *lockref) ...@@ -88,7 +88,7 @@ int lockref_get_not_zero(struct lockref *lockref)
EXPORT_SYMBOL(lockref_get_not_zero); EXPORT_SYMBOL(lockref_get_not_zero);
/** /**
* lockref_get_or_lock - Increments count unless the count is 0 * lockref_get_or_lock - Increments count unless the count is 0 or dead
* @lockref: pointer to lockref structure * @lockref: pointer to lockref structure
* Return: 1 if count updated successfully or 0 if count was zero * Return: 1 if count updated successfully or 0 if count was zero
* and we got the lock instead. * and we got the lock instead.
...@@ -97,14 +97,14 @@ int lockref_get_or_lock(struct lockref *lockref) ...@@ -97,14 +97,14 @@ int lockref_get_or_lock(struct lockref *lockref)
{ {
CMPXCHG_LOOP( CMPXCHG_LOOP(
new.count++; new.count++;
if (!old.count) if (old.count <= 0)
break; break;
, ,
return 1; return 1;
); );
spin_lock(&lockref->lock); spin_lock(&lockref->lock);
if (!lockref->count) if (lockref->count <= 0)
return 0; return 0;
lockref->count++; lockref->count++;
spin_unlock(&lockref->lock); spin_unlock(&lockref->lock);
...@@ -112,6 +112,26 @@ int lockref_get_or_lock(struct lockref *lockref) ...@@ -112,6 +112,26 @@ int lockref_get_or_lock(struct lockref *lockref)
} }
EXPORT_SYMBOL(lockref_get_or_lock); EXPORT_SYMBOL(lockref_get_or_lock);
/**
* lockref_put_return - Decrement reference count if possible
* @lockref: pointer to lockref structure
*
* Decrement the reference count and return the new value.
* If the lockref was dead or locked, return an error.
*/
int lockref_put_return(struct lockref *lockref)
{
CMPXCHG_LOOP(
new.count--;
if (old.count <= 0)
return -1;
,
return new.count;
);
return -1;
}
EXPORT_SYMBOL(lockref_put_return);
/** /**
* lockref_put_or_lock - decrements count unless count <= 1 before decrement * lockref_put_or_lock - decrements count unless count <= 1 before decrement
* @lockref: pointer to lockref structure * @lockref: pointer to lockref structure
...@@ -158,7 +178,7 @@ int lockref_get_not_dead(struct lockref *lockref) ...@@ -158,7 +178,7 @@ int lockref_get_not_dead(struct lockref *lockref)
CMPXCHG_LOOP( CMPXCHG_LOOP(
new.count++; new.count++;
if ((int)old.count < 0) if (old.count < 0)
return 0; return 0;
, ,
return 1; return 1;
...@@ -166,7 +186,7 @@ int lockref_get_not_dead(struct lockref *lockref) ...@@ -166,7 +186,7 @@ int lockref_get_not_dead(struct lockref *lockref)
spin_lock(&lockref->lock); spin_lock(&lockref->lock);
retval = 0; retval = 0;
if ((int) lockref->count >= 0) { if (lockref->count >= 0) {
lockref->count++; lockref->count++;
retval = 1; retval = 1;
} }
......
...@@ -434,7 +434,6 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data ...@@ -434,7 +434,6 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
*/ */
static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap) static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_cap)
{ {
struct dentry *dentry;
int rc = 0; int rc = 0;
struct cpu_vfs_cap_data vcaps; struct cpu_vfs_cap_data vcaps;
...@@ -446,9 +445,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c ...@@ -446,9 +445,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c
if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) if (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID)
return 0; return 0;
dentry = dget(bprm->file->f_path.dentry); rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps);
rc = get_vfs_caps_from_disk(dentry, &vcaps);
if (rc < 0) { if (rc < 0) {
if (rc == -EINVAL) if (rc == -EINVAL)
printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n", printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n",
...@@ -464,7 +461,6 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c ...@@ -464,7 +461,6 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_c
__func__, rc, bprm->filename); __func__, rc, bprm->filename);
out: out:
dput(dentry);
if (rc) if (rc)
bprm_clear_caps(bprm); bprm_clear_caps(bprm);
......
...@@ -1195,30 +1195,8 @@ static const struct file_operations sel_commit_bools_ops = { ...@@ -1195,30 +1195,8 @@ static const struct file_operations sel_commit_bools_ops = {
static void sel_remove_entries(struct dentry *de) static void sel_remove_entries(struct dentry *de)
{ {
struct list_head *node; d_genocide(de);
shrink_dcache_parent(de);
spin_lock(&de->d_lock);
node = de->d_subdirs.next;
while (node != &de->d_subdirs) {
struct dentry *d = list_entry(node, struct dentry, d_child);
spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
list_del_init(node);
if (d->d_inode) {
dget_dlock(d);
spin_unlock(&de->d_lock);
spin_unlock(&d->d_lock);
d_delete(d);
simple_unlink(de->d_inode, d);
dput(d);
spin_lock(&de->d_lock);
} else
spin_unlock(&d->d_lock);
node = de->d_subdirs.next;
}
spin_unlock(&de->d_lock);
} }
#define BOOL_DIR_NAME "booleans" #define BOOL_DIR_NAME "booleans"
...@@ -1668,37 +1646,13 @@ static int sel_make_class_dir_entries(char *classname, int index, ...@@ -1668,37 +1646,13 @@ static int sel_make_class_dir_entries(char *classname, int index,
return rc; return rc;
} }
static void sel_remove_classes(void)
{
struct list_head *class_node;
list_for_each(class_node, &class_dir->d_subdirs) {
struct dentry *class_subdir = list_entry(class_node,
struct dentry, d_child);
struct list_head *class_subdir_node;
list_for_each(class_subdir_node, &class_subdir->d_subdirs) {
struct dentry *d = list_entry(class_subdir_node,
struct dentry, d_child);
if (d->d_inode)
if (d->d_inode->i_mode & S_IFDIR)
sel_remove_entries(d);
}
sel_remove_entries(class_subdir);
}
sel_remove_entries(class_dir);
}
static int sel_make_classes(void) static int sel_make_classes(void)
{ {
int rc, nclasses, i; int rc, nclasses, i;
char **classes; char **classes;
/* delete any existing entries */ /* delete any existing entries */
sel_remove_classes(); sel_remove_entries(class_dir);
rc = security_get_classes(&classes, &nclasses); rc = security_get_classes(&classes, &nclasses);
if (rc) if (rc)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment