#include <sys/sdt.h>
#include <sys/atomic.h>
#include <nfs/nfs4.h>
extern uint32_t clientid_hash(void *);
extern bool_t clientid_compare(rfs4_entry_t, void *);
extern void *clientid_mkkey(rfs4_entry_t);
#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;
static const callback_sec_parms4 rfs4x_cbsec_none = {
.cb_secflavor = AUTH_NONE
};
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);
}
static bool_t
sessid_clientid_compare(rfs4_entry_t entry, void *key)
{
rfs4_session_t *sp = (rfs4_session_t *)entry;
clientid4 *idp = (clientid4 *)key;
return (*idp == sp->sn_clnt->rc_clientid);
}
static void *
sessid_clientid_mkkey(rfs4_entry_t entry)
{
return (&(((rfs4_session_t *)entry)->sn_clnt->rc_clientid));
}
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 if (SN_CB_CHAN_EST(sp)) {
sess_channel_t *bcp = SNTOBC(sp);
sess_bcsd_t *bsdp;
rw_enter(&bcp->cn_lock, RW_READER);
bsdp = CTOBSD(bcp);
ASSERT(bsdp != NULL);
status = slot_cb_status(bsdp->bsd_stok);
mutex_enter(&bsdp->bsd_lock);
bsdp->bsd_stat = status;
mutex_exit(&bsdp->bsd_lock);
rw_exit(&bcp->cn_lock);
}
if (status == NFS4_OK)
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) {
if (list_link_active(&sp->sn_node))
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(rfs4_session_t *sp)
{
if (sp->cn_attrs.ca_maxrequests > rfs4_max_slots) {
sp->cn_attrs.ca_maxrequests = rfs4_max_slots;
}
if (sp->cn_back_attrs.ca_maxrequests < 1) {
return (NFS4ERR_INVAL);
}
sp->sn_bc.maxreqs = MIN(sp->cn_back_attrs.ca_maxrequests,
rfs4_back_max_slots);
if (sp->cn_attrs.ca_maxrequestsize < NFS4_MIN_COMPOUND_REQSZ)
return (NFS4ERR_TOOSMALL);
if (sp->cn_back_attrs.ca_maxrequestsize < NFS4_MIN_CB_COMPOUND_REQSZ)
return (NFS4ERR_TOOSMALL);
if (sp->cn_attrs.ca_maxoperations > NFS4_COMPOUND_LIMIT)
sp->cn_attrs.ca_maxoperations = NFS4_COMPOUND_LIMIT;
if (sp->cn_back_attrs.ca_maxoperations < 2)
return (NFS4ERR_TOOSMALL);
if (sp->cn_attrs.ca_maxresponsesize < (sizeof (SEQUENCE4res) +
sizeof (PUTROOTFH4res) + sizeof (GETFH4res)))
return (NFS4ERR_TOOSMALL);
if (sp->cn_attrs.ca_maxresponsesize < NFS4_MIN_COMPOUND_RESPSZ)
return (NFS4ERR_TOOSMALL);
if (sp->cn_back_attrs.ca_maxresponsesize < NFS4_MIN_CB_COMPOUND_RESPSZ)
return (NFS4ERR_TOOSMALL);
sp->cn_attrs.ca_maxresponsesize_cached =
MIN(sp->cn_attrs.ca_maxresponsesize_cached,
NFS4_SLOT_CACHED_SIZE + NFS4_MIN_HDR_SEQSZ);
sp->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);
}
void
rfs41_deleg_rs_hold(rfs4_deleg_state_t *dsp)
{
atomic_inc_32(&dsp->rds_rs.refcnt);
}
void
rfs41_deleg_rs_rele(rfs4_deleg_state_t *dsp)
{
ASSERT(dsp->rds_rs.refcnt > 0);
if (atomic_add_32_nv(&dsp->rds_rs.refcnt, -1) == 0) {
bzero(dsp->rds_rs.sessid, sizeof (sessionid4));
dsp->rds_rs.seqid = dsp->rds_rs.slotno = 0;
}
}
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);
mutex_init(&bp->bsd_lock, NULL, MUTEX_DEFAULT, NULL);
cp->cn_csd = (sess_bcsd_t *)bp;
break;
}
return (cp);
}
void
rfs41_destroy_back_channel(sess_channel_t *bcp)
{
sess_bcsd_t *bsdp;
ASSERT(bcp != NULL);
bsdp = CTOBSD(bcp);
ASSERT(bsdp != NULL);
mutex_destroy(&bsdp->bsd_lock);
kmem_free(bsdp, sizeof (sess_bcsd_t));
rw_destroy(&bcp->cn_lock);
kmem_free(bcp, sizeof (sess_channel_t));
}
void
rfs4x_destroy_session_channel(rfs4_session_t *sp, channel_dir_from_server4 dir)
{
sess_channel_t *cp;
if (sp == NULL)
return;
if (dir == CDFS4_FORE && sp->sn_fore == NULL)
return;
if (dir == CDFS4_BACK && sp->sn_back == NULL)
return;
if (sp->sn_bdrpc) {
ASSERT(sp->sn_fore == sp->sn_back);
sp->sn_fore = NULL;
goto back;
}
if (dir == CDFS4_FORE || dir == CDFS4_BOTH) {
if (sp->sn_fore == NULL)
return;
cp = sp->sn_fore;
rw_destroy(&cp->cn_lock);
kmem_free(cp, sizeof (sess_channel_t));
sp->sn_fore = NULL;
}
if (dir == CDFS4_BACK || dir == CDFS4_BOTH) {
back:
if (sp->sn_back == NULL)
return;
cp = sp->sn_back;
rfs41_destroy_back_channel(cp);
sp->sn_back = 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;
rpcprog_t prog;
SVCMASTERXPRT *mxprt;
struct svc_req *req;
sess_bcsd_t *bsdp;
callback_sec_parms4 *secp = NULL;
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);
req = (struct svc_req *)ap->cs_req;
mxprt = (SVCMASTERXPRT *)req->rq_xprt->xp_master;
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_bc.cr = crget();
if (ap->cs_aotw.csa_sec_parms.csa_sec_parms_len == 0) {
secp = (callback_sec_parms4 *)&rfs4x_cbsec_none;
} else if (ap->cs_aotw.csa_sec_parms.csa_sec_parms_val != NULL) {
secp = ap->cs_aotw.csa_sec_parms.csa_sec_parms_val;
} else {
cmn_err(CE_WARN, "Invalid backchannel security.");
ap->cs_error = NFS4ERR_INVAL;
goto err;
}
if (!rfs4x_cbsec_valid(secp)) {
cmn_err(CE_WARN, "Unsupported backchannel security.");
ap->cs_error = NFS4ERR_INVAL;
goto err;
}
sp->sn_bc.secprms.csa_sec_parms_len = 1;
sp->sn_bc.secprms.csa_sec_parms_val = (callback_sec_parms4 *)
kmem_zalloc(sizeof (callback_sec_parms4), KM_SLEEP);
rfs4x_cbsec_init(sp->sn_bc.secprms.csa_sec_parms_val,
secp);
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);
bdrpc = ap->cs_aotw.csa_flags & CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
if (bdrpc) {
SVCCB_ARGS cbargs;
prog = sp->sn_bc.progno;
cbargs.xprt = mxprt;
cbargs.prog = prog;
cbargs.vers = NFS_CB;
cbargs.family = sp->sn_clnt->rc_addr.ss_family;
cbargs.tag = (void *)sp->sn_sessid;
if (SVC_CTL(req->rq_xprt, SVCCTL_SET_CBCONN, (void *)&cbargs)) {
bdrpc = 0;
}
}
sp->sn_bdrpc = bdrpc;
dir = bdrpc ? (CDFS4_FORE | CDFS4_BACK) : CDFS4_FORE;
ocp = rfs41_create_session_channel(dir);
ocp->cn_dir = dir;
sp->sn_fore = ocp;
sp->cn_attrs = ap->cs_aotw.csa_fore_chan_attrs;
sp->cn_back_attrs = ap->cs_aotw.csa_back_chan_attrs;
sle = sess_chan_limits(sp);
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 (sp->sn_bdrpc) {
bsdp = CTOBSD(ocp);
ASSERT(bsdp != NULL);
slot_table_create(&bsdp->bsd_stok, sp->sn_bc.maxreqs);
sp->sn_csflags |= CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
sp->sn_back = ocp;
} else {
sp->sn_csflags &= ~CREATE_SESSION4_FLAG_CONN_BACK_CHAN;
sp->sn_back = NULL;
}
sp->sn_slots = slots_alloc(sp->cn_attrs.ca_maxrequests);
return (TRUE);
err_free_chan:
rfs4x_destroy_session_channel(sp, CDFS4_BOTH);
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 = CTOBSD(sp->sn_back)) != NULL) {
slot_table_destroy(bsdp->bsd_stok);
bsdp->bsd_stok = NULL;
rfs4x_cb_chflush(sp);
}
rfs4x_cbsec_fini(sp);
if (sp->sn_slots) {
slots_free(sp->sn_slots, sp->cn_attrs.ca_maxrequests);
sp->sn_slots = NULL;
}
rfs4x_destroy_session_channel(sp, CDFS4_BOTH);
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, 2, 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);
nsrv4->rfs4_session_clid_idx = rfs4_index_create(
nsrv4->rfs4_session_tab, "session_clid_idx", clientid_hash,
sessid_clientid_compare, sessid_clientid_mkkey, FALSE);
}
void
rfs4x_state_fini(nfs4_srv_t *nsrv4)
{
}