1

NFSD 6.11 Release Notes

This is a light release containing optimizations, code clean-ups,
 and minor bug fixes. This development cycle focused on work outside
 of upstream kernel development:
 
 1. Continuing to build upstream CI for NFSD based on kdevops
 2. Continuing to focus on the quality of NFSD in LTS kernels
 3. Participation in IETF nfsv4 WG discussions about NFSv4 ACLs,
    directory delegation, and NFSv4.2 COPY offload
 
 Notable features in v6.11 that were not pulled through the NFSD tree
 include NFS server-side support for the new pNFS NVMe layout type
 [RFC9561]. Functional testing for pNFS block layouts like this one
 has been introduced to our kdevops CI harness. Work on improving
 the resolution of file attribute time stamps in local filesystems
 is also ongoing tree-wide.
 
 As always I am grateful to NFSD contributors, reviewers, testers,
 and bug reporters who participated during this cycle.
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEKLLlsBKG3yQ88j7+M2qzM29mf5cFAmaVM0cACgkQM2qzM29m
 f5fzOQ//c5CXIF3zCLIUofm5eZSP2zIszmHR75rVTEnf0Ehm2BJRF6VZiTvWXRpz
 bOuswxfV1Bds+TofbPIP8jqDcMp8NIXemdb6+QMwh4FDY4M8t1v6TRYt35L6Ulrq
 bSV81aRS622ofQ35sRzwxpGX6rB6YbB+5L4EKuxdEqRKSB8rCxQcjPy2qypcWlRC
 hEAGDe3IiVxTz4VQBpASRqbH9Udw/XEqIhv5c8aLtPvl8i+yWyV5m2G5FMRdBj49
 u8rCLoPi/mON8TDs2U4pbhcdgfBWWvGS6woFp6qrqM0wzXIPLalWsPGK3DUtuFUg
 onxsClJXMWUvW4k4hbjiqosduLGY/kMeX62Lx1dCj/gktrJpU0GDNR/XbBhHU+hj
 UT2CL8AfedC4FQekdyJri/rDgPiTMsf8UE0lgtchHMUXH0ztrjaRxMGiIFMm5vCl
 dJBMGJfCkKR/+U1YrGRQI0tPL8CJKYI8klOEhLoOsCr/WC9p4nvvAzSg4W9mNK5P
 ni4+KU4f/bj8U0Ap2bUacTpXj6W8VcwJWeuHahVA1Slo+eqXO401hj4W88dQmm9O
 ZDR5h+6PI6KoL/KL6I4EyOv+sIEtW3s18cEWbSSu3N/CPuhSGTx8d2J201shJXRN
 uDdMkvbwv48x20pgD2oTkPrZbJHOL3BK5/WPBg7pwpfkoRrBAhY=
 =Xd5e
 -----END PGP SIGNATURE-----

Merge tag 'nfsd-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux

Pull nfsd updates from Chuck Lever:
 "This is a light release containing optimizations, code clean-ups, and
  minor bug fixes.

  This development cycle focused on work outside of upstream kernel
  development:

   - Continuing to build upstream CI for NFSD based on kdevops

   - Continuing to focus on the quality of NFSD in LTS kernels

   - Participation in IETF nfsv4 WG discussions about NFSv4 ACLs,
     directory delegation, and NFSv4.2 COPY offload

  Notable features for v6.11 that do not come through the NFSD tree
  include NFS server-side support for the new pNFS NVMe layout type
  [RFC9561]. Functional testing for pNFS block layouts like this one has
  been introduced to our kdevops CI harness. Work on improving the
  resolution of file attribute time stamps in local filesystems is also
  ongoing tree-wide.

  As always I am grateful to NFSD contributors, reviewers, testers, and
  bug reporters who participated during this cycle"

* tag 'nfsd-6.11' of git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux:
  nfsd: nfsd_file_lease_notifier_call gets a file_lease as an argument
  gss_krb5: Fix the error handling path for crypto_sync_skcipher_setkey
  MAINTAINERS: Add a bugzilla link for NFSD
  nfsd: new netlink ops to get/set server pool_mode
  sunrpc: refactor pool_mode setting code
  nfsd: allow passing in array of thread counts via netlink
  nfsd: make nfsd_svc take an array of thread counts
  sunrpc: fix up the special handling of sv_nrpools == 1
  SUNRPC: Add a trace point in svc_xprt_deferred_close
  NFSD: Support write delegations in LAYOUTGET
  lockd: Use *-y instead of *-objs in Makefile
  NFSD: Fix nfsdcld warning
  svcrdma: Handle ADDR_CHANGE CM event properly
  svcrdma: Refactor the creation of listener CMA ID
  NFSD: remove unused structs 'nfsd3_voidargs'
  NFSD: harden svcxdr_dupstr() and svcxdr_tmpalloc() against integer overflows
This commit is contained in:
Linus Torvalds 2024-07-17 12:00:49 -07:00
commit 586a7a8542
21 changed files with 337 additions and 127 deletions

View File

@ -115,6 +115,15 @@ attribute-sets:
type: nest type: nest
nested-attributes: sock nested-attributes: sock
multi-attr: true multi-attr: true
-
name: pool-mode
attributes:
-
name: mode
type: string
-
name: npools
type: u32
operations: operations:
list: list:
@ -195,3 +204,21 @@ operations:
reply: reply:
attributes: attributes:
- addr - addr
-
name: pool-mode-set
doc: set the current server pool-mode
attribute-set: pool-mode
flags: [ admin-perm ]
do:
request:
attributes:
- mode
-
name: pool-mode-get
doc: get info about server pool-mode
attribute-set: pool-mode
do:
reply:
attributes:
- mode
- npools

View File

@ -12065,7 +12065,7 @@ R: Dai Ngo <Dai.Ngo@oracle.com>
R: Tom Talpey <tom@talpey.com> R: Tom Talpey <tom@talpey.com>
L: linux-nfs@vger.kernel.org L: linux-nfs@vger.kernel.org
S: Supported S: Supported
W: http://nfs.sourceforge.net/ B: https://bugzilla.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git T: git git://git.kernel.org/pub/scm/linux/kernel/git/cel/linux.git
F: Documentation/filesystems/nfs/ F: Documentation/filesystems/nfs/
F: fs/lockd/ F: fs/lockd/

View File

@ -7,8 +7,7 @@ ccflags-y += -I$(src) # needed for trace events
obj-$(CONFIG_LOCKD) += lockd.o obj-$(CONFIG_LOCKD) += lockd.o
lockd-objs-y += clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \ lockd-y := clntlock.o clntproc.o clntxdr.o host.o svc.o svclock.o \
svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o svcshare.o svcproc.o svcsubs.o mon.o trace.o xdr.o
lockd-objs-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o lockd-$(CONFIG_LOCKD_V4) += clnt4xdr.o xdr4.o svc4proc.o
lockd-objs-$(CONFIG_PROC_FS) += procfs.o lockd-$(CONFIG_PROC_FS) += procfs.o
lockd-objs := $(lockd-objs-y)

View File

@ -162,7 +162,7 @@ config NFSD_V4_SECURITY_LABEL
config NFSD_LEGACY_CLIENT_TRACKING config NFSD_LEGACY_CLIENT_TRACKING
bool "Support legacy NFSv4 client tracking methods (DEPRECATED)" bool "Support legacy NFSv4 client tracking methods (DEPRECATED)"
depends on NFSD_V4 depends on NFSD_V4
default n default y
help help
The NFSv4 server needs to store a small amount of information on The NFSv4 server needs to store a small amount of information on
stable storage in order to handle state recovery after reboot. Most stable storage in order to handle state recovery after reboot. Most

View File

@ -664,7 +664,7 @@ static int
nfsd_file_lease_notifier_call(struct notifier_block *nb, unsigned long arg, nfsd_file_lease_notifier_call(struct notifier_block *nb, unsigned long arg,
void *data) void *data)
{ {
struct file_lock *fl = data; struct file_lease *fl = data;
/* Only close files for F_SETLEASE leases */ /* Only close files for F_SETLEASE leases */
if (fl->c.flc_flags & FL_LEASE) if (fl->c.flc_flags & FL_LEASE)

View File

@ -40,6 +40,11 @@ static const struct nla_policy nfsd_listener_set_nl_policy[NFSD_A_SERVER_SOCK_AD
[NFSD_A_SERVER_SOCK_ADDR] = NLA_POLICY_NESTED(nfsd_sock_nl_policy), [NFSD_A_SERVER_SOCK_ADDR] = NLA_POLICY_NESTED(nfsd_sock_nl_policy),
}; };
/* NFSD_CMD_POOL_MODE_SET - do */
static const struct nla_policy nfsd_pool_mode_set_nl_policy[NFSD_A_POOL_MODE_MODE + 1] = {
[NFSD_A_POOL_MODE_MODE] = { .type = NLA_NUL_STRING, },
};
/* Ops table for nfsd */ /* Ops table for nfsd */
static const struct genl_split_ops nfsd_nl_ops[] = { static const struct genl_split_ops nfsd_nl_ops[] = {
{ {
@ -83,6 +88,18 @@ static const struct genl_split_ops nfsd_nl_ops[] = {
.doit = nfsd_nl_listener_get_doit, .doit = nfsd_nl_listener_get_doit,
.flags = GENL_CMD_CAP_DO, .flags = GENL_CMD_CAP_DO,
}, },
{
.cmd = NFSD_CMD_POOL_MODE_SET,
.doit = nfsd_nl_pool_mode_set_doit,
.policy = nfsd_pool_mode_set_nl_policy,
.maxattr = NFSD_A_POOL_MODE_MODE,
.flags = GENL_ADMIN_PERM | GENL_CMD_CAP_DO,
},
{
.cmd = NFSD_CMD_POOL_MODE_GET,
.doit = nfsd_nl_pool_mode_get_doit,
.flags = GENL_CMD_CAP_DO,
},
}; };
struct genl_family nfsd_nl_family __ro_after_init = { struct genl_family nfsd_nl_family __ro_after_init = {

View File

@ -23,6 +23,8 @@ int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info); int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_pool_mode_set_doit(struct sk_buff *skb, struct genl_info *info);
int nfsd_nl_pool_mode_get_doit(struct sk_buff *skb, struct genl_info *info);
extern struct genl_family nfsd_nl_family; extern struct genl_family nfsd_nl_family;

View File

@ -308,8 +308,6 @@ static void nfsaclsvc_release_access(struct svc_rqst *rqstp)
fh_put(&resp->fh); fh_put(&resp->fh);
} }
struct nfsd3_voidargs { int dummy; };
#define ST 1 /* status*/ #define ST 1 /* status*/
#define AT 21 /* attributes */ #define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */ #define pAT (1+AT) /* post attributes - conditional */

View File

@ -221,8 +221,6 @@ static void nfs3svc_release_getacl(struct svc_rqst *rqstp)
posix_acl_release(resp->acl_default); posix_acl_release(resp->acl_default);
} }
struct nfsd3_voidargs { int dummy; };
#define ST 1 /* status*/ #define ST 1 /* status*/
#define AT 21 /* attributes */ #define AT 21 /* attributes */
#define pAT (1+AT) /* post attributes - conditional */ #define pAT (1+AT) /* post attributes - conditional */

View File

@ -2269,7 +2269,7 @@ nfsd4_layoutget(struct svc_rqst *rqstp,
const struct nfsd4_layout_ops *ops; const struct nfsd4_layout_ops *ops;
struct nfs4_layout_stateid *ls; struct nfs4_layout_stateid *ls;
__be32 nfserr; __be32 nfserr;
int accmode = NFSD_MAY_READ_IF_EXEC; int accmode = NFSD_MAY_READ_IF_EXEC | NFSD_MAY_OWNER_OVERRIDE;
switch (lgp->lg_seg.iomode) { switch (lgp->lg_seg.iomode) {
case IOMODE_READ: case IOMODE_READ:
@ -2359,7 +2359,8 @@ nfsd4_layoutcommit(struct svc_rqst *rqstp,
struct nfs4_layout_stateid *ls; struct nfs4_layout_stateid *ls;
__be32 nfserr; __be32 nfserr;
nfserr = fh_verify(rqstp, current_fh, 0, NFSD_MAY_WRITE); nfserr = fh_verify(rqstp, current_fh, 0,
NFSD_MAY_WRITE | NFSD_MAY_OWNER_OVERRIDE);
if (nfserr) if (nfserr)
goto out; goto out;

View File

@ -2086,8 +2086,8 @@ do_init:
status = nn->client_tracking_ops->init(net); status = nn->client_tracking_ops->init(net);
out: out:
if (status) { if (status) {
printk(KERN_WARNING "NFSD: Unable to initialize client " pr_warn("NFSD: Unable to initialize client recovery tracking! (%d)\n", status);
"recovery tracking! (%d)\n", status); pr_warn("NFSD: Is nfsdcld running? If not, enable CONFIG_NFSD_LEGACY_CLIENT_TRACKING.\n");
nn->client_tracking_ops = NULL; nn->client_tracking_ops = NULL;
} }
return status; return status;

View File

@ -118,11 +118,11 @@ static int zero_clientid(clientid_t *clid)
* operation described in @argp finishes. * operation described in @argp finishes.
*/ */
static void * static void *
svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len) svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, size_t len)
{ {
struct svcxdr_tmpbuf *tb; struct svcxdr_tmpbuf *tb;
tb = kmalloc(sizeof(*tb) + len, GFP_KERNEL); tb = kmalloc(struct_size(tb, buf, len), GFP_KERNEL);
if (!tb) if (!tb)
return NULL; return NULL;
tb->next = argp->to_free; tb->next = argp->to_free;
@ -138,9 +138,9 @@ svcxdr_tmpalloc(struct nfsd4_compoundargs *argp, u32 len)
* buffer might end on a page boundary. * buffer might end on a page boundary.
*/ */
static char * static char *
svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len) svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, size_t len)
{ {
char *p = svcxdr_tmpalloc(argp, len + 1); char *p = svcxdr_tmpalloc(argp, size_add(len, 1));
if (!p) if (!p)
return NULL; return NULL;
@ -150,7 +150,7 @@ svcxdr_dupstr(struct nfsd4_compoundargs *argp, void *buf, u32 len)
} }
static void * static void *
svcxdr_savemem(struct nfsd4_compoundargs *argp, __be32 *p, u32 len) svcxdr_savemem(struct nfsd4_compoundargs *argp, __be32 *p, size_t len)
{ {
__be32 *tmp; __be32 *tmp;
@ -2146,7 +2146,7 @@ nfsd4_decode_clone(struct nfsd4_compoundargs *argp, union nfsd4_op_u *u)
*/ */
static __be32 static __be32
nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr, nfsd4_vbuf_from_vector(struct nfsd4_compoundargs *argp, struct xdr_buf *xdr,
char **bufp, u32 buflen) char **bufp, size_t buflen)
{ {
struct page **pages = xdr->pages; struct page **pages = xdr->pages;
struct kvec *head = xdr->head; struct kvec *head = xdr->head;

View File

@ -406,7 +406,7 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
return -EINVAL; return -EINVAL;
trace_nfsd_ctl_threads(net, newthreads); trace_nfsd_ctl_threads(net, newthreads);
mutex_lock(&nfsd_mutex); mutex_lock(&nfsd_mutex);
rv = nfsd_svc(newthreads, net, file->f_cred, NULL); rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
mutex_unlock(&nfsd_mutex); mutex_unlock(&nfsd_mutex);
if (rv < 0) if (rv < 0)
return rv; return rv;
@ -481,6 +481,14 @@ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
goto out_free; goto out_free;
trace_nfsd_ctl_pool_threads(net, i, nthreads[i]); trace_nfsd_ctl_pool_threads(net, i, nthreads[i]);
} }
/*
* There must always be a thread in pool 0; the admin
* can't shut down NFS completely using pool_threads.
*/
if (nthreads[0] == 0)
nthreads[0] = 1;
rv = nfsd_set_nrthreads(i, nthreads, net); rv = nfsd_set_nrthreads(i, nthreads, net);
if (rv) if (rv)
goto out_free; goto out_free;
@ -1637,7 +1645,7 @@ out_unlock:
*/ */
int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info) int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
{ {
int nthreads = 0, count = 0, nrpools, ret = -EOPNOTSUPP, rem; int *nthreads, count = 0, nrpools, i, ret = -EOPNOTSUPP, rem;
struct net *net = genl_info_net(info); struct net *net = genl_info_net(info);
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
const struct nlattr *attr; const struct nlattr *attr;
@ -1654,15 +1662,22 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
mutex_lock(&nfsd_mutex); mutex_lock(&nfsd_mutex);
nrpools = nfsd_nrpools(net); nrpools = max(count, nfsd_nrpools(net));
if (nrpools && count > nrpools) nthreads = kcalloc(nrpools, sizeof(int), GFP_KERNEL);
count = nrpools; if (!nthreads) {
ret = -ENOMEM;
/* XXX: make this handle non-global pool-modes */
if (count > 1)
goto out_unlock; goto out_unlock;
}
i = 0;
nlmsg_for_each_attr(attr, info->nlhdr, GENL_HDRLEN, rem) {
if (nla_type(attr) == NFSD_A_SERVER_THREADS) {
nthreads[i++] = nla_get_u32(attr);
if (i >= nrpools)
break;
}
}
nthreads = nla_get_u32(info->attrs[NFSD_A_SERVER_THREADS]);
if (info->attrs[NFSD_A_SERVER_GRACETIME] || if (info->attrs[NFSD_A_SERVER_GRACETIME] ||
info->attrs[NFSD_A_SERVER_LEASETIME] || info->attrs[NFSD_A_SERVER_LEASETIME] ||
info->attrs[NFSD_A_SERVER_SCOPE]) { info->attrs[NFSD_A_SERVER_SCOPE]) {
@ -1696,12 +1711,13 @@ int nfsd_nl_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
scope = nla_data(attr); scope = nla_data(attr);
} }
ret = nfsd_svc(nthreads, net, get_current_cred(), scope); ret = nfsd_svc(nrpools, nthreads, net, get_current_cred(), scope);
if (ret > 0)
ret = 0;
out_unlock: out_unlock:
mutex_unlock(&nfsd_mutex); mutex_unlock(&nfsd_mutex);
kfree(nthreads);
return ret == nthreads ? 0 : ret; return ret;
} }
/** /**
@ -2140,6 +2156,63 @@ err_free_msg:
return err; return err;
} }
/**
* nfsd_nl_pool_mode_set_doit - set the number of running threads
* @skb: reply buffer
* @info: netlink metadata and command arguments
*
* Return 0 on success or a negative errno.
*/
int nfsd_nl_pool_mode_set_doit(struct sk_buff *skb, struct genl_info *info)
{
const struct nlattr *attr;
if (GENL_REQ_ATTR_CHECK(info, NFSD_A_POOL_MODE_MODE))
return -EINVAL;
attr = info->attrs[NFSD_A_POOL_MODE_MODE];
return sunrpc_set_pool_mode(nla_data(attr));
}
/**
* nfsd_nl_pool_mode_get_doit - get info about pool_mode
* @skb: reply buffer
* @info: netlink metadata and command arguments
*
* Return 0 on success or a negative errno.
*/
int nfsd_nl_pool_mode_get_doit(struct sk_buff *skb, struct genl_info *info)
{
struct net *net = genl_info_net(info);
char buf[16];
void *hdr;
int err;
if (sunrpc_get_pool_mode(buf, ARRAY_SIZE(buf)) >= ARRAY_SIZE(buf))
return -ERANGE;
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL);
if (!skb)
return -ENOMEM;
err = -EMSGSIZE;
hdr = genlmsg_iput(skb, info);
if (!hdr)
goto err_free_msg;
err = nla_put_string(skb, NFSD_A_POOL_MODE_MODE, buf) |
nla_put_u32(skb, NFSD_A_POOL_MODE_NPOOLS, nfsd_nrpools(net));
if (err)
goto err_free_msg;
genlmsg_end(skb, hdr);
return genlmsg_reply(skb, info);
err_free_msg:
nlmsg_free(skb);
return err;
}
/** /**
* nfsd_net_init - Prepare the nfsd_net portion of a new net namespace * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace
* @net: a freshly-created network namespace * @net: a freshly-created network namespace

View File

@ -103,7 +103,8 @@ bool nfssvc_encode_voidres(struct svc_rqst *rqstp,
/* /*
* Function prototypes. * Function prototypes.
*/ */
int nfsd_svc(int nrservs, struct net *net, const struct cred *cred, const char *scope); int nfsd_svc(int n, int *nservers, struct net *net,
const struct cred *cred, const char *scope);
int nfsd_dispatch(struct svc_rqst *rqstp); int nfsd_dispatch(struct svc_rqst *rqstp);
int nfsd_nrthreads(struct net *); int nfsd_nrthreads(struct net *);

View File

@ -709,6 +709,19 @@ int nfsd_get_nrthreads(int n, int *nthreads, struct net *net)
return 0; return 0;
} }
/**
* nfsd_set_nrthreads - set the number of running threads in the net's service
* @n: number of array members in @nthreads
* @nthreads: array of thread counts for each pool
* @net: network namespace to operate within
*
* This function alters the number of running threads for the given network
* namespace in each pool. If passed an array longer then the number of pools
* the extra pool settings are ignored. If passed an array shorter than the
* number of pools, the missing values are interpreted as 0's.
*
* Returns 0 on success or a negative errno on error.
*/
int nfsd_set_nrthreads(int n, int *nthreads, struct net *net) int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
{ {
int i = 0; int i = 0;
@ -716,11 +729,18 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
int err = 0; int err = 0;
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
WARN_ON(!mutex_is_locked(&nfsd_mutex)); lockdep_assert_held(&nfsd_mutex);
if (nn->nfsd_serv == NULL || n <= 0) if (nn->nfsd_serv == NULL || n <= 0)
return 0; return 0;
/*
* Special case: When n == 1, pass in NULL for the pool, so that the
* change is distributed equally among them.
*/
if (n == 1)
return svc_set_num_threads(nn->nfsd_serv, NULL, nthreads[0]);
if (n > nn->nfsd_serv->sv_nrpools) if (n > nn->nfsd_serv->sv_nrpools)
n = nn->nfsd_serv->sv_nrpools; n = nn->nfsd_serv->sv_nrpools;
@ -743,31 +763,40 @@ int nfsd_set_nrthreads(int n, int *nthreads, struct net *net)
} }
} }
/*
* There must always be a thread in pool 0; the admin
* can't shut down NFS completely using pool_threads.
*/
if (nthreads[0] == 0)
nthreads[0] = 1;
/* apply the new numbers */ /* apply the new numbers */
for (i = 0; i < n; i++) { for (i = 0; i < n; i++) {
err = svc_set_num_threads(nn->nfsd_serv, err = svc_set_num_threads(nn->nfsd_serv,
&nn->nfsd_serv->sv_pools[i], &nn->nfsd_serv->sv_pools[i],
nthreads[i]); nthreads[i]);
if (err) if (err)
break; goto out;
} }
/* Anything undefined in array is considered to be 0 */
for (i = n; i < nn->nfsd_serv->sv_nrpools; ++i) {
err = svc_set_num_threads(nn->nfsd_serv,
&nn->nfsd_serv->sv_pools[i],
0);
if (err)
goto out;
}
out:
return err; return err;
} }
/* /**
* Adjust the number of threads and return the new number of threads. * nfsd_svc: start up or shut down the nfsd server
* This is also the function that starts the server if necessary, if * @n: number of array members in @nthreads
* this is the first time nrservs is nonzero. * @nthreads: array of thread counts for each pool
* @net: network namespace to operate within
* @cred: credentials to use for xprt creation
* @scope: server scope value (defaults to nodename)
*
* Adjust the number of threads in each pool and return the new
* total number of threads in the service.
*/ */
int int
nfsd_svc(int nrservs, struct net *net, const struct cred *cred, const char *scope) nfsd_svc(int n, int *nthreads, struct net *net, const struct cred *cred, const char *scope)
{ {
int error; int error;
struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct nfsd_net *nn = net_generic(net, nfsd_net_id);
@ -777,13 +806,6 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred, const char *scop
dprintk("nfsd: creating service\n"); dprintk("nfsd: creating service\n");
nrservs = max(nrservs, 0);
nrservs = min(nrservs, NFSD_MAXSERVS);
error = 0;
if (nrservs == 0 && nn->nfsd_serv == NULL)
goto out;
strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename, strscpy(nn->nfsd_name, scope ? scope : utsname()->nodename,
sizeof(nn->nfsd_name)); sizeof(nn->nfsd_name));
@ -795,7 +817,7 @@ nfsd_svc(int nrservs, struct net *net, const struct cred *cred, const char *scop
error = nfsd_startup_net(net, cred); error = nfsd_startup_net(net, cred);
if (error) if (error)
goto out_put; goto out_put;
error = svc_set_num_threads(serv, NULL, nrservs); error = nfsd_set_nrthreads(n, nthreads, net);
if (error) if (error)
goto out_put; goto out_put;
error = serv->sv_nrthreads; error = serv->sv_nrthreads;

View File

@ -85,6 +85,7 @@ struct svc_serv {
char * sv_name; /* service name */ char * sv_name; /* service name */
unsigned int sv_nrpools; /* number of thread pools */ unsigned int sv_nrpools; /* number of thread pools */
bool sv_is_pooled; /* is this a pooled service? */
struct svc_pool * sv_pools; /* array of thread pools */ struct svc_pool * sv_pools; /* array of thread pools */
int (*sv_threadfn)(void *data); int (*sv_threadfn)(void *data);
@ -398,6 +399,8 @@ struct svc_procedure {
/* /*
* Function prototypes. * Function prototypes.
*/ */
int sunrpc_set_pool_mode(const char *val);
int sunrpc_get_pool_mode(char *val, size_t size);
int svc_rpcb_setup(struct svc_serv *serv, struct net *net); int svc_rpcb_setup(struct svc_serv *serv, struct net *net);
void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net); void svc_rpcb_cleanup(struct svc_serv *serv, struct net *net);
int svc_bind(struct svc_serv *serv, struct net *net); int svc_bind(struct svc_serv *serv, struct net *net);

View File

@ -70,6 +70,14 @@ enum {
NFSD_A_SERVER_SOCK_MAX = (__NFSD_A_SERVER_SOCK_MAX - 1) NFSD_A_SERVER_SOCK_MAX = (__NFSD_A_SERVER_SOCK_MAX - 1)
}; };
enum {
NFSD_A_POOL_MODE_MODE = 1,
NFSD_A_POOL_MODE_NPOOLS,
__NFSD_A_POOL_MODE_MAX,
NFSD_A_POOL_MODE_MAX = (__NFSD_A_POOL_MODE_MAX - 1)
};
enum { enum {
NFSD_CMD_RPC_STATUS_GET = 1, NFSD_CMD_RPC_STATUS_GET = 1,
NFSD_CMD_THREADS_SET, NFSD_CMD_THREADS_SET,
@ -78,6 +86,8 @@ enum {
NFSD_CMD_VERSION_GET, NFSD_CMD_VERSION_GET,
NFSD_CMD_LISTENER_SET, NFSD_CMD_LISTENER_SET,
NFSD_CMD_LISTENER_GET, NFSD_CMD_LISTENER_GET,
NFSD_CMD_POOL_MODE_SET,
NFSD_CMD_POOL_MODE_GET,
__NFSD_CMD_MAX, __NFSD_CMD_MAX,
NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1) NFSD_CMD_MAX = (__NFSD_CMD_MAX - 1)

View File

@ -168,7 +168,7 @@ static int krb5_DK(const struct gss_krb5_enctype *gk5e,
goto err_return; goto err_return;
blocksize = crypto_sync_skcipher_blocksize(cipher); blocksize = crypto_sync_skcipher_blocksize(cipher);
if (crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len)) if (crypto_sync_skcipher_setkey(cipher, inkey->data, inkey->len))
goto err_return; goto err_free_cipher;
ret = -ENOMEM; ret = -ENOMEM;
inblockdata = kmalloc(blocksize, gfp_mask); inblockdata = kmalloc(blocksize, gfp_mask);

View File

@ -72,57 +72,100 @@ static struct svc_pool_map svc_pool_map = {
static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */ static DEFINE_MUTEX(svc_pool_map_mutex);/* protects svc_pool_map.count only */
static int static int
param_set_pool_mode(const char *val, const struct kernel_param *kp) __param_set_pool_mode(const char *val, struct svc_pool_map *m)
{ {
int *ip = (int *)kp->arg; int err, mode;
struct svc_pool_map *m = &svc_pool_map;
int err;
mutex_lock(&svc_pool_map_mutex); mutex_lock(&svc_pool_map_mutex);
err = -EBUSY;
if (m->count)
goto out;
err = 0; err = 0;
if (!strncmp(val, "auto", 4)) if (!strncmp(val, "auto", 4))
*ip = SVC_POOL_AUTO; mode = SVC_POOL_AUTO;
else if (!strncmp(val, "global", 6)) else if (!strncmp(val, "global", 6))
*ip = SVC_POOL_GLOBAL; mode = SVC_POOL_GLOBAL;
else if (!strncmp(val, "percpu", 6)) else if (!strncmp(val, "percpu", 6))
*ip = SVC_POOL_PERCPU; mode = SVC_POOL_PERCPU;
else if (!strncmp(val, "pernode", 7)) else if (!strncmp(val, "pernode", 7))
*ip = SVC_POOL_PERNODE; mode = SVC_POOL_PERNODE;
else else
err = -EINVAL; err = -EINVAL;
if (err)
goto out;
if (m->count == 0)
m->mode = mode;
else if (mode != m->mode)
err = -EBUSY;
out: out:
mutex_unlock(&svc_pool_map_mutex); mutex_unlock(&svc_pool_map_mutex);
return err; return err;
} }
static int static int
param_get_pool_mode(char *buf, const struct kernel_param *kp) param_set_pool_mode(const char *val, const struct kernel_param *kp)
{ {
int *ip = (int *)kp->arg; struct svc_pool_map *m = kp->arg;
switch (*ip) return __param_set_pool_mode(val, m);
}
int sunrpc_set_pool_mode(const char *val)
{
return __param_set_pool_mode(val, &svc_pool_map);
}
EXPORT_SYMBOL(sunrpc_set_pool_mode);
/**
* sunrpc_get_pool_mode - get the current pool_mode for the host
* @buf: where to write the current pool_mode
* @size: size of @buf
*
* Grab the current pool_mode from the svc_pool_map and write
* the resulting string to @buf. Returns the number of characters
* written to @buf (a'la snprintf()).
*/
int
sunrpc_get_pool_mode(char *buf, size_t size)
{
struct svc_pool_map *m = &svc_pool_map;
switch (m->mode)
{ {
case SVC_POOL_AUTO: case SVC_POOL_AUTO:
return sysfs_emit(buf, "auto\n"); return snprintf(buf, size, "auto");
case SVC_POOL_GLOBAL: case SVC_POOL_GLOBAL:
return sysfs_emit(buf, "global\n"); return snprintf(buf, size, "global");
case SVC_POOL_PERCPU: case SVC_POOL_PERCPU:
return sysfs_emit(buf, "percpu\n"); return snprintf(buf, size, "percpu");
case SVC_POOL_PERNODE: case SVC_POOL_PERNODE:
return sysfs_emit(buf, "pernode\n"); return snprintf(buf, size, "pernode");
default: default:
return sysfs_emit(buf, "%d\n", *ip); return snprintf(buf, size, "%d", m->mode);
} }
} }
EXPORT_SYMBOL(sunrpc_get_pool_mode);
static int
param_get_pool_mode(char *buf, const struct kernel_param *kp)
{
char str[16];
int len;
len = sunrpc_get_pool_mode(str, ARRAY_SIZE(str));
/* Ensure we have room for newline and NUL */
len = min_t(int, len, ARRAY_SIZE(str) - 2);
/* tack on the newline */
str[len] = '\n';
str[len + 1] = '\0';
return sysfs_emit(buf, str);
}
module_param_call(pool_mode, param_set_pool_mode, param_get_pool_mode, module_param_call(pool_mode, param_set_pool_mode, param_get_pool_mode,
&svc_pool_map.mode, 0644); &svc_pool_map, 0644);
/* /*
* Detect best pool mapping mode heuristically, * Detect best pool mapping mode heuristically,
@ -250,10 +293,8 @@ svc_pool_map_get(void)
int npools = -1; int npools = -1;
mutex_lock(&svc_pool_map_mutex); mutex_lock(&svc_pool_map_mutex);
if (m->count++) { if (m->count++) {
mutex_unlock(&svc_pool_map_mutex); mutex_unlock(&svc_pool_map_mutex);
WARN_ON_ONCE(m->npools <= 1);
return m->npools; return m->npools;
} }
@ -275,32 +316,21 @@ svc_pool_map_get(void)
m->mode = SVC_POOL_GLOBAL; m->mode = SVC_POOL_GLOBAL;
} }
m->npools = npools; m->npools = npools;
if (npools == 1)
/* service is unpooled, so doesn't hold a reference */
m->count--;
mutex_unlock(&svc_pool_map_mutex); mutex_unlock(&svc_pool_map_mutex);
return npools; return npools;
} }
/* /*
* Drop a reference to the global map of cpus to pools, if * Drop a reference to the global map of cpus to pools.
* pools were in use, i.e. if npools > 1.
* When the last reference is dropped, the map data is * When the last reference is dropped, the map data is
* freed; this allows the sysadmin to change the pool * freed; this allows the sysadmin to change the pool.
* mode using the pool_mode module option without
* rebooting or re-loading sunrpc.ko.
*/ */
static void static void
svc_pool_map_put(int npools) svc_pool_map_put(void)
{ {
struct svc_pool_map *m = &svc_pool_map; struct svc_pool_map *m = &svc_pool_map;
if (npools <= 1)
return;
mutex_lock(&svc_pool_map_mutex); mutex_lock(&svc_pool_map_mutex);
if (!--m->count) { if (!--m->count) {
kfree(m->to_pool); kfree(m->to_pool);
m->to_pool = NULL; m->to_pool = NULL;
@ -308,7 +338,6 @@ svc_pool_map_put(int npools)
m->pool_to = NULL; m->pool_to = NULL;
m->npools = 0; m->npools = 0;
} }
mutex_unlock(&svc_pool_map_mutex); mutex_unlock(&svc_pool_map_mutex);
} }
@ -553,9 +582,10 @@ struct svc_serv *svc_create_pooled(struct svc_program *prog,
serv = __svc_create(prog, stats, bufsize, npools, threadfn); serv = __svc_create(prog, stats, bufsize, npools, threadfn);
if (!serv) if (!serv)
goto out_err; goto out_err;
serv->sv_is_pooled = true;
return serv; return serv;
out_err: out_err:
svc_pool_map_put(npools); svc_pool_map_put();
return NULL; return NULL;
} }
EXPORT_SYMBOL_GPL(svc_create_pooled); EXPORT_SYMBOL_GPL(svc_create_pooled);
@ -585,7 +615,8 @@ svc_destroy(struct svc_serv **servp)
cache_clean_deferred(serv); cache_clean_deferred(serv);
svc_pool_map_put(serv->sv_nrpools); if (serv->sv_is_pooled)
svc_pool_map_put();
for (i = 0; i < serv->sv_nrpools; i++) { for (i = 0; i < serv->sv_nrpools; i++) {
struct svc_pool *pool = &serv->sv_pools[i]; struct svc_pool *pool = &serv->sv_pools[i];

View File

@ -157,6 +157,7 @@ int svc_print_xprts(char *buf, int maxlen)
*/ */
void svc_xprt_deferred_close(struct svc_xprt *xprt) void svc_xprt_deferred_close(struct svc_xprt *xprt)
{ {
trace_svc_xprt_close(xprt);
if (!test_and_set_bit(XPT_CLOSE, &xprt->xpt_flags)) if (!test_and_set_bit(XPT_CLOSE, &xprt->xpt_flags))
svc_xprt_enqueue(xprt); svc_xprt_enqueue(xprt);
} }

View File

@ -65,6 +65,8 @@
static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv,
struct net *net, int node); struct net *net, int node);
static int svc_rdma_listen_handler(struct rdma_cm_id *cma_id,
struct rdma_cm_event *event);
static struct svc_xprt *svc_rdma_create(struct svc_serv *serv, static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
struct net *net, struct net *net,
struct sockaddr *sa, int salen, struct sockaddr *sa, int salen,
@ -122,6 +124,41 @@ static void qp_event_handler(struct ib_event *event, void *context)
} }
} }
static struct rdma_cm_id *
svc_rdma_create_listen_id(struct net *net, struct sockaddr *sap,
void *context)
{
struct rdma_cm_id *listen_id;
int ret;
listen_id = rdma_create_id(net, svc_rdma_listen_handler, context,
RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(listen_id))
return listen_id;
/* Allow both IPv4 and IPv6 sockets to bind a single port
* at the same time.
*/
#if IS_ENABLED(CONFIG_IPV6)
ret = rdma_set_afonly(listen_id, 1);
if (ret)
goto out_destroy;
#endif
ret = rdma_bind_addr(listen_id, sap);
if (ret)
goto out_destroy;
ret = rdma_listen(listen_id, RPCRDMA_LISTEN_BACKLOG);
if (ret)
goto out_destroy;
return listen_id;
out_destroy:
rdma_destroy_id(listen_id);
return ERR_PTR(ret);
}
static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv,
struct net *net, int node) struct net *net, int node)
{ {
@ -247,17 +284,31 @@ static void handle_connect_req(struct rdma_cm_id *new_cma_id,
* *
* Return values: * Return values:
* %0: Do not destroy @cma_id * %0: Do not destroy @cma_id
* %1: Destroy @cma_id (never returned here) * %1: Destroy @cma_id
* *
* NB: There is never a DEVICE_REMOVAL event for INADDR_ANY listeners. * NB: There is never a DEVICE_REMOVAL event for INADDR_ANY listeners.
*/ */
static int svc_rdma_listen_handler(struct rdma_cm_id *cma_id, static int svc_rdma_listen_handler(struct rdma_cm_id *cma_id,
struct rdma_cm_event *event) struct rdma_cm_event *event)
{ {
struct sockaddr *sap = (struct sockaddr *)&cma_id->route.addr.src_addr;
struct svcxprt_rdma *cma_xprt = cma_id->context;
struct svc_xprt *cma_rdma = &cma_xprt->sc_xprt;
struct rdma_cm_id *listen_id;
switch (event->event) { switch (event->event) {
case RDMA_CM_EVENT_CONNECT_REQUEST: case RDMA_CM_EVENT_CONNECT_REQUEST:
handle_connect_req(cma_id, &event->param.conn); handle_connect_req(cma_id, &event->param.conn);
break; break;
case RDMA_CM_EVENT_ADDR_CHANGE:
listen_id = svc_rdma_create_listen_id(cma_rdma->xpt_net,
sap, cma_xprt);
if (IS_ERR(listen_id)) {
pr_err("Listener dead, address change failed for device %s\n",
cma_id->device->name);
} else
cma_xprt->sc_cm_id = listen_id;
return 1;
default: default:
break; break;
} }
@ -307,7 +358,6 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
{ {
struct rdma_cm_id *listen_id; struct rdma_cm_id *listen_id;
struct svcxprt_rdma *cma_xprt; struct svcxprt_rdma *cma_xprt;
int ret;
if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6) if (sa->sa_family != AF_INET && sa->sa_family != AF_INET6)
return ERR_PTR(-EAFNOSUPPORT); return ERR_PTR(-EAFNOSUPPORT);
@ -317,30 +367,13 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
set_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags); set_bit(XPT_LISTENER, &cma_xprt->sc_xprt.xpt_flags);
strcpy(cma_xprt->sc_xprt.xpt_remotebuf, "listener"); strcpy(cma_xprt->sc_xprt.xpt_remotebuf, "listener");
listen_id = rdma_create_id(net, svc_rdma_listen_handler, cma_xprt, listen_id = svc_rdma_create_listen_id(net, sa, cma_xprt);
RDMA_PS_TCP, IB_QPT_RC);
if (IS_ERR(listen_id)) { if (IS_ERR(listen_id)) {
ret = PTR_ERR(listen_id); kfree(cma_xprt);
goto err0; return (struct svc_xprt *)listen_id;
} }
/* Allow both IPv4 and IPv6 sockets to bind a single port
* at the same time.
*/
#if IS_ENABLED(CONFIG_IPV6)
ret = rdma_set_afonly(listen_id, 1);
if (ret)
goto err1;
#endif
ret = rdma_bind_addr(listen_id, sa);
if (ret)
goto err1;
cma_xprt->sc_cm_id = listen_id; cma_xprt->sc_cm_id = listen_id;
ret = rdma_listen(listen_id, RPCRDMA_LISTEN_BACKLOG);
if (ret)
goto err1;
/* /*
* We need to use the address from the cm_id in case the * We need to use the address from the cm_id in case the
* caller specified 0 for the port number. * caller specified 0 for the port number.
@ -349,12 +382,6 @@ static struct svc_xprt *svc_rdma_create(struct svc_serv *serv,
svc_xprt_set_local(&cma_xprt->sc_xprt, sa, salen); svc_xprt_set_local(&cma_xprt->sc_xprt, sa, salen);
return &cma_xprt->sc_xprt; return &cma_xprt->sc_xprt;
err1:
rdma_destroy_id(listen_id);
err0:
kfree(cma_xprt);
return ERR_PTR(ret);
} }
/* /*