fs_pin.c 1.99 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
Al Viro's avatar
Al Viro committed
2
#include <linux/fs.h>
Al Viro's avatar
Al Viro committed
3
#include <linux/sched.h>
Al Viro's avatar
Al Viro committed
4
#include <linux/slab.h>
5
#include "internal.h"
Al Viro's avatar
Al Viro committed
6 7 8 9 10 11 12
#include "mount.h"

static DEFINE_SPINLOCK(pin_lock);

void pin_remove(struct fs_pin *pin)
{
	spin_lock(&pin_lock);
13 14
	hlist_del_init(&pin->m_list);
	hlist_del_init(&pin->s_list);
Al Viro's avatar
Al Viro committed
15
	spin_unlock(&pin_lock);
Al Viro's avatar
Al Viro committed
16 17 18 19
	spin_lock_irq(&pin->wait.lock);
	pin->done = 1;
	wake_up_locked(&pin->wait);
	spin_unlock_irq(&pin->wait.lock);
Al Viro's avatar
Al Viro committed
20 21
}

22
void pin_insert_group(struct fs_pin *pin, struct vfsmount *m, struct hlist_head *p)
Al Viro's avatar
Al Viro committed
23 24
{
	spin_lock(&pin_lock);
25 26
	if (p)
		hlist_add_head(&pin->s_list, p);
Al Viro's avatar
Al Viro committed
27 28 29 30
	hlist_add_head(&pin->m_list, &real_mount(m)->mnt_pins);
	spin_unlock(&pin_lock);
}

31 32 33 34 35
void pin_insert(struct fs_pin *pin, struct vfsmount *m)
{
	pin_insert_group(pin, m, &m->mnt_sb->s_pins);
}

Al Viro's avatar
Al Viro committed
36 37
void pin_kill(struct fs_pin *p)
{
38
	wait_queue_entry_t wait;
Al Viro's avatar
Al Viro committed
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64

	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();
65
		if (likely(list_empty(&wait.entry)))
Al Viro's avatar
Al Viro committed
66 67 68 69 70 71 72 73 74 75 76
			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();
}

77
void mnt_pin_kill(struct mount *m)
Al Viro's avatar
Al Viro committed
78 79 80 81
{
	while (1) {
		struct hlist_node *p;
		rcu_read_lock();
82
		p = READ_ONCE(m->mnt_pins.first);
Al Viro's avatar
Al Viro committed
83 84 85 86
		if (!p) {
			rcu_read_unlock();
			break;
		}
Al Viro's avatar
Al Viro committed
87
		pin_kill(hlist_entry(p, struct fs_pin, m_list));
Al Viro's avatar
Al Viro committed
88 89 90
	}
}

91
void group_pin_kill(struct hlist_head *p)
Al Viro's avatar
Al Viro committed
92 93
{
	while (1) {
94
		struct hlist_node *q;
Al Viro's avatar
Al Viro committed
95
		rcu_read_lock();
96
		q = READ_ONCE(p->first);
97
		if (!q) {
Al Viro's avatar
Al Viro committed
98 99 100
			rcu_read_unlock();
			break;
		}
Al Viro's avatar
Al Viro committed
101
		pin_kill(hlist_entry(q, struct fs_pin, s_list));
Al Viro's avatar
Al Viro committed
102 103
	}
}