summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--fs/Makefile2
-rw-r--r--fs/fs_pin.c77
-rw-r--r--include/linux/acct.h6
-rw-r--r--include/linux/fs_pin.h17
-rw-r--r--kernel/acct.c127
5 files changed, 129 insertions, 100 deletions
diff --git a/fs/Makefile b/fs/Makefile
index 4030cbfbc9af..90c88529892b 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -11,7 +11,7 @@ obj-y := open.o read_write.o file_table.o super.o \
attr.o bad_inode.o file.o filesystems.o namespace.o \
seq_file.o xattr.o libfs.o fs-writeback.o \
pnode.o splice.o sync.o utimes.o \
- stack.o fs_struct.o statfs.o
+ stack.o fs_struct.o statfs.o fs_pin.o
ifeq ($(CONFIG_BLOCK),y)
obj-y += buffer.o block_dev.o direct-io.o mpage.o
diff --git a/fs/fs_pin.c b/fs/fs_pin.c
new file mode 100644
index 000000000000..f3ce0b874a44
--- /dev/null
+++ b/fs/fs_pin.c
@@ -0,0 +1,77 @@
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/fs_pin.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);
+
+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)
+{
+ spin_lock(&pin_lock);
+ hlist_del(&pin->m_list);
+ hlist_del(&pin->s_list);
+ spin_unlock(&pin_lock);
+}
+
+void pin_insert(struct fs_pin *pin, struct vfsmount *m)
+{
+ spin_lock(&pin_lock);
+ hlist_add_head(&pin->s_list, &m->mnt_sb->s_pins);
+ hlist_add_head(&pin->m_list, &real_mount(m)->mnt_pins);
+ spin_unlock(&pin_lock);
+}
+
+void acct_auto_close_mnt(struct hlist_head *list)
+{
+ while (1) {
+ struct hlist_node *p;
+ struct fs_pin *pin;
+ rcu_read_lock();
+ p = ACCESS_ONCE(list->first);
+ if (!p) {
+ rcu_read_unlock();
+ break;
+ }
+ pin = 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 acct_auto_close(struct hlist_head *list)
+{
+ while (1) {
+ struct hlist_node *p;
+ struct fs_pin *pin;
+ rcu_read_lock();
+ p = ACCESS_ONCE(list->first);
+ if (!p) {
+ rcu_read_unlock();
+ break;
+ }
+ pin = hlist_entry(p, 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);
+ }
+}
diff --git a/include/linux/acct.h b/include/linux/acct.h
index 65a4f889182e..137837929dbe 100644
--- a/include/linux/acct.h
+++ b/include/linux/acct.h
@@ -24,18 +24,16 @@ struct super_block;
struct pacct_struct;
struct pid_namespace;
extern int acct_parm[]; /* for sysctl */
-extern void acct_auto_close(struct hlist_head *);
-extern void acct_auto_close_mnt(struct hlist_head *);
extern void acct_collect(long exitcode, int group_dead);
extern void acct_process(void);
extern void acct_exit_ns(struct pid_namespace *);
#else
-#define acct_auto_close(x) do { } while (0)
-#define acct_auto_close_mnt(x) do { } while (0)
#define acct_collect(x,y) do { } while (0)
#define acct_process() do { } while (0)
#define acct_exit_ns(ns) do { } while (0)
#endif
+extern void acct_auto_close(struct hlist_head *);
+extern void acct_auto_close_mnt(struct hlist_head *);
/*
* ACCT_VERSION numbers as yet defined:
diff --git a/include/linux/fs_pin.h b/include/linux/fs_pin.h
new file mode 100644
index 000000000000..f66525e72ccf
--- /dev/null
+++ b/include/linux/fs_pin.h
@@ -0,0 +1,17 @@
+#include <linux/fs.h>
+
+struct fs_pin {
+ atomic_long_t count;
+ union {
+ struct {
+ struct hlist_node s_list;
+ struct hlist_node m_list;
+ };
+ struct rcu_head rcu;
+ };
+ void (*kill)(struct fs_pin *);
+};
+
+void pin_put(struct fs_pin *);
+void pin_remove(struct fs_pin *);
+void pin_insert(struct fs_pin *, struct vfsmount *);
diff --git a/kernel/acct.c b/kernel/acct.c
index afeaaa6f49bf..a7993a6cb604 100644
--- a/kernel/acct.c
+++ b/kernel/acct.c
@@ -59,7 +59,7 @@
#include <asm/div64.h>
#include <linux/blkdev.h> /* sector_div */
#include <linux/pid_namespace.h>
-#include <../fs/mount.h> /* will go away when we refactor */
+#include <linux/fs_pin.h>
/*
* These constants control the amount of freespace that suspend and
@@ -78,17 +78,6 @@ int acct_parm[3] = {4, 2, 30};
*/
static void do_acct_process(struct bsd_acct_struct *acct);
-struct fs_pin {
- atomic_long_t count;
- union {
- struct {
- struct hlist_node s_list;
- struct hlist_node m_list;
- };
- struct rcu_head rcu;
- };
-};
-
struct bsd_acct_struct {
struct fs_pin pin;
struct mutex lock;
@@ -100,13 +89,6 @@ struct bsd_acct_struct {
struct completion done;
};
-static void pin_free_rcu(struct rcu_head *head)
-{
- kfree(container_of(head, struct fs_pin, rcu));
-}
-
-static DEFINE_SPINLOCK(acct_lock);
-
/*
* Check the amount of free space and suspend/resume accordingly.
*/
@@ -142,29 +124,6 @@ out:
return acct->active;
}
-static void pin_put(struct fs_pin *p)
-{
- if (atomic_long_dec_and_test(&p->count))
- call_rcu(&p->rcu, pin_free_rcu);
-}
-
-static struct bsd_acct_struct *__acct_get(struct bsd_acct_struct *res)
-{
- if (!atomic_long_inc_not_zero(&res->pin.count)) {
- rcu_read_unlock();
- cpu_relax();
- return NULL;
- }
- rcu_read_unlock();
- mutex_lock(&res->lock);
- if (!res->ns) {
- mutex_unlock(&res->lock);
- pin_put(&res->pin);
- return NULL;
- }
- return res;
-}
-
static struct bsd_acct_struct *acct_get(struct pid_namespace *ns)
{
struct bsd_acct_struct *res;
@@ -176,9 +135,18 @@ again:
rcu_read_unlock();
return NULL;
}
- res = __acct_get(res);
- if (!res)
+ if (!atomic_long_inc_not_zero(&res->pin.count)) {
+ rcu_read_unlock();
+ cpu_relax();
goto again;
+ }
+ rcu_read_unlock();
+ mutex_lock(&res->lock);
+ if (!res->ns) {
+ mutex_unlock(&res->lock);
+ pin_put(&res->pin);
+ goto again;
+ }
return res;
}
@@ -203,19 +171,8 @@ static void acct_kill(struct bsd_acct_struct *acct,
init_completion(&acct->done);
schedule_work(&acct->work);
wait_for_completion(&acct->done);
- spin_lock(&acct_lock);
- hlist_del(&acct->pin.m_list);
- hlist_del(&acct->pin.s_list);
- spin_unlock(&acct_lock);
+ pin_remove(&acct->pin);
ns->bacct = new;
- if (new) {
- struct vfsmount *m = new->file->f_path.mnt;
- spin_lock(&acct_lock);
- hlist_add_head(&new->pin.s_list, &m->mnt_sb->s_pins);
- hlist_add_head(&new->pin.m_list, &real_mount(m)->mnt_pins);
- spin_unlock(&acct_lock);
- mutex_unlock(&new->lock);
- }
acct->ns = NULL;
atomic_long_dec(&acct->pin.count);
mutex_unlock(&acct->lock);
@@ -223,6 +180,19 @@ static void acct_kill(struct bsd_acct_struct *acct,
}
}
+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)
{
struct file *file;
@@ -254,25 +224,22 @@ static int acct_on(struct filename *pathname)
}
atomic_long_set(&acct->pin.count, 1);
+ acct->pin.kill = acct_pin_kill;
acct->file = file;
acct->needcheck = jiffies;
acct->ns = ns;
mutex_init(&acct->lock);
mnt = file->f_path.mnt;
mnt_pin(mnt);
+ mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */
+ pin_insert(&acct->pin, mnt);
old = acct_get(ns);
- mutex_lock_nested(&acct->lock, 1); /* nobody has seen it yet */
- if (old) {
+ if (old)
acct_kill(old, acct);
- } else {
+ else
ns->bacct = acct;
- spin_lock(&acct_lock);
- hlist_add_head(&acct->pin.s_list, &mnt->mnt_sb->s_pins);
- hlist_add_head(&acct->pin.m_list, &real_mount(mnt)->mnt_pins);
- spin_unlock(&acct_lock);
- mutex_unlock(&acct->lock);
- }
+ mutex_unlock(&acct->lock);
mntput(mnt); /* it's pinned, now give up active reference */
return 0;
}
@@ -312,36 +279,6 @@ SYSCALL_DEFINE1(acct, const char __user *, name)
return error;
}
-void acct_auto_close_mnt(struct hlist_head *list)
-{
- rcu_read_lock();
- while (1) {
- struct hlist_node *p = ACCESS_ONCE(list->first);
- if (!p)
- break;
- acct_kill(__acct_get(hlist_entry(p,
- struct bsd_acct_struct,
- pin.m_list)), NULL);
- rcu_read_lock();
- }
- rcu_read_unlock();
-}
-
-void acct_auto_close(struct hlist_head *list)
-{
- rcu_read_lock();
- while (1) {
- struct hlist_node *p = ACCESS_ONCE(list->first);
- if (!p)
- break;
- acct_kill(__acct_get(hlist_entry(p,
- struct bsd_acct_struct,
- pin.s_list)), NULL);
- rcu_read_lock();
- }
- rcu_read_unlock();
-}
-
void acct_exit_ns(struct pid_namespace *ns)
{
acct_kill(acct_get(ns), NULL);