/* * We have a single directory with several nodes in it.
*/ enum {
NFSD_Root = 1,
NFSD_List,
NFSD_Export_Stats,
NFSD_Export_features,
NFSD_Fh,
NFSD_FO_UnlockIP,
NFSD_FO_UnlockFS,
NFSD_Threads,
NFSD_Pool_Threads,
NFSD_Pool_Stats,
NFSD_Reply_Cache_Stats,
NFSD_Versions,
NFSD_Ports,
NFSD_MaxBlkSize,
NFSD_Filecache,
NFSD_Leasetime,
NFSD_Gracetime,
NFSD_RecoveryDir,
NFSD_V4EndGrace,
NFSD_MaxReserved
};
static ssize_t nfsctl_transaction_read(struct file *file, char __user *buf, size_t size, loff_t *pos)
{ if (! file->private_data) { /* An attempt to read a transaction file without writing * causes a 0-byte write so that the file can return * state information
*/
ssize_t rv = nfsctl_transaction_write(file, buf, 0, pos); if (rv < 0) return rv;
} return simple_transaction_read(file, buf, size, pos);
}
staticinlinestruct net *netns(struct file *file)
{ return file_inode(file)->i_sb->s_fs_info;
}
/* * write_unlock_ip - Release all locks used by a client * * Experimental. * * Input: * buf: '\n'-terminated C string containing a * presentation format IP address * size: length of C string in @buf * Output: * On success: returns zero if all specified locks were released; * returns one if one or more locks were not released * On error: return code is negative errno value
*/ static ssize_t write_unlock_ip(struct file *file, char *buf, size_t size)
{ struct sockaddr_storage address; struct sockaddr *sap = (struct sockaddr *)&address;
size_t salen = sizeof(address); char *fo_path; struct net *net = netns(file);
/* * write_unlock_fs - Release all locks on a local file system * * Experimental. * * Input: * buf: '\n'-terminated C string containing the * absolute pathname of a local file system * size: length of C string in @buf * Output: * On success: returns zero if all specified locks were released; * returns one if one or more locks were not released * On error: return code is negative errno value
*/ static ssize_t write_unlock_fs(struct file *file, char *buf, size_t size)
{ struct path path; char *fo_path; int error;
/* * XXX: Needs better sanity checking. Otherwise we could end up * releasing locks on the wrong file system. * * For example: * 1. Does the path refer to a directory? * 2. Is that directory a mount point, or * 3. Is that directory the root of an exported file system?
*/
error = nlmsvc_unlock_all_by_sb(path.dentry->d_sb);
nfsd4_revoke_states(netns(file), path.dentry->d_sb);
path_put(&path); return error;
}
/* * write_filehandle - Get a variable-length NFS file handle by path * * On input, the buffer contains a '\n'-terminated C string comprised of * three alphanumeric words separated by whitespace. The string may * contain escape sequences. * * Input: * buf: * domain: client domain name * path: export pathname * maxsize: numeric maximum size of * @buf * size: length of C string in @buf * Output: * On success: passed-in buffer filled with '\n'-terminated C * string containing a ASCII hex text version * of the NFS file handle; * return code is the size in bytes of the string * On error: return code is negative errno value
*/ static ssize_t write_filehandle(struct file *file, char *buf, size_t size)
{ char *dname, *path; int maxsize; char *mesg = buf; int len; struct auth_domain *dom; struct knfsd_fh fh;
if (size == 0) return -EINVAL;
if (buf[size-1] != '\n') return -EINVAL;
buf[size-1] = 0;
dname = mesg;
len = qword_get(&mesg, dname, size); if (len <= 0) return -EINVAL;
path = dname+len+1;
len = qword_get(&mesg, path, size); if (len <= 0) return -EINVAL;
len = get_int(&mesg, &maxsize); if (len) return len;
if (maxsize < NFS_FHSIZE) return -EINVAL;
maxsize = min(maxsize, NFS3_FHSIZE);
if (qword_get(&mesg, mesg, size) > 0) return -EINVAL;
/* * write_threads - Start NFSD, or report the current number of running threads * * Input: * buf: ignored * size: zero * Output: * On success: passed-in buffer filled with '\n'-terminated C * string numeric value representing the number of * running NFSD threads; * return code is the size in bytes of the string * On error: return code is zero * * OR * * Input: * buf: C string containing an unsigned * integer value representing the * number of NFSD threads to start * size: non-zero length of C string in @buf * Output: * On success: NFS service is started; * passed-in buffer filled with '\n'-terminated C * string numeric value representing the number of * running NFSD threads; * return code is the size in bytes of the string * On error: return code is zero or a negative errno value
*/ static ssize_t write_threads(struct file *file, char *buf, size_t size)
{ char *mesg = buf; int rv; struct net *net = netns(file);
if (size > 0) { int newthreads;
rv = get_int(&mesg, &newthreads); if (rv) return rv; if (newthreads < 0) return -EINVAL;
trace_nfsd_ctl_threads(net, newthreads);
mutex_lock(&nfsd_mutex);
rv = nfsd_svc(1, &newthreads, net, file->f_cred, NULL);
mutex_unlock(&nfsd_mutex); if (rv < 0) return rv;
} else
rv = nfsd_nrthreads(net);
/* * write_pool_threads - Set or report the current number of threads per pool * * Input: * buf: ignored * size: zero * * OR * * Input: * buf: C string containing whitespace- * separated unsigned integer values * representing the number of NFSD * threads to start in each pool * size: non-zero length of C string in @buf * Output: * On success: passed-in buffer filled with '\n'-terminated C * string containing integer values representing the * number of NFSD threads in each pool; * return code is the size in bytes of the string * On error: return code is zero or a negative errno value
*/ static ssize_t write_pool_threads(struct file *file, char *buf, size_t size)
{ /* if size > 0, look for an array of number of threads per node * and apply them then write out number of threads per node as reply
*/ char *mesg = buf; int i; int rv; int len; int npools; int *nthreads; struct net *net = netns(file);
mutex_lock(&nfsd_mutex);
npools = nfsd_nrpools(net); if (npools == 0) { /* * NFS is shut down. The admin can start it by * writing to the threads file but NOT the pool_threads * file, sorry. Report zero threads.
*/
mutex_unlock(&nfsd_mutex);
strcpy(buf, "0\n"); return strlen(buf);
}
if (vers == 4 && minor >= 0 &&
!nfsd_minorversion(nn, minor, NFSD_TEST))
supported = false; if (minor == 0 && supported) /* * special case for backward compatability. * +4.0 is never reported, it is implied by * +4, unless -4.0 is present.
*/ return 0; return snprintf(buf, remaining, format, sep,
supported ? '+' : '-', vers, minor);
}
if (size > 0) { if (nn->nfsd_serv) /* Cannot change versions without updating * nn->nfsd_serv->sv_xdrsize, and reallocing * rq_argp and rq_resp
*/ return -EBUSY; if (buf[size-1] != '\n') return -EINVAL;
buf[size-1] = 0;
trace_nfsd_ctl_version(netns(file), buf);
vers = mesg;
len = qword_get(&mesg, vers, size); if (len <= 0) return -EINVAL; do { enum vers_op cmd; unsigned minor;
sign = *vers; if (sign == '+' || sign == '-')
num = simple_strtol((vers+1), &minorp, 0); else
num = simple_strtol(vers, &minorp, 0); if (*minorp == '.') { if (num != 4) return -EINVAL; if (kstrtouint(minorp+1, 0, &minor) < 0) return -EINVAL;
}
cmd = sign == '-' ? NFSD_CLEAR : NFSD_SET; switch(num) { #ifdef CONFIG_NFSD_V2 case 2: #endif case 3:
nfsd_vers(nn, num, cmd); break; case 4: if (*minorp == '.') { if (nfsd_minorversion(nn, minor, cmd) < 0) return -EINVAL;
} elseif ((cmd == NFSD_SET) != nfsd_vers(nn, num, NFSD_TEST)) { /* * Either we have +4 and no minors are enabled, * or we have -4 and at least one minor is enabled. * In either case, propagate 'cmd' to all minors.
*/
minor = 0; while (nfsd_minorversion(nn, minor, cmd) >= 0)
minor++;
} break; default: /* Ignore requests to disable non-existent versions */ if (cmd == NFSD_SET) return -EINVAL;
}
vers += len + 1;
} while ((len = qword_get(&mesg, vers, size)) > 0); /* If all get turned off, turn them back on, as * having no versions is BAD
*/
nfsd_reset_versions(nn);
}
/* Now write current state into reply buffer */
sep = "";
remaining = SIMPLE_TRANSACTION_LIMIT; for (num=2 ; num <= 4 ; num++) { int minor; if (!nfsd_vers(nn, num, NFSD_AVAIL)) continue;
minor = -1; do {
len = nfsd_print_version_support(nn, buf, remaining,
sep, num, minor); if (len >= remaining) goto out;
remaining -= len;
buf += len;
tlen += len;
minor++; if (len)
sep = " ";
} while (num == 4 && minor <= NFSD_SUPPORTED_MINOR_VERSION);
}
out:
len = snprintf(buf, remaining, "\n"); if (len >= remaining) return -EINVAL; return tlen + len;
}
/* * write_versions - Set or report the available NFS protocol versions * * Input: * buf: ignored * size: zero * Output: * On success: passed-in buffer filled with '\n'-terminated C * string containing positive or negative integer * values representing the current status of each * protocol version; * return code is the size in bytes of the string * On error: return code is zero or a negative errno value * * OR * * Input: * buf: C string containing whitespace- * separated positive or negative * integer values representing NFS * protocol versions to enable ("+n") * or disable ("-n") * size: non-zero length of C string in @buf * Output: * On success: status of zero or more protocol versions has * been updated; passed-in buffer filled with * '\n'-terminated C string containing positive * or negative integer values representing the * current status of each protocol version; * return code is the size in bytes of the string * On error: return code is zero or a negative errno value
*/ static ssize_t write_versions(struct file *file, char *buf, size_t size)
{
ssize_t rv;
/* * A single 'fd' number was written, in which case it must be for * a socket of a supported family/protocol, and we use it as an * nfsd listener.
*/ static ssize_t __write_ports_addfd(char *buf, struct net *net, conststruct cred *cred)
{ char *mesg = buf; int fd, err; struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct svc_serv *serv;
if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks))
nfsd_destroy_serv(net);
return err;
}
/* * A transport listener is added by writing its transport name and * a port number.
*/ static ssize_t __write_ports_addxprt(char *buf, struct net *net, conststruct cred *cred)
{ char transport[16]; struct svc_xprt *xprt; int port, err; struct nfsd_net *nn = net_generic(net, nfsd_net_id); struct svc_serv *serv;
if (sscanf(buf, "%15s %5u", transport, &port) != 2) return -EINVAL;
if (port < 1 || port > USHRT_MAX) return -EINVAL;
trace_nfsd_ctl_ports_addxprt(net, transport, port);
err = nfsd_create_serv(net); if (err != 0) return err;
static ssize_t __write_ports(struct file *file, char *buf, size_t size, struct net *net)
{ if (size == 0) return __write_ports_names(buf, net);
if (isdigit(buf[0])) return __write_ports_addfd(buf, net, file->f_cred);
if (isalpha(buf[0])) return __write_ports_addxprt(buf, net, file->f_cred);
return -EINVAL;
}
/* * write_ports - Pass a socket file descriptor or transport name to listen on * * Input: * buf: ignored * size: zero * Output: * On success: passed-in buffer filled with a '\n'-terminated C * string containing a whitespace-separated list of * named NFSD listeners; * return code is the size in bytes of the string * On error: return code is zero or a negative errno value * * OR * * Input: * buf: C string containing an unsigned * integer value representing a bound * but unconnected socket that is to be * used as an NFSD listener; listen(3) * must be called for a SOCK_STREAM * socket, otherwise it is ignored * size: non-zero length of C string in @buf * Output: * On success: NFS service is started; * passed-in buffer filled with a '\n'-terminated C * string containing a unique alphanumeric name of * the listener; * return code is the size in bytes of the string * On error: return code is a negative errno value * * OR * * Input: * buf: C string containing a transport * name and an unsigned integer value * representing the port to listen on, * separated by whitespace * size: non-zero length of C string in @buf * Output: * On success: returns zero; NFS service is started * On error: return code is a negative errno value
*/ static ssize_t write_ports(struct file *file, char *buf, size_t size)
{
ssize_t rv;
/* * write_maxblksize - Set or report the current NFS blksize * * Input: * buf: ignored * size: zero * * OR * * Input: * buf: C string containing an unsigned * integer value representing the new * NFS blksize * size: non-zero length of C string in @buf * Output: * On success: passed-in buffer filled with '\n'-terminated C string * containing numeric value of the current NFS blksize * setting; * return code is the size in bytes of the string * On error: return code is zero or a negative errno value
*/ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
{ char *mesg = buf; struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
if (size > 0) { int bsize; int rv = get_int(&mesg, &bsize); if (rv) return rv;
trace_nfsd_ctl_maxblksize(netns(file), bsize);
/* force bsize into allowed range and * required alignment.
*/
bsize = max_t(int, bsize, 1024);
bsize = min_t(int, bsize, NFSSVC_MAXBLKSIZE);
bsize &= ~(1024-1);
mutex_lock(&nfsd_mutex); if (nn->nfsd_serv) {
mutex_unlock(&nfsd_mutex); return -EBUSY;
}
nfsd_max_blksize = bsize;
mutex_unlock(&nfsd_mutex);
}
if (size > 0) { if (nn->nfsd_serv) return -EBUSY;
rv = get_int(&mesg, &i); if (rv) return rv;
trace_nfsd_ctl_time(netns(file), dentry->d_name.name,
dentry->d_name.len, i);
/* * Some sanity checking. We don't have a reason for * these particular numbers, but problems with the * extremes are: * - Too short: the briefest network outage may * cause clients to lose all their locks. Also, * the frequent polling may be wasteful. * - Too long: do you really want reboot recovery * to take more than an hour? Or to make other * clients wait an hour before being able to * revoke a dead client's locks?
*/ if (i < 10 || i > 3600) return -EINVAL;
*time = i;
}
/* * write_leasetime - Set or report the current NFSv4 lease time * * Input: * buf: ignored * size: zero * * OR * * Input: * buf: C string containing an unsigned * integer value representing the new * NFSv4 lease expiry time * size: non-zero length of C string in @buf * Output: * On success: passed-in buffer filled with '\n'-terminated C * string containing unsigned integer value of the * current lease expiry time; * return code is the size in bytes of the string * On error: return code is zero or a negative errno value
*/ static ssize_t write_leasetime(struct file *file, char *buf, size_t size)
{ struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); return nfsd4_write_time(file, buf, size, &nn->nfsd4_lease, nn);
}
/* * write_gracetime - Set or report current NFSv4 grace period time * * As above, but sets the time of the NFSv4 grace period. * * Note this should never be set to less than the *previous* * lease-period time, but we don't try to enforce this. (In the common * case (a new boot), we don't know what the previous lease time was * anyway.)
*/ static ssize_t write_gracetime(struct file *file, char *buf, size_t size)
{ struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id); return nfsd4_write_time(file, buf, size, &nn->nfsd4_grace, nn);
}
/* * write_recoverydir - Set or report the pathname of the recovery directory * * Input: * buf: ignored * size: zero * * OR * * Input: * buf: C string containing the pathname * of the directory on a local file * system containing permanent NFSv4 * recovery data * size: non-zero length of C string in @buf * Output: * On success: passed-in buffer filled with '\n'-terminated C string * containing the current recovery pathname setting; * return code is the size in bytes of the string * On error: return code is zero or a negative errno value
*/ static ssize_t write_recoverydir(struct file *file, char *buf, size_t size)
{
ssize_t rv; struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
/* * write_v4_end_grace - release grace period for nfsd's v4.x lock manager * * Input: * buf: ignored * size: zero * OR * * Input: * buf: any value * size: non-zero length of C string in @buf * Output: * passed-in buffer filled with "Y" or "N" with a newline * and NULL-terminated C string. This indicates whether * the grace period has ended in the current net * namespace. Return code is the size in bytes of the * string. Writing a string that starts with 'Y', 'y', or * '1' to the file will end the grace period for nfsd's v4 * lock manager.
*/ static ssize_t write_v4_end_grace(struct file *file, char *buf, size_t size)
{ struct nfsd_net *nn = net_generic(netns(file), nfsd_net_id);
/* * @content is assumed to be a NUL-terminated string that lives * longer than the symlink itself.
*/ staticvoid _nfsd_symlink(struct dentry *parent, constchar *name, constchar *content)
{ struct inode *dir = parent->d_inode; struct dentry *dentry; int ret;
if (rqstp_index++ < cb->args[1]) /* already consumed */ continue; /* * Acquire rq_status_counter before parsing the rqst * fields. rq_status_counter is set to an odd value in * order to notify the consumers the rqstp fields are * meaningful.
*/
status_counter =
smp_load_acquire(&rqstp->rq_status_counter); if (!(status_counter & 1)) continue;
/* * Acquire rq_status_counter before reporting the rqst * fields to the user.
*/ if (smp_load_acquire(&rqstp->rq_status_counter) !=
status_counter) continue;
ret = nfsd_genl_rpc_status_compose_msg(skb, cb,
&genl_rqstp); if (ret) goto out;
}
}
/** * nfsd_nl_threads_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_threads_set_doit(struct sk_buff *skb, struct genl_info *info)
{ int *nthreads, nrpools = 0, i, ret = -EOPNOTSUPP, rem; struct net *net = genl_info_net(info); struct nfsd_net *nn = net_generic(net, nfsd_net_id); conststruct nlattr *attr; constchar *scope = NULL;
if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_THREADS)) return -EINVAL;
/* count number of SERVER_THREADS values */
nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_THREADS, info->nlhdr,
GENL_HDRLEN, rem)
nrpools++;
mutex_lock(&nfsd_mutex);
nthreads = kcalloc(nrpools, sizeof(int), GFP_KERNEL); if (!nthreads) {
ret = -ENOMEM; goto out_unlock;
}
i = 0;
nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_THREADS, info->nlhdr,
GENL_HDRLEN, rem) {
nthreads[i++] = nla_get_u32(attr); if (i >= nrpools) break;
}
if (info->attrs[NFSD_A_SERVER_GRACETIME] ||
info->attrs[NFSD_A_SERVER_LEASETIME] ||
info->attrs[NFSD_A_SERVER_SCOPE]) {
ret = -EBUSY; if (nn->nfsd_serv && nn->nfsd_serv->sv_nrthreads) goto out_unlock;
ret = -EINVAL;
attr = info->attrs[NFSD_A_SERVER_GRACETIME]; if (attr) {
u32 gracetime = nla_get_u32(attr);
if (gracetime < 10 || gracetime > 3600) goto out_unlock;
nn->nfsd4_grace = gracetime;
}
attr = info->attrs[NFSD_A_SERVER_LEASETIME]; if (attr) {
u32 leasetime = nla_get_u32(attr);
if (leasetime < 10 || leasetime > 3600) goto out_unlock;
nn->nfsd4_lease = leasetime;
}
attr = info->attrs[NFSD_A_SERVER_SCOPE]; if (attr)
scope = nla_data(attr);
}
ret = nfsd_svc(nrpools, nthreads, net, get_current_cred(), scope); if (ret > 0)
ret = 0;
out_unlock:
mutex_unlock(&nfsd_mutex);
kfree(nthreads); return ret;
}
/** * nfsd_nl_threads_get_doit - get 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_threads_get_doit(struct sk_buff *skb, struct genl_info *info)
{ struct net *net = genl_info_net(info); struct nfsd_net *nn = net_generic(net, nfsd_net_id); void *hdr; int err;
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM;
/** * nfsd_nl_version_set_doit - set the nfs enabled versions * @skb: reply buffer * @info: netlink metadata and command arguments * * Return 0 on success or a negative errno.
*/ int nfsd_nl_version_set_doit(struct sk_buff *skb, struct genl_info *info)
{ conststruct nlattr *attr; struct nfsd_net *nn; int i, rem;
if (GENL_REQ_ATTR_CHECK(info, NFSD_A_SERVER_PROTO_VERSION)) return -EINVAL;
mutex_lock(&nfsd_mutex);
nn = net_generic(genl_info_net(info), nfsd_net_id); if (nn->nfsd_serv) {
mutex_unlock(&nfsd_mutex); return -EBUSY;
}
/* clear current supported versions. */
nfsd_vers(nn, 2, NFSD_CLEAR);
nfsd_vers(nn, 3, NFSD_CLEAR); for (i = 0; i <= NFSD_SUPPORTED_MINOR_VERSION; i++)
nfsd_minorversion(nn, i, NFSD_CLEAR);
switch (major) { case 4:
nfsd_minorversion(nn, minor, enabled ? NFSD_SET : NFSD_CLEAR); break; case 3: case 2: if (!minor)
nfsd_vers(nn, major, enabled ? NFSD_SET : NFSD_CLEAR); break; default: break;
}
}
mutex_unlock(&nfsd_mutex);
return 0;
}
/** * nfsd_nl_version_get_doit - get the enabled status for all supported nfs versions * @skb: reply buffer * @info: netlink metadata and command arguments * * Return 0 on success or a negative errno.
*/ int nfsd_nl_version_get_doit(struct sk_buff *skb, struct genl_info *info)
{ struct nfsd_net *nn; int i, err; void *hdr;
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM;
/* Set the enabled flag if the version is enabled */ if (nfsd_vers(nn, i, NFSD_TEST) &&
(i < 4 || nfsd_minorversion(nn, j, NFSD_TEST)) &&
nla_put_flag(skb, NFSD_A_VERSION_ENABLED)) {
err = -EINVAL; goto err_nfsd_unlock;
}
/** * nfsd_nl_listener_set_doit - set the nfs running sockets * @skb: reply buffer * @info: netlink metadata and command arguments * * Return 0 on success or a negative errno.
*/ int nfsd_nl_listener_set_doit(struct sk_buff *skb, struct genl_info *info)
{ struct net *net = genl_info_net(info); struct svc_xprt *xprt, *tmp; conststruct nlattr *attr; struct svc_serv *serv;
LIST_HEAD(permsocks); struct nfsd_net *nn; booldelete = false; int err, rem;
mutex_lock(&nfsd_mutex);
err = nfsd_create_serv(net); if (err) {
mutex_unlock(&nfsd_mutex); return err;
}
nn = net_generic(net, nfsd_net_id);
serv = nn->nfsd_serv;
spin_lock_bh(&serv->sv_lock);
/* Move all of the old listener sockets to a temp list */
list_splice_init(&serv->sv_permsocks, &permsocks);
/* * Walk the list of server_socks from userland and move any that match * back to sv_permsocks
*/
nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr,
GENL_HDRLEN, rem) { struct nlattr *tb[NFSD_A_SOCK_MAX + 1]; constchar *xcl_name; struct sockaddr *sa;
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack) < 0) continue;
if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME]) continue;
if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa)) continue;
xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]);
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
/* Put back any matching sockets */
list_for_each_entry_safe(xprt, tmp, &permsocks, xpt_list) { /* This shouldn't be possible */ if (WARN_ON_ONCE(xprt->xpt_net != net)) {
list_move(&xprt->xpt_list, &serv->sv_permsocks); continue;
}
/* If everything matches, put it back */ if (!strcmp(xprt->xpt_class->xcl_name, xcl_name) &&
rpc_cmp_addr_port(sa, (struct sockaddr *)&xprt->xpt_local)) {
list_move(&xprt->xpt_list, &serv->sv_permsocks); break;
}
}
}
/* * If there are listener transports remaining on the permsocks list, * it means we were asked to remove a listener.
*/ if (!list_empty(&permsocks)) {
list_splice_init(&permsocks, &serv->sv_permsocks); delete = true;
}
spin_unlock_bh(&serv->sv_lock);
/* Do not remove listeners while there are active threads. */ if (serv->sv_nrthreads) {
err = -EBUSY; goto out_unlock_mtx;
}
/* * Since we can't delete an arbitrary llist entry, destroy the * remaining listeners and recreate the list.
*/ if (delete)
svc_xprt_destroy_all(serv, net);
/* walk list of addrs again, open any that still don't exist */
nlmsg_for_each_attr_type(attr, NFSD_A_SERVER_SOCK_ADDR, info->nlhdr,
GENL_HDRLEN, rem) { struct nlattr *tb[NFSD_A_SOCK_MAX + 1]; constchar *xcl_name; struct sockaddr *sa; int ret;
if (nla_parse_nested(tb, NFSD_A_SOCK_MAX, attr,
nfsd_sock_nl_policy, info->extack) < 0) continue;
if (!tb[NFSD_A_SOCK_ADDR] || !tb[NFSD_A_SOCK_TRANSPORT_NAME]) continue;
if (nla_len(tb[NFSD_A_SOCK_ADDR]) < sizeof(*sa)) continue;
xcl_name = nla_data(tb[NFSD_A_SOCK_TRANSPORT_NAME]);
sa = nla_data(tb[NFSD_A_SOCK_ADDR]);
xprt = svc_find_listener(serv, xcl_name, net, sa); if (xprt) { if (delete)
WARN_ONCE(1, "Transport type=%s already exists\n",
xcl_name);
svc_xprt_put(xprt); continue;
}
ret = svc_xprt_create_from_sa(serv, xcl_name, net, sa, 0,
get_current_cred()); /* always save the latest error */ if (ret < 0)
err = ret;
}
if (!serv->sv_nrthreads && list_empty(&nn->nfsd_serv->sv_permsocks))
nfsd_destroy_serv(net);
out_unlock_mtx:
mutex_unlock(&nfsd_mutex);
return err;
}
/** * nfsd_nl_listener_get_doit - get the nfs running listeners * @skb: reply buffer * @info: netlink metadata and command arguments * * Return 0 on success or a negative errno.
*/ int nfsd_nl_listener_get_doit(struct sk_buff *skb, struct genl_info *info)
{ struct svc_xprt *xprt; struct svc_serv *serv; struct nfsd_net *nn; void *hdr; int err;
skb = genlmsg_new(GENLMSG_DEFAULT_SIZE, GFP_KERNEL); if (!skb) return -ENOMEM;
/** * 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)
{ conststruct nlattr *attr;
if (GENL_REQ_ATTR_CHECK(info, NFSD_A_POOL_MODE_MODE)) return -EINVAL;
/** * 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;
/** * nfsd_net_init - Prepare the nfsd_net portion of a new net namespace * @net: a freshly-created network namespace * * This information stays around as long as the network namespace is * alive whether or not there is an NFSD instance running in the * namespace. * * Returns zero on success, or a negative errno otherwise.
*/ static __net_init int nfsd_net_init(struct net *net)
{ struct nfsd_net *nn = net_generic(net, nfsd_net_id); int retval; int i;
retval = nfsd_export_init(net); if (retval) goto out_export_error;
retval = nfsd_idmap_init(net); if (retval) goto out_idmap_error;
retval = percpu_counter_init_many(nn->counter, 0, GFP_KERNEL,
NFSD_STATS_COUNTERS_NUM); if (retval) goto out_repcache_error;
#if IS_ENABLED(CONFIG_NFS_LOCALIO) /** * nfsd_net_pre_exit - Disconnect localio clients from net namespace * @net: a network namespace that is about to be destroyed * * This invalidates ->net pointers held by localio clients * while they can still safely access nn->counter.
*/ static __net_exit void nfsd_net_pre_exit(struct net *net)
{ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
/** * nfsd_net_exit - Release the nfsd_net portion of a net namespace * @net: a network namespace that is about to be destroyed *
*/ static __net_exit void nfsd_net_exit(struct net *net)
{ struct nfsd_net *nn = net_generic(net, nfsd_net_id);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.