Skip to content

Commit

Permalink
fsnotify: allow groups to add private data to events
Browse files Browse the repository at this point in the history
inotify needs per group information attached to events.  This patch allows
groups to attach private information and implements a callback so that
information can be freed when an event is being destroyed.

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
Eric Paris committed Jun 11, 2009
1 parent 47882c6 commit e4aff11
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 9 deletions.
1 change: 1 addition & 0 deletions fs/notify/dnotify/dnotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,7 @@ static struct fsnotify_ops dnotify_fsnotify_ops = {
.should_send_event = dnotify_should_send_event,
.free_group_priv = NULL,
.freeing_mark = dnotify_freeing_mark,
.free_event_priv = NULL,
};

/*
Expand Down
52 changes: 48 additions & 4 deletions fs/notify/notification.c
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ void fsnotify_put_event(struct fsnotify_event *event)
if (event->data_type == FSNOTIFY_EVENT_PATH)
path_put(&event->path);

BUG_ON(!list_empty(&event->private_data_list));

kfree(event->file_name);
kmem_cache_free(fsnotify_event_cachep, event);
}
Expand All @@ -106,7 +108,29 @@ void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder)
}

/*
* check if 2 events contain the same information.
* Find the private data that the group previously attached to this event when
* the group added the event to the notification queue (fsnotify_add_notify_event)
*/
struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnotify_group *group, struct fsnotify_event *event)
{
struct fsnotify_event_private_data *lpriv;
struct fsnotify_event_private_data *priv = NULL;

assert_spin_locked(&event->lock);

list_for_each_entry(lpriv, &event->private_data_list, event_list) {
if (lpriv->group == group) {
priv = lpriv;
list_del(&priv->event_list);
break;
}
}
return priv;
}

/*
* Check if 2 events contain the same information. We do not compare private data
* but at this moment that isn't a problem for any know fsnotify listeners.
*/
static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new)
{
Expand Down Expand Up @@ -134,13 +158,17 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new
* event off the queue to deal with. If the event is successfully added to the
* group's notification queue, a reference is taken on event.
*/
int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event)
int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event,
struct fsnotify_event_private_data *priv)
{
struct fsnotify_event_holder *holder = NULL;
struct list_head *list = &group->notification_list;
struct fsnotify_event_holder *last_holder;
struct fsnotify_event *last_event;

/* easy to tell if priv was attached to the event */
INIT_LIST_HEAD(&priv->event_list);

/*
* There is one fsnotify_event_holder embedded inside each fsnotify_event.
* Check if we expect to be able to use that holder. If not alloc a new
Expand All @@ -158,8 +186,11 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even

mutex_lock(&group->notification_mutex);

if (group->q_len >= group->max_events)
if (group->q_len >= group->max_events) {
event = &q_overflow_event;
/* sorry, no private data on the overflow event */
priv = NULL;
}

spin_lock(&event->lock);

Expand All @@ -183,7 +214,7 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even
mutex_unlock(&group->notification_mutex);
if (holder != &event->holder)
fsnotify_destroy_event_holder(holder);
return 0;
return -EEXIST;
}
}

Expand All @@ -192,6 +223,8 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even

fsnotify_get_event(event);
list_add_tail(&holder->event_list, list);
if (priv)
list_add_tail(&priv->event_list, &event->private_data_list);
spin_unlock(&event->lock);
mutex_unlock(&group->notification_mutex);

Expand Down Expand Up @@ -252,10 +285,19 @@ struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group)
void fsnotify_flush_notify(struct fsnotify_group *group)
{
struct fsnotify_event *event;
struct fsnotify_event_private_data *priv;

mutex_lock(&group->notification_mutex);
while (!fsnotify_notify_queue_is_empty(group)) {
event = fsnotify_remove_notify_event(group);
/* if they don't implement free_event_priv they better not have attached any */
if (group->ops->free_event_priv) {
spin_lock(&event->lock);
priv = fsnotify_remove_priv_from_event(group, event);
spin_unlock(&event->lock);
if (priv)
group->ops->free_event_priv(priv);
}
fsnotify_put_event(event); /* matches fsnotify_add_notify_event */
}
mutex_unlock(&group->notification_mutex);
Expand All @@ -274,6 +316,8 @@ static void initialize_event(struct fsnotify_event *event)
event->inode = NULL;
event->data_type = FSNOTIFY_EVENT_NONE;

INIT_LIST_HEAD(&event->private_data_list);

event->to_tell = NULL;

event->file_name = NULL;
Expand Down
24 changes: 19 additions & 5 deletions include/linux/fsnotify_backend.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
struct fsnotify_group;
struct fsnotify_event;
struct fsnotify_mark_entry;
struct fsnotify_event_private_data;

/*
* Each group much define these ops. The fsnotify infrastructure will call
Expand All @@ -81,6 +82,7 @@ struct fsnotify_ops {
int (*handle_event)(struct fsnotify_group *group, struct fsnotify_event *event);
void (*free_group_priv)(struct fsnotify_group *group);
void (*freeing_mark)(struct fsnotify_mark_entry *entry, struct fsnotify_group *group);
void (*free_event_priv)(struct fsnotify_event_private_data *priv);
};

/*
Expand Down Expand Up @@ -157,6 +159,15 @@ struct fsnotify_event_holder {
struct list_head event_list;
};

/*
* Inotify needs to tack data onto an event. This struct lets us later find the
* correct private data of the correct group.
*/
struct fsnotify_event_private_data {
struct fsnotify_group *group;
struct list_head event_list;
};

/*
* all of the information about the original object we want to now send to
* a group. If you want to carry more info from the accessing task to the
Expand Down Expand Up @@ -196,6 +207,8 @@ struct fsnotify_event {
u32 sync_cookie; /* used to corrolate events, namely inotify mv events */
char *file_name;
size_t name_len;

struct list_head private_data_list; /* groups can store private data here */
};

/*
Expand Down Expand Up @@ -294,17 +307,18 @@ extern void fsnotify_put_group(struct fsnotify_group *group);
/* take a reference to an event */
extern void fsnotify_get_event(struct fsnotify_event *event);
extern void fsnotify_put_event(struct fsnotify_event *event);
/* find private data previously attached to an event */
extern struct fsnotify_event_private_data *fsnotify_get_priv_from_event(struct fsnotify_group *group,
struct fsnotify_event *event);
/* find private data previously attached to an event and unlink it */
extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnotify_group *group,
struct fsnotify_event *event);

/* attach the event to the group notification queue */
extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event);
extern int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event,
struct fsnotify_event_private_data *priv);
/* true if the group notification queue is empty */
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
/* return, but do not dequeue the first event on the notification queue */
extern struct fsnotify_event *fsnotify_peek_notify_event(struct fsnotify_group *group);
/* reutnr AND dequeue the first event on the notification queue */
/* return AND dequeue the first event on the notification queue */
extern struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group);

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

0 comments on commit e4aff11

Please sign in to comment.