* [RFC PATCH v2 03/11] dns_resolver: Clear .dns_resolver cache when the last client unloads
[not found] <20261103030226.2685251-1-jarkko@kernel.org>
@ 2026-11-03 3:02 ` Jarkko Sakkinen
2026-11-03 3:02 ` [RFC PATCH v2 05/11] smb: client: Clear idmap/SPNEGO caches when the last session is unloaded Jarkko Sakkinen
1 sibling, 0 replies; 2+ messages in thread
From: Jarkko Sakkinen @ 2026-11-03 3:02 UTC (permalink / raw)
To: David Howells
Cc: Eric Biggers, linux-integrity, Mimi Zohar, keyrings,
Jarkko Sakkinen, Marc Dionne, Trond Myklebust, Anna Schumaker,
Paulo Alcantara, Namjae Jeon, Ronnie Sahlberg, Shyam Prasad N,
Tom Talpey, Bharath SM, Ilya Dryomov, Alex Markuze, Xiubo Li,
Viacheslav Dubeyko, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Roberto Sassu, Dmitry Kasatkin,
Eric Snowberg, Paul Moore, James Morris, Serge E. Hallyn,
Thorsten Blum, Tim Bird, open list:AFS FILESYSTEM, open list,
open list:NFS, SUNRPC, AND LOCKD CLIENTS,
open list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
moderated list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
open list:CEPH COMMON CODE (LIBCEPH),
open list:NETWORKING [GENERAL], open list:SECURITY SUBSYSTEM
dns_query() caches resolved dns_resolver keys into the module-owned
.dns_resolver keyring until the TTL expires or module_exit() revokes the
keyring.
Once live keys pin their key type, cached entries keep dns_resolver.ko
pinned after its client modules unload.
Track active kernel client modules with dns_resolver_cache_get() and
dns_resolver_cache_put(), and do the necessary plumbing for:
1. nfsv4
2. cifs
3. libceph
4. kafs
When the last client unloads, clear .dns_resolver and flush key garbage
collection so dns_resolver.ko can unload immediately.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
fs/afs/main.c | 4 ++++
fs/nfs/dns_resolve.h | 7 ++++++-
fs/smb/client/cifsfs.c | 4 ++++
include/linux/dns_resolver.h | 13 ++++++++++---
net/ceph/ceph_common.c | 7 +++++++
net/dns_resolver/dns_key.c | 28 ++++++++++++++++++++++++++++
6 files changed, 59 insertions(+), 4 deletions(-)
diff --git a/fs/afs/main.c b/fs/afs/main.c
index 7a883c59976f..16ae947d371e 100644
--- a/fs/afs/main.c
+++ b/fs/afs/main.c
@@ -12,6 +12,7 @@
#include <linux/sched.h>
#include <linux/random.h>
#include <linux/proc_fs.h>
+#include <linux/dns_resolver.h>
#define CREATE_TRACE_POINTS
#include "internal.h"
@@ -183,6 +184,7 @@ static int __init afs_init(void)
if (ret < 0)
goto error_net;
+ dns_resolver_cache_get();
/* register the filesystems */
ret = afs_fs_init();
if (ret < 0)
@@ -199,6 +201,7 @@ static int __init afs_init(void)
error_proc:
afs_fs_exit();
error_fs:
+ dns_resolver_cache_put();
unregister_pernet_device(&afs_net_ops);
error_net:
destroy_workqueue(afs_lock_manager);
@@ -232,6 +235,7 @@ static void __exit afs_exit(void)
destroy_workqueue(afs_wq);
afs_clean_up_permit_cache();
rcu_barrier();
+ dns_resolver_cache_put();
}
module_exit(afs_exit);
diff --git a/fs/nfs/dns_resolve.h b/fs/nfs/dns_resolve.h
index fe3b172c4de1..4b9ecfb4f7fe 100644
--- a/fs/nfs/dns_resolve.h
+++ b/fs/nfs/dns_resolve.h
@@ -9,13 +9,18 @@
#ifdef CONFIG_NFS_USE_KERNEL_DNS
+#include <linux/dns_resolver.h>
+
static inline int nfs_dns_resolver_init(void)
{
+ dns_resolver_cache_get();
return 0;
}
static inline void nfs_dns_resolver_destroy(void)
-{}
+{
+ dns_resolver_cache_put();
+}
static inline int nfs_dns_resolver_cache_init(struct net *net)
{
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index bf199a5ca084..9788052599ce 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -31,6 +31,7 @@
#include <linux/xattr.h>
#include <linux/mm.h>
#include <linux/key-type.h>
+#include <linux/dns_resolver.h>
#include <linux/fileattr.h>
#include <uapi/linux/magic.h>
#include <net/ipv6.h>
@@ -2160,6 +2161,7 @@ init_cifs(void)
goto out_cifs_swn_init;
cifs_proc_init();
+ dns_resolver_cache_get();
rc = register_filesystem(&cifs_fs_type);
if (rc)
@@ -2174,6 +2176,7 @@ init_cifs(void)
return 0;
out_clean_proc:
+ dns_resolver_cache_put();
cifs_proc_clean();
exit_cifs_idmap();
out_cifs_swn_init:
@@ -2242,6 +2245,7 @@ exit_cifs(void)
destroy_workqueue(serverclose_wq);
destroy_workqueue(cfid_put_wq);
destroy_workqueue(cifsiod_wq);
+ dns_resolver_cache_put();
}
MODULE_AUTHOR("Steve French");
diff --git a/include/linux/dns_resolver.h b/include/linux/dns_resolver.h
index 976cbbdb2832..cc11e09b0df3 100644
--- a/include/linux/dns_resolver.h
+++ b/include/linux/dns_resolver.h
@@ -27,8 +27,15 @@
#include <uapi/linux/dns_resolver.h>
struct net;
-extern int dns_query(struct net *net, const char *type, const char *name, size_t namelen,
- const char *options, char **_result, time64_t *_expiry,
- bool invalidate);
+#if IS_ENABLED(CONFIG_DNS_RESOLVER)
+int dns_query(struct net *net, const char *type, const char *name, size_t namelen,
+ const char *options, char **_result, time64_t *_expiry,
+ bool invalidate);
+void dns_resolver_cache_get(void);
+void dns_resolver_cache_put(void);
+#else
+static inline void dns_resolver_cache_get(void) { }
+static inline void dns_resolver_cache_put(void) { }
+#endif
#endif /* _LINUX_DNS_RESOLVER_H */
diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c
index a797c7360e3c..df62a269cb73 100644
--- a/net/ceph/ceph_common.c
+++ b/net/ceph/ceph_common.c
@@ -8,6 +8,7 @@
#include <linux/in6.h>
#include <linux/key.h>
#include <keys/ceph-type.h>
+#include <linux/dns_resolver.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/nsproxy.h>
@@ -890,6 +891,9 @@ static int __init init_ceph_lib(void)
if (ret < 0)
goto out_msgr;
+#ifdef CONFIG_CEPH_LIB_USE_DNS_RESOLVER
+ dns_resolver_cache_get();
+#endif
pr_info("loaded (mon/osd proto %d/%d)\n",
CEPH_MONC_PROTOCOL, CEPH_OSDC_PROTOCOL);
@@ -913,6 +917,9 @@ static void __exit exit_ceph_lib(void)
ceph_msgr_exit();
ceph_crypto_shutdown();
ceph_debugfs_cleanup();
+#ifdef CONFIG_CEPH_LIB_USE_DNS_RESOLVER
+ dns_resolver_cache_put();
+#endif
}
module_init(init_ceph_lib);
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
index 451247864a63..63f4fac0dbc8 100644
--- a/net/dns_resolver/dns_key.c
+++ b/net/dns_resolver/dns_key.c
@@ -16,6 +16,7 @@
#include <linux/kernel.h>
#include <linux/keyctl.h>
#include <linux/err.h>
+#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/dns_resolver.h>
#include <keys/dns_resolver-type.h>
@@ -31,6 +32,8 @@ module_param_named(debug, dns_resolver_debug, uint, 0644);
MODULE_PARM_DESC(debug, "DNS Resolver debugging mask");
const struct cred *dns_resolver_cache;
+static DEFINE_MUTEX(dns_resolver_cache_mutex);
+static unsigned int dns_resolver_cache_users;
#define DNS_ERRORNO_OPTION "dnserror"
@@ -306,6 +309,31 @@ static long dns_resolver_read(const struct key *key,
return user_read(key, buffer, buflen);
}
+void dns_resolver_cache_get(void)
+{
+ mutex_lock(&dns_resolver_cache_mutex);
+ dns_resolver_cache_users++;
+ mutex_unlock(&dns_resolver_cache_mutex);
+}
+EXPORT_SYMBOL_GPL(dns_resolver_cache_get);
+
+void dns_resolver_cache_put(void)
+{
+ int ret;
+
+ mutex_lock(&dns_resolver_cache_mutex);
+ if (--dns_resolver_cache_users == 0 && dns_resolver_cache) {
+ ret = keyring_clear(dns_resolver_cache->thread_keyring);
+ if (ret)
+ pr_warn_ratelimited("dns_resolver: Failed to clear cache: %d\n",
+ ret);
+ else
+ key_gc_flush();
+ }
+ mutex_unlock(&dns_resolver_cache_mutex);
+}
+EXPORT_SYMBOL_GPL(dns_resolver_cache_put);
+
struct key_type key_type_dns_resolver = {
.name = "dns_resolver",
.flags = KEY_TYPE_NET_DOMAIN | KEY_TYPE_INSTANT_REAP,
--
2.47.3
^ permalink raw reply related [flat|nested] 2+ messages in thread* [RFC PATCH v2 05/11] smb: client: Clear idmap/SPNEGO caches when the last session is unloaded
[not found] <20261103030226.2685251-1-jarkko@kernel.org>
2026-11-03 3:02 ` [RFC PATCH v2 03/11] dns_resolver: Clear .dns_resolver cache when the last client unloads Jarkko Sakkinen
@ 2026-11-03 3:02 ` Jarkko Sakkinen
1 sibling, 0 replies; 2+ messages in thread
From: Jarkko Sakkinen @ 2026-11-03 3:02 UTC (permalink / raw)
To: David Howells
Cc: Eric Biggers, linux-integrity, Mimi Zohar, keyrings,
Jarkko Sakkinen, Paulo Alcantara, Namjae Jeon, Ronnie Sahlberg,
Shyam Prasad N, Tom Talpey, Bharath SM, Roberto Sassu,
Dmitry Kasatkin, Eric Snowberg, Paul Moore, James Morris,
Serge E. Hallyn,
open list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
moderated list:COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3),
open list, open list:SECURITY SUBSYSTEM
cifs caches cifs.idmap and cifs.spnego keys in module-owned thread
keyrings that are only destroyed in module_exit(). Once live keys pin
the key type, cached entries keep cifs pinned after the last SMB session
is torn down.
Serialize session count transitions with a mutex and clear the idmap and
SPNEGO cache keyrings when the last session is freed.
Signed-off-by: Jarkko Sakkinen <jarkko@kernel.org>
---
fs/smb/client/cifs_debug.c | 2 +-
fs/smb/client/cifs_spnego.c | 8 ++++++++
fs/smb/client/cifsacl.c | 8 ++++++++
fs/smb/client/cifsfs.c | 4 ++--
fs/smb/client/cifsglob.h | 2 +-
fs/smb/client/cifsproto.h | 8 ++++++++
fs/smb/client/misc.c | 19 +++++++++++++++++--
7 files changed, 45 insertions(+), 6 deletions(-)
diff --git a/fs/smb/client/cifs_debug.c b/fs/smb/client/cifs_debug.c
index 577f0ddede90..2eecd2516dfe 100644
--- a/fs/smb/client/cifs_debug.c
+++ b/fs/smb/client/cifs_debug.c
@@ -790,7 +790,7 @@ static int cifs_stats_proc_show(struct seq_file *m, void *v)
struct cifs_tcon *tcon;
seq_printf(m, "Resources in use\nCIFS Session: %d\n",
- sesInfoAllocCount.counter);
+ cifs_ses_alloc_count.counter);
seq_printf(m, "Share (unique mount targets): %d\n",
tconInfoAllocCount.counter);
seq_printf(m, "SMB Request/Response Buffer: %d Pool size: %d\n",
diff --git a/fs/smb/client/cifs_spnego.c b/fs/smb/client/cifs_spnego.c
index 44c407275680..3bd272c7d9fe 100644
--- a/fs/smb/client/cifs_spnego.c
+++ b/fs/smb/client/cifs_spnego.c
@@ -242,6 +242,14 @@ init_cifs_spnego(void)
return ret;
}
+void cifs_spnego_clear_cache(void)
+{
+ int ret;
+
+ ret = keyring_clear(spnego_cred->thread_keyring);
+ WARN_ON(ret);
+}
+
void
exit_cifs_spnego(void)
{
diff --git a/fs/smb/client/cifsacl.c b/fs/smb/client/cifsacl.c
index 346899da91eb..7504cedf6417 100644
--- a/fs/smb/client/cifsacl.c
+++ b/fs/smb/client/cifsacl.c
@@ -590,6 +590,14 @@ init_cifs_idmap(void)
return ret;
}
+void cifs_idmap_clear_cache(void)
+{
+ int ret;
+
+ ret = keyring_clear(root_cred->thread_keyring);
+ WARN_ON(ret);
+}
+
void
exit_cifs_idmap(void)
{
diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c
index 9788052599ce..f8a9a6f7dc49 100644
--- a/fs/smb/client/cifsfs.c
+++ b/fs/smb/client/cifsfs.c
@@ -84,7 +84,7 @@ DEFINE_SPINLOCK(GlobalMid_Lock); /* protects above & list operations on midQ ent
/*
* Global counters, updated atomically
*/
-atomic_t sesInfoAllocCount;
+atomic_t cifs_ses_alloc_count;
atomic_t tconInfoAllocCount;
atomic_t tcpSesNextId;
atomic_t tcpSesAllocCount;
@@ -2024,7 +2024,7 @@ init_cifs(void)
/*
* Initialize Global counters
*/
- atomic_set(&sesInfoAllocCount, 0);
+ atomic_set(&cifs_ses_alloc_count, 0);
atomic_set(&tconInfoAllocCount, 0);
atomic_set(&tcpSesNextId, 0);
atomic_set(&tcpSesAllocCount, 0);
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index d3ca688b6aba..c6e8d1340517 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -2095,7 +2095,7 @@ extern spinlock_t GlobalMid_Lock; /* protects above & list operations on midQ en
/*
* Global counters, updated atomically
*/
-extern atomic_t sesInfoAllocCount;
+extern atomic_t cifs_ses_alloc_count;
extern atomic_t tconInfoAllocCount;
extern atomic_t tcpSesNextId;
extern atomic_t tcpSesAllocCount;
diff --git a/fs/smb/client/cifsproto.h b/fs/smb/client/cifsproto.h
index f9ea35597bc2..2930a40d1b67 100644
--- a/fs/smb/client/cifsproto.h
+++ b/fs/smb/client/cifsproto.h
@@ -58,8 +58,16 @@ do { \
} while (0)
int init_cifs_idmap(void);
void exit_cifs_idmap(void);
+void cifs_idmap_clear_cache(void);
int init_cifs_spnego(void);
void exit_cifs_spnego(void);
+#ifdef CONFIG_CIFS_UPCALL
+void cifs_spnego_clear_cache(void);
+#else
+static inline void cifs_spnego_clear_cache(void)
+{
+}
+#endif
const char *build_path_from_dentry(struct dentry *direntry, void *page);
char *__build_path_from_dentry_optional_prefix(struct dentry *direntry,
void *page, const char *tree,
diff --git a/fs/smb/client/misc.c b/fs/smb/client/misc.c
index ec28f102ea08..4503434942f9 100644
--- a/fs/smb/client/misc.c
+++ b/fs/smb/client/misc.c
@@ -9,6 +9,8 @@
#include <linux/slab.h>
#include <linux/ctype.h>
#include <linux/mempool.h>
+#include <linux/key.h>
+#include <linux/mutex.h>
#include <linux/vmalloc.h>
#include "cifsglob.h"
#include "cifsproto.h"
@@ -33,6 +35,9 @@ struct tcon_list {
struct cifs_tcon *tcon;
};
+/* Serialize session admission with the last session's cache clear. */
+static DEFINE_MUTEX(cifs_session_mutex);
+
/* The xid serves as a useful identifier for each incoming vfs request,
in a similar way to the mid which is useful to track each sent smb,
and CurrentXid can also provide a running counter (although it
@@ -74,7 +79,6 @@ sesInfoAlloc(void)
ret_buf = kzalloc_obj(struct cifs_ses);
if (ret_buf) {
- atomic_inc(&sesInfoAllocCount);
spin_lock_init(&ret_buf->ses_lock);
ret_buf->ses_status = SES_NEW;
++ret_buf->ses_count;
@@ -84,6 +88,10 @@ sesInfoAlloc(void)
spin_lock_init(&ret_buf->iface_lock);
INIT_LIST_HEAD(&ret_buf->iface_list);
spin_lock_init(&ret_buf->chan_lock);
+
+ mutex_lock(&cifs_session_mutex);
+ atomic_inc(&cifs_ses_alloc_count);
+ mutex_unlock(&cifs_session_mutex);
}
return ret_buf;
}
@@ -99,7 +107,6 @@ sesInfoFree(struct cifs_ses *buf_to_free)
}
unload_nls(buf_to_free->local_nls);
- atomic_dec(&sesInfoAllocCount);
kfree(buf_to_free->serverOS);
kfree(buf_to_free->serverDomain);
kfree(buf_to_free->serverNOS);
@@ -115,6 +122,14 @@ sesInfoFree(struct cifs_ses *buf_to_free)
kref_put(&iface->refcount, release_iface);
spin_unlock(&buf_to_free->iface_lock);
kfree_sensitive(buf_to_free);
+
+ mutex_lock(&cifs_session_mutex);
+ if (atomic_dec_and_test(&cifs_ses_alloc_count)) {
+ cifs_idmap_clear_cache();
+ cifs_spnego_clear_cache();
+ key_gc_flush();
+ }
+ mutex_unlock(&cifs_session_mutex);
}
struct cifs_tcon *
--
2.47.3
^ permalink raw reply related [flat|nested] 2+ messages in thread