/* * Global list of nfs_uuid_t instances * that is protected by nfs_uuids_lock.
*/ static LIST_HEAD(nfs_uuids);
/* * Lock ordering: * 1: nfs_uuid->lock * 2: nfs_uuids_lock * 3: nfs_uuid->list_lock (aka nn->local_clients_lock) * * May skip locks in select cases, but never hold multiple * locks out of order.
*/
bool nfs_uuid_begin(nfs_uuid_t *nfs_uuid)
{
spin_lock(&nfs_uuid->lock); if (rcu_access_pointer(nfs_uuid->net)) { /* This nfs_uuid is already in use */
spin_unlock(&nfs_uuid->lock); returnfalse;
}
spin_lock(&nfs_uuids_lock); if (!list_empty(&nfs_uuid->list)) { /* This nfs_uuid is already in use */
spin_unlock(&nfs_uuids_lock);
spin_unlock(&nfs_uuid->lock); returnfalse;
}
list_add_tail(&nfs_uuid->list, &nfs_uuids);
spin_unlock(&nfs_uuids_lock);
spin_lock(&nfs_uuids_lock);
nfs_uuid = nfs_uuid_lookup_locked(uuid); if (!nfs_uuid) {
spin_unlock(&nfs_uuids_lock); return;
}
/* * We don't hold a ref on the net, but instead put * ourselves on @list (nn->local_clients) so the net * pointer can be invalidated.
*/
spin_lock(list_lock); /* list_lock is nn->local_clients_lock */
list_move(&nfs_uuid->list, list);
spin_unlock(list_lock);
spin_unlock(&nfs_uuids_lock); /* Once nfs_uuid is parented to @list, avoid global nfs_uuids_lock */
spin_lock(&nfs_uuid->lock);
void nfs_localio_enable_client(struct nfs_client *clp)
{ /* nfs_uuid_is_local() does the actual enablement */
trace_nfs_localio_enable_client(clp);
}
EXPORT_SYMBOL_GPL(nfs_localio_enable_client);
/* * Cleanup the nfs_uuid_t embedded in an nfs_client. * This is the long-form of nfs_uuid_init().
*/ staticbool nfs_uuid_put(nfs_uuid_t *nfs_uuid)
{ struct nfs_file_localio *nfl;
spin_lock(&nfs_uuid->lock); if (unlikely(!rcu_access_pointer(nfs_uuid->net))) {
spin_unlock(&nfs_uuid->lock); returnfalse;
}
RCU_INIT_POINTER(nfs_uuid->net, NULL);
if (nfs_uuid->dom) {
auth_domain_put(nfs_uuid->dom);
nfs_uuid->dom = NULL;
}
/* Walk list of files and ensure their last references dropped */
while ((nfl = list_first_entry_or_null(&nfs_uuid->files, struct nfs_file_localio,
list)) != NULL) { /* If nfs_uuid is already NULL, nfs_close_local_fh is * closing and we must wait, else we unlink and close.
*/ if (rcu_access_pointer(nfl->nfs_uuid) == NULL) { /* nfs_close_local_fh() is doing the * close and we must wait. until it unlinks
*/
wait_var_event_spinlock(nfs_uuid,
list_first_entry_or_null(
&nfs_uuid->files, struct nfs_file_localio,
list) != nfl,
&nfs_uuid->lock); continue;
}
/* Remove nfl from nfs_uuid->files list */
list_del_init(&nfl->list);
spin_unlock(&nfs_uuid->lock);
spin_lock(&nfs_uuid->lock); /* Now we can allow racing nfs_close_local_fh() to * skip the locking.
*/
store_release_wake_up(&nfl->nfs_uuid, RCU_INITIALIZER(NULL));
}
/* Remove client from nn->local_clients */ if (nfs_uuid->list_lock) {
spin_lock(nfs_uuid->list_lock);
BUG_ON(list_empty(&nfs_uuid->list));
list_del_init(&nfs_uuid->list);
spin_unlock(nfs_uuid->list_lock);
nfs_uuid->list_lock = NULL;
}
/* * Not running in nfsd context, so must safely get reference on nfsd_serv. * But the server may already be shutting down, if so disallow new localio. * uuid->net is NOT a counted reference, but rcu_read_lock() ensures that * if uuid->net is not NULL, then calling nfsd_net_try_get() is safe * and if it succeeds we will have an implied reference to the net. * * Otherwise NFS may not have ref on NFSD and therefore cannot safely * make 'nfs_to' calls.
*/
rcu_read_lock();
net = rcu_dereference(uuid->net); if (!net || !nfs_to->nfsd_net_try_get(net)) {
rcu_read_unlock(); return ERR_PTR(-ENXIO);
}
rcu_read_unlock(); /* We have an implied reference to net thanks to nfsd_net_try_get */
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt, cred,
nfs_fh, pnf, fmode); if (!IS_ERR(localio) && nfs_uuid_add_file(uuid, nfl) < 0) { /* Delete the cached file when racing with nfs_uuid_put() */
nfs_to_nfsd_file_put_local(pnf);
}
nfs_to_nfsd_net_put(net);
rcu_read_lock();
nfs_uuid = rcu_dereference(nfl->nfs_uuid); if (!nfs_uuid) { /* regular (non-LOCALIO) NFS will hammer this */
rcu_read_unlock(); return;
}
spin_lock(&nfs_uuid->lock); if (!rcu_access_pointer(nfl->nfs_uuid)) { /* nfs_uuid_put has finished here */
spin_unlock(&nfs_uuid->lock);
rcu_read_unlock(); return;
} if (list_empty(&nfl->list)) { /* nfs_uuid_put() has started closing files, wait for it * to finished
*/
spin_unlock(&nfs_uuid->lock);
rcu_read_unlock();
wait_var_event(&nfl->nfs_uuid,
rcu_access_pointer(nfl->nfs_uuid) == NULL); return;
} /* tell nfs_uuid_put() to wait for us */
RCU_INIT_POINTER(nfl->nfs_uuid, NULL);
spin_unlock(&nfs_uuid->lock);
rcu_read_unlock();
/* Remove nfl from nfs_uuid->files list and signal nfs_uuid_put() * that we are done. The moment we drop the spinlock the * nfs_uuid could be freed.
*/
spin_lock(&nfs_uuid->lock);
list_del_init(&nfl->list);
wake_up_var_locked(nfs_uuid, &nfs_uuid->lock);
spin_unlock(&nfs_uuid->lock);
}
EXPORT_SYMBOL_GPL(nfs_close_local_fh);
/* * The NFS LOCALIO code needs to call into NFSD using various symbols, * but cannot be statically linked, because that will make the NFS * module always depend on the NFSD module. * * 'nfs_to' provides NFS access to NFSD functions needed for LOCALIO, * its lifetime is tightly coupled to the NFSD module and will always * be available to NFS LOCALIO because any successful client<->server * LOCALIO handshake results in a reference on the NFSD module (above), * so NFS implicitly holds a reference to the NFSD module and its * functions in the 'nfs_to' nfsd_localio_operations cannot disappear. * * If the last NFS client using LOCALIO disconnects (and its reference * on NFSD dropped) then NFSD could be unloaded, resulting in 'nfs_to' * functions being invalid pointers. But if NFSD isn't loaded then NFS * will not be able to handshake with NFSD and will have no cause to * try to call 'nfs_to' function pointers. If/when NFSD is reloaded it * will reinitialize the 'nfs_to' function pointers and make LOCALIO * possible.
*/ conststruct nfsd_localio_operations *nfs_to;
EXPORT_SYMBOL_GPL(nfs_to);
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-04-29)
¤
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.