Skip to content

Commit

Permalink
fsnotify: add marks to inodes so groups can interpret how to handle t…
Browse files Browse the repository at this point in the history
…hose inodes

This patch creates a way for fsnotify groups to attach marks to inodes.
These marks have little meaning to the generic fsnotify infrastructure
and thus their meaning should be interpreted by the group that attached
them to the inode's list.

dnotify and inotify  will make use of these markings to indicate which
inodes are of interest to their respective groups.  But this implementation
has the useful property that in the future other listeners could actually
use the marks for the exact opposite reason, aka to indicate which inodes
it had NO interest in.

Signed-off-by: Eric Paris <eparis@redhat.com>
Acked-by: Al Viro <viro@zeniv.linux.org.uk>
Cc: Christoph Hellwig <hch@lst.de>
  • Loading branch information
eparis committed Jun 11, 2009
1 parent 9058652 commit 3be25f4
Show file tree
Hide file tree
Showing 9 changed files with 483 additions and 3 deletions.
9 changes: 9 additions & 0 deletions fs/inode.c
Expand Up @@ -22,6 +22,7 @@
#include <linux/cdev.h>
#include <linux/bootmem.h>
#include <linux/inotify.h>
#include <linux/fsnotify.h>
#include <linux/mount.h>
#include <linux/async.h>

Expand Down Expand Up @@ -189,6 +190,10 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
inode->i_private = NULL;
inode->i_mapping = mapping;

#ifdef CONFIG_FSNOTIFY
inode->i_fsnotify_mask = 0;
#endif

return inode;

out_free_security:
Expand Down Expand Up @@ -221,6 +226,7 @@ void destroy_inode(struct inode *inode)
BUG_ON(inode_has_buffers(inode));
ima_inode_free(inode);
security_inode_free(inode);
fsnotify_inode_delete(inode);
if (inode->i_sb->s_op->destroy_inode)
inode->i_sb->s_op->destroy_inode(inode);
else
Expand Down Expand Up @@ -252,6 +258,9 @@ void inode_init_once(struct inode *inode)
INIT_LIST_HEAD(&inode->inotify_watches);
mutex_init(&inode->inotify_mutex);
#endif
#ifdef CONFIG_FSNOTIFY
INIT_HLIST_HEAD(&inode->i_fsnotify_mark_entries);
#endif
}
EXPORT_SYMBOL(inode_init_once);

Expand Down
2 changes: 1 addition & 1 deletion fs/notify/Makefile
@@ -1,4 +1,4 @@
obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o
obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o

obj-y += dnotify/
obj-y += inotify/
13 changes: 13 additions & 0 deletions fs/notify/fsnotify.c
Expand Up @@ -25,6 +25,15 @@
#include <linux/fsnotify_backend.h>
#include "fsnotify.h"

/*
* Clear all of the marks on an inode when it is being evicted from core
*/
void __fsnotify_inode_delete(struct inode *inode)
{
fsnotify_clear_marks_by_inode(inode);
}
EXPORT_SYMBOL_GPL(__fsnotify_inode_delete);

/*
* This is the main call to fsnotify. The VFS calls into hook specific functions
* in linux/fsnotify.h. Those functions then in turn call here. Here will call
Expand All @@ -43,6 +52,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is)
if (!(mask & fsnotify_mask))
return;

if (!(mask & to_tell->i_fsnotify_mask))
return;
/*
* SRCU!! the groups list is very very much read only and the path is
* very hot. The VAST majority of events are not going to need to do
Expand All @@ -51,6 +62,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is)
idx = srcu_read_lock(&fsnotify_grp_srcu);
list_for_each_entry_rcu(group, &fsnotify_groups, group_list) {
if (mask & group->mask) {
if (!group->ops->should_send_event(group, to_tell, mask))
continue;
if (!event) {
event = fsnotify_create_event(to_tell, mask, data, data_is);
/* shit, we OOM'd and now we can't tell, maybe
Expand Down
5 changes: 5 additions & 0 deletions fs/notify/fsnotify.h
Expand Up @@ -12,4 +12,9 @@ extern struct srcu_struct fsnotify_grp_srcu;
extern struct list_head fsnotify_groups;
/* all bitwise OR of all event types (FS_*) for all fsnotify_groups */
extern __u32 fsnotify_mask;

/* final kfree of a group */
extern void fsnotify_final_destroy_group(struct fsnotify_group *group);
/* run the list of all marks associated with inode and flag them to be freed */
extern void fsnotify_clear_marks_by_inode(struct inode *inode);
#endif /* __FS_NOTIFY_FSNOTIFY_H_ */
49 changes: 48 additions & 1 deletion fs/notify/group.c
Expand Up @@ -54,6 +54,29 @@ void fsnotify_recalc_global_mask(void)
fsnotify_mask = mask;
}

/*
* 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;
struct fsnotify_mark_entry *entry;

spin_lock(&group->mark_lock);
list_for_each_entry(entry, &group->mark_entries, g_list)
mask |= entry->mask;
spin_unlock(&group->mark_lock);

group->mask = mask;

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

/*
* Take a reference to a group so things found under the fsnotify_grp_mutex
* can't get freed under us
Expand All @@ -66,14 +89,32 @@ static void fsnotify_get_group(struct fsnotify_group *group)
/*
* Final freeing of a group
*/
static void fsnotify_destroy_group(struct fsnotify_group *group)
void fsnotify_final_destroy_group(struct fsnotify_group *group)
{
if (group->ops->free_group_priv)
group->ops->free_group_priv(group);

kfree(group);
}

/*
* 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)
{
/* clear all inode mark entries for this group */
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);
}

/*
* 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
Expand Down Expand Up @@ -173,6 +214,10 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask,
group->group_num = group_num;
group->mask = mask;

spin_lock_init(&group->mark_lock);
atomic_set(&group->num_marks, 0);
INIT_LIST_HEAD(&group->mark_entries);

group->ops = ops;

mutex_lock(&fsnotify_grp_mutex);
Expand All @@ -188,6 +233,8 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask,
/* group not found, add a new one */
list_add_rcu(&group->group_list, &fsnotify_groups);
group->on_group_list = 1;
/* being on the fsnotify_groups list holds one num_marks */
atomic_inc(&group->num_marks);

mutex_unlock(&fsnotify_grp_mutex);

Expand Down

0 comments on commit 3be25f4

Please sign in to comment.