ghd.c revision 1dc8bc23152a02d4586ec1fd8612f7e8f57ceb42
/*
* 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"
/* ghd_poll() function codes: */
typedef enum {
GHD_POLL_REQUEST, /* wait for a specific request */
GHD_POLL_DEVICE, /* wait for a specific device to idle */
GHD_POLL_ALL /* wait for the whole bus to idle */
} gpoll_t;
/*
* Local functions:
*/
/*
* Local configuration variables
*/
static int
{
GDBG_ERROR(("ghd_doneq_init: add softintr failed cccp 0x%p\n",
(void *)cccp));
return (FALSE);
}
return (TRUE);
}
/*
* ghd_complete():
*
* The HBA driver calls this entry point when it's completely
* done processing a request.
*
* See the GHD_COMPLETE_INLINE() macro in ghd.h for the actual code.
*/
void
{
}
/*
* ghd_doneq_put_head():
*
* Mark the request done and prepend it to the doneq.
* See the GHD_DONEQ_PUT_HEAD_INLINE() macros in ghd.h for
* the actual code.
*/
void
{
}
/*
* ghd_doneq_put_tail():
*
* Mark the request done and append it to the doneq.
* See the GHD_DONEQ_PUT_TAIL_INLINE() macros in ghd.h for
* the actual code.
*/
void
{
}
static gcmd_t *
{
return (gcmdp);
}
static void
{
}
static void
{
/* trigger software interrupt for the completion callbacks */
/*
* If we are panicking we should just call the completion
* function directly as we can not use soft interrupts
* or timeouts during panic.
*/
if (!ddi_in_panic())
else
}
}
/* ***************************************************************** */
/*
*
* ghd_doneq_process()
*
* This function is called directly from the software interrupt
* handler.
*
* The doneq is protected by a separate mutex than the
* HBA mutex in order to avoid mutex contention on MP systems.
*
*/
static uint_t
{
int rc = DDI_INTR_UNCLAIMED;
for (;;) {
/* skip if FLAG_NOINTR request in progress */
if (cccp->ccc_hba_pollmode)
break;
/* pop the first one from the done Q */
break;
/* special request; processed here and discarded */
continue;
}
/*
* drop the mutex since completion
* function can re-enter the top half via
* ghd_transport()
*/
#ifdef notyet
/* I don't think this is ever necessary */
#endif
}
return (rc);
}
static void
{
/* lock the reset notify list while we operate on it */
/* don't call if HBA driver didn't set it */
if (cccp->ccc_hba_reset_notify_callback) {
}
}
}
/* ***************************************************************** */
/*
* ghd_register()
*
* Do the usual interrupt handler setup stuff.
*
* Also, set up three mutexes: the wait queue mutex, the HBA
* mutex, and the done queue mutex. The permitted locking
* orders are:
*
* 1. enter(waitq)
* 2. enter(activel)
* 3. enter(doneq)
* 4. enter(HBA) then enter(activel)
* 5. enter(HBA) then enter(doneq)
* 6. enter(HBA) then enter(waitq)
* 7. enter(waitq) then tryenter(HBA)
*
* Note: cases 6 and 7 won't deadlock because case 7 is always
* mutex_tryenter() call.
*
*/
int
ghd_register(char *labelp,
int inumber,
void *hba_handle,
void (*hba_complete)(void *, gcmd_t *, int),
int (*get_status)(void *, void *),
void (*process_intr)(void *, void *),
void (*hba_reset_notify_callback)(gtgt_t *,
{
/* initialize the HBA's list headers */
!= DDI_SUCCESS) {
return (FALSE);
}
cccp->ccc_iblock);
cccp->ccc_iblock);
/* Establish interrupt handler */
return (FALSE);
}
return (FALSE);
}
if (ghd_doneq_init(cccp)) {
return (TRUE);
}
/*
* ghd_doneq_init() returned error:
*/
return (FALSE);
}
void
{
}
int
{
int rc = DDI_INTR_UNCLAIMED;
int more;
GDBG_INTR(("ghd_intr(): cccp=0x%p status=0x%p\n",
cccp, intr_status));
for (;;) {
/* process the interrupt status */
}
if (ghd_waitq_process_and_mutex_hold(cccp)) {
continue;
}
if (more) {
continue;
}
GDBG_INTR(("ghd_intr(): done cccp=0x%p status=0x%p rc %d\n",
/*
* 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.
*/
return (rc);
}
}
static int
void *intr_status)
{
/* Que hora es? */
start_lbolt = ddi_get_lbolt();
while (!got_it) {
/* Give up yet? */
break;
/*
* delay 1 msec each time around the loop (this is an
* arbitrary delay value, any value should work) except
* zero because some devices don't like being polled too
* fast and it saturates the bus on an MP system.
*/
drv_usecwait(1000);
/*
* check for any new device status
*/
/*
* If something completed then try to start the
* next request from the wait queue. Don't release
* the HBA mutex because I don't know whether my
*/
(void) ghd_waitq_process_and_mutex_hold(cccp);
/*
* Process the first of any timed-out requests.
*/
/*
* Unqueue all the completed requests, look for mine
*/
/*
* If we got one and it's my request, then
* we're done.
*/
if (gcmdp == poll_gcmdp) {
continue;
}
/* fifo queue the other cmds on my local list */
}
/*
* Check whether we're done yet.
*/
switch (polltype) {
case GHD_POLL_DEVICE:
/*
* wait for everything queued on a specific device
*/
break;
case GHD_POLL_ALL:
/*
* if waiting for all outstanding requests and
* if active list is now empty then exit
*/
if (GHBA_NACTIVE(cccp) == 0)
break;
case GHD_POLL_REQUEST:
break;
}
}
if (L2_EMPTY(&gcmd_hold_queue)) {
return (got_it);
}
/*
* copy the local gcmd_hold_queue back to the doneq so
* that the order of completion callbacks is preserved
*/
}
return (got_it);
}
/*
* ghd_tran_abort()
*
* Abort specific command on a target.
*
*/
int
{
int rc;
/*
* call the driver's abort_cmd function
*/
case GCMD_STATE_WAITQ:
/* not yet started */
break;
case GCMD_STATE_ACTIVE:
/* in progress */
break;
default:
/* everything else, probably already being aborted */
goto exit;
}
/* stop the timer and remove it from the active list */
/* start a new timer and send out the abort command */
/* wait for the abort to complete */
}
exit:
return (rc);
}
/*
* ghd_tran_abort_lun()
*
* Abort all commands on a specific target.
*
*/
int
{
int rc;
/*
* call the HBA driver's abort_device function
*/
/* send out the abort device request */
/* wait for the device to go idle */
return (rc);
}
/*
* ghd_tran_reset_target()
*
* reset the target device
*
*
*/
int
{
/* send out the device reset request */
/* wait for the device to reset */
return (rc);
}
/*
* ghd_tran_reset_bus()
*
* reset the scsi bus
*
*/
int
{
int rc;
/* send out the bus reset request */
/*
* Wait for all active requests on this HBA to complete
*/
return (rc);
}
int
int polled,
void *intr_status)
{
if (polled) {
/*
* Grab the HBA mutex so no other requests are started
* until after this one completes.
*/
GDBG_START(("ghd_transport: polled"
" cccp 0x%p gdevp 0x%p gtgtp 0x%p gcmdp 0x%p\n",
/*
* Lock the doneq so no other thread flushes the Q.
*/
}
else {
GDBG_START(("ghd_transport: non-polled"
" cccp 0x%p gdevp 0x%p gtgtp 0x%p gcmdp 0x%p\n",
}
#endif
/*
* add this request to the tail of the waitq
*/
/*
* Add this request to the packet timer active list and start its
* abort timer.
*/
/*
* Check the device wait queue throttle and perhaps move
* some requests to the end of the HBA wait queue.
*/
if (!polled) {
/*
* See if the HBA mutex is available but use the
* tryenter so I don't deadlock.
*/
/* The HBA mutex isn't available */
return (TRAN_ACCEPT);
}
/*
* start as many requests as possible from the head
* of the HBA wait queue
*/
return (TRAN_ACCEPT);
}
/*
* If polled mode (FLAG_NOINTR specified in scsi_pkt flags),
* then ghd_poll() waits until the request completes or times out
* before returning.
*/
/* call HBA's completion function but don't do callback to target */
return (TRAN_ACCEPT);
}
int flag,
{
switch (flag) {
case SCSI_RESET_NOTIFY:
KM_SLEEP);
(void *)rnp);
break;
case SCSI_RESET_CANCEL:
for (rnp = (ghd_reset_notify_list_t *)
}
}
break;
default:
break;
}
return (rc);
}
/*
* freeze the HBA waitq output (see ghd_waitq_process_and_mutex_hold),
* presumably because of a SCSI reset, for delay milliseconds.
*/
void
{
/* freeze the waitq for delay milliseconds */
}
void
{
}
void
{
cccp->ccc_waitq_held = 0;
}
/*
* Trigger previously-registered reset notifications
*/
void
{
/* create magic doneq entry */
/* put at head of doneq so it's processed ASAP */
}