|
@@ -76,7 +76,6 @@ int acct_parm[3] = {4, 2, 30};
|
|
|
/*
|
|
|
* External references and all of the globals.
|
|
|
*/
|
|
|
-static void do_acct_process(struct bsd_acct_struct *acct);
|
|
|
|
|
|
struct bsd_acct_struct {
|
|
|
struct fs_pin pin;
|
|
@@ -91,6 +90,8 @@ struct bsd_acct_struct {
|
|
|
struct completion done;
|
|
|
};
|
|
|
|
|
|
+static void do_acct_process(struct bsd_acct_struct *acct);
|
|
|
+
|
|
|
/*
|
|
|
* Check the amount of free space and suspend/resume accordingly.
|
|
|
*/
|
|
@@ -132,13 +133,18 @@ static void acct_put(struct bsd_acct_struct *p)
|
|
|
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)
|
|
|
{
|
|
|
struct bsd_acct_struct *res;
|
|
|
again:
|
|
|
smp_rmb();
|
|
|
rcu_read_lock();
|
|
|
- res = ACCESS_ONCE(ns->bacct);
|
|
|
+ res = to_acct(ACCESS_ONCE(ns->bacct));
|
|
|
if (!res) {
|
|
|
rcu_read_unlock();
|
|
|
return NULL;
|
|
@@ -150,7 +156,7 @@ again:
|
|
|
}
|
|
|
rcu_read_unlock();
|
|
|
mutex_lock(&res->lock);
|
|
|
- if (!res->ns) {
|
|
|
+ if (res != to_acct(ACCESS_ONCE(ns->bacct))) {
|
|
|
mutex_unlock(&res->lock);
|
|
|
acct_put(res);
|
|
|
goto again;
|
|
@@ -158,6 +164,19 @@ again:
|
|
|
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)
|
|
|
{
|
|
|
struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work);
|
|
@@ -168,49 +187,13 @@ static void close_work(struct work_struct *work)
|
|
|
complete(&acct->done);
|
|
|
}
|
|
|
|
|
|
-static void acct_kill(struct bsd_acct_struct *acct)
|
|
|
-{
|
|
|
- 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);
|
|
|
- cmpxchg(&ns->bacct, acct, NULL);
|
|
|
- acct->ns = NULL;
|
|
|
- atomic_long_dec(&acct->count);
|
|
|
- mutex_unlock(&acct->lock);
|
|
|
- acct_put(acct);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-static void acct_pin_kill(struct fs_pin *pin)
|
|
|
-{
|
|
|
- struct bsd_acct_struct *acct;
|
|
|
- acct = container_of(pin, struct bsd_acct_struct, pin);
|
|
|
- if (!atomic_long_inc_not_zero(&acct->count)) {
|
|
|
- rcu_read_unlock();
|
|
|
- cpu_relax();
|
|
|
- return;
|
|
|
- }
|
|
|
- rcu_read_unlock();
|
|
|
- mutex_lock(&acct->lock);
|
|
|
- if (!acct->ns) {
|
|
|
- mutex_unlock(&acct->lock);
|
|
|
- acct_put(acct);
|
|
|
- acct = NULL;
|
|
|
- }
|
|
|
- acct_kill(acct);
|
|
|
-}
|
|
|
-
|
|
|
static int acct_on(struct filename *pathname)
|
|
|
{
|
|
|
struct file *file;
|
|
|
struct vfsmount *mnt, *internal;
|
|
|
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;
|
|
|
|
|
|
acct = kzalloc(sizeof(struct bsd_acct_struct), GFP_KERNEL);
|
|
@@ -252,18 +235,20 @@ static int acct_on(struct filename *pathname)
|
|
|
file->f_path.mnt = internal;
|
|
|
|
|
|
atomic_long_set(&acct->count, 1);
|
|
|
- acct->pin.kill = acct_pin_kill;
|
|
|
+ init_fs_pin(&acct->pin, acct_pin_kill);
|
|
|
acct->file = file;
|
|
|
acct->needcheck = jiffies;
|
|
|
acct->ns = ns;
|
|
|
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 */
|
|
|
pin_insert(&acct->pin, mnt);
|
|
|
|
|
|
- old = acct_get(ns);
|
|
|
- ns->bacct = acct;
|
|
|
- acct_kill(old);
|
|
|
+ rcu_read_lock();
|
|
|
+ old = xchg(&ns->bacct, &acct->pin);
|
|
|
mutex_unlock(&acct->lock);
|
|
|
+ pin_kill(old);
|
|
|
mnt_drop_write(mnt);
|
|
|
mntput(mnt);
|
|
|
return 0;
|
|
@@ -299,7 +284,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name)
|
|
|
mutex_unlock(&acct_on_mutex);
|
|
|
putname(tmp);
|
|
|
} else {
|
|
|
- acct_kill(acct_get(task_active_pid_ns(current)));
|
|
|
+ rcu_read_lock();
|
|
|
+ pin_kill(task_active_pid_ns(current)->bacct);
|
|
|
}
|
|
|
|
|
|
return error;
|
|
@@ -307,7 +293,8 @@ SYSCALL_DEFINE1(acct, const char __user *, name)
|
|
|
|
|
|
void acct_exit_ns(struct pid_namespace *ns)
|
|
|
{
|
|
|
- acct_kill(acct_get(ns));
|
|
|
+ rcu_read_lock();
|
|
|
+ pin_kill(ns->bacct);
|
|
|
}
|
|
|
|
|
|
/*
|