]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
smb: client: account smb directory cache usage and per-tcon totals
authorBharath SM <bharathsm@microsoft.com>
Mon, 29 Sep 2025 20:57:46 +0000 (15:57 -0500)
committerSteve French <stfrench@microsoft.com>
Thu, 2 Oct 2025 02:49:53 +0000 (21:49 -0500)
Add lightweight accounting for directory lease cache usage
to aid debugging and limiting cache size in future. Track
per-directory entry/byte counts and maintain per-tcon
aggregates. Also expose the totals in /proc/fs/cifs/open_dirs.

Signed-off-by: Bharath SM <bharathsm@microsoft.com>
Signed-off-by: Steve French <stfrench@microsoft.com>
fs/smb/client/cached_dir.c
fs/smb/client/cached_dir.h
fs/smb/client/cifs_debug.c
fs/smb/client/cifsfs.c
fs/smb/client/readdir.c

index b69daeb1301b3c801c7c122d138f622882da40a1..b6f538a1d5af8a59926f2ad0345f7a6a0a521da6 100644 (file)
@@ -697,6 +697,21 @@ static void free_cached_dir(struct cached_fid *cfid)
                kfree(dirent);
        }
 
+       /* adjust tcon-level counters and reset per-dir accounting */
+       if (cfid->cfids) {
+               if (cfid->dirents.entries_count)
+                       atomic_long_sub((long)cfid->dirents.entries_count,
+                                       &cfid->cfids->total_dirents_entries);
+               if (cfid->dirents.bytes_used) {
+                       atomic64_sub((long long)cfid->dirents.bytes_used,
+                                       &cfid->cfids->total_dirents_bytes);
+                       atomic64_sub((long long)cfid->dirents.bytes_used,
+                                       &cifs_dircache_bytes_used);
+               }
+       }
+       cfid->dirents.entries_count = 0;
+       cfid->dirents.bytes_used = 0;
+
        kfree(cfid->path);
        cfid->path = NULL;
        kfree(cfid);
@@ -792,6 +807,9 @@ struct cached_fids *init_cached_dirs(void)
        queue_delayed_work(cfid_put_wq, &cfids->laundromat_work,
                           dir_cache_timeout * HZ);
 
+       atomic_long_set(&cfids->total_dirents_entries, 0);
+       atomic64_set(&cfids->total_dirents_bytes, 0);
+
        return cfids;
 }
 
index 46b5a2fdf15b5ff1ed3a36e5857b78a3d36fc43e..c98f02943311517e5e46c5afbcd7f131d19fd3d5 100644 (file)
@@ -27,6 +27,9 @@ struct cached_dirents {
        struct mutex de_mutex;
        loff_t pos;              /* Expected ctx->pos */
        struct list_head entries;
+       /* accounting for cached entries in this directory */
+       unsigned long entries_count;
+       unsigned long bytes_used;
 };
 
 struct cached_fid {
@@ -62,8 +65,14 @@ struct cached_fids {
        struct list_head dying;
        struct work_struct invalidation_work;
        struct delayed_work laundromat_work;
+       /* aggregate accounting for all cached dirents under this tcon */
+       atomic_long_t total_dirents_entries;
+       atomic64_t total_dirents_bytes;
 };
 
+/* Module-wide directory cache accounting (defined in cifsfs.c) */
+extern atomic64_t cifs_dircache_bytes_used; /* bytes across all mounts */
+
 extern struct cached_fids *init_cached_dirs(void);
 extern void free_cached_dirs(struct cached_fids *cfids);
 extern int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
index 75f5f60f0bb8768314f287fdd548efc931b5117e..1fb71d2d31b5deff373b6025acc86c85915f7881 100644 (file)
@@ -331,7 +331,10 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
                                if (!cfids)
                                        continue;
                                spin_lock(&cfids->cfid_list_lock); /* check lock ordering */
-                               seq_printf(m, "Num entries: %d\n", cfids->num_entries);
+                               seq_printf(m, "Num entries: %d, cached_dirents: %lu entries, %llu bytes\n",
+                                               cfids->num_entries,
+                                               (unsigned long)atomic_long_read(&cfids->total_dirents_entries),
+                                               (unsigned long long)atomic64_read(&cfids->total_dirents_bytes));
                                list_for_each_entry(cfid, &cfids->entries, entry) {
                                        seq_printf(m, "0x%x 0x%llx 0x%llx     %s",
                                                tcon->tid,
@@ -342,6 +345,9 @@ static int cifs_debug_dirs_proc_show(struct seq_file *m, void *v)
                                                seq_printf(m, "\tvalid file info");
                                        if (cfid->dirents.is_valid)
                                                seq_printf(m, ", valid dirents");
+                                       if (!list_empty(&cfid->dirents.entries))
+                                               seq_printf(m, ", dirents: %lu entries, %lu bytes",
+                                               cfid->dirents.entries_count, cfid->dirents.bytes_used);
                                        seq_printf(m, "\n");
                                }
                                spin_unlock(&cfids->cfid_list_lock);
index 9a83f528d39d1e9b4d1e3e5e95b72a4180aa98b7..1775c2b7528f53c6f52d4e0db89ad9ccb25815a4 100644 (file)
@@ -121,6 +121,8 @@ unsigned int dir_cache_timeout = 30;
 module_param(dir_cache_timeout, uint, 0644);
 MODULE_PARM_DESC(dir_cache_timeout, "Number of seconds to cache directory contents for which we have a lease. Default: 30 "
                                 "Range: 1 to 65000 seconds, 0 to disable caching dir contents");
+/* Module-wide total cached dirents (in bytes) across all tcons */
+atomic64_t cifs_dircache_bytes_used = ATOMIC64_INIT(0);
 
 /*
  * Write-only module parameter to drop all cached directory entries across
index 4e5460206397440ddc9f5a8edd2739d28ab94790..f0ce26622a140b9a06e87ce4173e74f96a4a0492 100644 (file)
@@ -874,39 +874,42 @@ static void finished_cached_dirents_count(struct cached_dirents *cde,
        cde->is_valid = 1;
 }
 
-static void add_cached_dirent(struct cached_dirents *cde,
-                             struct dir_context *ctx,
-                             const char *name, int namelen,
-                             struct cifs_fattr *fattr,
-                                 struct file *file)
+static bool add_cached_dirent(struct cached_dirents *cde,
+                             struct dir_context *ctx, const char *name,
+                             int namelen, struct cifs_fattr *fattr,
+                             struct file *file)
 {
        struct cached_dirent *de;
 
        if (cde->file != file)
-               return;
+               return false;
        if (cde->is_valid || cde->is_failed)
-               return;
+               return false;
        if (ctx->pos != cde->pos) {
                cde->is_failed = 1;
-               return;
+               return false;
        }
        de = kzalloc(sizeof(*de), GFP_ATOMIC);
        if (de == NULL) {
                cde->is_failed = 1;
-               return;
+               return false;
        }
        de->namelen = namelen;
        de->name = kstrndup(name, namelen, GFP_ATOMIC);
        if (de->name == NULL) {
                kfree(de);
                cde->is_failed = 1;
-               return;
+               return false;
        }
        de->pos = ctx->pos;
 
        memcpy(&de->fattr, fattr, sizeof(struct cifs_fattr));
 
        list_add_tail(&de->entry, &cde->entries);
+       /* update accounting */
+       cde->entries_count++;
+       cde->bytes_used += sizeof(*de) + (size_t)namelen + 1;
+       return true;
 }
 
 static bool cifs_dir_emit(struct dir_context *ctx,
@@ -915,7 +918,8 @@ static bool cifs_dir_emit(struct dir_context *ctx,
                          struct cached_fid *cfid,
                          struct file *file)
 {
-       bool rc;
+       size_t delta_bytes = 0;
+       bool rc, added = false;
        ino_t ino = cifs_uniqueid_to_ino_t(fattr->cf_uniqueid);
 
        rc = dir_emit(ctx, name, namelen, ino, fattr->cf_dtype);
@@ -923,10 +927,20 @@ static bool cifs_dir_emit(struct dir_context *ctx,
                return rc;
 
        if (cfid) {
+               /* Cost of this entry */
+               delta_bytes = sizeof(struct cached_dirent) + (size_t)namelen + 1;
+
                mutex_lock(&cfid->dirents.de_mutex);
-               add_cached_dirent(&cfid->dirents, ctx, name, namelen,
-                                 fattr, file);
+               added = add_cached_dirent(&cfid->dirents, ctx, name, namelen,
+                                         fattr, file);
                mutex_unlock(&cfid->dirents.de_mutex);
+
+               if (added) {
+                       /* per-tcon then global for consistency with free path */
+                       atomic64_add((long long)delta_bytes, &cfid->cfids->total_dirents_bytes);
+                       atomic_long_inc(&cfid->cfids->total_dirents_entries);
+                       atomic64_add((long long)delta_bytes, &cifs_dircache_bytes_used);
+               }
        }
 
        return rc;