fsnotify: per group notification queue merge types
inotify only wishes to merge a new event with the last event on the notification fifo. fanotify is willing to merge any events including by means of bitwise OR masks of multiple events together. This patch moves the inotify event merging logic out of the generic fsnotify notification.c and into the inotify code. This allows each use of fsnotify to provide their own merge functionality. Signed-off-by: Eric Paris <eparis@redhat.com>
This commit is contained in:
parent
28c60e37f8
commit
74766bbfa9
4 changed files with 79 additions and 58 deletions
|
@ -32,6 +32,60 @@
|
||||||
|
|
||||||
#include "inotify.h"
|
#include "inotify.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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)
|
||||||
|
{
|
||||||
|
if ((old->mask == new->mask) &&
|
||||||
|
(old->to_tell == new->to_tell) &&
|
||||||
|
(old->data_type == new->data_type) &&
|
||||||
|
(old->name_len == new->name_len)) {
|
||||||
|
switch (old->data_type) {
|
||||||
|
case (FSNOTIFY_EVENT_INODE):
|
||||||
|
/* remember, after old was put on the wait_q we aren't
|
||||||
|
* allowed to look at the inode any more, only thing
|
||||||
|
* left to check was if the file_name is the same */
|
||||||
|
if (!old->name_len ||
|
||||||
|
!strcmp(old->file_name, new->file_name))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case (FSNOTIFY_EVENT_PATH):
|
||||||
|
if ((old->path.mnt == new->path.mnt) &&
|
||||||
|
(old->path.dentry == new->path.dentry))
|
||||||
|
return true;
|
||||||
|
break;
|
||||||
|
case (FSNOTIFY_EVENT_NONE):
|
||||||
|
if (old->mask & FS_Q_OVERFLOW)
|
||||||
|
return true;
|
||||||
|
else if (old->mask & FS_IN_IGNORED)
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int inotify_merge(struct list_head *list, struct fsnotify_event *event)
|
||||||
|
{
|
||||||
|
struct fsnotify_event_holder *last_holder;
|
||||||
|
struct fsnotify_event *last_event;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
/* and the list better be locked by something too */
|
||||||
|
spin_lock(&event->lock);
|
||||||
|
|
||||||
|
last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list);
|
||||||
|
last_event = last_holder->event;
|
||||||
|
if (event_compare(last_event, event))
|
||||||
|
ret = -EEXIST;
|
||||||
|
|
||||||
|
spin_unlock(&event->lock);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
|
static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event)
|
||||||
{
|
{
|
||||||
struct fsnotify_mark_entry *entry;
|
struct fsnotify_mark_entry *entry;
|
||||||
|
@ -62,7 +116,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev
|
||||||
fsn_event_priv->group = group;
|
fsn_event_priv->group = group;
|
||||||
event_priv->wd = wd;
|
event_priv->wd = wd;
|
||||||
|
|
||||||
ret = fsnotify_add_notify_event(group, event, fsn_event_priv);
|
ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
inotify_free_event_priv(fsn_event_priv);
|
inotify_free_event_priv(fsn_event_priv);
|
||||||
/* EEXIST says we tail matched, EOVERFLOW isn't something
|
/* EEXIST says we tail matched, EOVERFLOW isn't something
|
||||||
|
|
|
@ -531,7 +531,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry,
|
||||||
fsn_event_priv->group = group;
|
fsn_event_priv->group = group;
|
||||||
event_priv->wd = ientry->wd;
|
event_priv->wd = ientry->wd;
|
||||||
|
|
||||||
ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv);
|
ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL);
|
||||||
if (ret)
|
if (ret)
|
||||||
inotify_free_event_priv(fsn_event_priv);
|
inotify_free_event_priv(fsn_event_priv);
|
||||||
|
|
||||||
|
|
|
@ -104,7 +104,8 @@ struct fsnotify_event_holder *fsnotify_alloc_event_holder(void)
|
||||||
|
|
||||||
void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder)
|
void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder)
|
||||||
{
|
{
|
||||||
kmem_cache_free(fsnotify_event_holder_cachep, holder);
|
if (holder)
|
||||||
|
kmem_cache_free(fsnotify_event_holder_cachep, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -128,54 +129,18 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot
|
||||||
return priv;
|
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)
|
|
||||||
{
|
|
||||||
if ((old->mask == new->mask) &&
|
|
||||||
(old->to_tell == new->to_tell) &&
|
|
||||||
(old->data_type == new->data_type) &&
|
|
||||||
(old->name_len == new->name_len)) {
|
|
||||||
switch (old->data_type) {
|
|
||||||
case (FSNOTIFY_EVENT_INODE):
|
|
||||||
/* remember, after old was put on the wait_q we aren't
|
|
||||||
* allowed to look at the inode any more, only thing
|
|
||||||
* left to check was if the file_name is the same */
|
|
||||||
if (!old->name_len ||
|
|
||||||
!strcmp(old->file_name, new->file_name))
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case (FSNOTIFY_EVENT_PATH):
|
|
||||||
if ((old->path.mnt == new->path.mnt) &&
|
|
||||||
(old->path.dentry == new->path.dentry))
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
case (FSNOTIFY_EVENT_NONE):
|
|
||||||
if (old->mask & FS_Q_OVERFLOW)
|
|
||||||
return true;
|
|
||||||
else if (old->mask & FS_IN_IGNORED)
|
|
||||||
return false;
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add an event to the group notification queue. The group can later pull this
|
* Add an event to the group notification queue. The group can later pull this
|
||||||
* event off the queue to deal with. If the event is successfully added to the
|
* 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.
|
* 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_private_data *priv,
|
||||||
|
int (*merge)(struct list_head *, struct fsnotify_event *))
|
||||||
{
|
{
|
||||||
struct fsnotify_event_holder *holder = NULL;
|
struct fsnotify_event_holder *holder = NULL;
|
||||||
struct list_head *list = &group->notification_list;
|
struct list_head *list = &group->notification_list;
|
||||||
struct fsnotify_event_holder *last_holder;
|
int rc = 0;
|
||||||
struct fsnotify_event *last_event;
|
|
||||||
int ret = 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* There is one fsnotify_event_holder embedded inside each fsnotify_event.
|
* There is one fsnotify_event_holder embedded inside each fsnotify_event.
|
||||||
|
@ -196,11 +161,23 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even
|
||||||
|
|
||||||
if (group->q_len >= group->max_events) {
|
if (group->q_len >= group->max_events) {
|
||||||
event = q_overflow_event;
|
event = q_overflow_event;
|
||||||
ret = -EOVERFLOW;
|
rc = -EOVERFLOW;
|
||||||
/* sorry, no private data on the overflow event */
|
/* sorry, no private data on the overflow event */
|
||||||
priv = NULL;
|
priv = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!list_empty(list) && merge) {
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = merge(list, event);
|
||||||
|
if (ret) {
|
||||||
|
mutex_unlock(&group->notification_mutex);
|
||||||
|
if (holder != &event->holder)
|
||||||
|
fsnotify_destroy_event_holder(holder);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock(&event->lock);
|
spin_lock(&event->lock);
|
||||||
|
|
||||||
if (list_empty(&event->holder.event_list)) {
|
if (list_empty(&event->holder.event_list)) {
|
||||||
|
@ -215,18 +192,6 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even
|
||||||
goto alloc_holder;
|
goto alloc_holder;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!list_empty(list)) {
|
|
||||||
last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list);
|
|
||||||
last_event = last_holder->event;
|
|
||||||
if (event_compare(last_event, event)) {
|
|
||||||
spin_unlock(&event->lock);
|
|
||||||
mutex_unlock(&group->notification_mutex);
|
|
||||||
if (holder != &event->holder)
|
|
||||||
fsnotify_destroy_event_holder(holder);
|
|
||||||
return -EEXIST;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
group->q_len++;
|
group->q_len++;
|
||||||
holder->event = event;
|
holder->event = event;
|
||||||
|
|
||||||
|
@ -238,7 +203,7 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even
|
||||||
mutex_unlock(&group->notification_mutex);
|
mutex_unlock(&group->notification_mutex);
|
||||||
|
|
||||||
wake_up(&group->notification_waitq);
|
wake_up(&group->notification_waitq);
|
||||||
return ret;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -328,8 +328,10 @@ extern struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struc
|
||||||
struct fsnotify_event *event);
|
struct fsnotify_event *event);
|
||||||
|
|
||||||
/* attach the event to the group notification queue */
|
/* 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_private_data *priv);
|
struct fsnotify_event *event,
|
||||||
|
struct fsnotify_event_private_data *priv,
|
||||||
|
int (*merge)(struct list_head *, struct fsnotify_event *));
|
||||||
/* true if the group notification queue is empty */
|
/* true if the group notification queue is empty */
|
||||||
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
|
extern bool fsnotify_notify_queue_is_empty(struct fsnotify_group *group);
|
||||||
/* return, but do not dequeue the first event on the notification queue */
|
/* return, but do not dequeue the first event on the notification queue */
|
||||||
|
|
Loading…
Reference in a new issue