2019-05-19 16:51:48 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2009-05-22 01:01:20 +04:00
/*
* Copyright ( C ) 2008 Red Hat , Inc . , Eric Paris < eparis @ redhat . com >
*/
2009-05-22 01:01:37 +04:00
/*
* Basic idea behind the notification queue : An fsnotify group ( like inotify )
2012-09-20 05:48:02 +04:00
* sends the userspace notification about events asynchronously some time after
2009-05-22 01:01:37 +04:00
* 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 .
*/
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>
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2009-05-22 01:01:20 +04:00
# include <linux/fsnotify_backend.h>
# include "fsnotify.h"
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
2014-01-22 03:48:14 +04:00
void fsnotify_destroy_event ( struct fsnotify_group * group ,
struct fsnotify_event * event )
2009-05-22 01:01:20 +04:00
{
2014-01-22 03:48:14 +04:00
/* Overflow events are per-group and we don't want to free them */
2019-01-10 20:04:31 +03:00
if ( ! event | | event = = group - > overflow_event )
2009-05-22 01:01:20 +04:00
return ;
2016-10-08 02:56:58 +03:00
/*
* If the event is still queued , we have a problem . . . Do an unreliable
* lockless check first to avoid locking in the common case . The
* locking may be necessary for permission events which got removed
* from the list by a different CPU than the one freeing the event .
*/
if ( ! list_empty ( & event - > list ) ) {
spin_lock ( & group - > notification_lock ) ;
WARN_ON ( ! list_empty ( & event - > list ) ) ;
spin_unlock ( & group - > notification_lock ) ;
}
2021-10-25 22:27:27 +03:00
group - > ops - > free_event ( group , event ) ;
2009-05-22 01:01:50 +04:00
}
2009-05-22 01:01:20 +04:00
/*
2021-03-04 13:48:25 +03:00
* Try to add an event to the notification queue .
* The group can later pull this event off the queue to deal with .
* The group can use the @ merge hook to merge the event with a queued event .
* The group can use the @ insert hook to insert the event into hash table .
* The function returns :
* 0 if the event was added to a queue
* 1 if the event was merged with some other queued event
2016-09-20 00:44:27 +03:00
* 2 if the event was not queued - either the queue of events has overflown
2021-03-04 13:48:25 +03:00
* or the group is shutting down .
2009-05-22 01:01:20 +04:00
*/
2021-10-25 22:27:24 +03:00
int fsnotify_insert_event ( struct fsnotify_group * group ,
struct fsnotify_event * event ,
int ( * merge ) ( struct fsnotify_group * ,
struct fsnotify_event * ) ,
void ( * insert ) ( struct fsnotify_group * ,
struct fsnotify_event * ) )
2009-05-22 01:01:37 +04:00
{
2014-01-28 21:53:22 +04:00
int ret = 0 ;
2009-05-22 01:01:37 +04:00
struct list_head * list = & group - > notification_list ;
2009-05-22 01:01:50 +04:00
2014-01-22 03:48:14 +04:00
pr_debug ( " %s: group=%p event=%p \n " , __func__ , group , event ) ;
2009-05-22 01:01:37 +04:00
2016-10-08 02:56:52 +03:00
spin_lock ( & group - > notification_lock ) ;
2009-05-22 01:01:37 +04:00
2016-09-20 00:44:27 +03:00
if ( group - > shutdown ) {
2016-10-08 02:56:52 +03:00
spin_unlock ( & group - > notification_lock ) ;
2016-09-20 00:44:27 +03:00
return 2 ;
}
2018-02-21 17:07:52 +03:00
if ( event = = group - > overflow_event | |
group - > q_len > = group - > max_events ) {
2014-02-21 22:07:54 +04:00
ret = 2 ;
2014-01-22 03:48:14 +04:00
/* Queue overflow event only if it isn't already queued */
2014-02-21 22:14:11 +04:00
if ( ! list_empty ( & group - > overflow_event - > list ) ) {
2016-10-08 02:56:52 +03:00
spin_unlock ( & group - > notification_lock ) ;
2014-02-21 22:07:54 +04:00
return ret ;
}
2014-02-21 22:14:11 +04:00
event = group - > overflow_event ;
2014-02-21 22:07:54 +04:00
goto queue ;
2009-05-22 01:01:50 +04:00
}
2009-05-22 01:01:37 +04:00
2009-12-18 05:24:21 +03:00
if ( ! list_empty ( list ) & & merge ) {
2021-03-04 13:48:25 +03:00
ret = merge ( group , event ) ;
2014-01-28 21:53:22 +04:00
if ( ret ) {
2016-10-08 02:56:52 +03:00
spin_unlock ( & group - > notification_lock ) ;
2014-01-28 21:53:22 +04:00
return ret ;
2010-07-28 18:18:37 +04:00
}
2009-05-22 01:01:37 +04:00
}
2014-02-21 22:07:54 +04:00
queue :
2009-05-22 01:01:37 +04:00
group - > q_len + + ;
2014-01-22 03:48:14 +04:00
list_add_tail ( & event - > list , list ) ;
2021-03-04 13:48:25 +03:00
if ( insert )
insert ( group , event ) ;
2016-10-08 02:56:52 +03:00
spin_unlock ( & group - > notification_lock ) ;
2009-05-22 01:01:37 +04:00
wake_up ( & group - > notification_waitq ) ;
2011-10-15 01:43:39 +04:00
kill_fasync ( & group - > fsn_fa , SIGIO , POLL_IN ) ;
2014-01-28 21:53:22 +04:00
return ret ;
2009-05-22 01:01:37 +04:00
}
2019-01-09 15:15:23 +03:00
void fsnotify_remove_queued_event ( struct fsnotify_group * group ,
struct fsnotify_event * event )
{
assert_spin_locked ( & group - > notification_lock ) ;
/*
* We need to init list head for the case of overflow event so that
* check in fsnotify_add_event ( ) works
*/
list_del_init ( & event - > list ) ;
group - > q_len - - ;
}
2009-05-22 01:01:37 +04:00
/*
2021-03-04 13:48:22 +03:00
* Return the first event on the notification list without removing it .
* Returns NULL if the list is empty .
2009-05-22 01:01:37 +04:00
*/
2021-03-04 13:48:22 +03:00
struct fsnotify_event * fsnotify_peek_first_event ( struct fsnotify_group * group )
2009-05-22 01:01:20 +04:00
{
2016-10-08 02:57:01 +03:00
assert_spin_locked ( & group - > notification_lock ) ;
2009-05-22 01:01:20 +04:00
2021-03-04 13:48:22 +03:00
if ( fsnotify_notify_queue_is_empty ( group ) )
return NULL ;
2010-07-28 18:18:37 +04:00
2021-03-04 13:48:22 +03:00
return list_first_entry ( & group - > notification_list ,
struct fsnotify_event , list ) ;
2009-05-22 01:01:37 +04:00
}
/*
2021-03-04 13:48:22 +03:00
* Remove and return the first event from the notification list . It is the
* responsibility of the caller to destroy the obtained event
2009-05-22 01:01:37 +04:00
*/
2021-03-04 13:48:22 +03:00
struct fsnotify_event * fsnotify_remove_first_event ( struct fsnotify_group * group )
2009-05-22 01:01:37 +04:00
{
2021-03-04 13:48:22 +03:00
struct fsnotify_event * event = fsnotify_peek_first_event ( group ) ;
2009-05-22 01:01:37 +04:00
2021-03-04 13:48:22 +03:00
if ( ! event )
return NULL ;
pr_debug ( " %s: group=%p event=%p \n " , __func__ , group , event ) ;
fsnotify_remove_queued_event ( group , event ) ;
return event ;
2009-05-22 01:01:37 +04:00
}
/*
* 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 ;
2016-10-08 02:56:52 +03:00
spin_lock ( & group - > notification_lock ) ;
2009-05-22 01:01:37 +04:00
while ( ! fsnotify_notify_queue_is_empty ( group ) ) {
2014-08-07 03:03:26 +04:00
event = fsnotify_remove_first_event ( group ) ;
2016-10-08 02:56:52 +03:00
spin_unlock ( & group - > notification_lock ) ;
2014-01-22 03:48:14 +04:00
fsnotify_destroy_event ( group , event ) ;
2016-10-08 02:56:52 +03:00
spin_lock ( & group - > notification_lock ) ;
2009-05-22 01:01:37 +04:00
}
2016-10-08 02:56:52 +03:00
spin_unlock ( & group - > notification_lock ) ;
2009-05-22 01:01:37 +04:00
}