smb: client: account smb directory cache usage and per-tcon totals

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>
This commit is contained in:
Bharath SM
2025-09-29 15:57:46 -05:00
committed by Steve French
parent dde6667fa3
commit 63eb8bd6c8
5 changed files with 63 additions and 14 deletions

View 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;
}

View 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,

View 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);

View 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

View 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;