From: Dai Ngo Date: Fri, 13 Mar 2026 16:31:48 +0000 (-0400) Subject: NFSD: convert callback RPC program to per-net namespace X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=42cc13995967c1f3790cb106916eed8fab2a37b1;p=thirdparty%2Flinux.git NFSD: convert callback RPC program to per-net namespace The callback channel's rpc_program, rpc_version, rpc_stat, and per-procedure counts are declared as file-scope statics in nfs4callback.c, shared across all network namespaces. Forechannel RPC statistics are already maintained per-netns (via nfsd_svcstats in struct nfsd_net); the backchannel has no such separation. When backchannel statistics are eventually surfaced to userspace, the global counters would expose cross-namespace data. Allocate per-netns copies of these structures through a new opaque struct nfsd_net_cb, managed by nfsd_net_cb_init() and nfsd_net_cb_shutdown(). The struct definition is private to nfs4callback.c; struct nfsd_net holds only a pointer. Signed-off-by: Dai Ngo Reviewed-by: Jeff Layton Signed-off-by: Chuck Lever --- diff --git a/fs/nfsd/netns.h b/fs/nfsd/netns.h index 6ad3fe5d7e12..27da1a3edacb 100644 --- a/fs/nfsd/netns.h +++ b/fs/nfsd/netns.h @@ -25,6 +25,7 @@ #define SESSION_HASH_SIZE 512 struct cld_net; +struct nfsd_net_cb; struct nfsd4_client_tracking_ops; enum { @@ -228,6 +229,8 @@ struct nfsd_net { struct list_head local_clients; #endif siphash_key_t *fh_key; + + struct nfsd_net_cb *nfsd_cb; }; /* Simple check to find out if a given net was properly initialized */ diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 74effafdd0dc..50827405468d 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c @@ -1032,39 +1032,14 @@ static const struct rpc_procinfo nfs4_cb_procedures[] = { PROC(CB_GETATTR, COMPOUND, cb_getattr, cb_getattr), }; -static unsigned int nfs4_cb_counts[ARRAY_SIZE(nfs4_cb_procedures)]; -static const struct rpc_version nfs_cb_version4 = { -/* - * Note on the callback rpc program version number: despite language in rfc - * 5661 section 18.36.3 requiring servers to use 4 in this field, the - * official xdr descriptions for both 4.0 and 4.1 specify version 1, and - * in practice that appears to be what implementations use. The section - * 18.36.3 language is expected to be fixed in an erratum. - */ - .number = 1, - .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), - .procs = nfs4_cb_procedures, - .counts = nfs4_cb_counts, -}; - -static const struct rpc_version *nfs_cb_version[2] = { - [1] = &nfs_cb_version4, -}; - -static const struct rpc_program cb_program; - -static struct rpc_stat cb_stats = { - .program = &cb_program -}; - -#define NFS4_CALLBACK 0x40000000 -static const struct rpc_program cb_program = { - .name = "nfs4_cb", - .number = NFS4_CALLBACK, - .nrvers = ARRAY_SIZE(nfs_cb_version), - .version = nfs_cb_version, - .stats = &cb_stats, - .pipe_dir_name = "nfsd4_cb", +#define NFS4_CB_PROGRAM 0x40000000 +#define NFS4_CB_VERSION 1 + +struct nfsd_net_cb { + struct rpc_version version4; + const struct rpc_version *versions[NFS4_CB_VERSION + 1]; + struct rpc_program program; + struct rpc_stat stat; }; static int max_cb_time(struct net *net) @@ -1140,6 +1115,7 @@ static const struct cred *get_backchannel_cred(struct nfs4_client *clp, struct r static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses) { + struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); int maxtime = max_cb_time(clp->net); struct rpc_timeout timeparms = { .to_initval = maxtime, @@ -1152,14 +1128,14 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c .addrsize = conn->cb_addrlen, .saddress = (struct sockaddr *) &conn->cb_saddr, .timeout = &timeparms, - .program = &cb_program, - .version = 1, + .version = NFS4_CB_VERSION, .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), .cred = current_cred(), }; struct rpc_clnt *client; const struct cred *cred; + args.program = &nn->nfsd_cb->program; if (clp->cl_minorversion == 0) { if (!clp->cl_cred.cr_principal && (clp->cl_cred.cr_flavor >= RPC_AUTH_GSS_KRB5)) { @@ -1786,3 +1762,70 @@ bool nfsd4_run_cb(struct nfsd4_callback *cb) nfsd41_cb_inflight_end(clp); return queued; } + +/** + * nfsd_net_cb_shutdown - release per-netns callback RPC program resources + * @nn: NFS server network namespace + * + * Frees resources allocated by nfsd_net_cb_init(). + */ +void nfsd_net_cb_shutdown(struct nfsd_net *nn) +{ + struct nfsd_net_cb *cb = nn->nfsd_cb; + + if (cb) { + kfree(cb->version4.counts); + kfree(cb); + nn->nfsd_cb = NULL; + } +} + +/** + * nfsd_net_cb_init - initialize per-netns callback RPC program + * @nn: NFS server network namespace + * + * Sets up the callback RPC program, version table, procedure + * counts, and statistics structure for @nn. Caller must release + * these resources using nfsd_net_cb_shutdown(). + * + * Return: 0 on success, or -ENOMEM if allocation fails. + */ +int nfsd_net_cb_init(struct nfsd_net *nn) +{ + struct nfsd_net_cb *cb; + + cb = kzalloc(sizeof(*cb), GFP_KERNEL); + if (!cb) + return -ENOMEM; + + cb->version4.counts = kzalloc_objs(unsigned int, + ARRAY_SIZE(nfs4_cb_procedures), GFP_KERNEL); + if (!cb->version4.counts) { + kfree(cb); + return -ENOMEM; + } + /* + * Note on the callback rpc program version number: despite language + * in rfc 5661 section 18.36.3 requiring servers to use 4 in this + * field, the official xdr descriptions for both 4.0 and 4.1 specify + * version 1, and in practice that appears to be what implementations + * use. The section 18.36.3 language is expected to be fixed in an + * erratum. + */ + cb->version4.number = NFS4_CB_VERSION; + cb->version4.nrprocs = ARRAY_SIZE(nfs4_cb_procedures); + cb->version4.procs = nfs4_cb_procedures; + cb->versions[NFS4_CB_VERSION] = &cb->version4; + + cb->program.name = "nfs4_cb"; + cb->program.number = NFS4_CB_PROGRAM; + cb->program.nrvers = ARRAY_SIZE(cb->versions); + cb->program.version = &cb->versions[0]; + cb->program.pipe_dir_name = "nfsd4_cb"; + cb->program.stats = &cb->stat; + cb->stat.program = &cb->program; + + nn->nfsd_cb = cb; + + return 0; +} diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index 20ec00f323b4..39e7012a60d8 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c @@ -2203,6 +2203,9 @@ static __net_init int nfsd_net_init(struct net *net) int retval; int i; + retval = nfsd_net_cb_init(nn); + if (retval) + return retval; retval = nfsd_export_init(net); if (retval) goto out_export_error; @@ -2243,6 +2246,7 @@ out_repcache_error: out_idmap_error: nfsd_export_shutdown(net); out_export_error: + nfsd_net_cb_shutdown(nn); return retval; } @@ -2273,6 +2277,7 @@ static __net_exit void nfsd_net_exit(struct net *net) struct nfsd_net *nn = net_generic(net, nfsd_net_id); kfree_sensitive(nn->fh_key); + nfsd_net_cb_shutdown(nn); nfsd_proc_stat_shutdown(net); percpu_counter_destroy_many(nn->counter, NFSD_STATS_COUNTERS_NUM); nfsd_idmap_shutdown(net); diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index 9b05462da4cc..953675eba5c3 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h @@ -862,6 +862,8 @@ struct nfsd_file *find_any_file(struct nfs4_file *f); #ifdef CONFIG_NFSD_V4 void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb); void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb); +int nfsd_net_cb_init(struct nfsd_net *nn); +void nfsd_net_cb_shutdown(struct nfsd_net *nn); #else static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block *sb) { @@ -869,6 +871,13 @@ static inline void nfsd4_revoke_states(struct nfsd_net *nn, struct super_block * static inline void nfsd4_cancel_copy_by_sb(struct net *net, struct super_block *sb) { } +static inline int nfsd_net_cb_init(struct nfsd_net *nn) +{ + return 0; +} +static inline void nfsd_net_cb_shutdown(struct nfsd_net *nn) +{ +} #endif /* grace period management */