Skip to content

Commit

Permalink
fsnotify: vfsmount marks generic functions
Browse files Browse the repository at this point in the history
Much like inode-mark.c has all of the code dealing with marks on inodes
this patch adds a vfsmount-mark.c which has similar code but is intended
for marks on vfsmounts.

Signed-off-by: Eric Paris <eparis@redhat.com>
  • Loading branch information
Eric Paris committed Jul 28, 2010
1 parent 2504c5d commit 0d48b7f
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 10 deletions.
2 changes: 1 addition & 1 deletion fs/notify/Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o \
mark.o
mark.o vfsmount_mark.o

obj-y += dnotify/
obj-y += inotify/
Expand Down
6 changes: 6 additions & 0 deletions fs/notify/fsnotify.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ extern void fsnotify_flush_notify(struct fsnotify_group *group);
extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group, struct inode *inode,
int allow_dups);
/* add a mark to a vfsmount */
extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
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);
Expand All @@ -32,6 +36,8 @@ 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);

/* vfsmount specific destruction of a mark */
extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark);
/* inode specific destruction of a mark */
extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark);
/* run the list of all marks associated with inode and flag them to be freed */
Expand Down
20 changes: 11 additions & 9 deletions fs/notify/mark.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,11 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
void fsnotify_destroy_mark(struct fsnotify_mark *mark)
{
struct fsnotify_group *group;
struct inode *inode;
struct inode *inode = NULL;

spin_lock(&mark->lock);

group = mark->group;
inode = mark->i.inode;

BUG_ON(group && !inode);
BUG_ON(!group && inode);

/* if !group something else already marked this to die */
if (!group) {
Expand All @@ -136,8 +132,11 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)

spin_lock(&group->mark_lock);

if (mark->flags & FSNOTIFY_MARK_FLAG_INODE)
if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) {
fsnotify_destroy_inode_mark(mark);
inode = mark->i.inode;
} else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT)
fsnotify_destroy_vfsmount_mark(mark);
else
BUG();

Expand Down Expand Up @@ -169,8 +168,8 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
* is just a lazy update (and could be a perf win...)
*/


iput(inode);
if (inode)
iput(inode);

/*
* it's possible that this group tried to destroy itself, but this
Expand All @@ -192,7 +191,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
{
int ret = 0;

BUG_ON(mnt);
BUG_ON(inode && mnt);
BUG_ON(!inode && !mnt);

Expand Down Expand Up @@ -223,6 +221,10 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups);
if (ret)
goto err;
} else if (mnt) {
ret = fsnotify_add_vfsmount_mark(mark, group, mnt, allow_dups);
if (ret)
goto err;
} else {
BUG();
}
Expand Down
171 changes: 171 additions & 0 deletions fs/notify/vfsmount_mark.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* 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/fs.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/writeback.h> /* for inode_lock */

#include <asm/atomic.h>

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

void fsnotify_clear_marks_by_mount(struct vfsmount *mnt)
{
struct fsnotify_mark *mark, *lmark;
struct hlist_node *pos, *n;
LIST_HEAD(free_list);

spin_lock(&mnt->mnt_root->d_lock);
hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) {
list_add(&mark->m.free_m_list, &free_list);
hlist_del_init(&mark->m.m_list);
fsnotify_get_mark(mark);
}
spin_unlock(&mnt->mnt_root->d_lock);

list_for_each_entry_safe(mark, lmark, &free_list, m.free_m_list) {
fsnotify_destroy_mark(mark);
fsnotify_put_mark(mark);
}
}

/*
* Recalculate the mask of events relevant to a given vfsmount locked.
*/
static void fsnotify_recalc_vfsmount_mask_locked(struct vfsmount *mnt)
{
struct fsnotify_mark *mark;
struct hlist_node *pos;
__u32 new_mask = 0;

assert_spin_locked(&mnt->mnt_root->d_lock);

hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list)
new_mask |= mark->mask;
mnt->mnt_fsnotify_mask = new_mask;
}

/*
* Recalculate the mnt->mnt_fsnotify_mask, or the mask of all FS_* event types
* any notifier is interested in hearing for this mount point
*/
void fsnotify_recalc_vfsmount_mask(struct vfsmount *mnt)
{
spin_lock(&mnt->mnt_root->d_lock);
fsnotify_recalc_vfsmount_mask_locked(mnt);
spin_unlock(&mnt->mnt_root->d_lock);
}

void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark)
{
struct vfsmount *mnt = mark->m.mnt;

assert_spin_locked(&mark->lock);
assert_spin_locked(&mark->group->mark_lock);

spin_lock(&mnt->mnt_root->d_lock);

hlist_del_init(&mark->m.m_list);
mark->m.mnt = NULL;

fsnotify_recalc_vfsmount_mask_locked(mnt);

spin_unlock(&mnt->mnt_root->d_lock);
}

static struct fsnotify_mark *fsnotify_find_vfsmount_mark_locked(struct fsnotify_group *group,
struct vfsmount *mnt)
{
struct fsnotify_mark *mark;
struct hlist_node *pos;

assert_spin_locked(&mnt->mnt_root->d_lock);

hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list) {
if (mark->group == group) {
fsnotify_get_mark(mark);
return mark;
}
}
return NULL;
}

/*
* given a group and vfsmount, find the mark associated with that combination.
* if found take a reference to that mark and return it, else return NULL
*/
struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group,
struct vfsmount *mnt)
{
struct fsnotify_mark *mark;

spin_lock(&mnt->mnt_root->d_lock);
mark = fsnotify_find_vfsmount_mark_locked(group, mnt);
spin_unlock(&mnt->mnt_root->d_lock);

return mark;
}

/*
* Attach an initialized mark to a given group and vfsmount.
* These marks may be used for the fsnotify backend to determine which
* event types should be delivered to which groups.
*/
int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark,
struct fsnotify_group *group, struct vfsmount *mnt,
int allow_dups)
{
struct fsnotify_mark *lmark = NULL;
int ret = 0;

mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT;

/*
* LOCKING ORDER!!!!
* mark->lock
* group->mark_lock
* mnt->mnt_root->d_lock
*/
assert_spin_locked(&mark->lock);
assert_spin_locked(&group->mark_lock);

spin_lock(&mnt->mnt_root->d_lock);

if (!allow_dups)
lmark = fsnotify_find_vfsmount_mark_locked(group, mnt);
if (!lmark) {
mark->m.mnt = mnt;

hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks);

fsnotify_recalc_vfsmount_mask_locked(mnt);
} else {
ret = -EEXIST;
}

spin_unlock(&mnt->mnt_root->d_lock);

return ret;
}
2 changes: 2 additions & 0 deletions include/linux/fsnotify_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,8 @@ extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group

/* functions used to manipulate the marks attached to inodes */

/* run all marks associated with a vfsmount and update mnt->mnt_fsnotify_mask */
extern void fsnotify_recalc_vfsmount_mask(struct vfsmount *mnt);
/* run all marks associated with an inode and update inode->i_fsnotify_mask */
extern void fsnotify_recalc_inode_mask(struct inode *inode);
extern void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark));
Expand Down

0 comments on commit 0d48b7f

Please sign in to comment.