group.c 7.6 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/*
 *  Copyright (C) 2008 Red Hat, Inc., Eric Paris <eparis@redhat.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; see the file COPYING.  If not, write to
 *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <linux/list.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/srcu.h>
#include <linux/rculist.h>
#include <linux/wait.h>

#include <linux/fsnotify_backend.h>
#include "fsnotify.h"

#include <asm/atomic.h>

/* protects writes to fsnotify_groups and fsnotify_mask */
static DEFINE_MUTEX(fsnotify_grp_mutex);
/* protects reads while running the fsnotify_groups list */
struct srcu_struct fsnotify_grp_srcu;
35
/* all groups registered to receive inode filesystem notifications */
36
LIST_HEAD(fsnotify_inode_groups);
37 38
/* all groups registered to receive mount point filesystem notifications */
LIST_HEAD(fsnotify_vfsmount_groups);
39
/* bitwise OR of all events (FS_*) interesting to some group on this system */
40
__u32 fsnotify_inode_mask;
41 42
/* bitwise OR of all events (FS_*) interesting to some group on this system */
__u32 fsnotify_vfsmount_mask;
43 44 45 46 47 48 49 50

/*
 * When a new group registers or changes it's set of interesting events
 * this function updates the fsnotify_mask to contain all interesting events
 */
void fsnotify_recalc_global_mask(void)
{
	struct fsnotify_group *group;
51 52
	__u32 inode_mask = 0;
	__u32 vfsmount_mask = 0;
53 54 55
	int idx;

	idx = srcu_read_lock(&fsnotify_grp_srcu);
56
	list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list)
57 58 59 60
		inode_mask |= group->mask;
	list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list)
		vfsmount_mask |= group->mask;
		
61
	srcu_read_unlock(&fsnotify_grp_srcu, idx);
62 63 64

	fsnotify_inode_mask = inode_mask;
	fsnotify_vfsmount_mask = vfsmount_mask;
65 66
}

67 68 69 70 71 72 73 74 75 76
/*
 * Update the group->mask by running all of the marks associated with this
 * group and finding the bitwise | of all of the mark->mask.  If we change
 * the group->mask we need to update the global mask of events interesting
 * to the system.
 */
void fsnotify_recalc_group_mask(struct fsnotify_group *group)
{
	__u32 mask = 0;
	__u32 old_mask = group->mask;
77
	struct fsnotify_mark *mark;
78 79

	spin_lock(&group->mark_lock);
80 81
	list_for_each_entry(mark, &group->marks_list, g_list)
		mask |= mark->mask;
82 83 84 85 86 87 88 89
	spin_unlock(&group->mark_lock);

	group->mask = mask;

	if (old_mask != mask)
		fsnotify_recalc_global_mask();
}

90 91
void fsnotify_add_vfsmount_group(struct fsnotify_group *group)
{
E
Eric Paris 已提交
92 93
	struct fsnotify_group *group_iter;

94 95
	mutex_lock(&fsnotify_grp_mutex);

E
Eric Paris 已提交
96 97 98 99
	if (!group->on_vfsmount_group_list) {
		list_for_each_entry(group_iter, &fsnotify_vfsmount_groups,
				    vfsmount_group_list) {
			/* insert in front of this one? */
100
			if (group < group_iter) {
E
Eric Paris 已提交
101 102 103 104 105 106 107 108
				/* list_add_tail() insert in front of group_iter */
				list_add_tail_rcu(&group->inode_group_list,
						  &group_iter->inode_group_list);
				goto out;
			}
		}

		/* apparently we need to be the last entry */
109
		list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups);
E
Eric Paris 已提交
110 111
	}
out:
112 113 114 115 116
	group->on_vfsmount_group_list = 1;

	mutex_unlock(&fsnotify_grp_mutex);
}

117
void fsnotify_add_inode_group(struct fsnotify_group *group)
118
{
E
Eric Paris 已提交
119 120
	struct fsnotify_group *group_iter;

121
	mutex_lock(&fsnotify_grp_mutex);
122

123
	/* add to global group list */
E
Eric Paris 已提交
124 125 126
	if (!group->on_inode_group_list) {
		list_for_each_entry(group_iter, &fsnotify_inode_groups,
				    inode_group_list) {
127
			if (group < group_iter) {
E
Eric Paris 已提交
128 129 130 131 132 133 134 135
				/* list_add_tail() insert in front of group_iter */
				list_add_tail_rcu(&group->inode_group_list,
						  &group_iter->inode_group_list);
				goto out;
			}
		}

		/* apparently we need to be the last entry */
136
		list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups);
E
Eric Paris 已提交
137 138
	}
out:
139
	group->on_inode_group_list = 1;
140 141

	mutex_unlock(&fsnotify_grp_mutex);
142 143
}

144 145 146
/*
 * Final freeing of a group
 */
147
void fsnotify_final_destroy_group(struct fsnotify_group *group)
148
{
149 150 151
	/* clear the notification queue of all events */
	fsnotify_flush_notify(group);

152 153 154 155 156 157
	if (group->ops->free_group_priv)
		group->ops->free_group_priv(group);

	kfree(group);
}

158 159 160 161 162 163 164 165 166 167
/*
 * Trying to get rid of a group.  We need to first get rid of any outstanding
 * allocations and then free the group.  Remember that fsnotify_clear_marks_by_group
 * could miss marks that are being freed by inode and those marks could still
 * hold a reference to this group (via group->num_marks)  If we get into that
 * situtation, the fsnotify_final_destroy_group will get called when that final
 * mark is freed.
 */
static void fsnotify_destroy_group(struct fsnotify_group *group)
{
168
	/* clear all inode marks for this group */
169 170 171 172 173 174 175
	fsnotify_clear_marks_by_group(group);

	/* past the point of no return, matches the initial value of 1 */
	if (atomic_dec_and_test(&group->num_marks))
		fsnotify_final_destroy_group(group);
}

176 177 178 179 180 181 182 183 184
/*
 * Remove this group from the global list of groups that will get events
 * this can be done even if there are still references and things still using
 * this group.  This just stops the group from getting new events.
 */
static void __fsnotify_evict_group(struct fsnotify_group *group)
{
	BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex));

185 186 187
	if (group->on_inode_group_list)
		list_del_rcu(&group->inode_group_list);
	group->on_inode_group_list = 0;
188 189 190
	if (group->on_vfsmount_group_list)
		list_del_rcu(&group->vfsmount_group_list);
	group->on_vfsmount_group_list = 0;
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
}

/*
 * Called when a group is no longer interested in getting events.  This can be
 * used if a group is misbehaving or if for some reason a group should no longer
 * get any filesystem events.
 */
void fsnotify_evict_group(struct fsnotify_group *group)
{
	mutex_lock(&fsnotify_grp_mutex);
	__fsnotify_evict_group(group);
	mutex_unlock(&fsnotify_grp_mutex);
}

/*
 * Drop a reference to a group.  Free it if it's through.
 */
void fsnotify_put_group(struct fsnotify_group *group)
{
	if (!atomic_dec_and_mutex_lock(&group->refcnt, &fsnotify_grp_mutex))
		return;

	/*
	 * OK, now we know that there's no other users *and* we hold mutex,
	 * so no new references will appear
	 */
	__fsnotify_evict_group(group);

	/*
	 * now it's off the list, so the only thing we might care about is
	 * srcu access....
	 */
	mutex_unlock(&fsnotify_grp_mutex);
	synchronize_srcu(&fsnotify_grp_srcu);

	/* and now it is really dead. _Nothing_ could be seeing it */
	fsnotify_recalc_global_mask();
	fsnotify_destroy_group(group);
}

/*
232
 * Create a new fsnotify_group and hold a reference for the group returned.
233
 */
234
struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops)
235
{
236
	struct fsnotify_group *group;
237

E
Eric Paris 已提交
238
	group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL);
239 240 241
	if (!group)
		return ERR_PTR(-ENOMEM);

242
	/* set to 0 when there a no external references to this group */
243
	atomic_set(&group->refcnt, 1);
244 245 246 247 248 249
	/*
	 * hits 0 when there are no external references AND no marks for
	 * this group
	 */
	atomic_set(&group->num_marks, 1);

250 251 252 253 254
	mutex_init(&group->notification_mutex);
	INIT_LIST_HEAD(&group->notification_list);
	init_waitqueue_head(&group->notification_waitq);
	group->max_events = UINT_MAX;

255
	INIT_LIST_HEAD(&group->inode_group_list);
256
	INIT_LIST_HEAD(&group->vfsmount_group_list);
257

258
	spin_lock_init(&group->mark_lock);
259
	INIT_LIST_HEAD(&group->marks_list);
260

261 262 263 264
	group->ops = ops;

	return group;
}