#include <sys/param.h>
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsubr.h>
#include <sys/tihdr.h>
#include <sys/timod.h>
#include <sys/tiuser.h>
#include <sys/debug.h>
#include <sys/signal.h>
#include <sys/pcb.h>
#include <sys/user.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/policy.h>
#include <sys/inline.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/file.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/t_lock.h>
#include <sys/ddi.h>
#include <sys/vtrace.h>
#include <sys/callb.h>
#include <sys/strsun.h>
#include <sys/strlog.h>
#include <rpc/rpc_com.h>
#include <inet/common.h>
#include <rpc/types.h>
#include <sys/time.h>
#include <rpc/xdr.h>
#include <rpc/auth.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <rpc/clnt.h>
#include <rpc/svc.h>
#include <rpc/rpcsys.h>
#include <rpc/rpc_rdma.h>
#include <sys/conf.h>
#include <sys/modctl.h>
#include <sys/syscall.h>
extern struct streamtab rpcinfo;
static struct fmodsw fsw = {
"rpcmod",
&rpcinfo,
D_NEW|D_MP,
};
static struct modlstrmod modlstrmod = {
&mod_strmodops, "rpc interface str mod", &fsw
};
static struct sysent rpcsysent = {
2,
SE_32RVAL1 | SE_ARGC | SE_NOUNLOAD,
rpcsys
};
static struct modlsys modlsys = {
&mod_syscallops,
"RPC syscall",
&rpcsysent
};
#ifdef _SYSCALL32_IMPL
static struct modlsys modlsys32 = {
&mod_syscallops32,
"32-bit RPC syscall",
&rpcsysent
};
#endif
static struct modlinkage modlinkage = {
MODREV_1,
{
&modlsys,
#ifdef _SYSCALL32_IMPL
&modlsys32,
#endif
&modlstrmod,
NULL
}
};
int
_init(void)
{
int error = 0;
callb_id_t cid;
int status;
svc_init();
clnt_init();
cid = callb_add(connmgr_cpr_reset, 0, CB_CL_CPR_RPC, "rpc");
if (error = mod_install(&modlinkage)) {
clnt_fini();
if (cid != NULL)
(void) callb_delete(cid);
return (error);
}
rw_init(&rdma_lock, NULL, RW_DEFAULT, NULL);
mutex_init(&rdma_modload_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&rdma_wait.svc_cv, NULL, CV_DEFAULT, NULL);
mutex_init(&rdma_wait.svc_lock, NULL, MUTEX_DEFAULT, NULL);
mt_kstat_init();
status = ldi_ident_from_mod(&modlinkage, &rpcmod_li);
if (status != 0) {
cmn_err(CE_WARN, "ldi_ident_from_mod fails with %d", status);
rpcmod_li = NULL;
}
return (error);
}
int
_fini(void)
{
return (EBUSY);
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
extern int nulldev();
#define RPCMOD_ID 2049
int rmm_open(queue_t *, dev_t *, int, int, cred_t *);
int rmm_close(queue_t *, int, cred_t *);
int rmm_rput(queue_t *, mblk_t *);
int rmm_wput(queue_t *, mblk_t *);
int rmm_rsrv(queue_t *);
int rmm_wsrv(queue_t *);
int rpcmodopen(queue_t *, dev_t *, int, int, cred_t *);
int rpcmodclose(queue_t *, int, cred_t *);
void rpcmodrput(queue_t *, mblk_t *);
void rpcmodwput(queue_t *, mblk_t *);
void rpcmodrsrv();
void rpcmodwsrv(queue_t *);
static void rpcmodwput_other(queue_t *, mblk_t *);
static int mir_close(queue_t *q);
static int mir_open(queue_t *q, dev_t *devp, int flag, int sflag,
cred_t *credp);
static void mir_rput(queue_t *q, mblk_t *mp);
static void mir_rsrv(queue_t *q);
static void mir_wput(queue_t *q, mblk_t *mp);
static void mir_wsrv(queue_t *q);
static struct module_info rpcmod_info =
{RPCMOD_ID, "rpcmod", 0, INFPSZ, 256*1024, 1024};
static struct qinit rpcmodrinit = {
rmm_rput,
rmm_rsrv,
rmm_open,
rmm_close,
nulldev,
&rpcmod_info,
NULL
};
static struct qinit rpcmodwinit = {
rmm_wput,
rmm_wsrv,
rmm_open,
rmm_close,
nulldev,
&rpcmod_info,
NULL
};
struct streamtab rpcinfo = { &rpcmodrinit, &rpcmodwinit, NULL, NULL };
struct xprt_style_ops {
int (*xo_open)();
int (*xo_close)();
void (*xo_wput)();
void (*xo_wsrv)();
void (*xo_rput)();
void (*xo_rsrv)();
};
static struct xprt_style_ops xprt_clts_ops = {
rpcmodopen,
rpcmodclose,
rpcmodwput,
rpcmodwsrv,
rpcmodrput,
NULL
};
static struct xprt_style_ops xprt_cots_ops = {
mir_open,
mir_close,
mir_wput,
mir_wsrv,
mir_rput,
mir_rsrv
};
struct rpcm {
void *rm_krpc_cell;
struct xprt_style_ops *rm_ops;
int rm_type;
#define RM_CLOSING 0x1
uint_t rm_state;
uint_t rm_ref;
kmutex_t rm_lock;
kcondvar_t rm_cwait;
zoneid_t rm_zoneid;
};
struct temp_slot {
void *cell;
struct xprt_style_ops *ops;
int type;
mblk_t *info_ack;
kmutex_t lock;
kcondvar_t wait;
};
typedef struct mir_s {
void *mir_krpc_cell;
struct xprt_style_ops *rm_ops;
int mir_type;
mblk_t *mir_head_mp;
mblk_t *mir_tail_mp;
int32_t mir_frag_len;
int32_t mir_frag_header;
unsigned int
mir_ordrel_pending : 1,
mir_hold_inbound : 1,
mir_closing : 1,
mir_inrservice : 1,
mir_inwservice : 1,
mir_inwflushdata : 1,
mir_clntreq : 1,
mir_svc_no_more_msgs : 1,
mir_listen_stream : 1,
mir_unused : 1,
mir_timer_call : 1,
mir_junk_fill_thru_bit_31 : 21;
int mir_setup_complete;
timeout_id_t mir_timer_id;
clock_t mir_idle_timeout;
clock_t mir_use_timestamp;
uint_t *mir_max_msg_sizep;
zoneid_t mir_zoneid;
int mir_ref_cnt;
mblk_t *mir_svc_pend_mp;
kmutex_t mir_mutex;
kcondvar_t mir_condvar;
kcondvar_t mir_timer_cv;
} mir_t;
void tmp_rput(queue_t *q, mblk_t *mp);
struct xprt_style_ops tmpops = {
NULL,
NULL,
putnext,
NULL,
tmp_rput,
NULL
};
void
tmp_rput(queue_t *q, mblk_t *mp)
{
struct temp_slot *t = (struct temp_slot *)(q->q_ptr);
struct T_info_ack *pptr;
switch (mp->b_datap->db_type) {
case M_PCPROTO:
pptr = (struct T_info_ack *)mp->b_rptr;
switch (pptr->PRIM_type) {
case T_INFO_ACK:
mutex_enter(&t->lock);
t->info_ack = mp;
cv_signal(&t->wait);
mutex_exit(&t->lock);
return;
default:
break;
}
default:
break;
}
freemsg(mp);
}
int
rmm_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
mblk_t *bp;
struct temp_slot ts, *t;
struct T_info_ack *pptr;
int error = 0;
ASSERT(q != NULL);
if (q->q_ptr) {
TRACE_1(TR_FAC_KRPC, TR_RPCMODOPEN_END,
"rpcmodopen_end:(%s)", "q->qptr");
return (0);
}
t = &ts;
bzero(t, sizeof (*t));
q->q_ptr = (void *)t;
WR(q)->q_ptr = (void *)t;
if ((bp = allocb_cred(sizeof (struct T_info_req) +
sizeof (struct T_info_ack), crp, curproc->p_pid)) == NULL) {
return (ENOBUFS);
}
mutex_init(&t->lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&t->wait, NULL, CV_DEFAULT, NULL);
t->ops = &tmpops;
qprocson(q);
bp->b_datap->db_type = M_PCPROTO;
*(int32_t *)bp->b_wptr = (int32_t)T_INFO_REQ;
bp->b_wptr += sizeof (struct T_info_req);
putnext(WR(q), bp);
mutex_enter(&t->lock);
while (t->info_ack == NULL) {
if (cv_wait_sig(&t->wait, &t->lock) == 0) {
error = EINTR;
break;
}
}
mutex_exit(&t->lock);
if (error)
goto out;
pptr = (struct T_info_ack *)t->info_ack->b_rptr;
if (pptr->SERV_type == T_CLTS) {
if ((error = rpcmodopen(q, devp, flag, sflag, crp)) == 0)
((struct rpcm *)q->q_ptr)->rm_ops = &xprt_clts_ops;
} else {
if ((error = mir_open(q, devp, flag, sflag, crp)) == 0)
((mir_t *)q->q_ptr)->rm_ops = &xprt_cots_ops;
}
out:
if (error)
qprocsoff(q);
freemsg(t->info_ack);
mutex_destroy(&t->lock);
cv_destroy(&t->wait);
return (error);
}
int
rmm_rput(queue_t *q, mblk_t *mp)
{
(*((struct temp_slot *)q->q_ptr)->ops->xo_rput)(q, mp);
return (0);
}
int
rmm_rsrv(queue_t *q)
{
(*((struct temp_slot *)q->q_ptr)->ops->xo_rsrv)(q);
return (0);
}
int
rmm_wput(queue_t *q, mblk_t *mp)
{
(*((struct temp_slot *)q->q_ptr)->ops->xo_wput)(q, mp);
return (0);
}
int
rmm_wsrv(queue_t *q)
{
(*((struct temp_slot *)q->q_ptr)->ops->xo_wsrv)(q);
return (0);
}
int
rmm_close(queue_t *q, int flag, cred_t *crp)
{
return ((*((struct temp_slot *)q->q_ptr)->ops->xo_close)(q, flag, crp));
}
int
rpcmodopen(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *crp)
{
struct rpcm *rmp;
TRACE_0(TR_FAC_KRPC, TR_RPCMODOPEN_START, "rpcmodopen_start:");
if (secpolicy_rpcmod_open(crp) != 0)
return (EPERM);
rmp = kmem_zalloc(sizeof (*rmp), KM_SLEEP);
mutex_init(&rmp->rm_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&rmp->rm_cwait, NULL, CV_DEFAULT, NULL);
rmp->rm_zoneid = rpc_zoneid();
rmp->rm_type = 0;
q->q_ptr = (void *)rmp;
WR(q)->q_ptr = (void *)rmp;
TRACE_1(TR_FAC_KRPC, TR_RPCMODOPEN_END, "rpcmodopen_end:(%s)", "end");
return (0);
}
int
rpcmodclose(queue_t *q, int flag, cred_t *crp)
{
struct rpcm *rmp;
ASSERT(q != NULL);
rmp = (struct rpcm *)q->q_ptr;
mutex_enter(&rmp->rm_lock);
rmp->rm_state |= RM_CLOSING;
if (rmp->rm_type == RPC_SERVER) {
flushq(q, FLUSHDATA);
qenable(WR(q));
if (rmp->rm_ref) {
mutex_exit(&rmp->rm_lock);
svc_queueclean(q);
mutex_enter(&rmp->rm_lock);
while (rmp->rm_ref)
cv_wait(&rmp->rm_cwait, &rmp->rm_lock);
}
mutex_exit(&rmp->rm_lock);
qprocsoff(q);
svc_queueclose(q);
} else {
mutex_exit(&rmp->rm_lock);
qprocsoff(q);
}
q->q_ptr = NULL;
WR(q)->q_ptr = NULL;
mutex_destroy(&rmp->rm_lock);
cv_destroy(&rmp->rm_cwait);
kmem_free(rmp, sizeof (*rmp));
return (0);
}
void
rpcmodrput(queue_t *q, mblk_t *mp)
{
struct rpcm *rmp;
union T_primitives *pptr;
int hdrsz;
TRACE_0(TR_FAC_KRPC, TR_RPCMODRPUT_START, "rpcmodrput_start:");
ASSERT(q != NULL);
rmp = (struct rpcm *)q->q_ptr;
if (rmp->rm_type == 0) {
freemsg(mp);
return;
}
switch (mp->b_datap->db_type) {
default:
putnext(q, mp);
break;
case M_PROTO:
case M_PCPROTO:
ASSERT((mp->b_wptr - mp->b_rptr) >= sizeof (int32_t));
pptr = (union T_primitives *)mp->b_rptr;
if (pptr->type == T_UNITDATA_IND) {
mutex_enter(&rmp->rm_lock);
if (rmp->rm_state & RM_CLOSING) {
mutex_exit(&rmp->rm_lock);
putnext(q, mp);
break;
}
switch (rmp->rm_type) {
case RPC_CLIENT:
mutex_exit(&rmp->rm_lock);
hdrsz = mp->b_wptr - mp->b_rptr;
if (hdrsz < TUNITDATAINDSZ ||
hdrsz < (pptr->unitdata_ind.OPT_length +
pptr->unitdata_ind.OPT_offset) ||
hdrsz < (pptr->unitdata_ind.SRC_length +
pptr->unitdata_ind.SRC_offset)) {
freemsg(mp);
return;
}
clnt_clts_dispatch_notify(mp, hdrsz,
rmp->rm_zoneid);
return;
case RPC_SERVER:
if (rmp->rm_krpc_cell &&
svc_queuereq(q, mp, TRUE)) {
rmp->rm_ref++;
mutex_exit(&rmp->rm_lock);
} else {
mutex_exit(&rmp->rm_lock);
freemsg(mp);
}
return;
default:
mutex_exit(&rmp->rm_lock);
freemsg(mp);
return;
}
} else if (pptr->type == T_UDERROR_IND) {
mutex_enter(&rmp->rm_lock);
hdrsz = mp->b_wptr - mp->b_rptr;
if (hdrsz < TUDERRORINDSZ ||
hdrsz < (pptr->uderror_ind.OPT_length +
pptr->uderror_ind.OPT_offset) ||
hdrsz < (pptr->uderror_ind.DEST_length +
pptr->uderror_ind.DEST_offset)) {
mutex_exit(&rmp->rm_lock);
freemsg(mp);
return;
}
mutex_exit(&rmp->rm_lock);
freemsg(mp);
RPCLOG(32, "rpcmodrput: unitdata error received at "
"%ld\n", gethrestime_sec());
return;
}
putnext(q, mp);
break;
}
TRACE_0(TR_FAC_KRPC, TR_RPCMODRPUT_END,
"rpcmodrput_end:");
}
void
rpcmodwput(queue_t *q, mblk_t *mp)
{
struct rpcm *rmp;
ASSERT(q != NULL);
switch (mp->b_datap->db_type) {
case M_PROTO:
case M_PCPROTO:
break;
default:
rpcmodwput_other(q, mp);
return;
}
if (canputnext(q)) {
putnext(q, mp);
return;
}
rmp = (struct rpcm *)q->q_ptr;
ASSERT(rmp != NULL);
mutex_enter(&rmp->rm_lock);
if (canputnext(q) || (rmp->rm_state & RM_CLOSING)) {
mutex_exit(&rmp->rm_lock);
putnext(q, mp);
} else {
mutex_exit(&rmp->rm_lock);
(void) putq(q, mp);
}
}
static void
rpcmodwput_other(queue_t *q, mblk_t *mp)
{
struct rpcm *rmp;
struct iocblk *iocp;
rmp = (struct rpcm *)q->q_ptr;
ASSERT(rmp != NULL);
switch (mp->b_datap->db_type) {
case M_IOCTL:
iocp = (struct iocblk *)mp->b_rptr;
ASSERT(iocp != NULL);
switch (iocp->ioc_cmd) {
case RPC_CLIENT:
case RPC_SERVER:
mutex_enter(&rmp->rm_lock);
rmp->rm_type = iocp->ioc_cmd;
mutex_exit(&rmp->rm_lock);
mp->b_datap->db_type = M_IOCACK;
qreply(q, mp);
return;
default:
putnext(q, mp);
return;
}
default:
break;
}
putnext(q, mp);
}
void
rpcmodwsrv(queue_t *q)
{
struct rpcm *rmp;
mblk_t *mp = NULL;
rmp = (struct rpcm *)q->q_ptr;
ASSERT(rmp != NULL);
while ((mp = getq(q)) != NULL) {
if (rmp->rm_type == RPC_SERVER || canputnext(q)) {
putnext(q, mp);
continue;
}
(void) putbq(q, mp);
return;
}
}
void
rpcmod_hold(queue_t *q)
{
struct rpcm *rmp = (struct rpcm *)q->q_ptr;
mutex_enter(&rmp->rm_lock);
rmp->rm_ref++;
mutex_exit(&rmp->rm_lock);
}
void
rpcmod_release(queue_t *q, mblk_t *bp, bool_t enable __unused)
{
struct rpcm *rmp;
if (bp)
freemsg(bp);
rmp = (struct rpcm *)q->q_ptr;
mutex_enter(&rmp->rm_lock);
rmp->rm_ref--;
if (rmp->rm_ref == 0 && (rmp->rm_state & RM_CLOSING)) {
cv_broadcast(&rmp->rm_cwait);
}
mutex_exit(&rmp->rm_lock);
}
#define MIR_CLNT_IDLE_TIMEOUT (5 * (60 * 1000L))
#define MIR_SVC_IDLE_TIMEOUT (6 * (60 * 1000L))
#define MIR_SVC_ORDREL_TIMEOUT (10 * (60 * 1000L))
#define MIR_LASTFRAG 0x80000000
#define MIR_SVC_QUIESCED(mir) \
(mir->mir_ref_cnt == 0 && mir->mir_inrservice == 0)
#define MIR_CLEAR_INRSRV(mir_ptr) { \
(mir_ptr)->mir_inrservice = 0; \
if ((mir_ptr)->mir_type == RPC_SERVER && \
(mir_ptr)->mir_closing) \
cv_signal(&(mir_ptr)->mir_condvar); \
}
#define MIR_WCANPUTNEXT(mir_ptr, write_q) \
(canputnext(write_q) || ((mir_ptr)->mir_svc_no_more_msgs == 1))
static int mir_clnt_dup_request(queue_t *q, mblk_t *mp);
static void mir_rput_proto(queue_t *q, mblk_t *mp);
static int mir_svc_policy_notify(queue_t *q, int event);
static void mir_svc_start(queue_t *wq);
static void mir_svc_idle_start(queue_t *, mir_t *);
static void mir_svc_idle_stop(queue_t *, mir_t *);
static void mir_svc_start_close(queue_t *, mir_t *);
static void mir_clnt_idle_do_stop(queue_t *);
static void mir_clnt_idle_stop(queue_t *, mir_t *);
static void mir_clnt_idle_start(queue_t *, mir_t *);
static void mir_wput(queue_t *q, mblk_t *mp);
static void mir_wput_other(queue_t *q, mblk_t *mp);
static void mir_wsrv(queue_t *q);
static void mir_disconnect(queue_t *, mir_t *ir);
static int mir_check_len(queue_t *, mblk_t *);
static void mir_timer(void *);
extern void (*mir_start)(queue_t *);
extern void (*clnt_stop_idle)(queue_t *);
clock_t clnt_idle_timeout = MIR_CLNT_IDLE_TIMEOUT;
clock_t svc_idle_timeout = MIR_SVC_IDLE_TIMEOUT;
clock_t svc_ordrel_timeout = MIR_SVC_ORDREL_TIMEOUT;
extern uint_t *clnt_max_msg_sizep;
extern uint_t *svc_max_msg_sizep;
uint_t clnt_max_msg_size = RPC_MAXDATASIZE;
uint_t svc_max_msg_size = RPC_MAXDATASIZE;
uint_t mir_krpc_cell_null;
static void
mir_timer_stop(mir_t *mir)
{
timeout_id_t tid;
ASSERT(MUTEX_HELD(&mir->mir_mutex));
while (mir->mir_timer_call)
cv_wait(&mir->mir_timer_cv, &mir->mir_mutex);
mir->mir_timer_call = B_TRUE;
if ((tid = mir->mir_timer_id) != 0) {
mir->mir_timer_id = 0;
mutex_exit(&mir->mir_mutex);
(void) untimeout(tid);
mutex_enter(&mir->mir_mutex);
}
mir->mir_timer_call = B_FALSE;
cv_broadcast(&mir->mir_timer_cv);
}
static void
mir_timer_start(queue_t *q, mir_t *mir, clock_t intrvl)
{
timeout_id_t tid;
ASSERT(MUTEX_HELD(&mir->mir_mutex));
while (mir->mir_timer_call)
cv_wait(&mir->mir_timer_cv, &mir->mir_mutex);
mir->mir_timer_call = B_TRUE;
if ((tid = mir->mir_timer_id) != 0) {
mutex_exit(&mir->mir_mutex);
(void) untimeout(tid);
mutex_enter(&mir->mir_mutex);
}
if (!mir->mir_closing) {
mir->mir_timer_id = timeout(mir_timer, q,
MSEC_TO_TICK(intrvl));
}
mir->mir_timer_call = B_FALSE;
cv_broadcast(&mir->mir_timer_cv);
}
static int
mir_clnt_dup_request(queue_t *q, mblk_t *mp)
{
mblk_t *mp1;
uint32_t new_xid;
uint32_t old_xid;
ASSERT(MUTEX_HELD(&((mir_t *)q->q_ptr)->mir_mutex));
new_xid = BE32_TO_U32(&mp->b_rptr[4]);
if ((mp1 = q->q_first) != NULL) {
do {
old_xid = BE32_TO_U32(&mp1->b_rptr[4]);
if (new_xid == old_xid)
return (1);
} while ((mp1 = mp1->b_next) != NULL);
}
return (0);
}
static int
mir_close(queue_t *q)
{
mir_t *mir = q->q_ptr;
mblk_t *mp;
bool_t queue_cleaned = FALSE;
RPCLOG(32, "rpcmod: mir_close of q 0x%p\n", (void *)q);
ASSERT(MUTEX_NOT_HELD(&mir->mir_mutex));
mutex_enter(&mir->mir_mutex);
if ((mp = mir->mir_head_mp) != NULL) {
mir->mir_head_mp = NULL;
mir->mir_tail_mp = NULL;
freemsg(mp);
}
mir->mir_closing = B_TRUE;
mir_timer_stop(mir);
if (mir->mir_type == RPC_SERVER) {
flushq(q, FLUSHDATA);
mir_svc_start_close(WR(q), mir);
while ((!MIR_SVC_QUIESCED(mir)) || mir->mir_inwservice == 1) {
if (mir->mir_ref_cnt && !mir->mir_inrservice &&
(queue_cleaned == FALSE)) {
mutex_exit(&mir->mir_mutex);
svc_queueclean(q);
queue_cleaned = TRUE;
mutex_enter(&mir->mir_mutex);
continue;
}
if (mir->mir_inwservice == 1)
qenable(WR(q));
cv_wait(&mir->mir_condvar, &mir->mir_mutex);
}
mutex_exit(&mir->mir_mutex);
connmgr_destroy(WR(q));
qprocsoff(q);
svc_queueclose(q);
} else {
mutex_exit(&mir->mir_mutex);
qprocsoff(q);
}
mutex_destroy(&mir->mir_mutex);
cv_destroy(&mir->mir_condvar);
cv_destroy(&mir->mir_timer_cv);
kmem_free(mir, sizeof (mir_t));
return (0);
}
static void
mir_svc_idle_stop(queue_t *q, mir_t *mir)
{
ASSERT(MUTEX_HELD(&mir->mir_mutex));
ASSERT((q->q_flag & QREADR) == 0);
ASSERT(mir->mir_type == RPC_SERVER);
RPCLOG(16, "rpcmod: mir_svc_idle_stop of q 0x%p\n", (void *)q);
mir_timer_stop(mir);
}
static void
mir_svc_idle_start(queue_t *q, mir_t *mir)
{
ASSERT(MUTEX_HELD(&mir->mir_mutex));
ASSERT((q->q_flag & QREADR) == 0);
ASSERT(mir->mir_type == RPC_SERVER);
RPCLOG(16, "rpcmod: mir_svc_idle_start q 0x%p\n", (void *)q);
if (mir->mir_closing) {
RPCLOG(16, "mir_svc_idle_start - closing: 0x%p\n",
(void *)q);
if (mir->mir_inwservice == 0)
cv_signal(&mir->mir_condvar);
} else {
RPCLOG(16, "mir_svc_idle_start - reset %s timer\n",
mir->mir_ordrel_pending ? "ordrel" : "normal");
mir_timer_start(q, mir, mir->mir_ordrel_pending ?
svc_ordrel_timeout : mir->mir_idle_timeout);
}
}
static int
mir_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
{
mir_t *mir;
RPCLOG(32, "rpcmod: mir_open of q 0x%p\n", (void *)q);
if (!mir_start)
mir_start = mir_svc_start;
if (!clnt_stop_idle)
clnt_stop_idle = mir_clnt_idle_do_stop;
if (!clnt_max_msg_sizep)
clnt_max_msg_sizep = &clnt_max_msg_size;
if (!svc_max_msg_sizep)
svc_max_msg_sizep = &svc_max_msg_size;
mir = kmem_zalloc(sizeof (mir_t), KM_SLEEP);
mir->mir_hold_inbound = 1;
mir->mir_frag_len = -(int32_t)sizeof (uint32_t);
mir->mir_zoneid = rpc_zoneid();
mutex_init(&mir->mir_mutex, NULL, MUTEX_DEFAULT, NULL);
cv_init(&mir->mir_condvar, NULL, CV_DRIVER, NULL);
cv_init(&mir->mir_timer_cv, NULL, CV_DRIVER, NULL);
q->q_ptr = (char *)mir;
WR(q)->q_ptr = (char *)mir;
noenable(q);
qprocson(q);
return (0);
}
static void
mir_rput(queue_t *q, mblk_t *mp)
{
int excess;
int32_t frag_len, frag_header;
mblk_t *cont_mp, *head_mp, *tail_mp, *mp1;
mir_t *mir = q->q_ptr;
boolean_t stop_timer = B_FALSE;
ASSERT(mir != NULL);
if (mir->mir_type == 0) {
freemsg(mp);
return;
}
ASSERT(MUTEX_NOT_HELD(&mir->mir_mutex));
switch (mp->b_datap->db_type) {
case M_DATA:
break;
case M_PROTO:
case M_PCPROTO:
if (MBLKL(mp) < sizeof (t_scalar_t)) {
RPCLOG(1, "mir_rput: runt TPI message (%d bytes)\n",
(int)MBLKL(mp));
freemsg(mp);
return;
}
if (((union T_primitives *)mp->b_rptr)->type != T_DATA_IND) {
mir_rput_proto(q, mp);
return;
}
mp1 = mp;
mp = mp->b_cont;
freeb(mp1);
break;
case M_SETOPTS:
if (MBLKL(mp) >= sizeof (struct stroptions)) {
struct stroptions *stropts;
stropts = (struct stroptions *)mp->b_rptr;
if ((stropts->so_flags & SO_HIWAT) &&
!(stropts->so_flags & SO_BAND)) {
(void) strqset(q, QHIWAT, 0, stropts->so_hiwat);
}
}
putnext(q, mp);
return;
case M_FLUSH:
RPCLOG(32, "mir_rput: ignoring M_FLUSH %x ", *mp->b_rptr);
RPCLOG(32, "on q 0x%p\n", (void *)q);
putnext(q, mp);
return;
default:
putnext(q, mp);
return;
}
mutex_enter(&mir->mir_mutex);
if (mir->mir_svc_no_more_msgs) {
ASSERT(mir->mir_type == RPC_SERVER);
mutex_exit(&mir->mir_mutex);
freemsg(mp);
return;
}
frag_len = mir->mir_frag_len;
frag_header = mir->mir_frag_header;
head_mp = mir->mir_head_mp;
tail_mp = mir->mir_tail_mp;
do {
cont_mp = mp->b_cont;
mp->b_cont = NULL;
if (MBLKL(mp) == 0) {
freeb(mp);
continue;
}
while (frag_len < 0 && mp->b_rptr < mp->b_wptr) {
frag_len++;
frag_header <<= 8;
frag_header += *mp->b_rptr++;
}
if (MBLKL(mp) == 0 && frag_len < 0) {
freeb(mp);
continue;
}
ASSERT(frag_len >= 0);
if (head_mp == NULL) {
ASSERT(tail_mp == NULL);
head_mp = tail_mp = mp;
} else {
tail_mp->b_cont = mp;
tail_mp = mp;
}
frag_len += MBLKL(mp);
excess = frag_len - (frag_header & ~MIR_LASTFRAG);
if (excess < 0) {
continue;
}
if (excess > 0) {
if ((mp1 = dupb(mp)) == NULL &&
(mp1 = copyb(mp)) == NULL) {
freemsg(head_mp);
freemsg(cont_mp);
RPCLOG0(1, "mir_rput: dupb/copyb failed\n");
mir->mir_frag_header = 0;
mir->mir_frag_len = -(int32_t)sizeof (uint32_t);
mir->mir_head_mp = NULL;
mir->mir_tail_mp = NULL;
mir_disconnect(q, mir);
return;
}
mp1->b_cont = cont_mp;
cont_mp = mp1;
mp1->b_rptr = mp1->b_wptr - excess;
mp->b_wptr -= excess;
}
frag_len = -(int32_t)sizeof (uint32_t);
if (!(frag_header & MIR_LASTFRAG)) {
frag_header = 0;
continue;
}
frag_header = 0;
switch (mir->mir_type) {
case RPC_CLIENT:
if (clnt_dispatch_notify(head_mp, mir->mir_zoneid)) {
mir->mir_clntreq = 1;
mir->mir_use_timestamp = ddi_get_lbolt();
} else {
freemsg(head_mp);
}
break;
case RPC_SERVER:
if (!mir->mir_hold_inbound) {
if (mir->mir_krpc_cell) {
if (mir_check_len(q, head_mp))
return;
if (q->q_first == NULL &&
svc_queuereq(q, head_mp, TRUE)) {
if (mir->mir_ref_cnt++ == 0)
stop_timer = B_TRUE;
} else {
(void) putq(q, head_mp);
mir->mir_inrservice = B_TRUE;
}
} else {
mir_krpc_cell_null++;
freemsg(head_mp);
}
} else {
(void) putq(q, head_mp);
mir->mir_inrservice = B_TRUE;
}
break;
default:
RPCLOG(1, "mir_rput: unknown mir_type %d\n",
mir->mir_type);
freemsg(head_mp);
break;
}
head_mp = tail_mp = NULL;
} while ((mp = cont_mp) != NULL);
if (head_mp != NULL && mir->mir_setup_complete &&
mir_check_len(q, head_mp))
return;
mir->mir_frag_header = frag_header;
mir->mir_frag_len = frag_len;
mir->mir_head_mp = head_mp;
mir->mir_tail_mp = tail_mp;
if (stop_timer) {
RPCLOG(16, "mir_rput: stopping idle timer on 0x%p because "
"ref cnt going to non zero\n", (void *)WR(q));
mir_svc_idle_stop(WR(q), mir);
}
mutex_exit(&mir->mir_mutex);
}
static void
mir_rput_proto(queue_t *q, mblk_t *mp)
{
mir_t *mir = (mir_t *)q->q_ptr;
uint32_t type;
uint32_t reason = 0;
ASSERT(MUTEX_NOT_HELD(&mir->mir_mutex));
type = ((union T_primitives *)mp->b_rptr)->type;
switch (mir->mir_type) {
case RPC_CLIENT:
switch (type) {
case T_DISCON_IND:
reason = ((struct T_discon_ind *)
(mp->b_rptr))->DISCON_reason;
case T_ORDREL_IND:
mutex_enter(&mir->mir_mutex);
if (mir->mir_head_mp) {
freemsg(mir->mir_head_mp);
mir->mir_head_mp = (mblk_t *)0;
mir->mir_tail_mp = (mblk_t *)0;
}
mir->mir_idle_timeout = clnt_idle_timeout;
mir_clnt_idle_stop(WR(q), mir);
mir_clnt_idle_start(WR(q), mir);
mutex_exit(&mir->mir_mutex);
clnt_dispatch_notifyall(WR(q), type, reason);
freemsg(mp);
return;
case T_ERROR_ACK:
{
struct T_error_ack *terror;
terror = (struct T_error_ack *)mp->b_rptr;
RPCLOG(1, "mir_rput_proto T_ERROR_ACK for queue 0x%p",
(void *)q);
RPCLOG(1, " ERROR_prim: %s,",
rpc_tpiprim2name(terror->ERROR_prim));
RPCLOG(1, " TLI_error: %s,",
rpc_tpierr2name(terror->TLI_error));
RPCLOG(1, " UNIX_error: %d\n", terror->UNIX_error);
if (terror->ERROR_prim == T_DISCON_REQ) {
clnt_dispatch_notifyall(WR(q), type, reason);
freemsg(mp);
return;
} else {
if (clnt_dispatch_notifyconn(WR(q), mp))
return;
}
break;
}
case T_OK_ACK:
{
struct T_ok_ack *tok = (struct T_ok_ack *)mp->b_rptr;
if (tok->CORRECT_prim == T_DISCON_REQ) {
clnt_dispatch_notifyall(WR(q), type, reason);
freemsg(mp);
return;
} else {
if (clnt_dispatch_notifyconn(WR(q), mp))
return;
}
break;
}
case T_CONN_CON:
case T_INFO_ACK:
case T_OPTMGMT_ACK:
if (clnt_dispatch_notifyconn(WR(q), mp))
return;
break;
case T_BIND_ACK:
break;
default:
RPCLOG(1, "mir_rput: unexpected message %d "
"for kRPC client\n",
((union T_primitives *)mp->b_rptr)->type);
break;
}
break;
case RPC_SERVER:
switch (type) {
case T_BIND_ACK:
{
struct T_bind_ack *tbind;
tbind = (struct T_bind_ack *)mp->b_rptr;
if (tbind->CONIND_number > 0) {
mutex_enter(&mir->mir_mutex);
mir_svc_idle_stop(WR(q), mir);
mir->mir_listen_stream = 1;
mutex_exit(&mir->mir_mutex);
}
break;
}
case T_DISCON_IND:
case T_ORDREL_IND:
RPCLOG(16, "mir_rput_proto: got %s indication\n",
type == T_DISCON_IND ? "disconnect"
: "orderly release");
if (mir->mir_listen_stream)
break;
mutex_enter(&mir->mir_mutex);
mir_svc_start_close(WR(q), mir);
if (MIR_SVC_QUIESCED(mir)) {
mutex_exit(&mir->mir_mutex);
break;
}
RPCLOG(16, "mir_rput_proto: not idle, so "
"disconnect/ord rel indication not passed "
"upstream on 0x%p\n", (void *)q);
if (mir->mir_svc_pend_mp) {
if (type == T_DISCON_IND) {
RPCLOG(16, "mir_rput_proto: replacing"
" held disconnect/ord rel"
" indication with disconnect on"
" 0x%p\n", (void *)q);
freemsg(mir->mir_svc_pend_mp);
mir->mir_svc_pend_mp = mp;
} else {
RPCLOG(16, "mir_rput_proto: already "
"held a disconnect/ord rel "
"indication. freeing ord rel "
"ind on 0x%p\n", (void *)q);
freemsg(mp);
}
} else
mir->mir_svc_pend_mp = mp;
mutex_exit(&mir->mir_mutex);
return;
default:
break;
}
break;
default:
break;
}
putnext(q, mp);
}
static void
mir_rsrv(queue_t *q)
{
mir_t *mir;
mblk_t *mp;
boolean_t stop_timer = B_FALSE;
mir = (mir_t *)q->q_ptr;
mutex_enter(&mir->mir_mutex);
mp = NULL;
switch (mir->mir_type) {
case RPC_SERVER:
if (mir->mir_ref_cnt == 0)
mir->mir_hold_inbound = 0;
if (mir->mir_hold_inbound)
break;
while (mp = getq(q)) {
if (mir->mir_krpc_cell &&
(mir->mir_svc_no_more_msgs == 0)) {
if (mir_check_len(q, mp))
return;
if (svc_queuereq(q, mp, TRUE)) {
if (mir->mir_ref_cnt++ == 0)
stop_timer = B_TRUE;
} else {
(void) putbq(q, mp);
break;
}
} else {
if (mir->mir_krpc_cell == NULL)
mir_krpc_cell_null++;
freemsg(mp);
}
}
break;
case RPC_CLIENT:
break;
default:
RPCLOG(1, "mir_rsrv: unexpected mir_type %d\n", mir->mir_type);
if (q->q_first == NULL)
MIR_CLEAR_INRSRV(mir);
mutex_exit(&mir->mir_mutex);
return;
}
if (stop_timer) {
RPCLOG(16, "mir_rsrv stopping idle timer on 0x%p because ref "
"cnt going to non zero\n", (void *)WR(q));
mir_svc_idle_stop(WR(q), mir);
}
if (q->q_first == NULL) {
mblk_t *cmp = NULL;
MIR_CLEAR_INRSRV(mir);
if (mir->mir_type == RPC_SERVER && MIR_SVC_QUIESCED(mir)) {
cmp = mir->mir_svc_pend_mp;
mir->mir_svc_pend_mp = NULL;
}
mutex_exit(&mir->mir_mutex);
if (cmp != NULL) {
RPCLOG(16, "mir_rsrv: line %d: sending a held "
"disconnect/ord rel indication upstream\n",
__LINE__);
putnext(q, cmp);
}
return;
}
mutex_exit(&mir->mir_mutex);
}
static int mir_svc_policy_fails;
static int
mir_svc_policy_notify(queue_t *q, int event)
{
mblk_t *mp;
#ifdef DEBUG
mir_t *mir = (mir_t *)q->q_ptr;
ASSERT(MUTEX_NOT_HELD(&mir->mir_mutex));
#endif
ASSERT(q->q_flag & QREADR);
mp = allocb(sizeof (int), BPRI_HI);
if (!mp) {
mir_svc_policy_fails++;
RPCLOG(16, "mir_svc_policy_notify: could not allocate event "
"%d\n", event);
return (ENOMEM);
}
U32_TO_BE32(event, mp->b_rptr);
mp->b_wptr = mp->b_rptr + sizeof (int);
putnext(q, mp);
return (0);
}
static void
mir_svc_start_close(queue_t *wq, mir_t *mir)
{
ASSERT(MUTEX_HELD(&mir->mir_mutex));
ASSERT((wq->q_flag & QREADR) == 0);
ASSERT(mir->mir_type == RPC_SERVER);
mir->mir_svc_no_more_msgs = 1;
mir->mir_hold_inbound = 0;
qenable(RD(wq));
qenable(wq);
}
void
mir_svc_hold(queue_t *wq)
{
mir_t *mir = (mir_t *)wq->q_ptr;
mutex_enter(&mir->mir_mutex);
mir->mir_ref_cnt++;
mutex_exit(&mir->mir_mutex);
}
void
mir_svc_release(queue_t *wq, mblk_t *mp, bool_t enable)
{
mir_t *mir = (mir_t *)wq->q_ptr;
mblk_t *cmp = NULL;
ASSERT((wq->q_flag & QREADR) == 0);
if (mp)
freemsg(mp);
if (enable)
qenable(RD(wq));
mutex_enter(&mir->mir_mutex);
if ((mir->mir_ref_cnt == 1) && (mir->mir_inrservice == 0)) {
cmp = mir->mir_svc_pend_mp;
mir->mir_svc_pend_mp = NULL;
}
if (cmp) {
RPCLOG(16, "mir_svc_release: sending a held "
"disconnect/ord rel indication upstream on queue 0x%p\n",
(void *)RD(wq));
mutex_exit(&mir->mir_mutex);
putnext(RD(wq), cmp);
mutex_enter(&mir->mir_mutex);
}
if (mir->mir_ref_cnt == 1 && mir->mir_inrservice == 0) {
RPCLOG(16, "mir_svc_release starting idle timer on 0x%p "
"because ref cnt is zero\n", (void *) wq);
mir_svc_idle_start(wq, mir);
}
mir->mir_ref_cnt--;
ASSERT(mir->mir_ref_cnt >= 0);
if ((mir->mir_ref_cnt == 0) && mir->mir_closing)
cv_signal(&mir->mir_condvar);
mutex_exit(&mir->mir_mutex);
}
static void
mir_svc_start(queue_t *wq)
{
mir_t *mir = (mir_t *)wq->q_ptr;
mir->mir_setup_complete = 1;
qenable(RD(wq));
}
static void
mir_clnt_idle_stop(queue_t *wq, mir_t *mir)
{
ASSERT(MUTEX_HELD(&mir->mir_mutex));
ASSERT((wq->q_flag & QREADR) == 0);
ASSERT(mir->mir_type == RPC_CLIENT);
mir_timer_stop(mir);
}
static void
mir_clnt_idle_start(queue_t *wq, mir_t *mir)
{
ASSERT(MUTEX_HELD(&mir->mir_mutex));
ASSERT((wq->q_flag & QREADR) == 0);
ASSERT(mir->mir_type == RPC_CLIENT);
mir_timer_start(wq, mir, mir->mir_idle_timeout);
}
static void
mir_clnt_idle_do_stop(queue_t *wq)
{
mir_t *mir = (mir_t *)wq->q_ptr;
RPCLOG(1, "mir_clnt_idle_do_stop: wq 0x%p\n", (void *)wq);
ASSERT(MUTEX_NOT_HELD(&mir->mir_mutex));
mutex_enter(&mir->mir_mutex);
mir_clnt_idle_stop(wq, mir);
mutex_exit(&mir->mir_mutex);
}
static void
mir_timer(void *arg)
{
queue_t *wq = (queue_t *)arg;
mir_t *mir = (mir_t *)wq->q_ptr;
boolean_t notify;
clock_t now;
mutex_enter(&mir->mir_mutex);
if (mir->mir_timer_call) {
mutex_exit(&mir->mir_mutex);
return;
}
mir->mir_timer_id = 0;
switch (mir->mir_type) {
case RPC_CLIENT:
now = ddi_get_lbolt();
if (mir->mir_clntreq > 0 && mir->mir_use_timestamp +
MSEC_TO_TICK(mir->mir_idle_timeout) - now >= 0) {
clock_t tout;
tout = mir->mir_idle_timeout -
TICK_TO_MSEC(now - mir->mir_use_timestamp);
if (tout < 0)
tout = 1000;
#if 0
printf("mir_timer[%d < %d + %d]: reset client timer "
"to %d (ms)\n", TICK_TO_MSEC(now),
TICK_TO_MSEC(mir->mir_use_timestamp),
mir->mir_idle_timeout, tout);
#endif
mir->mir_clntreq = 0;
mir_timer_start(wq, mir, tout);
mutex_exit(&mir->mir_mutex);
return;
}
#if 0
printf("mir_timer[%d]: doing client timeout\n", now / hz);
#endif
mir->mir_idle_timeout = clnt_idle_timeout;
mir_clnt_idle_start(wq, mir);
mutex_exit(&mir->mir_mutex);
clnt_dispatch_notifyall(wq, T_ORDREL_REQ, 0);
return;
case RPC_SERVER:
if (!MIR_SVC_QUIESCED(mir)) {
mutex_exit(&mir->mir_mutex);
return;
}
notify = !mir->mir_inrservice;
mutex_exit(&mir->mir_mutex);
if (notify) {
RPCLOG(16, "mir_timer: telling stream head listener "
"to close stream (0x%p)\n", (void *) RD(wq));
(void) mir_svc_policy_notify(RD(wq), 1);
}
return;
default:
RPCLOG(1, "mir_timer: unexpected mir_type %d\n",
mir->mir_type);
mutex_exit(&mir->mir_mutex);
return;
}
}
static void
mir_wput(queue_t *q, mblk_t *mp)
{
uint_t frag_header;
mir_t *mir = (mir_t *)q->q_ptr;
uchar_t *rptr = mp->b_rptr;
if (!mir) {
freemsg(mp);
return;
}
if (mp->b_datap->db_type != M_DATA) {
mir_wput_other(q, mp);
return;
}
if (mir->mir_ordrel_pending == 1) {
freemsg(mp);
RPCLOG(16, "mir_wput wq 0x%p: got data after T_ORDREL_REQ\n",
(void *)q);
return;
}
frag_header = (uint_t)DLEN(mp);
frag_header |= MIR_LASTFRAG;
if ((rptr - mp->b_datap->db_base) < sizeof (uint32_t) ||
!IS_P2ALIGNED(mp->b_rptr, sizeof (uint32_t))) {
RPCLOG(1, "mir_wput: kRPC did not leave space for record "
"fragment header (%d bytes left)\n",
(int)(rptr - mp->b_datap->db_base));
freemsg(mp);
return;
}
rptr -= sizeof (uint32_t);
*(uint32_t *)rptr = htonl(frag_header);
mp->b_rptr = rptr;
mutex_enter(&mir->mir_mutex);
if (mir->mir_type == RPC_CLIENT) {
mir->mir_clntreq = 1;
mir->mir_use_timestamp = ddi_get_lbolt();
}
if (!mir->mir_inwservice && MIR_WCANPUTNEXT(mir, q)) {
mutex_exit(&mir->mir_mutex);
putnext(q, mp);
return;
}
switch (mir->mir_type) {
case RPC_CLIENT:
if (mir_clnt_dup_request(q, mp)) {
mutex_exit(&mir->mir_mutex);
freemsg(mp);
return;
}
break;
case RPC_SERVER:
mir->mir_hold_inbound = 1;
break;
default:
RPCLOG(1, "mir_wput: unexpected mir_type %d\n", mir->mir_type);
break;
}
mir->mir_inwservice = 1;
(void) putq(q, mp);
mutex_exit(&mir->mir_mutex);
}
static void
mir_wput_other(queue_t *q, mblk_t *mp)
{
mir_t *mir = (mir_t *)q->q_ptr;
struct iocblk *iocp;
uchar_t *rptr = mp->b_rptr;
bool_t flush_in_svc = FALSE;
ASSERT(MUTEX_NOT_HELD(&mir->mir_mutex));
switch (mp->b_datap->db_type) {
case M_IOCTL:
iocp = (struct iocblk *)rptr;
switch (iocp->ioc_cmd) {
case RPC_CLIENT:
mutex_enter(&mir->mir_mutex);
if (mir->mir_type != 0 &&
mir->mir_type != iocp->ioc_cmd) {
ioc_eperm:
mutex_exit(&mir->mir_mutex);
iocp->ioc_error = EPERM;
iocp->ioc_count = 0;
mp->b_datap->db_type = M_IOCACK;
qreply(q, mp);
return;
}
mir->mir_type = iocp->ioc_cmd;
mir->mir_hold_inbound = 0;
mir->mir_max_msg_sizep = &clnt_max_msg_size;
mir->mir_idle_timeout = clnt_idle_timeout;
mir_clnt_idle_start(q, mir);
mutex_exit(&mir->mir_mutex);
mp->b_datap->db_type = M_IOCACK;
qreply(q, mp);
return;
case RPC_SERVER:
mutex_enter(&mir->mir_mutex);
if (mir->mir_type != 0 &&
mir->mir_type != iocp->ioc_cmd)
goto ioc_eperm;
mir->mir_type = iocp->ioc_cmd;
mir->mir_max_msg_sizep = &svc_max_msg_size;
mir->mir_idle_timeout = svc_idle_timeout;
RPCLOG(16, "mir_wput_other starting idle timer on 0x%p "
"because we got RPC_SERVER ioctl\n", (void *)q);
mir_svc_idle_start(q, mir);
mutex_exit(&mir->mir_mutex);
mp->b_datap->db_type = M_IOCACK;
qreply(q, mp);
return;
default:
break;
}
break;
case M_PROTO:
if (mir->mir_type == RPC_CLIENT) {
flush_in_svc = TRUE;
}
if ((mp->b_wptr - rptr) < sizeof (uint32_t) ||
!IS_P2ALIGNED(rptr, sizeof (uint32_t)))
break;
switch (((union T_primitives *)rptr)->type) {
case T_DATA_REQ:
freemsg(mp);
return;
case T_ORDREL_REQ:
RPCLOG(8, "mir_wput_other wq 0x%p: got T_ORDREL_REQ\n",
(void *)q);
mutex_enter(&mir->mir_mutex);
if (mir->mir_type != RPC_SERVER) {
mir->mir_inwservice = 1;
(void) putq(q, mp);
mutex_exit(&mir->mir_mutex);
return;
}
RPCLOG(16, "mir_wput_other wq 0x%p: got T_ORDREL_REQ "
" so calling mir_svc_start_close\n", (void *)q);
mir_svc_start_close(q, mir);
if (mir->mir_ordrel_pending) {
freemsg(mp);
mutex_exit(&mir->mir_mutex);
return;
}
if (!MIR_SVC_QUIESCED(mir) ||
mir->mir_inwservice == 1) {
mir->mir_inwservice = 1;
(void) putq(q, mp);
RPCLOG(16, "mir_wput_other: queuing "
"T_ORDREL_REQ on 0x%p\n", (void *)q);
mutex_exit(&mir->mir_mutex);
return;
}
mir->mir_ordrel_pending = 1;
RPCLOG(16, "mir_wput_other: calling mir_svc_idle_start"
" on 0x%p because we got T_ORDREL_REQ\n",
(void *)q);
mir_svc_idle_start(q, mir);
mutex_exit(&mir->mir_mutex);
break;
case T_CONN_REQ:
mutex_enter(&mir->mir_mutex);
if (mir->mir_head_mp != NULL) {
freemsg(mir->mir_head_mp);
mir->mir_head_mp = NULL;
mir->mir_tail_mp = NULL;
}
mir->mir_frag_len = -(int32_t)sizeof (uint32_t);
mir->mir_idle_timeout = clnt_idle_timeout;
mir_clnt_idle_stop(q, mir);
mir_clnt_idle_start(q, mir);
mutex_exit(&mir->mir_mutex);
break;
default:
break;
}
default:
if (mp->b_datap->db_type >= QPCTL) {
if (mp->b_datap->db_type == M_FLUSH) {
if (mir->mir_type == RPC_CLIENT &&
*mp->b_rptr & FLUSHW) {
RPCLOG(32, "mir_wput_other: flushing "
"wq 0x%p\n", (void *)q);
if (*mp->b_rptr & FLUSHBAND) {
flushband(q, *(mp->b_rptr + 1),
FLUSHDATA);
} else {
flushq(q, FLUSHDATA);
}
} else {
RPCLOG(32, "mir_wput_other: ignoring "
"M_FLUSH on wq 0x%p\n", (void *)q);
}
}
break;
}
mutex_enter(&mir->mir_mutex);
if (mir->mir_inwservice == 0 && MIR_WCANPUTNEXT(mir, q)) {
mutex_exit(&mir->mir_mutex);
break;
}
mir->mir_inwservice = 1;
mir->mir_inwflushdata = flush_in_svc;
(void) putq(q, mp);
mutex_exit(&mir->mir_mutex);
qenable(q);
return;
}
putnext(q, mp);
}
static void
mir_wsrv(queue_t *q)
{
mblk_t *mp;
mir_t *mir;
bool_t flushdata;
mir = (mir_t *)q->q_ptr;
mutex_enter(&mir->mir_mutex);
flushdata = mir->mir_inwflushdata;
mir->mir_inwflushdata = 0;
while (mp = getq(q)) {
if (mp->b_datap->db_type == M_DATA) {
if (flushdata || mir->mir_ordrel_pending == 1) {
freemsg(mp);
continue;
}
if (!MIR_WCANPUTNEXT(mir, q)) {
(void) putbq(q, mp);
mutex_exit(&mir->mir_mutex);
return;
}
mutex_exit(&mir->mir_mutex);
putnext(q, mp);
mutex_enter(&mir->mir_mutex);
continue;
}
if (mir->mir_type != RPC_SERVER ||
((union T_primitives *)mp->b_rptr)->type !=
T_ORDREL_REQ) {
mutex_exit(&mir->mir_mutex);
putnext(q, mp);
mutex_enter(&mir->mir_mutex);
continue;
}
if (mir->mir_ordrel_pending == 1) {
freemsg(mp);
continue;
}
mir->mir_ordrel_pending = 1;
RPCLOG(16, "mir_wsrv: sending ordrel req on q 0x%p\n",
(void *)q);
mutex_exit(&mir->mir_mutex);
putnext(q, mp);
mutex_enter(&mir->mir_mutex);
}
if (q->q_first == NULL)
mir->mir_inwservice = 0;
if (mir->mir_type != RPC_SERVER) {
mutex_exit(&mir->mir_mutex);
return;
}
if (MIR_SVC_QUIESCED(mir) && !(mir->mir_listen_stream)) {
RPCLOG(16, "mir_wsrv: calling mir_svc_idle_start on 0x%p "
"because mir slot is idle\n", (void *)q);
mir_svc_idle_start(q, mir);
}
if (mir->mir_hold_inbound) {
mir->mir_hold_inbound = 0;
qenable(RD(q));
}
mutex_exit(&mir->mir_mutex);
}
static void
mir_disconnect(queue_t *q, mir_t *mir)
{
ASSERT(MUTEX_HELD(&mir->mir_mutex));
switch (mir->mir_type) {
case RPC_CLIENT:
mir->mir_idle_timeout = clnt_idle_timeout;
mir_clnt_idle_start(WR(q), mir);
mutex_exit(&mir->mir_mutex);
clnt_dispatch_notifyall(WR(q), T_DISCON_REQ, 0);
break;
case RPC_SERVER:
mir->mir_svc_no_more_msgs = 1;
mir_svc_idle_stop(WR(q), mir);
mutex_exit(&mir->mir_mutex);
RPCLOG(16, "mir_disconnect: telling "
"stream head listener to disconnect stream "
"(0x%p)\n", (void *) q);
(void) mir_svc_policy_notify(q, 2);
break;
default:
mutex_exit(&mir->mir_mutex);
break;
}
}
static int
mir_check_len(queue_t *q, mblk_t *head_mp)
{
mir_t *mir = q->q_ptr;
uint_t maxsize = 0;
size_t msg_len = msgdsize(head_mp);
if (mir->mir_max_msg_sizep != NULL)
maxsize = *mir->mir_max_msg_sizep;
if (maxsize == 0 || msg_len <= maxsize)
return (0);
freemsg(head_mp);
mir->mir_head_mp = NULL;
mir->mir_tail_mp = NULL;
mir->mir_frag_header = 0;
mir->mir_frag_len = -(int32_t)sizeof (uint32_t);
if (mir->mir_type != RPC_SERVER || mir->mir_setup_complete) {
cmn_err(CE_NOTE,
"kRPC: record fragment from %s of size(%lu) exceeds "
"maximum (%u). Disconnecting",
(mir->mir_type == RPC_CLIENT) ? "server" :
(mir->mir_type == RPC_SERVER) ? "client" :
"test tool", msg_len, maxsize);
}
mir_disconnect(q, mir);
return (1);
}