Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 59eda0e0 authored by Al Viro's avatar Al Viro
Browse files

new fs_pin killing logics



Signed-off-by: default avatarAl Viro <viro@zeniv.linux.org.uk>
parent fdab684d
Loading
Loading
Loading
Loading
+48 −6
Original line number Diff line number Diff line
#include <linux/fs.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/fs_pin.h>
#include "internal.h"
@@ -12,6 +13,10 @@ void pin_remove(struct fs_pin *pin)
	hlist_del(&pin->m_list);
	hlist_del(&pin->s_list);
	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_group(struct fs_pin *pin, struct vfsmount *m, struct hlist_head *p)
@@ -28,19 +33,58 @@ 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)
{
	while (1) {
		struct hlist_node *p;
		struct fs_pin *pin;
		rcu_read_lock();
		p = ACCESS_ONCE(m->mnt_pins.first);
		if (!p) {
			rcu_read_unlock();
			break;
		}
		pin = hlist_entry(p, struct fs_pin, m_list);
		pin->kill(pin);
		pin_kill(hlist_entry(p, struct fs_pin, m_list));
	}
}

@@ -48,14 +92,12 @@ void group_pin_kill(struct hlist_head *p)
{
	while (1) {
		struct hlist_node *q;
		struct fs_pin *pin;
		rcu_read_lock();
		q = ACCESS_ONCE(p->first);
		if (!q) {
			rcu_read_unlock();
			break;
		}
		pin = hlist_entry(q, struct fs_pin, s_list);
		pin->kill(pin);
		pin_kill(hlist_entry(q, struct fs_pin, s_list));
	}
}
+12 −1
Original line number Diff line number Diff line
#include <linux/fs.h>
#include <linux/wait.h>

struct fs_pin {
	wait_queue_head_t	wait;
	int			done;
	struct hlist_node	s_list;
	struct hlist_node	m_list;
	void (*kill)(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_insert_group(struct fs_pin *, struct vfsmount *, struct hlist_head *);
void pin_insert(struct fs_pin *, struct vfsmount *);
void pin_kill(struct fs_pin *);
+2 −2
Original line number Diff line number Diff line
@@ -19,7 +19,7 @@ struct pidmap {
#define BITS_PER_PAGE_MASK	(BITS_PER_PAGE-1)
#define PIDMAP_ENTRIES		((PID_MAX_LIMIT+BITS_PER_PAGE-1)/BITS_PER_PAGE)

struct bsd_acct_struct;
struct fs_pin;

struct pid_namespace {
	struct kref kref;
@@ -37,7 +37,7 @@ struct pid_namespace {
	struct dentry *proc_thread_self;
#endif
#ifdef CONFIG_BSD_PROCESS_ACCT
	struct bsd_acct_struct *bacct;
	struct fs_pin *bacct;
#endif
	struct user_namespace *user_ns;
	struct work_struct proc_work;
+34 −47
Original line number Diff line number Diff line
@@ -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 @@ static struct bsd_acct_struct *acct_get(struct pid_namespace *ns)
	}
	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,51 +164,27 @@ static struct bsd_acct_struct *acct_get(struct pid_namespace *ns)
	return res;
}

static void close_work(struct work_struct *work)
{
	struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work);
	struct file *file = acct->file;
	if (file->f_op->flush)
		file->f_op->flush(file, NULL);
	__fput_sync(file);
	complete(&acct->done);
}

static void acct_kill(struct bsd_acct_struct *acct)
static void acct_pin_kill(struct fs_pin *pin)
{
	if (acct) {
		struct pid_namespace *ns = acct->ns;
	struct bsd_acct_struct *acct = to_acct(pin);
	mutex_lock(&acct->lock);
	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);
	cmpxchg(&acct->ns->bacct, pin, NULL);
	mutex_unlock(&acct->lock);
	pin_remove(pin);
	acct_put(acct);
}
}

static void acct_pin_kill(struct fs_pin *pin)
static void close_work(struct work_struct *work)
{
	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);
	struct bsd_acct_struct *acct = container_of(work, struct bsd_acct_struct, work);
	struct file *file = acct->file;
	if (file->f_op->flush)
		file->f_op->flush(file, NULL);
	__fput_sync(file);
	complete(&acct->done);
}

static int acct_on(struct filename *pathname)
@@ -210,7 +192,8 @@ 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);
}

/*