2009-05-22 01:01:20 +04:00
/*
* 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 0213 9 , USA .
*/
2009-05-22 01:01:37 +04:00
/*
* Basic idea behind the notification queue : An fsnotify group ( like inotify )
* sends the userspace notification about events asyncronously some time after
* the event happened . When inotify gets an event it will need to add that
* event to the group notify queue . Since a single event might need to be on
* multiple group ' s notification queues we can ' t add the event directly to each
* queue and instead add a small " event_holder " to each queue . This event_holder
* has a pointer back to the original event . Since the majority of events are
* going to end up on one , and only one , notification queue we embed one
* event_holder into each event . This means we have a single allocation instead
* of always needing two . If the embedded event_holder is already in use by
* another group a new event_holder ( from fsnotify_event_holder_cachep ) will be
* allocated and used .
*/
2010-07-28 18:18:37 +04:00
# include <linux/file.h>
2009-05-22 01:01:20 +04:00
# include <linux/fs.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/list.h>
2009-05-22 01:01:47 +04:00
# include <linux/module.h>
2009-05-22 01:01:20 +04:00
# include <linux/mount.h>
# include <linux/mutex.h>
# include <linux/namei.h>
# include <linux/path.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
# include <asm/atomic.h>
# include <linux/fsnotify_backend.h>
# include "fsnotify.h"
static struct kmem_cache * fsnotify_event_cachep ;
2009-05-22 01:01:37 +04:00
static struct kmem_cache * fsnotify_event_holder_cachep ;
/*
* This is a magic event we send when the q is too full . Since it doesn ' t
* hold real event information we just keep one system wide and use it any time
* it is needed . It ' s refcnt is set 1 at kernel init time and will never
* get set to 0 so it will never get ' freed '
*/
2009-12-18 04:12:06 +03:00
static struct fsnotify_event * q_overflow_event ;
2009-05-22 01:01:47 +04:00
static atomic_t fsnotify_sync_cookie = ATOMIC_INIT ( 0 ) ;
/**
* fsnotify_get_cookie - return a unique cookie for use in synchronizing events .
* Called from fsnotify_move , which is inlined into filesystem modules .
*/
u32 fsnotify_get_cookie ( void )
{
return atomic_inc_return ( & fsnotify_sync_cookie ) ;
}
EXPORT_SYMBOL_GPL ( fsnotify_get_cookie ) ;
2009-05-22 01:01:37 +04:00
/* return true if the notify queue is empty, false otherwise */
bool fsnotify_notify_queue_is_empty ( struct fsnotify_group * group )
{
BUG_ON ( ! mutex_is_locked ( & group - > notification_mutex ) ) ;
return list_empty ( & group - > notification_list ) ? true : false ;
}
2009-05-22 01:01:20 +04:00
void fsnotify_get_event ( struct fsnotify_event * event )
{
atomic_inc ( & event - > refcnt ) ;
}
void fsnotify_put_event ( struct fsnotify_event * event )
{
if ( ! event )
return ;
if ( atomic_dec_and_test ( & event - > refcnt ) ) {
2010-07-28 18:18:37 +04:00
pr_debug ( " %s: event=%p \n " , __func__ , event ) ;
2010-07-28 18:18:37 +04:00
if ( event - > data_type = = FSNOTIFY_EVENT_FILE )
fput ( event - > file ) ;
2009-05-22 01:01:20 +04:00
2009-05-22 01:01:50 +04:00
BUG_ON ( ! list_empty ( & event - > private_data_list ) ) ;
2009-05-22 01:01:43 +04:00
kfree ( event - > file_name ) ;
2009-12-18 05:24:27 +03:00
put_pid ( event - > tgid ) ;
2009-05-22 01:01:20 +04:00
kmem_cache_free ( fsnotify_event_cachep , event ) ;
}
}
2009-05-22 01:01:37 +04:00
struct fsnotify_event_holder * fsnotify_alloc_event_holder ( void )
{
return kmem_cache_alloc ( fsnotify_event_holder_cachep , GFP_KERNEL ) ;
}
void fsnotify_destroy_event_holder ( struct fsnotify_event_holder * holder )
{
2009-12-18 05:24:21 +03:00
if ( holder )
kmem_cache_free ( fsnotify_event_holder_cachep , holder ) ;
2009-05-22 01:01:37 +04:00
}
/*
2009-05-22 01:01:50 +04:00
* 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 ;
}
2009-05-22 01:01:20 +04:00
/*
2009-05-22 01:01:37 +04:00
* 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
* group ' s notification queue , a reference is taken on event .
2009-05-22 01:01:20 +04:00
*/
2010-07-28 18:18:37 +04:00
struct fsnotify_event * fsnotify_add_notify_event ( struct fsnotify_group * group , struct fsnotify_event * event ,
struct fsnotify_event_private_data * priv ,
struct fsnotify_event * ( * merge ) ( struct list_head * ,
struct fsnotify_event * ) )
2009-05-22 01:01:37 +04:00
{
2010-07-28 18:18:37 +04:00
struct fsnotify_event * return_event = NULL ;
2009-05-22 01:01:37 +04:00
struct fsnotify_event_holder * holder = NULL ;
struct list_head * list = & group - > notification_list ;
2009-05-22 01:01:50 +04:00
2010-07-28 18:18:37 +04:00
pr_debug ( " %s: group=%p event=%p priv=%p \n " , __func__ , group , event , priv ) ;
2009-05-22 01:01:37 +04:00
/*
* 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
* holder .
* For the overflow event it ' s possible that something will use the in
* event holder before we get the lock so we may need to jump back and
* alloc a new holder , this can ' t happen for most events . . .
*/
if ( ! list_empty ( & event - > holder . event_list ) ) {
alloc_holder :
holder = fsnotify_alloc_event_holder ( ) ;
if ( ! holder )
2010-07-28 18:18:37 +04:00
return ERR_PTR ( - ENOMEM ) ;
2009-05-22 01:01:37 +04:00
}
mutex_lock ( & group - > notification_mutex ) ;
2009-05-22 01:01:50 +04:00
if ( group - > q_len > = group - > max_events ) {
2009-12-18 04:12:06 +03:00
event = q_overflow_event ;
2010-07-28 18:18:37 +04:00
/*
* we need to return the overflow event
* which means we need a ref
*/
fsnotify_get_event ( event ) ;
return_event = event ;
2009-05-22 01:01:50 +04:00
/* sorry, no private data on the overflow event */
priv = NULL ;
}
2009-05-22 01:01:37 +04:00
2009-12-18 05:24:21 +03:00
if ( ! list_empty ( list ) & & merge ) {
2010-07-28 18:18:37 +04:00
struct fsnotify_event * tmp ;
2009-12-18 05:24:21 +03:00
2010-07-28 18:18:37 +04:00
tmp = merge ( list , event ) ;
if ( tmp ) {
2009-12-18 05:24:21 +03:00
mutex_unlock ( & group - > notification_mutex ) ;
2010-07-28 18:18:37 +04:00
if ( return_event )
fsnotify_put_event ( return_event ) ;
2009-12-18 05:24:21 +03:00
if ( holder ! = & event - > holder )
fsnotify_destroy_event_holder ( holder ) ;
2010-07-28 18:18:37 +04:00
return tmp ;
2009-12-18 05:24:21 +03:00
}
}
2009-05-22 01:01:37 +04:00
spin_lock ( & event - > lock ) ;
if ( list_empty ( & event - > holder . event_list ) ) {
if ( unlikely ( holder ) )
fsnotify_destroy_event_holder ( holder ) ;
holder = & event - > holder ;
} else if ( unlikely ( ! holder ) ) {
/* between the time we checked above and got the lock the in
* event holder was used , go back and get a new one */
spin_unlock ( & event - > lock ) ;
mutex_unlock ( & group - > notification_mutex ) ;
2010-07-28 18:18:37 +04:00
if ( return_event ) {
fsnotify_put_event ( return_event ) ;
return_event = NULL ;
}
2009-05-22 01:01:37 +04:00
goto alloc_holder ;
}
group - > q_len + + ;
holder - > event = event ;
fsnotify_get_event ( event ) ;
list_add_tail ( & holder - > event_list , list ) ;
2009-05-22 01:01:50 +04:00
if ( priv )
list_add_tail ( & priv - > event_list , & event - > private_data_list ) ;
2009-05-22 01:01:37 +04:00
spin_unlock ( & event - > lock ) ;
mutex_unlock ( & group - > notification_mutex ) ;
wake_up ( & group - > notification_waitq ) ;
2010-07-28 18:18:37 +04:00
return return_event ;
2009-05-22 01:01:37 +04:00
}
/*
* Remove and return the first event from the notification list . There is a
* reference held on this event since it was on the list . It is the responsibility
* of the caller to drop this reference .
*/
struct fsnotify_event * fsnotify_remove_notify_event ( struct fsnotify_group * group )
2009-05-22 01:01:20 +04:00
{
struct fsnotify_event * event ;
2009-05-22 01:01:37 +04:00
struct fsnotify_event_holder * holder ;
2009-05-22 01:01:20 +04:00
2009-05-22 01:01:37 +04:00
BUG_ON ( ! mutex_is_locked ( & group - > notification_mutex ) ) ;
2009-05-22 01:01:20 +04:00
2010-07-28 18:18:37 +04:00
pr_debug ( " %s: group=%p \n " , __func__ , group ) ;
2009-05-22 01:01:37 +04:00
holder = list_first_entry ( & group - > notification_list , struct fsnotify_event_holder , event_list ) ;
event = holder - > event ;
spin_lock ( & event - > lock ) ;
holder - > event = NULL ;
list_del_init ( & holder - > event_list ) ;
spin_unlock ( & event - > lock ) ;
/* event == holder means we are referenced through the in event holder */
if ( holder ! = & event - > holder )
fsnotify_destroy_event_holder ( holder ) ;
group - > q_len - - ;
return event ;
}
/*
* This will not remove the event , that must be done with fsnotify_remove_notify_event ( )
*/
struct fsnotify_event * fsnotify_peek_notify_event ( struct fsnotify_group * group )
{
struct fsnotify_event * event ;
struct fsnotify_event_holder * holder ;
BUG_ON ( ! mutex_is_locked ( & group - > notification_mutex ) ) ;
holder = list_first_entry ( & group - > notification_list , struct fsnotify_event_holder , event_list ) ;
event = holder - > event ;
return event ;
}
/*
* Called when a group is being torn down to clean up any outstanding
* event notifications .
*/
void fsnotify_flush_notify ( struct fsnotify_group * group )
{
struct fsnotify_event * event ;
2009-05-22 01:01:50 +04:00
struct fsnotify_event_private_data * priv ;
2009-05-22 01:01:37 +04:00
mutex_lock ( & group - > notification_mutex ) ;
while ( ! fsnotify_notify_queue_is_empty ( group ) ) {
event = fsnotify_remove_notify_event ( group ) ;
2009-05-22 01:01:50 +04:00
/* 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 ) ;
}
2009-05-22 01:01:37 +04:00
fsnotify_put_event ( event ) ; /* matches fsnotify_add_notify_event */
}
mutex_unlock ( & group - > notification_mutex ) ;
}
static void initialize_event ( struct fsnotify_event * event )
{
INIT_LIST_HEAD ( & event - > holder . event_list ) ;
2009-05-22 01:01:20 +04:00
atomic_set ( & event - > refcnt , 1 ) ;
spin_lock_init ( & event - > lock ) ;
2009-05-22 01:01:50 +04:00
INIT_LIST_HEAD ( & event - > private_data_list ) ;
2009-05-22 01:01:37 +04:00
}
2009-12-18 05:24:22 +03:00
/*
* Caller damn well better be holding whatever mutex is protecting the
2009-12-18 05:24:22 +03:00
* old_holder - > event_list and the new_event must be a clean event which
* cannot be found anywhere else in the kernel .
2009-12-18 05:24:22 +03:00
*/
int fsnotify_replace_event ( struct fsnotify_event_holder * old_holder ,
struct fsnotify_event * new_event )
{
struct fsnotify_event * old_event = old_holder - > event ;
2009-12-18 05:24:22 +03:00
struct fsnotify_event_holder * new_holder = & new_event - > holder ;
enum event_spinlock_class {
SPINLOCK_OLD ,
SPINLOCK_NEW ,
} ;
2009-12-18 05:24:22 +03:00
2010-07-28 18:18:37 +04:00
pr_debug ( " %s: old_event=%p new_event=%p \n " , __func__ , old_event , new_event ) ;
2009-12-18 05:24:22 +03:00
/*
2009-12-18 05:24:22 +03:00
* if the new_event ' s embedded holder is in use someone
* screwed up and didn ' t give us a clean new event .
2009-12-18 05:24:22 +03:00
*/
2009-12-18 05:24:22 +03:00
BUG_ON ( ! list_empty ( & new_holder - > event_list ) ) ;
2009-12-18 05:24:22 +03:00
2009-12-18 05:24:22 +03:00
spin_lock_nested ( & old_event - > lock , SPINLOCK_OLD ) ;
spin_lock_nested ( & new_event - > lock , SPINLOCK_NEW ) ;
2009-12-18 05:24:22 +03:00
new_holder - > event = new_event ;
list_replace_init ( & old_holder - > event_list , & new_holder - > event_list ) ;
spin_unlock ( & new_event - > lock ) ;
spin_unlock ( & old_event - > lock ) ;
/* event == holder means we are referenced through the in event holder */
if ( old_holder ! = & old_event - > holder )
fsnotify_destroy_event_holder ( old_holder ) ;
fsnotify_get_event ( new_event ) ; /* on the list take reference */
fsnotify_put_event ( old_event ) ; /* off the list, drop reference */
return 0 ;
}
2009-12-18 05:24:21 +03:00
struct fsnotify_event * fsnotify_clone_event ( struct fsnotify_event * old_event )
{
struct fsnotify_event * event ;
event = kmem_cache_alloc ( fsnotify_event_cachep , GFP_KERNEL ) ;
if ( ! event )
return NULL ;
2010-07-28 18:18:37 +04:00
pr_debug ( " %s: old_event=%p new_event=%p \n " , __func__ , old_event , event ) ;
2009-12-18 05:24:21 +03:00
memcpy ( event , old_event , sizeof ( * event ) ) ;
initialize_event ( event ) ;
if ( event - > name_len ) {
event - > file_name = kstrdup ( old_event - > file_name , GFP_KERNEL ) ;
if ( ! event - > file_name ) {
kmem_cache_free ( fsnotify_event_cachep , event ) ;
return NULL ;
}
}
2009-12-18 05:24:27 +03:00
event - > tgid = get_pid ( old_event - > tgid ) ;
2010-07-28 18:18:37 +04:00
if ( event - > data_type = = FSNOTIFY_EVENT_FILE )
get_file ( event - > file ) ;
2009-12-18 05:24:21 +03:00
return event ;
}
2009-05-22 01:01:37 +04:00
/*
* fsnotify_create_event - Allocate a new event which will be sent to each
* group ' s handle_event function if the group was interested in this
* particular event .
*
* @ to_tell the inode which is supposed to receive the event ( sometimes a
* parent of the inode to which the event happened .
* @ mask what actually happened .
* @ data pointer to the object which was actually affected
* @ data_type flag indication if the data is a file , path , inode , nothing . . .
2009-05-22 01:01:43 +04:00
* @ name the filename , if available
2009-05-22 01:01:37 +04:00
*/
2009-05-22 01:01:47 +04:00
struct fsnotify_event * fsnotify_create_event ( struct inode * to_tell , __u32 mask , void * data ,
2010-02-08 20:53:52 +03:00
int data_type , const unsigned char * name ,
u32 cookie , gfp_t gfp )
2009-05-22 01:01:37 +04:00
{
struct fsnotify_event * event ;
2009-12-18 04:12:07 +03:00
event = kmem_cache_zalloc ( fsnotify_event_cachep , gfp ) ;
2009-05-22 01:01:37 +04:00
if ( ! event )
return NULL ;
2010-07-28 18:18:37 +04:00
pr_debug ( " %s: event=%p to_tell=%p mask=%x data=%p data_type=%d \n " ,
__func__ , event , to_tell , mask , data , data_type ) ;
2009-05-22 01:01:37 +04:00
initialize_event ( event ) ;
2009-05-22 01:01:43 +04:00
if ( name ) {
2009-07-15 23:49:52 +04:00
event - > file_name = kstrdup ( name , gfp ) ;
2009-05-22 01:01:43 +04:00
if ( ! event - > file_name ) {
kmem_cache_free ( fsnotify_event_cachep , event ) ;
return NULL ;
}
event - > name_len = strlen ( event - > file_name ) ;
}
2009-05-22 01:01:47 +04:00
2009-12-18 05:24:27 +03:00
event - > tgid = get_pid ( task_tgid ( current ) ) ;
2009-05-22 01:01:47 +04:00
event - > sync_cookie = cookie ;
2009-05-22 01:01:20 +04:00
event - > to_tell = to_tell ;
2009-12-18 05:24:21 +03:00
event - > data_type = data_type ;
2009-05-22 01:01:20 +04:00
switch ( data_type ) {
2010-07-28 18:18:37 +04:00
case FSNOTIFY_EVENT_FILE : {
event - > file = data ;
2010-07-28 18:18:38 +04:00
/*
* if this file is about to disappear hold an extra reference
* until we return to __fput so we don ' t have to worry about
* future get / put destroying the file under us or generating
* additional events . Notice that we change f_mode without
* holding f_lock . This is safe since this is the only possible
* reference to this object in the kernel ( it was about to be
* freed , remember ? )
*/
if ( ! atomic_long_read ( & event - > file - > f_count ) ) {
event - > file - > f_mode | = FMODE_NONOTIFY ;
get_file ( event - > file ) ;
}
2010-07-28 18:18:37 +04:00
get_file ( event - > file ) ;
2009-05-22 01:01:20 +04:00
break ;
}
case FSNOTIFY_EVENT_INODE :
event - > inode = data ;
break ;
case FSNOTIFY_EVENT_NONE :
event - > inode = NULL ;
2010-07-28 18:18:37 +04:00
event - > file = NULL ;
2009-05-22 01:01:20 +04:00
break ;
default :
BUG ( ) ;
}
event - > mask = mask ;
return event ;
}
__init int fsnotify_notification_init ( void )
{
fsnotify_event_cachep = KMEM_CACHE ( fsnotify_event , SLAB_PANIC ) ;
2009-05-22 01:01:37 +04:00
fsnotify_event_holder_cachep = KMEM_CACHE ( fsnotify_event_holder , SLAB_PANIC ) ;
2009-12-18 04:12:06 +03:00
q_overflow_event = fsnotify_create_event ( NULL , FS_Q_OVERFLOW , NULL ,
FSNOTIFY_EVENT_NONE , NULL , 0 ,
GFP_KERNEL ) ;
if ( ! q_overflow_event )
panic ( " unable to allocate fsnotify q_overflow_event \n " ) ;
2009-05-22 01:01:20 +04:00
return 0 ;
}
subsys_initcall ( fsnotify_notification_init ) ;