#include <sys/param.h>
#include <sys/types.h>
#include <sys/user.h>
#include <sys/systm.h>
#include <sys/sysmacros.h>
#include <sys/errno.h>
#include <sys/kmem.h>
#include <sys/debug.h>
#include <sys/systm.h>
#include <sys/kstat.h>
#include <sys/sdt.h>
#include <sys/ddi.h>
#include <sys/cmn_err.h>
#include <sys/time.h>
#include <sys/list.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <rpc/clnt.h>
#include <rpc/rpc_msg.h>
#include <rpc/svc.h>
#include <rpc/rpc_tags.h>
static int rpc_cmp_tag_nolock(rpc_xprt_taglist_t *, void *);
void
rpc_taghd_init(rpc_tag_hd_t *taghd, offset_t taglst_off)
{
mutex_init(&taghd->rth_lock, NULL, MUTEX_DEFAULT, NULL);
list_create(&taghd->rth_list, sizeof (rpc_tag_t),
offsetof(rpc_tag_t, rt_next));
taghd->rth_xpoff = taglst_off;
}
void
rpc_taghd_destroy(rpc_tag_hd_t *taghd)
{
mutex_destroy(&taghd->rth_lock);
list_destroy(&taghd->rth_list);
}
void
rpc_init_taglist(void **xlst)
{
rpc_xprt_taglist_t *xptlst;
xptlst = kmem_zalloc(sizeof (rpc_xprt_taglist_t), KM_SLEEP);
mutex_init(&xptlst->rxtl_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&xptlst->rxtl_cv, NULL, CV_DEFAULT, NULL);
list_create(&xptlst->rxtl_list, sizeof (rpc_xprt_tag_t),
offsetof(rpc_xprt_tag_t, rxt_next));
*xlst = (void *)xptlst;
}
void
rpc_destroy_taglist(void **xlst)
{
rpc_xprt_taglist_t *xptlst;
xptlst = (rpc_xprt_taglist_t *)*xlst;
mutex_destroy(&xptlst->rxtl_lock);
cv_destroy(&xptlst->rxtl_cv);
ASSERT(list_is_empty(&xptlst->rxtl_list));
list_destroy(&xptlst->rxtl_list);
kmem_free(xptlst, sizeof (rpc_xprt_taglist_t));
*xlst = NULL;
}
void
rpc_tag_rele_nolock(rpc_tag_t *tag)
{
ASSERT(mutex_owned(&(tag)->rt_lock));
ASSERT((tag)->rt_ref >= 1);
(tag)->rt_ref--;
DTRACE_PROBE1(rpc__tag__rele, rpc_tag_t *, tag);
if ((tag)->rt_ref == 0)
cv_signal(&(tag)->rt_cv);
}
void
rpc_tag_rele(rpc_tag_t *tag)
{
mutex_enter(&tag->rt_lock);
rpc_tag_rele_nolock(tag);
mutex_exit(&tag->rt_lock);
}
void
rpc_tag_hold(rpc_tag_t *tag)
{
mutex_enter(&tag->rt_lock);
RPC_TAG_HOLD_LOCKED(tag);
mutex_exit(&tag->rt_lock);
}
rpc_tag_t *
rpc_lookup_tag(rpc_tag_hd_t *taghd, void *tgid, int creat)
{
rpc_tag_t *tag;
mutex_enter(&taghd->rth_lock);
tag = list_head(&taghd->rth_list);
while (tag) {
if (RPC_TAG_CMP(tag->rt_id, tgid) == 0) {
mutex_enter(&tag->rt_lock);
RPC_TAG_HOLD_LOCKED(tag);
mutex_exit(&tag->rt_lock);
mutex_exit(&taghd->rth_lock);
return (tag);
}
tag = list_next(&taghd->rth_list, tag);
}
if (creat == FALSE) {
mutex_exit(&taghd->rth_lock);
return (NULL);
}
tag = (rpc_tag_t *)kmem_zalloc(sizeof (rpc_tag_t), KM_SLEEP);
bcopy(tgid, tag->rt_id, sizeof (tagid));
mutex_init(&tag->rt_lock, NULL, MUTEX_DEFAULT, NULL);
cv_init(&tag->rt_cv, NULL, CV_DEFAULT, NULL);
list_create(&tag->rt_xplist, sizeof (rpc_tag_xprt_t),
offsetof(rpc_tag_xprt_t, rtx_xprt_next));
list_insert_tail(&taghd->rth_list, tag);
tag->rt_ref = 1;
mutex_exit(&taghd->rth_lock);
return (tag);
}
void *
rpc_get_next_xprt(rpc_tag_t *tag, void **last)
{
rpc_tag_xprt_t *rtxp = (rpc_tag_xprt_t *)*last;
if (rtxp == NULL)
rtxp = list_head(&tag->rt_xplist);
else
rtxp = list_next(&tag->rt_xplist, rtxp);
*last = (void *)rtxp;
if (rtxp == NULL)
return (NULL);
return (rtxp->rtx_xprt);
}
int
rpc_cmp_tag(void *xlst, void *tgid)
{
rpc_xprt_taglist_t *xtlst;
int ret;
xtlst = (rpc_xprt_taglist_t *)xlst;
mutex_enter(&xtlst->rxtl_lock);
ret = rpc_cmp_tag_nolock(xtlst, tgid);
mutex_exit(&xtlst->rxtl_lock);
return (ret);
}
static int
rpc_cmp_tag_nolock(rpc_xprt_taglist_t *xtlst, void *tgid)
{
rpc_xprt_tag_t *xptag;
if (xtlst->rxtl_flags & RPC_XP_TGL_DESTROY) {
cv_wait(&xtlst->rxtl_cv, &xtlst->rxtl_lock);
}
xptag = list_head(&xtlst->rxtl_list);
while (xptag) {
if (bcmp(tgid, xptag->rxt_tag->rt_id, sizeof (tagid)) == 0) {
return (TRUE);
}
xptag = list_next(&xtlst->rxtl_list, xptag);
}
return (FALSE);
}
void
rpc_add_tag(rpc_tag_hd_t *taghd, void *xprt, void *tgid)
{
rpc_xprt_taglist_t *xtlst;
rpc_xprt_tag_t *xptag;
rpc_tag_t *tag;
rpc_tag_xprt_t *rtxp;
tag = rpc_lookup_tag(taghd, tgid, TRUE);
xtlst = *(XPRT2TLST(xprt, taghd->rth_xpoff));
mutex_enter(&xtlst->rxtl_lock);
if (rpc_cmp_tag_nolock(xtlst, tgid)) {
mutex_exit(&xtlst->rxtl_lock);
return;
}
xptag = kmem_zalloc(sizeof (rpc_xprt_tag_t), KM_SLEEP);
xptag->rxt_tag = tag;
list_insert_tail(&xtlst->rxtl_list, xptag);
mutex_exit(&xtlst->rxtl_lock);
rtxp = kmem_zalloc(sizeof (rpc_tag_xprt_t), KM_SLEEP);
rtxp->rtx_xprt = xprt;
mutex_enter(&tag->rt_lock);
list_insert_tail(&tag->rt_xplist, rtxp);
mutex_exit(&tag->rt_lock);
}
void
rpc_remove_tag(rpc_tag_hd_t *tghd, void *xprt, void *tgid)
{
rpc_xprt_tag_t *xptag;
rpc_xprt_taglist_t *xtlst;
if (tgid == NULL)
return;
xtlst = *(XPRT2TLST(xprt, tghd->rth_xpoff));
mutex_enter(&xtlst->rxtl_lock);
if (xtlst->rxtl_flags & RPC_XP_TGL_DESTROY) {
cv_wait(&xtlst->rxtl_cv, &xtlst->rxtl_lock);
}
xptag = list_head(&xtlst->rxtl_list);
while (xptag) {
if (bcmp(xptag->rxt_tag->rt_id, tgid, sizeof (tagid)) != 0) {
xptag = list_next(&xtlst->rxtl_list, xptag);
continue;
}
list_remove(&xtlst->rxtl_list, xptag);
kmem_free(xptag, sizeof (rpc_xprt_tag_t));
break;
}
mutex_exit(&xtlst->rxtl_lock);
}
void
rpc_remove_all_tag(rpc_tag_hd_t *tghd, void *xprt)
{
rpc_xprt_tag_t *xptag;
rpc_xprt_taglist_t *xtlst;
rpc_tag_t *tag;
xtlst = *(XPRT2TLST(xprt, tghd->rth_xpoff));
mutex_enter(&xtlst->rxtl_lock);
xtlst->rxtl_flags |= RPC_XP_TGL_DESTROY;
xptag = list_head(&xtlst->rxtl_list);
while (xptag) {
list_remove(&xtlst->rxtl_list, xptag);
mutex_exit(&xtlst->rxtl_lock);
tag = xptag->rxt_tag;
mutex_enter(&tag->rt_lock);
rpc_remove_xprt(tghd, tag, xprt);
mutex_exit(&tag->rt_lock);
kmem_free(xptag, sizeof (rpc_xprt_tag_t));
mutex_enter(&xtlst->rxtl_lock);
xptag = list_head(&xtlst->rxtl_list);
}
xtlst->rxtl_flags &= ~RPC_XP_TGL_DESTROY;
cv_signal(&xtlst->rxtl_cv);
mutex_exit(&xtlst->rxtl_lock);
}
int
rpc_is_taglist_empty(void *xlst)
{
int ret;
rpc_xprt_taglist_t *xtlst;
xtlst = (rpc_xprt_taglist_t *)xlst;
mutex_enter(&xtlst->rxtl_lock);
ret = list_is_empty(&xtlst->rxtl_list);
mutex_exit(&xtlst->rxtl_lock);
return (ret);
}
void
rpc_remove_xprt(rpc_tag_hd_t *taghd, rpc_tag_t *tag, void *xprt)
{
rpc_tag_xprt_t *rtxp;
ASSERT(MUTEX_HELD(&tag->rt_lock));
rtxp = list_head(&tag->rt_xplist);
ASSERT(rtxp != NULL);
while (rtxp) {
if (rtxp->rtx_xprt == xprt) {
list_remove(&tag->rt_xplist, rtxp);
kmem_free(rtxp, sizeof (rpc_tag_xprt_t));
RPC_TAG_RELE_LOCKED(tag);
break;
}
rtxp = list_next(&tag->rt_xplist, rtxp);
}
}
void
rpc_remove_all_xprt(rpc_tag_hd_t *taghd, rpc_tag_t *tag)
{
rpc_tag_xprt_t *rtxp;
ASSERT(MUTEX_HELD(&tag->rt_lock));
rtxp = list_head(&tag->rt_xplist);
ASSERT(rtxp != NULL);
while (rtxp) {
list_remove(&tag->rt_xplist, rtxp);
rpc_remove_tag(taghd, rtxp->rtx_xprt, tag->rt_id);
kmem_free(rtxp, sizeof (rpc_tag_xprt_t));
RPC_TAG_RELE_LOCKED(tag);
rtxp = list_head(&tag->rt_xplist);
}
}
int
rpc_tag_swap(rpc_tag_hd_t *taghd, void *oldtag, void *newtag)
{
rpc_tag_t *tag;
tag = rpc_lookup_tag(taghd, oldtag, FALSE);
if (tag == NULL)
return (FALSE);
mutex_enter(&tag->rt_lock);
bcopy(newtag, tag->rt_id, sizeof (tagid));
RPC_TAG_RELE_LOCKED(tag);
mutex_exit(&tag->rt_lock);
return (TRUE);
}
void
rpc_destroy_tag(rpc_tag_hd_t *taghd, void *tgid)
{
rpc_tag_t *tag;
tag = rpc_lookup_tag(taghd, tgid, FALSE);
if (tag == NULL)
return;
mutex_enter(&tag->rt_lock);
if (tag->rt_flags & RPC_TAG_DESTROY) {
RPC_TAG_RELE_LOCKED(tag);
mutex_exit(&tag->rt_lock);
return;
}
tag->rt_flags |= RPC_TAG_DESTROY;
mutex_exit(&tag->rt_lock);
mutex_enter(&taghd->rth_lock);
list_remove(&taghd->rth_list, tag);
mutex_exit(&taghd->rth_lock);
mutex_enter(&tag->rt_lock);
RPC_TAG_RELE_LOCKED(tag);
tryagain:
rpc_remove_all_xprt(taghd, tag);
if (tag->rt_ref > 1) {
cv_wait(&tag->rt_cv, &tag->rt_lock);
goto tryagain;
}
mutex_exit(&tag->rt_lock);
mutex_destroy(&tag->rt_lock);
cv_destroy(&tag->rt_cv);
ASSERT(list_is_empty(&tag->rt_xplist));
list_destroy(&tag->rt_xplist);
kmem_free(tag, sizeof (rpc_tag_t));
}