// SPDX-License-Identifier: GPL-2.0-or-later /* client.c: NFS client sharing and management code * * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. * Written by David Howells (dhowells@redhat.com)
*/
/* * Allocate a shared client record * * Since these are allocated/deallocated very rarely, we don't * bother putting them in a slab cache...
*/ struct nfs_client *nfs_alloc_client(conststruct nfs_client_initdata *cl_init)
{ struct nfs_client *clp; int err = -ENOMEM;
if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) goto error_0;
clp->cl_minorversion = cl_init->minorversion;
clp->cl_nfs_mod = cl_init->nfs_mod; if (!get_nfs_version(clp->cl_nfs_mod)) goto error_dealloc;
/* * Find an nfs_client on the list that matches the initialisation data * that is supplied.
*/ staticstruct nfs_client *nfs_match_client(conststruct nfs_client_initdata *data)
{ struct nfs_client *clp; conststruct sockaddr *sap = (struct sockaddr *)data->addr; struct nfs_net *nn = net_generic(data->net, nfs_net_id); int error;
again:
list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { conststruct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; /* Don't match clients that failed to initialise properly */ if (clp->cl_cons_state < 0) continue;
/* If a client is still initializing then we need to wait */ if (clp->cl_cons_state > NFS_CS_READY) {
refcount_inc(&clp->cl_count);
spin_unlock(&nn->nfs_client_lock);
error = nfs_wait_client_init_complete(clp);
nfs_put_client(clp);
spin_lock(&nn->nfs_client_lock); if (error < 0) return ERR_PTR(error); goto again;
}
/* Different NFS versions cannot share the same nfs_client */ if (clp->rpc_ops != data->nfs_mod->rpc_ops) continue;
if (clp->cl_proto != data->proto) continue; /* Match nfsv4 minorversion */ if (clp->cl_minorversion != data->minorversion) continue;
/* Match request for a dedicated DS */ if (test_bit(NFS_CS_DS, &data->init_flags) !=
test_bit(NFS_CS_DS, &clp->cl_flags)) continue;
/* Match the full socket address */ if (!rpc_cmp_addr_port(sap, clap)) /* Match all xprt_switch full socket addresses */ if (IS_ERR(clp->cl_rpcclient) ||
!rpc_clnt_xprt_switch_has_addr(clp->cl_rpcclient,
sap)) continue;
/* Match the xprt security policy */ if (clp->cl_xprtsec.policy != data->xprtsec.policy) continue; if (clp->cl_xprtsec.policy == RPC_XPRTSEC_TLS_X509) { if (clp->cl_xprtsec.cert_serial !=
data->xprtsec.cert_serial) continue; if (clp->cl_xprtsec.privkey_serial !=
data->xprtsec.privkey_serial) continue;
}
/* * Return true if @clp is done initializing, false if still working on it. * * Use nfs_client_init_status to check if it was successful.
*/ bool nfs_client_init_is_complete(conststruct nfs_client *clp)
{ return clp->cl_cons_state <= NFS_CS_READY;
}
EXPORT_SYMBOL_GPL(nfs_client_init_is_complete);
/* * Return 0 if @clp was successfully initialized, -errno otherwise. * * This must be called *after* nfs_client_init_is_complete() returns true, * otherwise it will pop WARN_ON_ONCE and return -EINVAL
*/ int nfs_client_init_status(conststruct nfs_client *clp)
{ /* called without checking nfs_client_init_is_complete */ if (clp->cl_cons_state > NFS_CS_READY) {
WARN_ON_ONCE(1); return -EINVAL;
} return clp->cl_cons_state;
}
EXPORT_SYMBOL_GPL(nfs_client_init_status);
int nfs_wait_client_init_complete(conststruct nfs_client *clp)
{ return wait_event_killable(nfs_client_active_wq,
nfs_client_init_is_complete(clp));
}
EXPORT_SYMBOL_GPL(nfs_wait_client_init_complete);
/* * Found an existing client. Make sure it's ready before returning.
*/ staticstruct nfs_client *
nfs_found_client(conststruct nfs_client_initdata *cl_init, struct nfs_client *clp)
{ int error;
/* * Look up a client by IP address and protocol version * - creates a new record if one doesn't yet exist
*/ struct nfs_client *nfs_get_client(conststruct nfs_client_initdata *cl_init)
{ struct nfs_client *clp, *new = NULL; struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id); conststruct nfs_rpc_ops *rpc_ops = cl_init->nfs_mod->rpc_ops;
if (cl_init->hostname == NULL) {
WARN_ON(1); return ERR_PTR(-EINVAL);
}
/* see if the client already exists */ do {
spin_lock(&nn->nfs_client_lock);
clp = nfs_match_client(cl_init); if (clp) {
spin_unlock(&nn->nfs_client_lock); if (new)
new->rpc_ops->free_client(new); if (IS_ERR(clp)) return clp; return nfs_found_client(cl_init, clp);
} if (new) {
list_add_tail(&new->cl_share_link,
&nn->nfs_client_list);
spin_unlock(&nn->nfs_client_lock); new = rpc_ops->init_client(new, cl_init); if (!IS_ERR(new))
nfs_local_probe_async(new); returnnew;
}
spin_unlock(&nn->nfs_client_lock);
new = rpc_ops->alloc_client(cl_init);
} while (!IS_ERR(new));
returnnew;
}
EXPORT_SYMBOL_GPL(nfs_get_client);
/* * Mark a server as ready or failed
*/ void nfs_mark_client_ready(struct nfs_client *clp, int state)
{
smp_wmb();
clp->cl_cons_state = state;
wake_up_all(&nfs_client_active_wq);
}
EXPORT_SYMBOL_GPL(nfs_mark_client_ready);
/* * Initialise the timeout values for a connection
*/ void nfs_init_timeout_values(struct rpc_timeout *to, int proto, int timeo, int retrans)
{
to->to_initval = timeo * HZ / 10;
to->to_retries = retrans;
switch (proto) { case XPRT_TRANSPORT_TCP: case XPRT_TRANSPORT_TCP_TLS: case XPRT_TRANSPORT_RDMA: if (retrans == NFS_UNSPEC_RETRANS)
to->to_retries = NFS_DEF_TCP_RETRANS; if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0)
to->to_initval = NFS_DEF_TCP_TIMEO * HZ / 10; if (to->to_initval > NFS_MAX_TCP_TIMEOUT)
to->to_initval = NFS_MAX_TCP_TIMEOUT;
to->to_increment = to->to_initval;
to->to_maxval = to->to_initval + (to->to_increment * to->to_retries); if (to->to_maxval > NFS_MAX_TCP_TIMEOUT)
to->to_maxval = NFS_MAX_TCP_TIMEOUT; if (to->to_maxval < to->to_initval)
to->to_maxval = to->to_initval;
to->to_exponential = 0; break; case XPRT_TRANSPORT_UDP: if (retrans == NFS_UNSPEC_RETRANS)
to->to_retries = NFS_DEF_UDP_RETRANS; if (timeo == NFS_UNSPEC_TIMEO || to->to_initval == 0)
to->to_initval = NFS_DEF_UDP_TIMEO * HZ / 10; if (to->to_initval > NFS_MAX_UDP_TIMEOUT)
to->to_initval = NFS_MAX_UDP_TIMEOUT;
to->to_maxval = NFS_MAX_UDP_TIMEOUT;
to->to_exponential = 1; break; default:
BUG();
}
}
EXPORT_SYMBOL_GPL(nfs_init_timeout_values);
/* * Load up the server record from information gained in an fsinfo record
*/ staticvoid nfs_server_set_fsinfo(struct nfs_server *server, struct nfs_fsinfo *fsinfo)
{ struct nfs_client *clp = server->nfs_client; unsignedlong max_rpc_payload, raw_max_rpc_payload;
/* Work out a lot of parameters */ if (server->rsize == 0)
server->rsize = nfs_io_size(fsinfo->rtpref, clp->cl_proto); if (server->wsize == 0)
server->wsize = nfs_io_size(fsinfo->wtpref, clp->cl_proto);
/* * Grab the destination's particulars, including lease expiry time. * * Returns zero if probe succeeded and retrieved FSID matches the FSID * we have cached.
*/ int nfs_probe_server(struct nfs_server *server, struct nfs_fh *mntfh)
{ struct nfs_fattr *fattr; int error;
fattr = nfs_alloc_fattr(); if (fattr == NULL) return -ENOMEM;
/* Sanity: the probe won't work if the destination server
* does not recognize the migrated FH. */
error = nfs_probe_fsinfo(server, mntfh, fattr);
/* Zero out the NFS state stuff */
INIT_LIST_HEAD(&server->client_link);
INIT_LIST_HEAD(&server->master_link);
INIT_LIST_HEAD(&server->delegations);
INIT_LIST_HEAD(&server->layouts);
INIT_LIST_HEAD(&server->state_owners_lru);
INIT_LIST_HEAD(&server->ss_copies);
INIT_LIST_HEAD(&server->ss_src_copies);
/* * Create a version 2 or 3 volume record * - keyed on server and FSID
*/ struct nfs_server *nfs_create_server(struct fs_context *fc)
{ struct nfs_fs_context *ctx = nfs_fc2context(fc); struct nfs_server *server; struct nfs_fattr *fattr; int error;
server = nfs_alloc_server(); if (!server) return ERR_PTR(-ENOMEM);
/* * set up the iterator to start reading from the server list and return the first item
*/ staticvoid *nfs_server_list_start(struct seq_file *m, loff_t *_pos)
__acquires(&nn->nfs_client_lock)
{ struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
/* lock the list against modification */
spin_lock(&nn->nfs_client_lock); return seq_list_start_head(&nn->nfs_client_list, *_pos);
}
/* * move to next server
*/ staticvoid *nfs_server_list_next(struct seq_file *p, void *v, loff_t *pos)
{ struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
/* * clean up after reading from the transports list
*/ staticvoid nfs_server_list_stop(struct seq_file *p, void *v)
__releases(&nn->nfs_client_lock)
{ struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
spin_unlock(&nn->nfs_client_lock);
}
/* * display a header line followed by a load of call lines
*/ staticint nfs_server_list_show(struct seq_file *m, void *v)
{ struct nfs_client *clp; struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
/* display header on line 1 */ if (v == &nn->nfs_client_list) {
seq_puts(m, "NV SERVER PORT USE HOSTNAME\n"); return 0;
}
/* display one transport per line on subsequent lines */
clp = list_entry(v, struct nfs_client, cl_share_link);
/* Check if the client is initialized */ if (clp->cl_cons_state != NFS_CS_READY) return 0;
/* * set up the iterator to start reading from the volume list and return the first item
*/ staticvoid *nfs_volume_list_start(struct seq_file *m, loff_t *_pos)
__acquires(&nn->nfs_client_lock)
{ struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
/* lock the list against modification */
spin_lock(&nn->nfs_client_lock); return seq_list_start_head(&nn->nfs_volume_list, *_pos);
}
/* * clean up after reading from the transports list
*/ staticvoid nfs_volume_list_stop(struct seq_file *p, void *v)
__releases(&nn->nfs_client_lock)
{ struct nfs_net *nn = net_generic(seq_file_net(p), nfs_net_id);
spin_unlock(&nn->nfs_client_lock);
}
/* * display a header line followed by a load of call lines
*/ staticint nfs_volume_list_show(struct seq_file *m, void *v)
{ struct nfs_server *server; struct nfs_client *clp; char dev[13]; // 8 for 2^24, 1 for ':', 3 for 2^8, 1 for '\0' char fsid[34]; // 2 * 16 for %llx, 1 for ':', 1 for '\0' struct nfs_net *nn = net_generic(seq_file_net(m), nfs_net_id);
/* display header on line 1 */ if (v == &nn->nfs_volume_list) {
seq_puts(m, "NV SERVER PORT DEV FSID" " FSC\n"); return 0;
} /* display one transport per line on subsequent lines */
server = list_entry(v, struct nfs_server, master_link);
clp = server->nfs_client;
int nfs_fs_proc_net_init(struct net *net)
{ struct nfs_net *nn = net_generic(net, nfs_net_id); struct proc_dir_entry *p;
nn->proc_nfsfs = proc_net_mkdir(net, "nfsfs", net->proc_net); if (!nn->proc_nfsfs) goto error_0;
/* a file of servers with which we're dealing */
p = proc_create_net("servers", S_IFREG|S_IRUGO, nn->proc_nfsfs,
&nfs_server_list_ops, sizeof(struct seq_net_private)); if (!p) goto error_1;
/* a file of volumes that we have mounted */
p = proc_create_net("volumes", S_IFREG|S_IRUGO, nn->proc_nfsfs,
&nfs_volume_list_ops, sizeof(struct seq_net_private)); if (!p) goto error_1; return 0;
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.