/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "ghd.h"
/*ARGSUSED*/
gtgt_t *
void *hba_private,
{
/*
* initialize the per instance structure
*/
/*
* set the queue's maxactive to 1 if
* property not specified on target or hba devinfo node
*/
/* initialize the linked list pointers */
/*
* grab both mutexes so the queue structures
* stay stable while adding this instance to the linked lists
*/
/*
* Search the HBA's linked list of device structures.
*
* If this device is already attached then link this instance
* to the existing per-device-structure on the ccc_devs list.
*
*/
GDBG_WAITQ(("ghd_target_init(%d,%d) found gdevp 0x%p"
goto foundit;
}
}
/*
* Not found. This is the first instance for this device.
*/
/* allocate the per-device-structure */
/*
* link this second level queue to the HBA's first
* level queue
*/
GDBG_WAITQ(("ghd_target_init(%d,%d) new gdevp 0x%p gtgtp 0x%p"
maxactive));
/* save the ptr to the per device structure */
/* Add the per instance structure to the per device list */
return (gtgtp);
}
/*ARGSUSED*/
void
{
GDBG_WAITQ(("ghd_target_free(%d,%d) gdevp-0x%p gtgtp 0x%p\n",
/*
* grab both mutexes so the queue structures
* stay stable while deleting this instance
*/
/*
* remove this per-instance structure from the device list and
* free the memory
*/
GDBG_WAITQ(("ghd_target_free: N=1 gdevp 0x%p\n",
(void *)gdevp));
/*
* If there's now just one instance left attached to this
* device then reset the queue's max active value
* from that instance's saved value.
*/
} else if (gdevp->gd_ninstances == 0) {
/* else no instances left */
GDBG_WAITQ(("ghd_target_free: N=0 gdevp 0x%p\n",
(void *)gdevp));
/* detach this per-dev-structure from the HBA's dev list */
}
else {
/* leave maxactive set to 1 */
GDBG_WAITQ(("ghd_target_free: N>1 gdevp 0x%p\n",
(void *)gdevp));
}
#endif
}
void
{
GDBG_WAITQ(("ghd_waitq_shuffle_up: cccp 0x%p gdevp 0x%p N %ld "
GDEV_MAXACTIVE(gdevp)));
for (;;) {
/*
* Now check the device wait queue throttle to see if I can
* shuffle up a request to the HBA wait queue.
*/
GDBG_WAITQ(("ghd_waitq_shuffle_up: N>MAX gdevp 0x%p\n",
(void *)gdevp));
return;
}
/*
* single thread requests while multiple instances
* because the different target drives might have
* conflicting maxactive throttles.
*/
GDBG_WAITQ(("ghd_waitq_shuffle_up: multi gdevp 0x%p\n",
(void *)gdevp));
return;
}
/*
* promote the topmost request from the device queue to
* the HBA queue.
*/
/* the device is empty so we're done */
GDBG_WAITQ(("ghd_waitq_shuffle_up: MT gdevp 0x%p\n",
(void *)gdevp));
return;
}
GDEV_NACTIVE(gdevp)++;
gcmdp->cmd_waitq_level++;
GDBG_WAITQ(("ghd_waitq_shuffle_up: gdevp 0x%p gcmdp 0x%p\n",
}
}
void
{
#endif
/*
* Adjust all queue counters. If this request is being aborted
* it might only have made it to the target queue. Otherwise,
* both the target and hba queue have to be adjusted when a
* request is completed normally. The cmd_waitq_level value
* indicates which queue counters need to be adjusted. It's
* incremented as the request progresses up the queues.
*/
switch (gcmdp->cmd_waitq_level) {
case 0:
break;
case 1:
/*
* If this is an early-timeout, or early-abort, the request
* is still linked onto a waitq. Remove it now. If it's
* an active request and no longer on the waitq then calling
* L2_delete a second time does no harm.
*/
break;
case 2:
if (GDEV_NACTIVE(gdevp) == 0)
debug_enter("\n\nGHD WAITQ DELETE\n\n");
#endif
GDEV_NACTIVE(gdevp)--;
break;
case 3:
/* it's an active or completed command */
debug_enter("\n\nGHD WAITQ DELETE\n\n");
#endif
GDEV_NACTIVE(gdevp)--;
GHBA_NACTIVE(cccp)--;
break;
default:
/* this shouldn't happen */
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",
/*
* There's probably now more room in the HBA queue. Move
* up as many requests as possible.
*/
}
int
{
for (;;) {
/* return if the list is empty */
GDBG_WAITQ(("ghd_waitq_proc: MT cccp 0x%p qp 0x%p\n",
break;
}
/* return if the HBA is too active */
GDBG_WAITQ(("ghd_waitq_proc: N>M cccp 0x%p qp 0x%p"
" N %ld max %ld\n", (void *)cccp,
GHBA_MAXACTIVE(cccp)));
break;
}
/*
* bail out if the wait queue has been
* "held" by the HBA driver
*/
if (cccp->ccc_waitq_held) {
GDBG_WAITQ(("ghd_waitq_proc: held"));
return (rc);
}
if (cccp->ccc_waitq_frozen) {
lbolt = ddi_get_lbolt();
if (time_to_wait > 0) {
/*
* stay frozen; we'll be called again
* by ghd_timeout_softintr()
*/
GDBG_WAITQ(("ghd_waitq_proc: frozen"));
return (rc);
} else {
/* unfreeze and continue */
GDBG_WAITQ(("ghd_waitq_proc: unfreezing"));
cccp->ccc_waitq_freezetime = 0;
cccp->ccc_waitq_freezedelay = 0;
cccp->ccc_waitq_frozen = 0;
}
}
GHBA_NACTIVE(cccp)++;
gcmdp->cmd_waitq_level++;
/*
* Start up the next I/O request
*/
/* if the HBA rejected the request, requeue it */
GHBA_NACTIVE(cccp)--;
gcmdp->cmd_waitq_level--;
GDBG_WAITQ(("ghd_waitq_proc: busy cccp 0x%p gcmdp 0x%p"
cccp->ccc_hba_handle));
break;
}
GDBG_WAITQ(("ghd_waitq_proc: ++ cccp 0x%p gcmdp 0x%p N %ld\n",
}
return (rc);
}
void
{
GDBG_WAITQ(("ghd_waitq_process_and_mutex_exit: cccp 0x%p\n",
(void *)cccp));
(void) ghd_waitq_process_and_mutex_hold(cccp);
/*
* Release the mutexes in the opposite order that they
* were acquired to prevent requests queued by
* ghd_transport() from getting hung up in the wait queue.
*/
}