mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-22 07:27:12 +08:00
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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user