#include <sys/types.h>
#include <sys/kmem.h>
#include <sys/note.h>
#include "ghd.h"
gtgt_t *
ghd_target_init(dev_info_t *hba_dip,
dev_info_t *tgt_dip,
ccc_t *cccp,
size_t tgt_private_size,
void *hba_private,
ushort_t target,
uchar_t lun)
{
_NOTE(ARGUNUSED(hba_dip))
gtgt_t *gtgtp;
size_t size = sizeof (*gtgtp) + tgt_private_size;
gdev_t *gdevp;
ulong_t maxactive;
gtgtp = kmem_zalloc(size, KM_SLEEP);
gtgtp->gt_tgt_private = (void *)(gtgtp + 1);
gtgtp->gt_size = size;
gtgtp->gt_hba_private = hba_private;
gtgtp->gt_target = target;
gtgtp->gt_lun = lun;
gtgtp->gt_ccc = cccp;
maxactive = ddi_getprop(DDI_DEV_T_ANY, tgt_dip, 0, "ghd-maxactive", 1);
gtgtp->gt_maxactive = maxactive;
GTGT_INIT(gtgtp);
mutex_enter(&cccp->ccc_hba_mutex);
mutex_enter(&cccp->ccc_waitq_mutex);
gdevp = CCCP2GDEVP(cccp);
while (gdevp != NULL) {
if (gdevp->gd_target == target && gdevp->gd_lun == lun) {
GDBG_WAITQ(("ghd_target_init(%d,%d) found gdevp 0x%p"
" gtgtp 0x%p max %lu\n", target, lun,
(void *)gdevp, (void *)gtgtp, maxactive));
goto foundit;
}
gdevp = GDEV_NEXTP(gdevp);
}
gdevp = kmem_zalloc(sizeof (*gdevp), KM_SLEEP);
gdevp->gd_target = target;
gdevp->gd_lun = lun;
GDEV_QATTACH(gdevp, cccp, maxactive);
GDBG_WAITQ(("ghd_target_init(%d,%d) new gdevp 0x%p gtgtp 0x%p"
" max %lu\n", target, lun, (void *)gdevp, (void *)gtgtp,
maxactive));
foundit:
gtgtp->gt_gdevp = gdevp;
GTGT_ATTACH(gtgtp, gdevp);
ghd_waitq_process_and_mutex_exit(cccp);
return (gtgtp);
}
void
ghd_target_free(dev_info_t *hba_dip,
dev_info_t *tgt_dip,
ccc_t *cccp,
gtgt_t *gtgtp)
{
_NOTE(ARGUNUSED(hba_dip,tgt_dip))
gdev_t *gdevp = gtgtp->gt_gdevp;
GDBG_WAITQ(("ghd_target_free(%d,%d) gdevp-0x%p gtgtp 0x%p\n",
gtgtp->gt_target, gtgtp->gt_lun, (void *)gdevp, (void *)gtgtp));
mutex_enter(&cccp->ccc_hba_mutex);
mutex_enter(&cccp->ccc_waitq_mutex);
ASSERT(gdevp->gd_ninstances > 0);
GTGT_DEATTACH(gtgtp, gdevp);
kmem_free((caddr_t)gtgtp, gtgtp->gt_size);
if (gdevp->gd_ninstances == 1) {
GDBG_WAITQ(("ghd_target_free: N=1 gdevp 0x%p\n",
(void *)gdevp));
gtgtp = GDEVP2GTGTP(gdevp);
GDEV_MAXACTIVE(gdevp) = gtgtp->gt_maxactive;
} else if (gdevp->gd_ninstances == 0) {
GDBG_WAITQ(("ghd_target_free: N=0 gdevp 0x%p\n",
(void *)gdevp));
GDEV_QDETACH(gdevp, cccp);
kmem_free(gdevp, sizeof (*gdevp));
}
#if defined(GHD_DEBUG) || defined(__lint)
else {
GDBG_WAITQ(("ghd_target_free: N>1 gdevp 0x%p\n",
(void *)gdevp));
}
#endif
ghd_waitq_process_and_mutex_exit(cccp);
}
void
ghd_waitq_shuffle_up(ccc_t *cccp, gdev_t *gdevp)
{
gcmd_t *gcmdp;
ASSERT(mutex_owned(&cccp->ccc_waitq_mutex));
GDBG_WAITQ(("ghd_waitq_shuffle_up: cccp 0x%p gdevp 0x%p N %ld "
"max %ld\n", (void *)cccp, (void *)gdevp, GDEV_NACTIVE(gdevp),
GDEV_MAXACTIVE(gdevp)));
for (;;) {
if (GDEV_NACTIVE(gdevp) >= GDEV_MAXACTIVE(gdevp)) {
GDBG_WAITQ(("ghd_waitq_shuffle_up: N>MAX gdevp 0x%p\n",
(void *)gdevp));
return;
}
if (gdevp->gd_ninstances > 1 && GDEV_NACTIVE(gdevp) > 0) {
GDBG_WAITQ(("ghd_waitq_shuffle_up: multi gdevp 0x%p\n",
(void *)gdevp));
return;
}
if ((gcmdp = L2_remove_head(&GDEV_QHEAD(gdevp))) == NULL) {
GDBG_WAITQ(("ghd_waitq_shuffle_up: MT gdevp 0x%p\n",
(void *)gdevp));
return;
}
L2_add(&GHBA_QHEAD(cccp), &gcmdp->cmd_q, gcmdp);
GDEV_NACTIVE(gdevp)++;
gcmdp->cmd_waitq_level++;
GDBG_WAITQ(("ghd_waitq_shuffle_up: gdevp 0x%p gcmdp 0x%p\n",
(void *)gdevp, (void *)gcmdp));
}
}
void
ghd_waitq_delete(ccc_t *cccp, gcmd_t *gcmdp)
{
gtgt_t *gtgtp = GCMDP2GTGTP(gcmdp);
gdev_t *gdevp = gtgtp->gt_gdevp;
#if defined(GHD_DEBUG) || defined(__lint)
Q_t *qp = &gdevp->gd_waitq;
#endif
ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
mutex_enter(&cccp->ccc_waitq_mutex);
switch (gcmdp->cmd_waitq_level) {
case 0:
break;
case 1:
L2_delete(&gcmdp->cmd_q);
break;
case 2:
L2_delete(&gcmdp->cmd_q);
#if defined(GHD_DEBUG) || defined(__lint)
if (GDEV_NACTIVE(gdevp) == 0)
debug_enter("\n\nGHD WAITQ DELETE\n\n");
#endif
GDEV_NACTIVE(gdevp)--;
break;
case 3:
#if defined(GHD_DEBUG) || defined(__lint)
if (GDEV_NACTIVE(gdevp) == 0 || GHBA_NACTIVE(cccp) == 0)
debug_enter("\n\nGHD WAITQ DELETE\n\n");
#endif
GDEV_NACTIVE(gdevp)--;
GHBA_NACTIVE(cccp)--;
break;
default:
#if defined(GHD_DEBUG) || defined(__lint)
debug_enter("\n\nGHD WAITQ LEVEL > 3\n\n");
#endif
break;
}
GDBG_WAITQ(("ghd_waitq_delete: gcmdp 0x%p qp 0x%p level %ld\n",
(void *)gcmdp, (void *)qp, gcmdp->cmd_waitq_level));
ghd_waitq_shuffle_up(cccp, gdevp);
mutex_exit(&cccp->ccc_waitq_mutex);
}
int
ghd_waitq_process_and_mutex_hold(ccc_t *cccp)
{
gcmd_t *gcmdp;
int rc = FALSE;
ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
ASSERT(mutex_owned(&cccp->ccc_waitq_mutex));
for (;;) {
if (L2_EMPTY(&GHBA_QHEAD(cccp))) {
GDBG_WAITQ(("ghd_waitq_proc: MT cccp 0x%p qp 0x%p\n",
(void *)cccp, (void *)&cccp->ccc_waitq));
break;
}
if (GHBA_NACTIVE(cccp) >= GHBA_MAXACTIVE(cccp)) {
GDBG_WAITQ(("ghd_waitq_proc: N>M cccp 0x%p qp 0x%p"
" N %ld max %ld\n", (void *)cccp,
(void *)&cccp->ccc_waitq,
GHBA_NACTIVE(cccp),
GHBA_MAXACTIVE(cccp)));
break;
}
if (cccp->ccc_waitq_held) {
GDBG_WAITQ(("ghd_waitq_proc: held"));
return (rc);
}
if (cccp->ccc_waitq_frozen) {
clock_t lbolt, delay_in_hz, time_to_wait;
delay_in_hz =
drv_usectohz(cccp->ccc_waitq_freezedelay * 1000);
lbolt = ddi_get_lbolt();
time_to_wait = delay_in_hz -
(lbolt - cccp->ccc_waitq_freezetime);
if (time_to_wait > 0) {
GDBG_WAITQ(("ghd_waitq_proc: frozen"));
return (rc);
} else {
GDBG_WAITQ(("ghd_waitq_proc: unfreezing"));
cccp->ccc_waitq_freezetime = 0;
cccp->ccc_waitq_freezedelay = 0;
cccp->ccc_waitq_frozen = 0;
}
}
gcmdp = (gcmd_t *)L2_remove_head(&GHBA_QHEAD(cccp));
GHBA_NACTIVE(cccp)++;
gcmdp->cmd_waitq_level++;
mutex_exit(&cccp->ccc_waitq_mutex);
ASSERT(gcmdp != NULL);
gcmdp->cmd_state = GCMD_STATE_ACTIVE;
if (!(*cccp->ccc_hba_start)(cccp->ccc_hba_handle, gcmdp)) {
gcmdp->cmd_state = GCMD_STATE_WAITQ;
mutex_enter(&cccp->ccc_waitq_mutex);
GHBA_NACTIVE(cccp)--;
gcmdp->cmd_waitq_level--;
L2_add_head(&GHBA_QHEAD(cccp), &gcmdp->cmd_q, gcmdp);
GDBG_WAITQ(("ghd_waitq_proc: busy cccp 0x%p gcmdp 0x%p"
" handle 0x%p\n", (void *)cccp, (void *)gcmdp,
cccp->ccc_hba_handle));
break;
}
rc = TRUE;
mutex_enter(&cccp->ccc_waitq_mutex);
GDBG_WAITQ(("ghd_waitq_proc: ++ cccp 0x%p gcmdp 0x%p N %ld\n",
(void *)cccp, (void *)gcmdp, GHBA_NACTIVE(cccp)));
}
ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
ASSERT(mutex_owned(&cccp->ccc_waitq_mutex));
return (rc);
}
void
ghd_waitq_process_and_mutex_exit(ccc_t *cccp)
{
ASSERT(mutex_owned(&cccp->ccc_hba_mutex));
ASSERT(mutex_owned(&cccp->ccc_waitq_mutex));
GDBG_WAITQ(("ghd_waitq_process_and_mutex_exit: cccp 0x%p\n",
(void *)cccp));
(void) ghd_waitq_process_and_mutex_hold(cccp);
mutex_exit(&cccp->ccc_hba_mutex);
mutex_exit(&cccp->ccc_waitq_mutex);
}