#include <linux/srcu.h>
#include <linux/types.h>
-/* all groups which receive inode fsnotify events */
-extern struct list_head fsnotify_inode_groups;
-/* all groups which receive vfsmount fsnotify events */
-extern struct list_head fsnotify_vfsmount_groups;
-
/* destroy all events sitting in this groups notification queue */
extern void fsnotify_flush_notify(struct fsnotify_group *group);
struct fsnotify_group *group, struct vfsmount *mnt,
int allow_dups);
-/* add a group to the inode group list */
-extern void fsnotify_add_inode_group(struct fsnotify_group *group);
-/* add a group to the vfsmount group list */
-extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group);
/* final kfree of a group */
extern void fsnotify_final_destroy_group(struct fsnotify_group *group);
#include <asm/atomic.h>
-/* protects writes to fsnotify_groups and fsnotify_mask */
-static DEFINE_MUTEX(fsnotify_grp_mutex);
-/* all groups registered to receive inode filesystem notifications */
-LIST_HEAD(fsnotify_inode_groups);
-/* all groups registered to receive mount point filesystem notifications */
-LIST_HEAD(fsnotify_vfsmount_groups);
-
-void fsnotify_add_vfsmount_group(struct fsnotify_group *group)
-{
- struct fsnotify_group *group_iter;
-
- mutex_lock(&fsnotify_grp_mutex);
-
- if (!group->on_vfsmount_group_list) {
- list_for_each_entry(group_iter, &fsnotify_vfsmount_groups,
- vfsmount_group_list) {
- /* insert in front of this one? */
- if (group < group_iter) {
- /* 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 */
- list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups);
- }
-out:
- group->on_vfsmount_group_list = 1;
-
- mutex_unlock(&fsnotify_grp_mutex);
-}
-
-void fsnotify_add_inode_group(struct fsnotify_group *group)
-{
- struct fsnotify_group *group_iter;
-
- mutex_lock(&fsnotify_grp_mutex);
-
- /* add to global group list */
- if (!group->on_inode_group_list) {
- list_for_each_entry(group_iter, &fsnotify_inode_groups,
- inode_group_list) {
- if (group < group_iter) {
- /* 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 */
- list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups);
- }
-out:
- group->on_inode_group_list = 1;
-
- mutex_unlock(&fsnotify_grp_mutex);
-}
-
/*
* Final freeing of a group
*/
}
/*
- * 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));
-
- if (group->on_inode_group_list)
- list_del_rcu(&group->inode_group_list);
- group->on_inode_group_list = 0;
- if (group->on_vfsmount_group_list)
- list_del_rcu(&group->vfsmount_group_list);
- group->on_vfsmount_group_list = 0;
-}
-
-/*
- * 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);
-
- mutex_unlock(&fsnotify_grp_mutex);
-
- fsnotify_destroy_group(group);
+ if (atomic_dec_and_test(&group->refcnt))
+ fsnotify_destroy_group(group);
}
/*
init_waitqueue_head(&group->notification_waitq);
group->max_events = UINT_MAX;
- INIT_LIST_HEAD(&group->inode_group_list);
- INIT_LIST_HEAD(&group->vfsmount_group_list);
-
spin_lock_init(&group->mark_lock);
INIT_LIST_HEAD(&group->marks_list);
*/
struct fsnotify_group {
/*
- * global list of all groups receiving events from fsnotify.
- * anchored by fsnotify_inode_groups and protected by either fsnotify_grp_mutex
- * or fsnotify_grp_srcu depending on write vs read.
- */
- struct list_head inode_group_list;
- /*
- * same as above except anchored by fsnotify_vfsmount_groups
- */
- struct list_head vfsmount_group_list;
-
- /*
* How the refcnt is used is up to each group. When the refcnt hits 0
* fsnotify will clean up all of the resources associated with this group.
* As an example, the dnotify group will always have a refcnt=1 and that
* a group */
struct list_head marks_list; /* all inode marks for this group */
- /* prevents double list_del of group_list. protected by global fsnotify_grp_mutex */
- bool on_inode_group_list;
- bool on_vfsmount_group_list;
-
/* groups can define private fields here or use the void *private */
union {
void *private;