summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/fs/nfs/nfs4x_state.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/fs/nfs/nfs4x_state.c')
-rw-r--r--usr/src/uts/common/fs/nfs/nfs4x_state.c576
1 files changed, 576 insertions, 0 deletions
diff --git a/usr/src/uts/common/fs/nfs/nfs4x_state.c b/usr/src/uts/common/fs/nfs/nfs4x_state.c
new file mode 100644
index 0000000000..aba02734fc
--- /dev/null
+++ b/usr/src/uts/common/fs/nfs/nfs4x_state.c
@@ -0,0 +1,576 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ * Copyright 2011 Nexenta Systems, Inc. All rights reserved.
+ * Copyright 2017 RackTop Systems.
+ */
+
+#include <sys/sdt.h>
+#include <sys/atomic.h>
+#include <nfs/nfs4.h>
+
+#ifdef DEBUG
+#define RFS4_TABSIZE 17
+#else
+#define RFS4_TABSIZE 2047
+#endif
+
+#define RFS4_MAXTABSZ 1024*1024
+
+slotid4 rfs4_max_slots = MAXSLOTS; /* fore channel */
+slotid4 rfs4_back_max_slots = MAXSLOTS_BACK; /* back channel */
+
+typedef union {
+ /* Both members have the same size */
+ struct {
+ uint32_t pad0;
+ uint32_t pad1;
+ uint32_t start_time; /* NFS server start time */
+ uint32_t s_id; /* unique session index */
+ } impl_id;
+ sessionid4 id4;
+} rfs4_sid;
+
+/*
+ * --------------------------------------------------------
+ * MDS - NFSv4.1 Sessions
+ * --------------------------------------------------------
+ */
+static uint32_t
+sessid_hash(void *key)
+{
+ rfs4_sid *idp = key;
+
+ return (idp->impl_id.s_id);
+}
+
+static bool_t
+sessid_compare(rfs4_entry_t entry, void *key)
+{
+ rfs4_session_t *sp = (rfs4_session_t *)entry;
+ sessionid4 *idp = (sessionid4 *)key;
+
+ return (bcmp(idp, &sp->sn_sessid, sizeof (sessionid4)) == 0);
+}
+
+static void *
+sessid_mkkey(rfs4_entry_t entry)
+{
+ rfs4_session_t *sp = (rfs4_session_t *)entry;
+
+ return (&sp->sn_sessid);
+}
+
+/* ARGSUSED */
+static bool_t
+cmp_false(rfs4_entry_t entry, void *key)
+{
+ return (FALSE);
+}
+
+/* ARGSUSED */
+static void *
+mkkey_null(rfs4_entry_t entry)
+{
+ return (NULL);
+}
+
+void
+rfs4x_session_rele(rfs4_session_t *sp)
+{
+ rfs4_dbe_rele(sp->sn_dbe);
+}
+
+void
+rfs4x_session_hold(rfs4_session_t *sp)
+{
+ rfs4_dbe_hold(sp->sn_dbe);
+}
+
+rfs4_session_t *
+rfs4x_findsession_by_id(sessionid4 sessid)
+{
+ rfs4_session_t *sp;
+ bool_t create = FALSE;
+ nfs4_srv_t *nsrv4 = nfs4_get_srv();
+
+ rw_enter(&nsrv4->findsession_lock, RW_READER);
+ sp = (rfs4_session_t *)rfs4_dbsearch(nsrv4->rfs4_session_idx,
+ sessid, &create, NULL, RFS4_DBS_VALID);
+ rw_exit(&nsrv4->findsession_lock);
+
+ return (sp);
+}
+
+/*
+ * A clientid can have multiple sessions associated with it. Hence,
+ * performing a raw 'mds_findsession' (even for a create) might
+ * yield a list of sessions associated with the clientid in question.
+ * Call rfs4_dbseach() function with key that cannot be found
+ * and create an association between the session table and both
+ * primary (sessionid) index and secondary (clientid) index for the
+ * newly created session.
+ */
+
+rfs4_session_t *
+rfs4x_createsession(session41_create_t *ap)
+{
+ static volatile uint32_t session_id_counter;
+
+ rfs4_session_t *sp = NULL;
+ bool_t create = TRUE;
+ rfs4_sid key = {0, 0, 0, 0};
+ nfs4_srv_t *nsrv4 = nfs4_get_srv();
+
+ /*
+ * Use unique counter for s_id and s_id to ensure that
+ * created entry will have the same index in dbi_buckets[]
+ */
+ ap->cs_id = key.impl_id.s_id = atomic_inc_32_nv(&session_id_counter);
+
+ rw_enter(&nsrv4->findsession_lock, RW_WRITER);
+ if ((sp = (rfs4_session_t *)rfs4_dbsearch(nsrv4->rfs4_session_idx,
+ &key, &create, (void *)ap, RFS4_DBS_VALID)) == NULL) {
+ DTRACE_PROBE1(mds__srv__createsession__fail,
+ session41_create_t *, ap);
+ }
+ rw_exit(&nsrv4->findsession_lock);
+ return (sp);
+}
+
+/* return success of operation */
+static bool_t
+client_insert_session(rfs4_client_t *cp, rfs4_session_t *sp)
+{
+ bool_t res = TRUE;
+
+ rfs4_dbe_lock(cp->rc_dbe);
+ if (cp->rc_destroying)
+ res = FALSE;
+ else
+ list_insert_tail(&cp->rc_sessions, sp);
+ rfs4_dbe_unlock(cp->rc_dbe);
+
+ return (res);
+}
+
+static void
+client_remove_session(rfs4_client_t *cp, rfs4_session_t *sp)
+{
+ rfs4_dbe_lock(cp->rc_dbe);
+ if (list_link_active(&sp->sn_node))
+ list_remove(&cp->rc_sessions, sp);
+ rfs4_dbe_unlock(cp->rc_dbe);
+}
+
+/*
+ * Invalidate the session in the DB (so it can't be found anymore)
+ */
+nfsstat4
+rfs4x_destroysession(rfs4_session_t *sp, unsigned useref)
+{
+ nfsstat4 status = NFS4_OK;
+
+ /*
+ * RFC 7862 Section 14.1.3:
+ * In hindsight, the NFSv4.1 specification should have
+ * mandated that DESTROY_SESSION either abort or complete
+ * all outstanding operations.
+ */
+ rfs4_dbe_lock(sp->sn_dbe);
+ if (rfs4_dbe_refcnt(sp->sn_dbe) > useref)
+ status = NFS4ERR_DELAY;
+ else
+ rfs4_dbe_invalidate(sp->sn_dbe);
+ rfs4_dbe_unlock(sp->sn_dbe);
+
+ if (status == NFS4_OK)
+ client_remove_session(sp->sn_clnt, sp);
+
+ return (status);
+}
+
+/* Invalidate all client's sessions */
+void
+rfs4x_client_session_remove(rfs4_client_t *cp)
+{
+ rfs4_session_t *sp;
+
+ /*
+ * Client is forcibly closing so invalidate all sessions
+ * without checking the refcount.
+ */
+ rfs4_dbe_lock(cp->rc_dbe);
+ while ((sp = list_head(&cp->rc_sessions)) != NULL) {
+ list_remove(&cp->rc_sessions, sp);
+
+ rfs4_dbe_lock(sp->sn_dbe);
+ rfs4_dbe_invalidate(sp->sn_dbe);
+ rfs4_dbe_unlock(sp->sn_dbe);
+
+ }
+ rfs4_dbe_unlock(cp->rc_dbe);
+}
+
+nfsstat4
+sess_chan_limits(sess_channel_t *scp)
+{
+ if (scp->cn_attrs.ca_maxrequests > rfs4_max_slots) {
+ scp->cn_attrs.ca_maxrequests = rfs4_max_slots;
+ }
+
+ if (scp->cn_back_attrs.ca_maxrequests > rfs4_back_max_slots)
+ scp->cn_back_attrs.ca_maxrequests = rfs4_back_max_slots;
+
+
+ if (scp->cn_attrs.ca_maxoperations > NFS4_COMPOUND_LIMIT)
+ scp->cn_attrs.ca_maxoperations = NFS4_COMPOUND_LIMIT;
+
+ /*
+ * Lower limit should be set to smallest sane COMPOUND. Even
+ * though a singleton SEQUENCE op is the very smallest COMPOUND,
+ * it's also quite boring. For all practical purposes, the lower
+ * limit for creating a sess is limited to:
+ *
+ * [SEQUENCE + PUTROOTFH + GETFH]
+ *
+ * XXX - can't limit READ's to a specific threshold, otherwise
+ * we artificially limit the clients to perform reads of
+ * AT LEAST that granularity, which is WRONG !!! Same goes
+ * for READDIR's and GETATTR's.
+ */
+ if (scp->cn_attrs.ca_maxresponsesize < (sizeof (SEQUENCE4res) +
+ sizeof (PUTROOTFH4res) + sizeof (GETFH4res)))
+ return (NFS4ERR_TOOSMALL);
+ return (NFS4_OK);
+}
+
+/*
+ * NFSv4.1 Slot replay cache
+ */
+static void
+rfs41_cleanup_slot(rfs4_slot_t *se)
+{
+ rfs4_compound_free((COMPOUND4res *)&se->se_buf);
+}
+
+static rfs4_slot_t *
+slots_alloc(size_t n)
+{
+ rfs4_slot_t *p;
+ int i;
+
+ p = kmem_zalloc(sizeof (rfs4_slot_t) * n, KM_SLEEP);
+ for (i = 0; i < n; i++) {
+ mutex_init(&p[i].se_lock, NULL, MUTEX_DEFAULT, NULL);
+ }
+
+ return (p);
+}
+
+static void
+slots_free(rfs4_slot_t *slots, size_t n)
+{
+ int i;
+
+ for (i = 0; i < n; i++) {
+ rfs4_slot_t *slot = &slots[i];
+
+ mutex_destroy(&slot->se_lock);
+
+ if (slot->se_flags & RFS4_SLOT_CACHED) {
+ rfs41_cleanup_slot(slot);
+ }
+ }
+ kmem_free(slots, sizeof (rfs4_slot_t) * n);
+}
+
+/* Additional functions */
+
+/* check csa_flags for OP_CREATE_SESSION */
+bool_t
+nfs4x_csa_flags_valid(uint32_t flags)
+{
+ if (flags & ~CREATE_SESSION4_FLAG_MASK)
+ return (FALSE);
+
+ return (TRUE);
+}
+
+sess_channel_t *
+rfs41_create_session_channel(channel_dir_from_server4 dir)
+{
+ sess_channel_t *cp;
+ sess_bcsd_t *bp;
+
+ cp = (sess_channel_t *)kmem_zalloc(sizeof (sess_channel_t), KM_SLEEP);
+ rw_init(&cp->cn_lock, NULL, RW_DEFAULT, NULL);
+
+ switch (dir) {
+ case CDFS4_FORE:
+ break;
+
+ case CDFS4_BOTH:
+ case CDFS4_BACK:
+ /* BackChan Specific Data */
+ bp = (sess_bcsd_t *)kmem_zalloc(sizeof (sess_bcsd_t), KM_SLEEP);
+ rw_init(&bp->bsd_rwlock, NULL, RW_DEFAULT, NULL);
+ cp->cn_csd = (sess_bcsd_t *)bp;
+ break;
+ }
+ return (cp);
+}
+
+void
+rfs41_destroy_session_channel(rfs4_session_t *sp)
+{
+ sess_channel_t *cp;
+ sess_bcsd_t *bp;
+
+ if (sp->sn_back != NULL) {
+ /* only one channel for both direction for now */
+ ASSERT(sp->sn_fore == sp->sn_back);
+
+ cp = sp->sn_back;
+ bp = (sess_bcsd_t *)cp->cn_csd;
+ rw_destroy(&bp->bsd_rwlock);
+ kmem_free(bp, sizeof (sess_bcsd_t));
+ } else {
+ cp = sp->sn_fore;
+ }
+
+ rw_destroy(&cp->cn_lock);
+ kmem_free(cp, sizeof (sess_channel_t));
+
+ sp->sn_back = NULL;
+ sp->sn_fore = NULL;
+}
+
+static bool_t
+rfs4_session_create(rfs4_entry_t u_entry, void *arg)
+{
+ rfs4_session_t *sp = (rfs4_session_t *)u_entry;
+ session41_create_t *ap = (session41_create_t *)arg;
+ sess_channel_t *ocp = NULL;
+ rfs4_sid *sidp;
+ bool_t bdrpc = FALSE;
+ channel_dir_from_server4 dir;
+ nfsstat4 sle;
+ nfs4_srv_t *nsrv4 = nfs4_get_srv();
+
+ ASSERT(sp != NULL);
+ if (sp == NULL)
+ return (FALSE);
+
+ /*
+ * Back pointer/ref to parent data struct (rfs4_client_t)
+ */
+ sp->sn_clnt = (rfs4_client_t *)ap->cs_client;
+ rfs4_dbe_hold(sp->sn_clnt->rc_dbe);
+
+ /*
+ * Handcrafting the session id
+ */
+ sidp = (rfs4_sid *)&sp->sn_sessid;
+ sidp->impl_id.pad0 = 0x00000000;
+ sidp->impl_id.pad1 = 0xFFFFFFFF;
+ sidp->impl_id.start_time = nsrv4->rfs4_start_time;
+ sidp->impl_id.s_id = ap->cs_id;
+
+ /*
+ * Process csa_flags; note that CREATE_SESSION4_FLAG_CONN_BACK_CHAN
+ * is processed below since it affects direction and setup of the
+ * backchannel accordingly.
+ */
+ if (!nfs4x_csa_flags_valid(ap->cs_aotw.csa_flags)) {
+ ap->cs_error = NFS4ERR_INVAL;
+ goto err;
+ }
+
+ sp->sn_csflags = 0;
+ if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_PERSIST)
+ /* XXX - Worry about persistence later */
+ sp->sn_csflags &= ~CREATE_SESSION4_FLAG_PERSIST;
+
+ if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_CONN_RDMA)
+ /* XXX - No RDMA for now */
+ sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_RDMA;
+
+ /*
+ * Initialize some overall sessions values
+ */
+ sp->sn_bc.progno = ap->cs_aotw.csa_cb_program;
+ sp->sn_laccess = nfs_sys_uptime();
+ sp->sn_flags = 0;
+ sp->sn_rcached = 0;
+
+ /*
+ * Check if client has specified that the FORE channel should
+ * also be used for call back traffic (ie. bidir RPC). If so,
+ * let's try to accomodate the request.
+ */
+ DTRACE_PROBE1(csa__flags, uint32_t, ap->cs_aotw.csa_flags);
+
+ /*
+ * Session's channel flags depending on bdrpc
+ * TODO: Add backchannel handling, i.e. when bdrpc is TRUE
+ */
+ dir = bdrpc ? (CDFS4_FORE | CDFS4_BACK) : CDFS4_FORE;
+ ocp = rfs41_create_session_channel(dir);
+ ocp->cn_dir = dir;
+ sp->sn_fore = ocp;
+
+ /*
+ * Check if channel attrs will be flexible enough for future
+ * purposes. Channel attribute enforcement is done as part of
+ * COMPOUND processing.
+ */
+ ocp->cn_attrs = ap->cs_aotw.csa_fore_chan_attrs;
+ ocp->cn_back_attrs = ap->cs_aotw.csa_back_chan_attrs;
+ if (sle = sess_chan_limits(ocp)) {
+ ap->cs_error = sle;
+ goto err_free_chan;
+ }
+
+ /* will fail if client is going to destroy */
+ if (!client_insert_session(sp->sn_clnt, sp)) {
+ ap->cs_error = NFS4ERR_DELAY;
+ goto err_free_chan;
+ }
+
+ /*
+ * No need for locks/synchronization at this time,
+ * since we're barely creating the session.
+ */
+ if (bdrpc) {
+ /* Need to be implemented */
+ VERIFY(0);
+ } else {
+ /*
+ * If not doing bdrpc, then we expect the client to perform
+ * an explicit BIND_CONN_TO_SESSION if it wants callback
+ * traffic. Subsequently, the cb channel should be set up
+ * at that point along with its corresponding slot (see
+ * rfs41_bc_setup).
+ */
+ sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
+ sp->sn_back = NULL;
+
+ /*
+ * XXX 08/15/2008 (rick) - if the channel is not bidir when
+ * created in CREATE_SESSION, then we should save off
+ * the ap->cs_aotw.csa_back_chan_attrs in case later
+ * a bc2s is called to create the back channel.
+ */
+ }
+
+ /*
+ * Now we allocate space for the slrc, initializing each slot's
+ * sequenceid and slotid to zero and a (pre)cached result of
+ * NFS4ERR_SEQ_MISORDERED. Note that we zero out the entries
+ * by virtue of the z-alloc.
+ */
+ sp->sn_slots = slots_alloc(ocp->cn_attrs.ca_maxrequests);
+
+ return (TRUE);
+
+err_free_chan:
+ rfs41_destroy_session_channel(sp);
+err:
+ rfs4_dbe_rele(sp->sn_clnt->rc_dbe);
+ return (FALSE);
+}
+
+static void
+rfs4_session_destroy(rfs4_entry_t u_entry)
+{
+ rfs4_session_t *sp = (rfs4_session_t *)u_entry;
+ sess_bcsd_t *bsdp;
+
+ if (SN_CB_CHAN_EST(sp) && (bsdp = sp->sn_back->cn_csd) != NULL) {
+ slots_free(bsdp->bsd_slots,
+ sp->sn_back->cn_back_attrs.ca_maxrequests);
+ bsdp->bsd_slots = NULL;
+ }
+
+ /*
+ * Nuke slot replay cache for this session
+ */
+ if (sp->sn_slots) {
+ slots_free(sp->sn_slots, sp->sn_fore->cn_attrs.ca_maxrequests);
+ sp->sn_slots = NULL;
+ }
+
+ /*
+ * XXX - A session can have multiple BC clnt handles that need
+ * to be discarded. rfs4_session_inval calls CLNT_DESTROY
+ * which will remove the CB client handle from the global
+ * list (cb_clnt_list) now. This will have to change once
+ * we manage the BC clnt handles per session.
+ */
+
+ /*
+ * Remove the fore and back channels.
+ */
+ rfs41_destroy_session_channel(sp);
+
+ client_remove_session(sp->sn_clnt, sp);
+
+ rfs4_client_rele(sp->sn_clnt);
+}
+
+static bool_t
+rfs4_session_expiry(rfs4_entry_t u_entry)
+{
+ rfs4_session_t *sp = (rfs4_session_t *)u_entry;
+
+ if (sp == NULL || rfs4_dbe_is_invalid(sp->sn_dbe))
+ return (TRUE);
+
+ if (rfs4_lease_expired(sp->sn_clnt))
+ return (TRUE);
+
+ return (FALSE);
+}
+
+void
+rfs4x_state_init_locked(nfs4_srv_t *nsrv4)
+{
+ rw_init(&nsrv4->findsession_lock, NULL, RW_DEFAULT, NULL);
+
+ nsrv4->rfs4_session_tab = rfs4_table_create(nsrv4->nfs4_server_state,
+ "Session", 5 * rfs4_lease_time, 1, rfs4_session_create,
+ rfs4_session_destroy, rfs4_session_expiry, sizeof (rfs4_session_t),
+ RFS4_TABSIZE, RFS4_MAXTABSZ/8, 100);
+
+ nsrv4->rfs4_session_idx = rfs4_index_create(nsrv4->rfs4_session_tab,
+ "session_idx", sessid_hash, sessid_compare, sessid_mkkey, TRUE);
+}
+
+void
+rfs4x_state_fini(nfs4_srv_t *nsrv4)
+{
+ /* All tables will be destroyed by caller */
+ rw_destroy(&nsrv4->findsession_lock);
+}