#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;
slotid4 rfs4_back_max_slots = MAXSLOTS_BACK;
typedef union {
struct {
uint32_t pad0;
uint32_t pad1;
uint32_t start_time;
uint32_t s_id;
} impl_id;
sessionid4 id4;
} rfs4_sid;
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);
}
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();
sp = (rfs4_session_t *)rfs4_dbsearch(nsrv4->rfs4_session_idx,
sessid, &create, NULL, RFS4_DBS_VALID);
return (sp);
}
rfs4_session_t *
rfs4x_createsession(session41_create_t *ap)
{
static 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();
ap->cs_id = key.impl_id.s_id = atomic_inc_32_nv(&session_id_counter);
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);
}
return (sp);
}
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);
}
nfsstat4
rfs4x_destroysession(rfs4_session_t *sp, unsigned useref)
{
nfsstat4 status = NFS4_OK;
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);
}
void
rfs4x_client_session_remove(rfs4_client_t *cp)
{
rfs4_session_t *sp;
rfs4_dbe_lock(cp->rc_dbe);
while ((sp = list_head(&cp->rc_sessions)) != NULL) {
list_remove(&cp->rc_sessions, sp);
rfs4_dbe_invalidate(sp->sn_dbe);
}
rfs4_dbe_unlock(cp->rc_dbe);
}
#define NFS4_MIN_COMPOUND_REQSZ (( \
2 + 2 + \
1 + \
3 + \
(RNDUP(NFS4_SESSIONID_SIZE) / BYTES_PER_XDR_UNIT) + \
4) * \
BYTES_PER_XDR_UNIT)
#define NFS4_MIN_COMPOUND_RESPSZ (( \
2 + \
1 + \
1 + \
3 + \
(RNDUP(NFS4_SESSIONID_SIZE) / BYTES_PER_XDR_UNIT) + \
4) * \
BYTES_PER_XDR_UNIT)
#define NFS4_MIN_CB_COMPOUND_REQSZ NFS4_MIN_COMPOUND_REQSZ
#define NFS4_MIN_CB_COMPOUND_RESPSZ NFS4_MIN_COMPOUND_RESPSZ
#define NFS4_SLOT_CACHED_SIZE 2048
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;
if (scp->cn_attrs.ca_maxrequestsize < NFS4_MIN_COMPOUND_REQSZ)
return (NFS4ERR_TOOSMALL);
if (scp->cn_attrs.ca_maxresponsesize < NFS4_MIN_COMPOUND_RESPSZ)
return (NFS4ERR_TOOSMALL);
if (scp->cn_back_attrs.ca_maxrequestsize < NFS4_MIN_CB_COMPOUND_REQSZ)
return (NFS4ERR_TOOSMALL);
if (scp->cn_back_attrs.ca_maxresponsesize < NFS4_MIN_CB_COMPOUND_RESPSZ)
return (NFS4ERR_TOOSMALL);
scp->cn_attrs.ca_maxresponsesize_cached =
MIN(scp->cn_attrs.ca_maxresponsesize_cached,
NFS4_SLOT_CACHED_SIZE + NFS4_MIN_HDR_SEQSZ);
scp->cn_back_attrs.ca_maxresponsesize_cached = 0;
return (NFS4_OK);
}
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);
}
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:
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) {
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);
sp->sn_clnt = (rfs4_client_t *)ap->cs_client;
rfs4_dbe_hold(sp->sn_clnt->rc_dbe);
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;
if (!nfs4x_csa_flags_valid(ap->cs_aotw.csa_flags)) {
ap->cs_error = NFS4ERR_INVAL;
goto err;
}
sp->sn_csflags = ap->cs_aotw.csa_flags;
if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_PERSIST)
sp->sn_csflags &= ~CREATE_SESSION4_FLAG_PERSIST;
if (ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_CONN_RDMA)
sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_RDMA;
sp->sn_bc.progno = ap->cs_aotw.csa_cb_program;
sp->sn_laccess = nfs_sys_uptime();
sp->sn_flags = 0;
sp->sn_rcached = 0;
DTRACE_PROBE1(csa__flags, uint32_t, ap->cs_aotw.csa_flags);
dir = bdrpc ? (CDFS4_FORE | CDFS4_BACK) : CDFS4_FORE;
ocp = rfs41_create_session_channel(dir);
ocp->cn_dir = dir;
sp->sn_fore = ocp;
ocp->cn_attrs = ap->cs_aotw.csa_fore_chan_attrs;
ocp->cn_back_attrs = ap->cs_aotw.csa_back_chan_attrs;
sle = sess_chan_limits(ocp);
if (sle != NFS4_OK) {
ap->cs_error = sle;
goto err_free_chan;
}
if (!client_insert_session(sp->sn_clnt, sp)) {
ap->cs_error = NFS4ERR_DELAY;
goto err_free_chan;
}
if (bdrpc) {
VERIFY(0);
} else {
sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
sp->sn_back = NULL;
}
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;
}
if (sp->sn_slots) {
slots_free(sp->sn_slots, sp->sn_fore->cn_attrs.ca_maxrequests);
sp->sn_slots = NULL;
}
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)
{
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, -1);
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)
{
}