This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 50c25dee279f6f48b853bab1002794be7c3e8c6c
Author: ligd <[email protected]>
AuthorDate: Wed Nov 11 19:37:18 2026 +0800

    fs/inotify: count watches per event class
    
    Only IN_ACCESS and IN_MODIFY watches are counted, so every open,
    close, create, delete and rename runs lib_realpath() on its path
    even when no watch exists anywhere.  On a busy file system this
    work is pure overhead, and it runs with g_inotify.lock held.
    
    Keep one counter per IN_* bit and let notify_check_mask() report
    whether any watch can receive the event.  notify_queue_path_event()
    now returns before the realpath when none can.  IN_MOVED_FROM also
    reaches IN_MOVE_SELF watches, and IN_DELETE frees the watch list of
    the deleted path, so both are checked against the watches they can
    affect.  The rename cookie is still advanced on an early return, so
    an IN_MOVED_TO watch keeps seeing a new cookie per rename.
    
    This also fixes the count update when inotify_add_watch() replaces
    the mask of an existing watch.  It added tmpmask ^ newmask, so a
    class dropped from the mask was counted up instead of down and its
    counter never returned to zero.  Count the new mask in and the old
    mask out instead.
    
    Signed-off-by: ligd <[email protected]>
    Signed-off-by: jingfei <[email protected]>
---
 fs/vfs/fs_inotify.c | 94 +++++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 73 insertions(+), 21 deletions(-)

diff --git a/fs/vfs/fs_inotify.c b/fs/vfs/fs_inotify.c
index 5724ce0e882..2cfc3364b87 100644
--- a/fs/vfs/fs_inotify.c
+++ b/fs/vfs/fs_inotify.c
@@ -46,6 +46,11 @@
 #include "sched/sched.h"
 #include "fs_heap.h"
 
+/* Event classes a watch can register for, IN_UNMOUNT being the highest */
+
+#define INOTIFY_CLASSES (IN_ALL_EVENTS | IN_UNMOUNT)
+#define INOTIFY_NCLASS  14
+
 /****************************************************************************
  * Private Types
  ****************************************************************************/
@@ -89,8 +94,10 @@ struct inotify_global_s
   mutex_t  lock;               /* Enforces global exclusive access */
   int      event_cookie;       /* Event cookie */
   int      watch_cookie;       /* Watch cookie */
-  uint32_t read_count;         /* Number of read events */
-  uint32_t write_count;        /* Number of write events */
+
+  /* Number of watches registered for each IN_* bit */
+
+  uint32_t count[INOTIFY_NCLASS];
   struct   hsearch_data hash;  /* Hash table for watch lists */
 };
 
@@ -155,8 +162,13 @@ static struct inotify_global_s g_inotify =
 
 static void inotify_add_count(uint32_t mask)
 {
-  g_inotify.read_count += (mask & IN_ACCESS) ? 1 : 0;
-  g_inotify.write_count += (mask & IN_MODIFY) ? 1 : 0;
+  int i;
+
+  mask &= INOTIFY_CLASSES;
+  for (i = 0; mask != 0; i++, mask >>= 1)
+    {
+      g_inotify.count[i] += mask & 1;
+    }
 }
 
 /****************************************************************************
@@ -169,23 +181,53 @@ static void inotify_add_count(uint32_t mask)
 
 static void inotify_sub_count(uint32_t mask)
 {
-  g_inotify.read_count -= (mask & IN_ACCESS) ? 1 : 0;
-  g_inotify.write_count -= (mask & IN_MODIFY) ? 1 : 0;
+  int i;
+
+  mask &= INOTIFY_CLASSES;
+  for (i = 0; mask != 0; i++, mask >>= 1)
+    {
+      g_inotify.count[i] -= mask & 1;
+    }
 }
 
 /****************************************************************************
  * Name: notify_check_mask
  *
  * Description:
- *   Check if the count is valid.
+ *   Return OK if some watch may receive an event of this mask, -EBADF if
+ *   none can.
  *
  ****************************************************************************/
 
 static int notify_check_mask(uint32_t mask)
 {
-  return (((mask & IN_ACCESS) && g_inotify.read_count == 0) ||
-          ((mask & IN_MODIFY) && g_inotify.write_count == 0)) ?
-          -EBADF : OK;
+  int i;
+
+  /* IN_MOVED_FROM also reaches IN_MOVE_SELF watches.  IN_DELETE frees
+   * the watch list of the deleted path whatever its watches asked for,
+   * so it matters as long as any watch exists.
+   */
+
+  if (mask & IN_MOVED_FROM)
+    {
+      mask |= IN_MOVE_SELF;
+    }
+
+  if (mask & IN_DELETE)
+    {
+      mask |= INOTIFY_CLASSES;
+    }
+
+  mask &= INOTIFY_CLASSES;
+  for (i = 0; mask != 0; i++, mask >>= 1)
+    {
+      if ((mask & 1) && g_inotify.count[i] != 0)
+        {
+          return OK;
+        }
+    }
+
+  return -EBADF;
 }
 
 /****************************************************************************
@@ -885,6 +927,25 @@ static void notify_queue_path_event(FAR const char *path, 
uint32_t mask)
   FAR char *pathbuffer;
   uint32_t cookie = 0;
 
+  /* Advance the cookie even if nobody watches IN_MOVED_FROM, so that the
+   * IN_MOVED_TO of each rename still carries its own cookie.
+   */
+
+  if (mask & IN_MOVE)
+    {
+      if (mask & IN_MOVED_FROM)
+        {
+          ++g_inotify.event_cookie;
+        }
+
+      cookie = g_inotify.event_cookie;
+    }
+
+  if (notify_check_mask(mask) < 0)
+    {
+      return;
+    }
+
   pathbuffer = lib_get_pathbuffer();
   if (pathbuffer == NULL)
     {
@@ -898,16 +959,6 @@ static void notify_queue_path_event(FAR const char *path, 
uint32_t mask)
       return;
     }
 
-  if (mask & IN_MOVE)
-    {
-      if (mask & IN_MOVED_FROM)
-        {
-          ++g_inotify.event_cookie;
-        }
-
-      cookie = g_inotify.event_cookie;
-    }
-
   list = inotify_get_watch_list(abspath);
   inotify_queue_parent_event(abspath, mask, cookie);
   lib_put_pathbuffer(pathbuffer);
@@ -1136,7 +1187,8 @@ int inotify_add_watch(int fd, FAR const char *pathname, 
uint32_t mask)
         }
 
       ret = old->wd;
-      inotify_add_count(tmpmask ^ old->mask);
+      inotify_add_count(old->mask);
+      inotify_sub_count(tmpmask);
     }
   else
     {

Reply via email to