/* * Copyright (c) 2001 The Regents of the University of Michigan. * All rights reserved. * * Kendrick Smith <kmsmith@umich.edu> * Andy Adamson <kandros@umich.edu> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
*/
/* * Currently used for the del_recall_lru and file hash table. In an * effort to decrease the scope of the client_mutex, this spinlock may * eventually cover more:
*/ static DEFINE_SPINLOCK(state_lock);
/* * A waitqueue for all in-progress 4.0 CLOSE operations that are waiting for * the refcount on the open stateid to drop.
*/ static DECLARE_WAIT_QUEUE_HEAD(close_wq);
/* * A waitqueue where a writer to clients/#/ctl destroying a client can * wait for cl_rpc_users to drop to 0 and then for the client to be * unhashed.
*/ static DECLARE_WAIT_QUEUE_HEAD(expiry_wq);
/* must be called under the client_lock */ staticinlinevoid
renew_client_locked(struct nfs4_client *clp)
{ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id);
if (is_session_dead(ses)) return nfserr_badsession;
status = get_client_locked(ses->se_client); if (status) return status;
atomic_inc(&ses->se_ref); return nfs_ok;
}
/* * Since this is just an optimization, we don't try very hard if it * turns out not to succeed. We'll requeue it on NFS4ERR_DELAY, and * just quit trying on anything else.
*/ switch (task->tk_status) { case -NFS4ERR_DELAY:
rpc_delay(task, 1 * HZ); return 0; default: return 1;
}
}
/* * We store the NONE, READ, WRITE, and BOTH bits separately in the * st_{access,deny}_bmap field of the stateid, in order to track not * only what share bits are currently in force, but also what * combinations of share bits previous opens have used. This allows us * to enforce the recommendation in * https://datatracker.ietf.org/doc/html/rfc7530#section-16.19.4 that * the server return an error if the client attempt to downgrade to a * combination of share bits not explicable by closing some of its * previous opens. * * This enforcement is arguably incomplete, since we don't keep * track of access/deny bit combinations; so, e.g., we allow: * * OPEN allow read, deny write * OPEN allow both, deny none * DOWNGRADE allow read, deny none * * which we should reject. * * But you could also argue that our current code is already overkill, * since it only exists to return NFS4ERR_INVAL on incorrect client * behavior.
*/ staticunsignedint
bmap_to_share_mode(unsignedlong bmap)
{ int i; unsignedint access = 0;
for (i = 1; i < 4; i++) { if (test_bit(i, &bmap))
access |= i;
} return access;
}
/* set share access for a given stateid */ staticinlinevoid
set_access(u32 access, struct nfs4_ol_stateid *stp)
{ unsignedchar mask = 1 << access;
/* test whether a given stateid is denying specific access */ staticinlinebool
test_deny(u32 deny, struct nfs4_ol_stateid *stp)
{ unsignedchar mask = 1 << deny;
return (bool)(stp->st_deny_bmap & mask);
}
staticint nfs4_access_to_omode(u32 access)
{ switch (access & NFS4_SHARE_ACCESS_BOTH) { case NFS4_SHARE_ACCESS_READ: return O_RDONLY; case NFS4_SHARE_ACCESS_WRITE: return O_WRONLY; case NFS4_SHARE_ACCESS_BOTH: return O_RDWR;
}
WARN_ON_ONCE(1); return O_RDONLY;
}
if (!f) return NULL;
spin_lock(&f->fi_lock);
ret = nfsd_file_get(f->fi_fds[O_RDWR]); if (!ret) {
ret = nfsd_file_get(f->fi_fds[O_WRONLY]); if (!ret)
ret = nfsd_file_get(f->fi_fds[O_RDONLY]);
}
spin_unlock(&f->fi_lock); return ret;
}
if (f->fi_fds[O_RDWR]) return f->fi_fds[O_RDWR]; if (f->fi_fds[O_WRONLY]) return f->fi_fds[O_WRONLY]; if (f->fi_fds[O_RDONLY]) return f->fi_fds[O_RDONLY]; return NULL;
}
/* * Start with a single page hash table to reduce resizing churn * on light workloads.
*/
.min_size = 256,
.automatic_shrinking = true,
};
/* * Check if courtesy clients have conflicting access and resolve it if possible * * access: is op_share_access if share_access is true. * Check if access mode, op_share_access, would conflict with * the current deny mode of the file 'fp'. * access: is op_share_deny if share_access is false. * Check if the deny mode, op_share_deny, would conflict with * current access of the file 'fp'. * stp: skip checking this entry. * new_stp: normal open, not open upgrade. * * Function returns: * false - access/deny mode conflict with normal client. * true - no conflict or conflict with courtesy client(s) is resolved.
*/ staticbool
nfs4_resolve_deny_conflicts_locked(struct nfs4_file *fp, bool new_stp, struct nfs4_ol_stateid *stp, u32 access, bool share_access)
{ struct nfs4_ol_stateid *st; bool resolvable = true; unsignedchar bmap; struct nfsd_net *nn; struct nfs4_client *clp;
lockdep_assert_held(&fp->fi_lock);
list_for_each_entry(st, &fp->fi_stateids, st_perfile) { /* ignore lock stateid */ if (st->st_openstp) continue; if (st == stp && new_stp) continue; /* check file access against deny mode or vice versa */
bmap = share_access ? st->st_deny_bmap : st->st_access_bmap; if (!(access & bmap_to_share_mode(bmap))) continue;
clp = st->st_stid.sc_client; if (try_to_expire_client(clp)) continue;
resolvable = false; break;
} if (resolvable) {
clp = stp->st_stid.sc_client;
nn = net_generic(clp->net, nfsd_net_id);
mod_delayed_work(laundry_wq, &nn->laundromat_work, 0);
} return resolvable;
}
static __be32 nfs4_file_check_deny(struct nfs4_file *fp, u32 deny)
{ /* Common case is that there is no deny mode. */ if (deny) { /* Does this deny mode make sense? */ if (deny & ~NFS4_SHARE_DENY_BOTH) return nfserr_inval;
if ((deny & NFS4_SHARE_DENY_READ) &&
atomic_read(&fp->fi_access[O_RDONLY])) return nfserr_share_denied;
if (access & NFS4_SHARE_ACCESS_WRITE)
__nfs4_file_put_access(fp, O_WRONLY); if (access & NFS4_SHARE_ACCESS_READ)
__nfs4_file_put_access(fp, O_RDONLY);
}
/* * Allocate a new open/delegation state counter. This is needed for * pNFS for proper return on close semantics. * * Note that we only allocate it for pNFS-enabled exports, otherwise * all pointers to struct nfs4_clnt_odstate are always NULL.
*/ staticstruct nfs4_clnt_odstate *
alloc_clnt_odstate(struct nfs4_client *clp)
{ struct nfs4_clnt_odstate *co;
co = kmem_cache_zalloc(odstate_slab, GFP_KERNEL); if (co) {
co->co_client = clp;
refcount_set(&co->co_odcount, 1);
} return co;
}
stid = nfs4_alloc_stid(clp, stateid_slab, nfs4_free_ol_stateid); if (!stid) return NULL;
return openlockstateid(stid);
}
/* * As the sc_free callback of deleg, this may be called by nfs4_put_stid * in nfsd_break_one_deleg. * Considering nfsd_break_one_deleg is called with the flc->flc_lock held, * this function mustn't ever sleep.
*/ staticvoid nfs4_free_deleg(struct nfs4_stid *stid)
{ struct nfs4_delegation *dp = delegstateid(stid);
/* * When we recall a delegation, we should be careful not to hand it * out again straight away. * To ensure this we keep a pair of bloom filters ('new' and 'old') * in which the filehandles of recalled delegations are "stored". * If a filehandle appear in either filter, a delegation is blocked. * When a delegation is recalled, the filehandle is stored in the "new" * filter. * Every 30 seconds we swap the filters and clear the "new" one, * unless both are empty of course. This results in delegations for a * given filehandle being blocked for between 30 and 60 seconds. * * Each filter is 256 bits. We hash the filehandle to 32bit and use the * low 3 bytes as hash-table indices. * * 'blocked_delegations_lock', which is always taken in block_delegations(), * is used to manage concurrent access. Testing does not need the lock * except when swapping the two filters.
*/ static DEFINE_SPINLOCK(blocked_delegations_lock); staticstruct bloom_pair { int entries, old_entries;
time64_t swap_time; intnew; /* index into 'set' */
DECLARE_BITMAP(set[2], 256);
} blocked_delegations;
/** * nfs4_delegation_exists - Discover if this delegation already exists * @clp: a pointer to the nfs4_client we're granting a delegation to * @fp: a pointer to the nfs4_file we're granting a delegation on * * Return: * On success: true iff an existing delegation is found
*/
/** * hash_delegation_locked - Add a delegation to the appropriate lists * @dp: a pointer to the nfs4_delegation we are adding. * @fp: a pointer to the nfs4_file we're granting a delegation on * * Return: * On success: NULL if the delegation was successfully hashed. * * On error: -EAGAIN if one was previously granted to this * nfs4_client for this nfs4_file. Delegation is not hashed. *
*/
spin_lock(&state_lock);
unhashed = unhash_delegation_locked(dp, SC_STATUS_CLOSED);
spin_unlock(&state_lock); if (unhashed)
destroy_unhashed_deleg(dp);
}
/** * revoke_delegation - perform nfs4 delegation structure cleanup * @dp: pointer to the delegation * * This function assumes that it's called either from the administrative * interface (nfsd4_revoke_states()) that's revoking a specific delegation * stateid or it's called from a laundromat thread (nfsd4_landromat()) that * determined that this specific state has expired and needs to be revoked * (both mark state with the appropriate stid sc_status mode). It is also * assumed that a reference was taken on the @dp state. * * If this function finds that the @dp state is SC_STATUS_FREED it means * that a FREE_STATEID operation for this stateid has been processed and * we can proceed to removing it from recalled list. However, if @dp state * isn't marked SC_STATUS_FREED, it means we need place it on the cl_revoked * list and wait for the FREE_STATEID to arrive from the client. At the same * time, we need to mark it as SC_STATUS_FREEABLE to indicate to the * nfsd4_free_stateid() function that this stateid has already been added * to the cl_revoked list and that nfsd4_free_stateid() is now responsible * for removing it from the list. Inspection of where the delegation state * in the revocation process is protected by the clp->cl_lock.
*/ staticvoid revoke_delegation(struct nfs4_delegation *dp)
{ struct nfs4_client *clp = dp->dl_stid.sc_client;
/* * A stateid that had a deny mode associated with it is being released * or downgraded. Recalculate the deny mode on the file.
*/ staticvoid
recalculate_deny_mode(struct nfs4_file *fp)
{ struct nfs4_ol_stateid *stp;
u32 old_deny;
for (i = 1; i < 4; i++) { if ((i & deny) != i) {
change = true;
clear_deny(i, stp);
}
}
/* Recalculate per-file deny mode if there was a change */ if (change)
recalculate_deny_mode(stp->st_stid.sc_file);
}
/* release all access and file references for a given stateid */ staticvoid
release_all_access(struct nfs4_ol_stateid *stp)
{ int i; struct nfs4_file *fp = stp->st_stid.sc_file;
if (fp && stp->st_deny_bmap != 0)
recalculate_deny_mode(fp);
for (i = 1; i < 4; i++) { if (test_access(i, stp))
nfs4_file_put_access(stp->st_stid.sc_file, i);
clear_access(i, stp);
}
}
/* * Put the persistent reference to an already unhashed generic stateid, while * holding the cl_lock. If it's the last reference, then put it onto the * reaplist for later destruction.
*/ staticvoid put_ol_stateid_locked(struct nfs4_ol_stateid *stp, struct list_head *reaplist)
{ struct nfs4_stid *s = &stp->st_stid; struct nfs4_client *clp = s->sc_client;
lockdep_assert_held(&clp->cl_lock);
WARN_ON_ONCE(!list_empty(&stp->st_locks));
if (!refcount_dec_and_test(&s->sc_count)) {
wake_up_all(&close_wq); return;
}
idr_remove(&clp->cl_stateids, s->sc_stateid.si_opaque.so_id); if (s->sc_status & SC_STATUS_ADMIN_REVOKED)
atomic_dec(&s->sc_client->cl_admin_revoked);
list_add(&stp->st_locks, reaplist);
}
/* * Free a list of generic stateids that were collected earlier after being * fully unhashed.
*/ staticvoid
free_ol_stateid_reaplist(struct list_head *reaplist)
{ struct nfs4_ol_stateid *stp; struct nfs4_file *fp;
might_sleep();
while (!list_empty(reaplist)) {
stp = list_first_entry(reaplist, struct nfs4_ol_stateid,
st_locks);
list_del(&stp->st_locks);
fp = stp->st_stid.sc_file;
stp->st_stid.sc_free(&stp->st_stid); if (fp)
put_nfs4_file(fp);
}
}
spin_lock(&nn->client_lock);
s = oo->oo_last_closed_stid; if (s) {
list_del_init(&oo->oo_close_lru);
oo->oo_last_closed_stid = NULL;
}
spin_unlock(&nn->client_lock); if (s)
nfs4_put_stid(&s->st_stid);
}
/** * nfsd4_revoke_states - revoke all nfsv4 states associated with given filesystem * @net: used to identify instance of nfsd (there is one per net namespace) * @sb: super_block used to identify target filesystem * * All nfs4 states (open, lock, delegation, layout) held by the server instance * and associated with a file on the given filesystem will be revoked resulting * in any files being closed and so all references from nfsd to the filesystem * being released. Thus nfsd will no longer prevent the filesystem from being * unmounted. * * The clients which own the states will subsequently being notified that the * states have been "admin-revoked".
*/ void nfsd4_revoke_states(struct net *net, struct super_block *sb)
{ struct nfsd_net *nn = net_generic(net, nfsd_net_id); unsignedint idhashval; unsignedint sc_types;
/* * Bump the seqid on cstate->replay_owner, and clear replay_owner if it * won't be used for replay.
*/ void nfsd4_bump_seqid(struct nfsd4_compound_state *cstate, __be32 nfserr)
{ struct nfs4_stateowner *so = cstate->replay_owner;
if (nfserr == nfserr_replay_me) return;
if (!seqid_mutating_err(ntohl(nfserr))) {
nfsd4_cstate_clear_replay(cstate); return;
} if (!so) return; if (so->so_is_open_owner)
release_last_closed_stateid(openowner(so));
so->so_seqid++; return;
}
/* * The protocol defines ca_maxresponssize_cached to include the size of * the rpc header, but all we need to cache is the data starting after * the end of the initial SEQUENCE operation--the rest we regenerate * each time. Therefore we can advertise a ca_maxresponssize_cached * value that is the number of bytes in our cache plus a few additional * bytes. In order to stay on the safe side, and not promise more than * we can cache, those additional bytes must be the minimum possible: 24 * bytes of rpc header (xid through accept state, with AUTH_NULL * verifier), 12 for the compound header (with zero-length tag), and 44 * for the SEQUENCE op response:
*/ #define NFSD_MIN_HDR_SEQ_SZ (24 + 12 + 44)
staticstruct shrinker *nfsd_slot_shrinker; static DEFINE_SPINLOCK(nfsd_session_list_lock); static LIST_HEAD(nfsd_session_list); /* The sum of "target_slots-1" on every session. The shrinker can push this * down, though it can take a little while for the memory to actually * be freed. The "-1" is because we can never free slot 0 while the * session is active.
*/ static atomic_t nfsd_total_target_slots = ATOMIC_INIT(0);
staticvoid
free_session_slots(struct nfsd4_session *ses, int from)
{ int i;
if (from >= ses->se_fchannel.maxreqs) return;
for (i = from; i < ses->se_fchannel.maxreqs; i++) { struct nfsd4_slot *slot = xa_load(&ses->se_slots, i);
/* * Save the seqid in case we reactivate this slot. * This will never require a memory allocation so GFP * flag is irrelevant
*/
xa_store(&ses->se_slots, i, xa_mk_value(slot->sl_seqid), 0);
free_svc_cred(&slot->sl_cred);
kfree(slot);
}
ses->se_fchannel.maxreqs = from; if (ses->se_target_maxslots > from) { int new_target = from ?: 1;
atomic_sub(ses->se_target_maxslots - new_target, &nfsd_total_target_slots);
ses->se_target_maxslots = new_target;
}
}
/** * reduce_session_slots - reduce the target max-slots of a session if possible * @ses: The session to affect * @dec: how much to decrease the target by * * This interface can be used by a shrinker to reduce the target max-slots * for a session so that some slots can eventually be freed. * It uses spin_trylock() as it may be called in a context where another * spinlock is held that has a dependency on client_lock. As shrinkers are * best-effort, skiping a session is client_lock is already held has no * great coast * * Return value: * The number of slots that the target was reduced by.
*/ staticint
reduce_session_slots(struct nfsd4_session *ses, int dec)
{ struct nfsd_net *nn = net_generic(ses->se_client->net,
nfsd_net_id); int ret = 0;
if (ses->se_target_maxslots <= 1) return ret; if (!spin_trylock(&nn->client_lock)) return ret;
ret = min(dec, ses->se_target_maxslots-1);
ses->se_target_maxslots -= ret;
atomic_sub(ret, &nfsd_total_target_slots);
ses->se_slot_gen += 1; if (ses->se_slot_gen == 0) { int i;
ses->se_slot_gen = 1; for (i = 0; i < ses->se_fchannel.maxreqs; i++) { struct nfsd4_slot *slot = xa_load(&ses->se_slots, i);
slot->sl_generation = 0;
}
}
spin_unlock(&nn->client_lock); return ret;
}
/* * The RPC and NFS session headers are never saved in * the slot reply cache buffer.
*/
size = fattrs->maxresp_cached < NFSD_MIN_HDR_SEQ_SZ ?
0 : fattrs->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
nfsd4_hash_conn(conn, ses);
ret = nfsd4_register_conn(conn); if (ret) /* oops; xprt is already down: */
nfsd4_conn_lost(&conn->cn_xpt_user); /* We may have gained or lost a callback channel: */
nfsd4_probe_callback_sync(ses->se_client);
}
if (cses->flags & SESSION4_BACK_CHAN)
dir |= NFS4_CDFC4_BACK; return alloc_conn(rqstp, dir);
}
/* must be called under client_lock */ staticvoid nfsd4_del_conns(struct nfsd4_session *s)
{ struct nfs4_client *clp = s->se_client; struct nfsd4_conn *c;
spin_lock(&clp->cl_lock); while (!list_empty(&s->se_conns)) {
c = list_first_entry(&s->se_conns, struct nfsd4_conn, cn_persession);
list_del_init(&c->cn_persession);
spin_unlock(&clp->cl_lock);
{ struct sockaddr *sa = svc_addr(rqstp); /* * This is a little silly; with sessions there's no real * use for the callback address. Use the peer address * as a reasonable default for now, but consider fixing * the rpc client not to require an address in the * future:
*/
rpc_copy_addr((struct sockaddr *)&clp->cl_cb_conn.cb_addr, sa);
clp->cl_cb_conn.cb_addrlen = svc_addr_len(sa);
}
}
/* caller must hold client_lock */ staticstruct nfsd4_session *
__find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net)
{ struct nfsd4_session *elem; int idx; struct nfsd_net *nn = net_generic(net, nfsd_net_id);
lockdep_assert_held(&nn->client_lock);
dump_sessionid(__func__, sessionid);
idx = hash_sessionid(sessionid); /* Search in the appropriate list */
list_for_each_entry(elem, &nn->sessionid_hashtbl[idx], se_hash) { if (!memcmp(elem->se_sessionid.data, sessionid->data,
NFS4_MAX_SESSIONID_LEN)) { return elem;
}
}
dprintk("%s: session not found\n", __func__); return NULL;
}
staticstruct nfsd4_session *
find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid, struct net *net,
__be32 *ret)
{ struct nfsd4_session *session;
__be32 status = nfserr_badsession;
session = __find_in_sessionid_hashtbl(sessionid, net); if (!session) goto out;
status = nfsd4_get_session_locked(session); if (status)
session = NULL;
out:
*ret = status; return session;
}
/* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ staticint
STALE_CLIENTID(clientid_t *clid, struct nfsd_net *nn)
{ /* * We're assuming the clid was not given out from a boot * precisely 2^32 (about 136 years) before this one. That seems * a safe assumption:
*/ if (clid->cl_boot == (u32)nn->boot_time) return 0;
trace_nfsd_clid_stale(clid); return 1;
}
/* must be called under the client_lock */ staticvoid
unhash_client_locked(struct nfs4_client *clp)
{ struct nfsd_net *nn = net_generic(clp->net, nfsd_net_id); struct nfsd4_session *ses;
lockdep_assert_held(&nn->client_lock);
/* Mark the client as expired! */
clp->cl_time = 0; /* Make it invisible */ if (!list_empty(&clp->cl_idhash)) {
list_del_init(&clp->cl_idhash); if (test_bit(NFSD4_CLIENT_CONFIRMED, &clp->cl_flags))
rb_erase(&clp->cl_namenode, &nn->conf_name_tree); else
rb_erase(&clp->cl_namenode, &nn->unconf_name_tree);
}
list_del_init(&clp->cl_lru);
spin_lock(&clp->cl_lock);
spin_lock(&nfsd_session_list_lock);
list_for_each_entry(ses, &clp->cl_sessions, se_perclnt) {
list_del_init(&ses->se_hash);
list_del_init(&ses->se_all_sessions);
}
spin_unlock(&nfsd_session_list_lock);
spin_unlock(&clp->cl_lock);
}
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.