scsiMgrLib.c
上传用户:nvosite88
上传日期:2007-01-17
资源大小:4983k
文件大小:53k
- /* scsiMgrLib.c - SCSI manager library (SCSI-2) */
- /* Copyright 1989-1996 Wind River Systems, Inc. */
- #include "copyright_wrs.h"
- /*
- modification history
- --------------------
- 03d,10jul97,dds added library support for adaptec chips.
- 01c,29oct96,dgp doc: editing for newly published SCSI libraries
- 01b,21oct96,dds removed NOMANUAL from functions called by
- the driver interface.
- 01a,12sep96,dds added the scsiMgr routines from scsi2Lib.c
- */
- /*
- DESCRIPTION
- This SCSI-2 library implements the SCSI manager. The purpose of the SCSI
- manager is to manage SCSI threads between requesting VxWorks tasks and the
- SCSI controller. The SCSI manager handles SCSI events and SCSI threads
- but allocation and de-allocation of SCSI threads is not the manager's
- responsiblity. SCSI thread management includes despatching threads and
- scheduling multiple threads (which are performed by the SCSI manager, plus
- allocation and de-allocation of threads (which are performed by routines in
- scsi2Lib).
- The SCSI manager is spawned as a VxWorks task upon initialization
- of the SCSI interface within VxWorks. The entry point of the SCSI manager task
- is scsiMgr(). The SCSI manager task is usually spawned
- during initialization of the SCSI controller driver. The driver's
- xxxCtrlCreateScsi2() routine is typically responsible for such SCSI interface
- initializations.
- Once the SCSI manager has been initialized, it is ready to handle SCSI
- requests from VxWorks tasks. The SCSI manager has the following resposibilities:
- .iP
- It processes requests from client tasks.
- .iP
- It activates a SCSI transaction thread by appending it to the target
- device's wait queue and allocating a specified time period
- to execute a transaction.
- .iP
- It handles timeout events which cause threads to be aborted.
- .iP
- It receives event notifications from the SCSI driver interrupt service
- routine (ISR) and processes the event.
- .iP
- It responds to events generated by the controller hardware, such as
- disconnection and information transfer requests.
- .iP
- It replies to clients when their requests have completed or
- aborted.
- .LP
- One SCSI manager task must be spawned per SCSI controller. Thus, if a
- particular hardware platform contains more than one SCSI controller then
- that number of SCSI manager tasks must be spawned by the controller-driver
- intialization routine.
- INCLUDE FILES
- scsiLib.h, scsi2Lib.h
- SEE ALSO: scsiLib, scsi2Lib, scsiCommonLib, scsiDirectLib, scsiSeqLib,
- scsiCtrlLib,
- .I "American National Standard for Information Systems - Small Computer"
- .I "System Interface (SCSI-2), ANSI X3T9,"
- .pG "I/O System, Local File Systems"
- */
- #define INCLUDE_SCSI2
- #include "vxWorks.h"
- #include "ioLib.h"
- #include "intLib.h"
- #include "ctype.h"
- #include "cacheLib.h"
- #include "stdlib.h"
- #include "errnoLib.h"
- #include "taskLib.h"
- #include "lstLib.h"
- #include "logLib.h"
- #include "msgQLib.h"
- #include "string.h"
- #include "stdio.h"
- #include "sysLib.h"
- #include "scsiLib.h"
- #include "wdLib.h"
- /* globals variables */
- int scsiMgrActionSemOptions = SEM_Q_FIFO; /* only one task waits on it */
- int scsiThreadReplyQOptions = MSG_Q_FIFO; /* only one task waits on it */
- int scsiMgrEventQSize = SCSI_DEF_EVENT_Q_SIZE;
- int scsiMgrTimeoutQSize = SCSI_DEF_TIMEOUT_Q_SIZE;
- int scsiMgrRequestQSize = SCSI_DEF_REQUEST_Q_SIZE;
- int scsiMgrReplyQSize = SCSI_DEF_REPLY_Q_SIZE;
- /* global functions */
- STATUS scsiMgrRequestExecute (SCSI_CTRL * pScsiCtrl,
- SCSI_REQUEST * pRequest,
- SCSI_REPLY * pReply);
- VOID scsiMgrPhysDevTagInit (SCSI_PHYS_DEV * pScsiPhysDev);
- /* SCSI system manager functions */
- LOCAL BOOL scsiMgrMsgGet (RING_ID queueId, void * buffer, int length);
- LOCAL void scsiMgrEventProc (SCSI_CTRL * pScsiCtrl,
- SCSI_EVENT * pEvent);
- LOCAL void scsiMgrTimeoutProc (SCSI_CTRL * pScsiCtrl,
- SCSI_TIMEOUT * pTimeout);
- LOCAL void scsiMgrRequestProc (SCSI_CTRL * pScsiCtrl,
- SCSI_REQUEST * pRequest);
- LOCAL void scsiMgrReplyProc (SCSI_CTRL * pScsiCtrl,
- SCSI_REPLY * pReply);
- LOCAL void scsiMgrActivateRequest (SCSI_REQUEST * pRequest);
- LOCAL void scsiMgrRequestComplete (SCSI_THREAD * pThread);
- LOCAL void scsiMgrCompleteReply (SCSI_REPLY * pReply);
- LOCAL void scsiMgrTimeoutNotify (SCSI_THREAD * pThread);
- LOCAL SCSI_THREAD * scsiMgrRunnableThreadGet (SCSI_CTRL * pScsiCtrl);
- LOCAL void scsiMgrThreadDespatch (SCSI_THREAD * pThread);
- LOCAL void scsiMgrThreadComplete (SCSI_THREAD * pThread);
- LOCAL void scsiMgrThreadDefer (SCSI_THREAD * pThread);
- LOCAL STATUS scsiMgrThreadActivate (SCSI_THREAD * pThread);
- LOCAL void scsiMgrPhysDevReset (SCSI_PHYS_DEV * pScsiPhysDev);
- LOCAL void scsiMgrPhysDevEvent (SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG tagNum,
- SCSI_DEV_EVENT_TYPE type);
- LOCAL SCSI_THREAD *scsiMgrPhysDevRunnableThreadGet
- (SCSI_PHYS_DEV * pScsiPhysDev);
- LOCAL void scsiMgrPhysDevWaitQAdd (SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_THREAD * pThread);
- LOCAL STATUS scsiMgrPhysDevWaitQRemove (SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_THREAD * pThread);
- LOCAL void scsiMgrPhysDevActiveQAdd (SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_THREAD * pThread);
- LOCAL STATUS scsiMgrPhysDevActiveQRemove (SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_THREAD * pThread);
- LOCAL STATUS scsiMgrPhysDevTagAllocate (SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG_TYPE tagType,
- SCSI_TAG * pTagNum);
- LOCAL void scsiMgrPhysDevTagFree (SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG tagNum);
- LOCAL STATUS scsiMgrPhysDevTagTypeValidate (SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG_TYPE tagType,
- BOOL * pTagged);
- /*******************************************************************************
- *
- * scsiMgr - SCSI system manager
- *
- * Manage a SCSI (bus interface) controller. This entails:
- *
- * - carrying out requests on behalf of client tasks to perform SCSI
- * operations on threads, such as executing a transaction
- *
- * - responding to events generated by the controller hardware, such as
- * disconnection, information transfer request, etc.
- *
- * - handling timeout events which cause threads to be aborted
- *
- * - replying to clients when their requests have been completed or
- * aborted.
- *
- * NOTE
- * This function should not be called by application programs. It is normally
- * spawned automatically by the controller driver to form a SCSI manager task
- * for each SCSI interface.
- *
- * INTERNAL
- *
- * The details of controller event handling are hardware-specific. The event
- * message and event handling routines are made "virtual", i.e. implemented
- * by the controller driver.
- *
- * The order in which the various events and requests are processed is
- * important and must not be changed. Reasons for this vary from obvious to
- * pretty subtle - see comments for other "scsiMgr...()" functions for further
- * information.
- *
- * RETURNS: this function does not normally return
- *
- * NOMANUAL
- */
- void scsiMgr
- (
- SCSI_CTRL * pScsiCtrl /* SCSI controller to be used */
- )
- {
- SCSI_REQUEST request;
- SCSI_REPLY reply;
- SCSI_TIMEOUT timeout;
- SCSI_EVENT * pEvent;
- SCSI_THREAD * pThread;
-
- /* initialise */
- pEvent = malloc (pScsiCtrl->eventSize);
- if (pEvent == NULL)
- return;
- /*
- * Service loop
- */
- while (TRUE)
- {
- /*
- * Wait for an event, timeout or client request to be posted
- */
- semTake (pScsiCtrl->actionSem, WAIT_FOREVER);
- /*
- * Service all controller events ...
- */
- while (scsiMgrMsgGet (pScsiCtrl->eventQ, pEvent, pScsiCtrl->eventSize))
- {
- scsiMgrEventProc (pScsiCtrl, pEvent);
- }
- /*
- * Service any timeouts ...
- */
- while (scsiMgrMsgGet (pScsiCtrl->timeoutQ, &timeout, sizeof (timeout)))
- {
- scsiMgrTimeoutProc (pScsiCtrl, &timeout);
- }
- /*
- * Service any client requests ...
- */
- while (scsiMgrMsgGet (pScsiCtrl->requestQ, &request, sizeof (request)))
- {
- scsiMgrRequestProc (pScsiCtrl, &request);
- }
- /*
- * Forward any replies to clients ...
- */
- while (scsiMgrMsgGet (pScsiCtrl->replyQ, &reply, sizeof (reply)))
- {
- scsiMgrReplyProc (pScsiCtrl, &reply);
- }
- /*
- * ... finally, despatch the highest priority request if possible
- */
- if ((pThread = scsiMgrRunnableThreadGet (pScsiCtrl)) != 0)
- {
- scsiMgrThreadDespatch (pThread);
- }
- }
- }
- /*******************************************************************************
- *
- * scsiMgrMsgGet - get the next message, if any, from a SCSI manager msg queue
- *
- * Extract the next message, if any is available, from the specified queue and
- * copy it into the specified buffer. If no message is available, do not wait.
- *
- * NOTE:
- * SCSI manager message queues are implemented using ring buffers because there
- * is a separate mechanism for synchronisation. This code is careful to
- * always read complete messages from the ring buffer. A single reader (i.e.,
- * the SCSI manager task) is assumed.
- */
- LOCAL BOOL scsiMgrMsgGet
- (
- RING_ID queue, /* SCSI manager msg queue to read */
- void * buffer, /* buffer to copy msg into */
- int length /* (exact) length of msg expected */
- )
- {
- if (rngNBytes (queue) < length)
- return (FALSE);
- if (rngBufGet (queue, buffer, length) != length)
- {
- logMsg ("scsiMgrMsgGet: rngBufGet failedn", 0, 0, 0, 0, 0, 0);
- return (FALSE);
- }
-
- return (TRUE);
- }
- /*******************************************************************************
- *
- * scsiMgrEventProc - process a SCSI controller event
- *
- * Call the appropriate controller-specific function to process the event.
- *
- * NOTE:
- * This function exists primarily as a place-holder for debugging and possible
- * future enhancements such as event logging.
- */
- LOCAL void scsiMgrEventProc
- (
- SCSI_CTRL * pScsiCtrl,
- SCSI_EVENT * pEvent
- )
- {
- (*pScsiCtrl->scsiEventProc) (pScsiCtrl, pEvent);
- }
- /*******************************************************************************
- *
- * scsiMgrTimeoutProc - process a request timeout event
- *
- * First try to remove the thread from the device's wait queue. If it was
- * on the queue, simply complete the request (with a timeout error) because
- * it has not yet had a tag allocated, etc.
- *
- * If the thread wasn't on the device's wait queue, see whether its tag number
- * corresponds to a current thread. If not, the thread must have completed,
- * so the timeout request can be ignored.
- *
- * If the tag number corresponds to an in-progress thread, call the controller
- * thread abort routine. If this returns TRUE, the thread is currently active
- * on the controller, which will manage the abort process. Otherwise (the
- * thread is not currently active on the controller) abort it here.
- *
- * Possible race conditions and how they are avoided:
- *
- * 1) thread completes "just before" the watchdog expires. The following
- * cases may occur:
- *
- * a) thread completion event and timeout event are processed in the
- * same SCSI manager activation cycle. In this case, the thread's
- * tag number must be free (timeouts are processed before any new
- * threads are started) and the timeout will be ignored.
- *
- * b) thread completion event is processed but no timeout event occurs
- * in the same activation cycle. In this case, a new thread using
- * the same tag number may have been started by the time the next
- * activation cycle occurs. However, since the first thread's
- * watchdog is cancelled during completion processing in the first
- * cycle, if a timeout event has not occurred by the end of that
- * cycle, it can never occur and the newly activated thread is safe.
- *
- * 2) thread completion occurs "just after" the watchdog expires. In this
- * case, the completion event will not be processed until the next
- * activation cycle at the earliest. There are two cases:
- *
- * a) the thread is currently active on the controller when the timeout
- * is processed: in this case, the controller driver's abort routine
- * must ensure that the thread is (possibly) aborted and (definitely)
- * completed, in that order. It doesn't necessarily do this in a
- * single activation cycle.
- *
- * b) the thread is not currently active on the controller when the
- * timeout occurs: in this case, the request is completed with a
- * timeout error and the tag is marked aborted in the first cycle.
- * When an attempt is later made to re-establish this thread, the
- * identification will fail due to the aborted state of the tag.
- * The controller driver must detect this and force the connected
- * device to disconnect, after which it must free up the tag for
- * re-use by another thread.
- *
- * NOTE:
- * This routine is complex because completion of the thread (i.e. replying to
- * the client) must be decoupled from what is happening on the SCSI bus, if
- * the timeout is to be useful. This implies that even after the thread has
- * been completed, the corresponding nexus may continue to exist on the
- * target device. This is sorted out by the physical device event processing
- * (see "scsiMgrPhysDevEvent ()").
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrTimeoutProc
- (
- SCSI_CTRL * pScsiCtrl,
- SCSI_TIMEOUT * pTimeout
- )
- {
- SCSI_THREAD * pThread = pTimeout->thread;
- SCSI_PHYS_DEV * pScsiPhysDev = pThread->pScsiPhysDev;
- SCSI_TAG tagNum = pThread->tagNumber;
- BOOL doAbort;
- SCSI_DEBUG_MSG ("scsiMgrTimeoutProc: thread 0x%08x (state = %d) timed outn"
- , (int) pThread, pThread->state, 0, 0, 0, 0);
- /*
- * Ensure thread is not in device's wait queue
- */
- if (scsiMgrPhysDevWaitQRemove (pScsiPhysDev, pThread) == OK)
- {
- doAbort = TRUE;
- }
- /*
- * Thread is either current or completed: look up tag to decide
- */
- else if (scsiMgrPhysDevActiveThreadFind (pScsiPhysDev, tagNum) != pThread)
- {
- doAbort = FALSE;
- }
- /*
- * Thread is current: call controller to abort if connected.
- */
- else if ((*pScsiCtrl->scsiThreadAbort) (pScsiCtrl, pThread))
- {
- doAbort = FALSE;
- }
- /*
- * Thread is current, but not connected. Remove thread from active
- * list and complete the request. Tag completion will be handled
- * by controller driver when reconnection occurs.
- */
- else
- {
- if (scsiMgrPhysDevActiveQRemove (pScsiPhysDev, pThread) != OK)
- {
- logMsg ("scsiMgrTimeoutProc: thread not on active queue !n",
- 0, 0, 0, 0, 0, 0);
- return;
- }
- doAbort = TRUE;
- }
- /*
- * If "doAbort" is true, the thread must be completed now and it has
- * been removed from the device's wait or active queue. If "doAbort"
- * is false, either the thread has already been completed or the
- * controller driver will complete it in due course.
- */
- if (doAbort)
- {
- pThread->status = ERROR;
- pThread->errNum = S_scsiLib_ABORTED;
- scsiMgrThreadComplete (pThread);
- }
- }
- /*******************************************************************************
- *
- * scsiMgrRequestProc - process a client request
- *
- * Parse the request type and act accordingly.
- *
- * NOTE
- * Currently only one request type (activate a transaction) is accepted,
- * however this routine is intended to allow other request types in future
- * (e.g., to support SCSI target mode).
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrRequestProc
- (
- SCSI_CTRL * pScsiCtrl,
- SCSI_REQUEST * pRequest
- )
- {
- SCSI_REQUEST_TYPE type = pRequest->type;
- SCSI_DEBUG_MSG ("scsiMgrRequestProc: client request: %dn",
- type, 0, 0, 0, 0, 0);
-
- switch (type)
- {
- case SCSI_REQUEST_ACTIVATE:
- scsiMgrActivateRequest (pRequest);
- break;
- default:
- logMsg ("scsiMgrRequestProc: invalid action (%d) requestedn",
- type, 0, 0, 0, 0, 0);
- break;
- }
- }
- /*******************************************************************************
- *
- * scsiMgrReplyProc - reply to a client
- *
- * Parse the reply type and act accordingly.
- *
- * NOTE
- * Currently only one reply type (transaction complete) is accepted, however
- * this routine is intended to allow other reply types in future (e.g., to
- * support SCSI target mode).
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrReplyProc
- (
- SCSI_CTRL * pScsiCtrl,
- SCSI_REPLY * pReply
- )
- {
- SCSI_REPLY_TYPE type = pReply->type;
- SCSI_DEBUG_MSG ("scsiMgrReplyProc: client reply: %dn",
- type, 0, 0, 0, 0, 0);
-
- switch (type)
- {
- case SCSI_REPLY_COMPLETE:
- scsiMgrCompleteReply (pReply);
- break;
- default:
- logMsg ("scsiMgrReplyProc: invalid action (%d) requestedn",
- type, 0, 0, 0, 0, 0);
- break;
- }
- }
- /*******************************************************************************
- *
- * scsiMgrActivateRequest - process an "Activate" request from a client
- *
- * Start a watchdog timer for the transaction, then add the thread to the
- * target device's wait queue. It will be despatched at the next opportunity.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrActivateRequest
- (
- SCSI_REQUEST * pRequest
- )
- {
- SCSI_THREAD * pThread = pRequest->thread;
-
- SCSI_DEBUG_MSG ("scsiMgrActivateRequest: thread 0x%08xn",
- (int) pThread, 0, 0, 0, 0, 0);
- wdStart (pThread->wdog, pThread->timeout,
- (FUNCPTR) scsiMgrTimeoutNotify,
- (int) pThread);
- scsiMgrPhysDevWaitQAdd (pThread->pScsiPhysDev, pThread);
- pThread->state = SCSI_THREAD_WAITING;
- }
- /*******************************************************************************
- *
- * scsiMgrRequestComplete - terminate processing of an "Activate" request
- *
- * Cancel the watchdog timer associated with the thread, and post a message
- * requesting the client be sent a reply.
- *
- * NOTE:
- * It might seem peculiar not to just reply directly to the client here.
- * The current approach allows the SCSI manager to better prioritise its
- * activities, and avoids a subtle race condition which could otherwise occur.
- *
- * (The exact scenario is as follows: suppose a client task of higher priority
- * than the SCSI manager issues a SCSI command and then deletes the physical
- * device. If this routine unblocked the client rather than deferring this
- * to the top-level loop of the SCSI manager, the client would pre-empt the
- * SCSI manager and could then delete a device for which the SCSI manager may
- * still have some outstanding housekeeping to carry out. This would most
- * likely cause the SCSI manager task to fail. Although this sounds like a
- * contrived and unlikely scenario, it would actually happen during SCSI
- * auto-configuration by the root task or the shell.)
- */
- LOCAL void scsiMgrRequestComplete
- (
- SCSI_THREAD * pThread
- )
- {
- SCSI_REPLY reply;
- SCSI_CTRL * pScsiCtrl = pThread->pScsiCtrl;
-
- wdCancel (pThread->wdog);
- reply.type = SCSI_REPLY_COMPLETE;
- reply.thread = pThread;
- reply.status = pThread->status;
- reply.errNum = pThread->errNum;
- if (rngBufPut (pScsiCtrl->replyQ,
- (char *) &reply, sizeof (reply)) != sizeof (reply))
- {
- logMsg ("scsiMgrRequestComplete: rngBufPut failedn",
- 0, 0, 0, 0, 0, 0);
- }
- }
- /******************************************************************************
- *
- * scsiMgrCompleteReply - process a "Complete" reply to be sent to a client
- *
- * Build an appropriate reply message and send it on the thread's message
- * queue. This unblocks the client task. Th thread contains status and
- * errno values associated with the reply.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrCompleteReply
- (
- SCSI_REPLY * pReply
- )
- {
- SCSI_THREAD * pThread = pReply->thread;
- SCSI_DEBUG_MSG ("scsiMgrCompleteReply: thread 0x%08x "
- "(status: %d, errno: %d)n",
- (int) pThread, pThread->status, pThread->errNum, 0, 0, 0);
-
- if (msgQSend (pThread->replyQ, (char *) pReply,
- sizeof (SCSI_REPLY),
- NO_WAIT,
- MSG_PRI_NORMAL) != OK)
- {
- SCSI_ERROR_MSG ("scsiMgrCompleteReply: can't post reply message "
- "(errno = %d)n", errno, 0, 0, 0, 0, 0);
- }
- }
- /******************************************************************************
- *
- * scsiMgrRequestExecute - execute a SCSI request
- *
- * (This routine is called by a client task to have the SCSI manager execute a
- * request.)
- *
- * Post the request message on the SCSI manager's request queue, notifying it
- * that there is something there by giving its semaphore. Wait for the SCSI
- * manager's reply using the message queue associated with the thread.
- *
- * NOTE:
- * Since the SCSI manager's request queue is implemented using a ring buffer,
- * access by multiple clients is serialised within this routine. This is
- * achieved using the controller's mutex semaphore, which is also used to
- * serialise access to the free thread list. This will not cause deadlock.
- *
- * The return value of this function tells the caller whether the basic
- * request/reply mechanism worked or not, not whether or not the request
- * itself succeeded. In fact, this function should never return ERROR unless
- * the software has failed or data structures have been corrupted.
- *
- * This function should not be called directly by application programs.
- *
- * RETURNS: OK, or ERROR if the request could not be executed.
- *
- * NOMANUAL
- */
- STATUS scsiMgrRequestExecute
- (
- SCSI_CTRL * pScsiCtrl,
- SCSI_REQUEST * pRequest,
- SCSI_REPLY * pReply
- )
- {
- int requestSize = sizeof (SCSI_REQUEST);
- int replySize = sizeof (SCSI_REPLY);
-
- /*
- * Send request to SCSI manager task
- */
- semTake (pScsiCtrl->mutexSem, WAIT_FOREVER);
- if (rngFreeBytes (pScsiCtrl->requestQ) < requestSize)
- {
- semGive (pScsiCtrl->mutexSem);
- logMsg ("scsiMgrRequestExecute: request queue is fulln",
- 0, 0, 0, 0, 0, 0);
- return (ERROR);
- }
-
- if (rngBufPut (pScsiCtrl->requestQ,
- (char *) pRequest, requestSize) != requestSize)
- {
- semGive (pScsiCtrl->mutexSem);
- logMsg ("scsiMgrRequestExecute: rngBufPut failedn",
- 0, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- semGive (pScsiCtrl->mutexSem);
- /*
- * Notify SCSI manager that request has been posted
- */
- semGive (pScsiCtrl->actionSem);
-
- /*
- * Wait for request to be executed by SCSI manager
- */
- if (msgQReceive (pRequest->thread->replyQ,
- (char *) pReply, replySize, WAIT_FOREVER) != replySize)
- {
- SCSI_DEBUG_MSG ("scsiMgrRequestExecute: msgQReceive failed "
- "(errno = %d)n",
- errno, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- return (OK);
- }
- /*******************************************************************************
- *
- * scsiMgrTimeoutNotify - notify the SCSI manager of a transaction timeout
- *
- * Post a timeout message on the appropriate SCSI manager queue, then notify
- * the SCSI manager that there is something to do.
- *
- * NOTE:
- * No access serialisation is required because timeout messages are only
- * posted by the timeout watchdog ISR.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrTimeoutNotify
- (
- SCSI_THREAD * pThread
- )
- {
- SCSI_CTRL * pScsiCtrl = pThread->pScsiCtrl;
-
- SCSI_TIMEOUT timeout;
- timeout.thread = pThread;
- if (rngBufPut (pScsiCtrl->timeoutQ,
- (char *) &timeout, sizeof (timeout)) != sizeof (timeout))
- {
- logMsg ("scsiMgrTimeoutNotify: can't post timeout messagen",
- 0, 0, 0, 0, 0, 0);
- }
- semGive (pScsiCtrl->actionSem);
- }
- /*******************************************************************************
- *
- * scsiMgrEventNotify - notify the SCSI manager of a SCSI (controller) event
- *
- * This routine posts an event message on the appropriate SCSI manager queue,
- * then notifies the SCSI manager that there is a message to be accepted.
- *
- * NOTE:
- * This routine should not be called by application programs.
- *
- * No access serialization is required, because event messages are only
- * posted by the SCSI controller ISR. See the reference entry for
- * scsiBusResetNotify().
- *
- * RETURNS: OK, or ERROR if the SCSI manager's event queue is full.
- *
- * SEE ALSO: scsiBusResetNotify()
- */
- STATUS scsiMgrEventNotify
- (
- SCSI_CTRL * pScsiCtrl, /* pointer to SCSI controller structure */
- SCSI_EVENT * pEvent, /* pointer to the SCSI event */
- int eventSize /* size of the event information */
- )
- {
- if (rngBufPut (pScsiCtrl->eventQ, (char *) pEvent, eventSize) != eventSize)
- {
- logMsg ("scsiMgrEventNotify: can't post event messagen",
- 0, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- semGive (pScsiCtrl->actionSem);
- return (OK);
- }
- /*******************************************************************************
- *
- * scsiMgrBusReset - handle a controller-bus reset event
- *
- * This routine resets in turn: each attached physical device, each target,
- * and the controller-finite-state machine. In practice, this routine
- * implements the SCSI hard reset option.
- *
- * NOTE:
- * This routine does not physically reset the SCSI bus; see scsiBusReset().
- * This routine should not be called by application programs.
- *
- * RETURNS: N/A
- */
- void scsiMgrBusReset
- (
- SCSI_CTRL * pScsiCtrl /* SCSI ctrlr on which bus reset */
- )
- {
- UINT i;
- /*
- * Reset all physical devices
- */
- for (i = 0; i < SCSI_MAX_PHYS_DEVS; ++i)
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pScsiCtrl->physDevArr[i];
- if (pScsiPhysDev != 0)
- scsiMgrPhysDevReset (pScsiPhysDev);
- }
- /*
- * Reset all SCSI targets
- */
- for (i = 0; i < SCSI_MAX_TARGETS; ++i)
- scsiTargetReset (pScsiCtrl, i);
- /*
- * Reset controller state
- */
- scsiMgrCtrlEvent (pScsiCtrl, SCSI_EVENT_DISCONNECTED);
- }
- /*******************************************************************************
- *
- * scsiMgrRunnableThreadGet - find the next thread to run, if any
- *
- * If the controller is active, no thread can be started. Otherwise, find the
- * highest priority waiting thread by querying each physical device. If a
- * runnable thread is found, do not remove it from any wait queue.
- *
- * NOTE: a simple round-robin scheme is used to avoid unfairly favouring
- * threads from any particular physical device, when they all have equal
- * priority.
- *
- * RETURNS: thread ptr, or 0 if no runnable thread exists
- */
- LOCAL SCSI_THREAD * scsiMgrRunnableThreadGet
- (
- SCSI_CTRL * pScsiCtrl
- )
- {
- SCSI_PRIORITY bestPriority;
- SCSI_THREAD * pBestThread;
- int bestDevIndex;
- int devIndex;
- int i;
- /*
- * Can't do anything if the controller is active !
- */
- if (pScsiCtrl->scsiSpecialHandler == NULL)
- if (pScsiCtrl->active)
- return (0);
- /*
- * Find best available thread on all existing physical devices
- */
- bestPriority = SCSI_THREAD_MIN_PRIORITY;
- pBestThread = 0;
- bestDevIndex = 0;
- devIndex = pScsiCtrl->nextDev;
- for (i = 0; i < SCSI_MAX_PHYS_DEVS; ++i)
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pScsiCtrl->physDevArr [devIndex];
- /*
- * If this phys dev exists, and has a runnable thread with
- * higher priority than the best so far, remember it.
- */
- if (pScsiPhysDev != 0)
- {
- SCSI_THREAD * pThread;
-
- pThread = scsiMgrPhysDevRunnableThreadGet (pScsiPhysDev);
- if ((pThread != 0) &&
- SCSI_IS_HIGHER_PRIORITY (pThread->priority, bestPriority))
- {
- bestDevIndex = devIndex;
- bestPriority = pThread->priority;
- pBestThread = pThread;
- }
- }
- if (++devIndex >= SCSI_MAX_PHYS_DEVS)
- devIndex = 0;
- }
- if (pBestThread != 0)
- {
- pScsiCtrl->nextDev = bestDevIndex + 1;
- if (pScsiCtrl->nextDev >= SCSI_MAX_PHYS_DEVS)
- pScsiCtrl->nextDev = 0;
- }
-
- return (pBestThread);
- }
- /*******************************************************************************
- *
- * scsiMgrThreadDespatch - despatch a new thread
- *
- * Remove the thread from its device's wait queue, activate it and install it
- * on the device's active queue. If activation fails, complete the thread
- * with a suitable error code.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrThreadDespatch
- (
- SCSI_THREAD * pThread
- )
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pThread->pScsiPhysDev;
- scsiMgrPhysDevWaitQRemove (pScsiPhysDev, pThread);
-
- if (scsiMgrThreadActivate (pThread) != OK)
- {
- pThread->status = ERROR;
- pThread->errNum = errno;
-
- scsiMgrThreadComplete (pThread);
- return;
- }
- scsiMgrPhysDevActiveQAdd (pScsiPhysDev, pThread);
- }
- /******************************************************************************
- *
- * scsiMgrThreadComplete - complete an initiator thread
- *
- * Check for contingent allegiance condition by parsing the SCSI status set
- * by the target device; set or clear the physical device's contingent
- * allegiance flag accordingly. (This affects the access serialisation rules
- * used to schedule threads on the device: for more details see
- * "scsiMgrPhysDevRunnableThreadGet()".)
- *
- * Finish processing the client's request (i.e., eventually, reply).
- *
- * NOTE:
- * The thread must be a valid initiator thread (from a client request) and
- * must not be on either a device wait queue or a device active queue.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrThreadComplete
- (
- SCSI_THREAD * pThread /* ptr to thread info */
- )
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pThread->pScsiPhysDev;
- SCSI_DEBUG_MSG ("scsiMgrThreadComplete: thread 0x%08xn",
- (int) pThread, 0, 0, 0, 0, 0);
- /*
- * Check for contingent allegiance condition on physical device
- */
- if (pThread->status == OK)
- {
- UINT scsiStatus = *pThread->statusAddress;
- switch (scsiStatus & SCSI_STATUS_MASK)
- {
- case SCSI_STATUS_CHECK_CONDITION:
- case SCSI_STATUS_CMD_TERMINATED:
- SCSI_DEBUG_MSG ("scsiMgrThreadComplete: device 0x%08x: "
- "contingent allegiance detectedn",
- (int) pScsiPhysDev, 0, 0, 0, 0, 0);
- pScsiPhysDev->pendingCA = TRUE;
- break;
- default:
- if (pScsiPhysDev->pendingCA)
- {
- SCSI_DEBUG_MSG ("scsiMgrThreadComplete: device 0x%08x: "
- "contingent allegiance clearedn",
- (int) pScsiPhysDev, 0, 0, 0, 0, 0);
- pScsiPhysDev->pendingCA = FALSE;
- }
- break;
- }
- }
- /*
- * Finish processing the client's request
- */
- scsiMgrRequestComplete (pThread);
- }
- /******************************************************************************
- *
- * scsiMgrThreadDefer - defer execution of an initiator thread
- *
- * (This routine is called if a thread "loses arbitration" to reselection
- * during an attempt to select a target device.)
- *
- * Remove the thread from its physical device's active queue and re-insert it
- * onto the wait queue to be re-despatched at the next opportunity.
- *
- * NOTE:
- * The check that the thread actually was on the active queue avoids any
- * possibility of races with the transaction timeout mechanism.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrThreadDefer
- (
- SCSI_THREAD * pThread /* ptr to thread info */
- )
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pThread->pScsiPhysDev;
- SCSI_DEBUG_MSG ("scsiMgrThreadDefer: thread 0x%08xn",
- (int) pThread, 0, 0, 0, 0, 0);
- if (scsiMgrPhysDevActiveQRemove (pScsiPhysDev, pThread) == OK)
- {
- scsiMgrPhysDevWaitQAdd (pScsiPhysDev, pThread);
- pThread->state = SCSI_THREAD_WAITING;
- }
- }
- /*******************************************************************************
- *
- * scsiMgrThreadActivate - start running the specified thread
- *
- * Allocate a tag number (if required) for the target physical device, build
- * a corresponding identification message, and call the controller-specific
- * thread activation routine. Notify the controller and the thread of the
- * activation event.
- *
- * RETURNS: OK, or ERROR if activation failed.
- */
- LOCAL STATUS scsiMgrThreadActivate
- (
- SCSI_THREAD * pThread
- )
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pThread->pScsiPhysDev;
- SCSI_CTRL * pScsiCtrl = pThread->pScsiCtrl;
- /*
- * Allocate a tag number according to required type
- */
- if (pThread->tagType == SCSI_TAG_DEFAULT)
- pThread->tagType = pScsiPhysDev->tagType;
- if (scsiMgrPhysDevTagAllocate (pScsiPhysDev, pThread->tagType,
- &pThread->tagNumber) != OK)
- {
- SCSI_DEBUG_MSG ("scsiMgrThreadActivate: thread 0x%08x: "
- "can't allocate tagn",
- (int) pThread, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- /*
- * Build identification message using this tag
- */
- pThread->identMsgLength = scsiIdentMsgBuild (pThread->identMsg,
- pScsiPhysDev,
- pThread->tagType,
- pThread->tagNumber);
- /*
- * Initialise active and saved data pointers
- */
- pThread->activeDataLength = pThread->dataLength;
- pThread->activeDataAddress = pThread->dataAddress;
- pThread->savedDataLength = pThread->dataLength;
- pThread->savedDataAddress = pThread->dataAddress;
- /*
- * Activate the thread using the controller driver
- */
- if ((*pScsiCtrl->scsiThreadActivate) (pScsiCtrl, pThread) != OK)
- {
- SCSI_DEBUG_MSG ("scsiMgrThreadActivate: thread 0x%08x: "
- "controller activation failedn",
- (int) pThread, 0, 0, 0, 0, 0);
- return (ERROR);
- }
- scsiMgrCtrlEvent (pScsiCtrl, SCSI_EVENT_CONNECTED);
- scsiMgrThreadEvent (pThread, SCSI_THREAD_EVENT_ACTIVATED);
- return (OK);
- }
- /*******************************************************************************
- *
- * scsiMgrCtrlEvent - send an event to the SCSI controller state machine
- *
- * This routine is called by the thread driver whenever selection,
- * reselection, or disconnection occurs or when a thread is activated.
- * It manages a simple finite-state machine for the SCSI controller.
- *
- * NOTE:
- * This function should not be called by application programs.
- *
- * RETURNS: N/A
- */
- void scsiMgrCtrlEvent
- (
- SCSI_CTRL * pScsiCtrl,
- SCSI_EVENT_TYPE eventType
- )
- {
- switch (eventType)
- {
- case SCSI_EVENT_CONNECTED:
- pScsiCtrl->active = TRUE;
- break;
- case SCSI_EVENT_DISCONNECTED:
- pScsiCtrl->active = FALSE;
- break;
- default:
- logMsg ("scsiMgrCtrlEvent: invalid event type (%d)n",
- eventType, 0, 0, 0, 0, 0);
- break;
- }
- }
- /*******************************************************************************
- *
- * scsiMgrThreadEvent - send an event to the thread state machine
- *
- * This routine forwards an event to the thread's physical device. If the
- * event is completion or deferral, it frees up the tag which was allocated
- * when the thread was activated and either completes or defers the thread.
- *
- * NOTE:
- * This function should not be called by application programs.
- *
- * The thread passed into this function does not have to be an active client
- * thread (it may be an identification thread).
- *
- * If the thread has no corresponding physical device, this routine does
- * nothing. (This occassionally occurs if an unexpected disconnection
- * or bus reset happens when an identification thread has not yet identified
- * which physical device it corresponds to.
- *
- * RETURNS: N/A
- */
- void scsiMgrThreadEvent
- (
- SCSI_THREAD * pThread,
- SCSI_THREAD_EVENT_TYPE eventType
- )
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pThread->pScsiPhysDev;
- SCSI_TAG tagNum = pThread->tagNumber;
- /*
- * Check for threads with no associated physical device. In this case,
- * there is nothing to be done.
- */
- if (pScsiPhysDev == 0)
- return;
- switch (eventType)
- {
- case SCSI_THREAD_EVENT_ACTIVATED:
- scsiMgrPhysDevEvent (pScsiPhysDev, tagNum, SCSI_DEV_ACTIVATED);
- break;
-
- case SCSI_THREAD_EVENT_DISCONNECTED:
- scsiMgrPhysDevEvent (pScsiPhysDev, tagNum, SCSI_DEV_DISCONNECTED);
- break;
-
- case SCSI_THREAD_EVENT_RECONNECTED:
- scsiMgrPhysDevEvent (pScsiPhysDev, tagNum, SCSI_DEV_RECONNECTED);
- break;
-
- case SCSI_THREAD_EVENT_COMPLETED:
- scsiMgrPhysDevEvent (pScsiPhysDev, tagNum, SCSI_DEV_COMPLETED);
- scsiMgrPhysDevTagFree (pScsiPhysDev, tagNum);
- if (scsiMgrPhysDevActiveQRemove (pScsiPhysDev, pThread) == OK)
- scsiMgrThreadComplete (pThread);
- break;
-
- case SCSI_THREAD_EVENT_DEFERRED:
- scsiMgrPhysDevEvent (pScsiPhysDev, tagNum, SCSI_DEV_COMPLETED);
- scsiMgrPhysDevTagFree (pScsiPhysDev, tagNum);
- scsiMgrThreadDefer (pThread);
- break;
- default:
- logMsg ("scsiMgrThreadEvent: invalid event type (%d)n",
- eventType, 0, 0, 0, 0, 0);
- break;
- }
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevReset - reset a physical device
- *
- * Clean up all threads currently in progress on the device, failing them
- * with an error code corresponding to SCSI bus reset.
- *
- * Clean up any state variables associated with the device.
- *
- * Any threads waiting for the device to become available are not affected,
- * and will continue to be executed normally.
- *
- * NOTE: all threads are assumed to be physically disconnected (not to be
- * reconnected) by the target device before this routine is called.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrPhysDevReset
- (
- SCSI_PHYS_DEV *pScsiPhysDev
- )
- {
- SCSI_THREAD * pThread;
- /*
- * Clean up all active threads: none are connected after a reset
- */
- while ((pThread = (SCSI_THREAD *) lstGet (&pScsiPhysDev->activeThreads))
- != 0)
- {
- pThread->status = ERROR;
- pThread->errNum = S_scsiLib_BUS_RESET;
- scsiMgrThreadComplete (pThread);
- }
- /*
- * Re-initialise tag system
- */
- scsiMgrPhysDevTagInit (pScsiPhysDev);
-
- /*
- * Reset miscellaneous state variables
- */
- pScsiPhysDev->connected = FALSE;
- pScsiPhysDev->pendingCA = FALSE;
- pScsiPhysDev->nexus = SCSI_NEXUS_NONE;
- pScsiPhysDev->curTag = SCSI_TAG_NONE;
-
- pScsiPhysDev->nTaggedNexus = 0;
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevEvent - notify the physical device of an event
- *
- * Maintain the physical device's state variables according to the type of
- * event and the tag involved. These state variables affect the scheduling
- * of new threads on the device (see "scsiMgrPhysDevRunnableThreadGet ()").
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrPhysDevEvent
- (
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG tagNum,
- SCSI_DEV_EVENT_TYPE eventType
- )
- {
- /*
- * Validate tag number
- */
- if ((tagNum != SCSI_TAG_NONE) && (tagNum >= pScsiPhysDev->nTags))
- {
- logMsg ("scsiMgrPhysDevEvent: device 0x%08x: invalid tag (%d)n",
- (int) pScsiPhysDev, tagNum, 0, 0, 0, 0);
- return;
- }
- /*
- * Handle the event for this tag (and the device as a whole)
- */
- switch (eventType)
- {
- case SCSI_DEV_ACTIVATED:
- pScsiPhysDev->curTag = tagNum;
- pScsiPhysDev->connected = TRUE;
- if (tagNum == SCSI_TAG_NONE)
- {
- if (pScsiPhysDev->pendingCA)
- pScsiPhysDev->savedNexus = pScsiPhysDev->nexus;
- pScsiPhysDev->nexus = SCSI_NEXUS_ITL;
- }
- else
- {
- pScsiPhysDev->nexus = SCSI_NEXUS_ITLQ;
-
- ++pScsiPhysDev->nTaggedNexus;
- }
- break;
- case SCSI_DEV_DISCONNECTED:
- pScsiPhysDev->curTag = SCSI_TAG_NONE;
- pScsiPhysDev->connected = FALSE;
- break;
- case SCSI_DEV_RECONNECTED:
- pScsiPhysDev->curTag = tagNum;
- pScsiPhysDev->connected = TRUE;
- break;
-
- case SCSI_DEV_COMPLETED:
- pScsiPhysDev->curTag = NONE;
- pScsiPhysDev->connected = FALSE;
- if (tagNum == SCSI_TAG_NONE)
- {
- pScsiPhysDev->nexus = (pScsiPhysDev->pendingCA)
- ? pScsiPhysDev->savedNexus
- : SCSI_NEXUS_NONE;
- }
-
- else if (--pScsiPhysDev->nTaggedNexus == 0)
- pScsiPhysDev->nexus = SCSI_NEXUS_NONE;
- break;
- default:
- logMsg ("scsiMgrPhysDevEvent: device 0x%08x: "
- "invalid event type (%d)n",
- (int) pScsiPhysDev, eventType, 0, 0, 0, 0);
- break;
- }
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevWaitQAdd - add a thread to a physical device's wait queue
- *
- * Insert a thread into the priority-ordered queue of pending requests for
- * a physical device. Requests of the same priority are queued in FIFO
- * order. Threads which are recovering from contingent allegiance condition
- * (i.e. have tag type SENSE_RECOVERY) are considered to have higher priority
- * than all other threads waiting for the device.
- *
- * The wait queue is examined to determine the next thread that may be
- * despatched on the device - see "scsiMgrPhysDevRunnableThreadGet()".
- *
- * NOTE:
- * Treating the SENSE_RECOVERY tag type as an extra-high priority is a
- * debatable design. It may be better to just queue it in correct priority
- * order and have "scsiMgrPhysDevRunnableThreadGet()" scan the wait queue for
- * the first thread with this tag type, when the device has a contingent
- * allegiance condition.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrPhysDevWaitQAdd
- (
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_THREAD * pThread
- )
- {
- NODE * pPrev = 0;
- if (pThread->tagType != SCSI_TAG_SENSE_RECOVERY)
- {
- SCSI_THREAD * pCur;
- for (pCur = (SCSI_THREAD *) lstFirst (&pScsiPhysDev->waitingThreads);
- pCur != 0;
- pCur = (SCSI_THREAD *) lstNext ((NODE *) pCur))
- {
- if ((pCur->tagType != SCSI_TAG_SENSE_RECOVERY) &&
- SCSI_IS_HIGHER_PRIORITY (pThread->priority, pCur->priority))
- break;
-
- pPrev = (NODE *) pCur;
- }
- }
- lstInsert (&pScsiPhysDev->waitingThreads, pPrev, (NODE *) pThread);
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevWaitQRemove - remove a thread from a phys dev's wait queue
- *
- * NOTE:
- * It would be nice if "lstDelete()" told you whether or not the target node
- * was on the list - then three would be no need to have the "lstFind" first.
- *
- * RETURNS: OK, or ERROR if the thread was not on the queue.
- */
- LOCAL STATUS scsiMgrPhysDevWaitQRemove
- (
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_THREAD * pThread
- )
- {
- if (lstFind (&pScsiPhysDev->waitingThreads, (NODE *) pThread) == ERROR)
- return (ERROR);
-
- lstDelete (&pScsiPhysDev->waitingThreads, (NODE *) pThread);
- return (OK);
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevRunnableThreadGet - find a runnable thread on this phys dev
- *
- * If the physical device is connected, no new thread can be started.
- * Otherwise, the thread at the front of the wait queue is the only thread to
- * consider (it is the highest priority thread waiting for access to this
- * device). If there is such a thread, consider its tag type and the current
- * nexus type on the physical device to determine whether or not the thread
- * can be started.
- *
- * Essentially, if the device currently has an ITL nexus it cannot have any
- * new threads started. If it has an ITLQ nexus, another tagged thread can be
- * started provided there is a tag available, or a sense recovery (untagged)
- * thread can be started.
- *
- * RETURNS: the highest priority thread which can be started on this device,
- * or 0 if no threads are available or the device can't start a new thread.
- */
- LOCAL SCSI_THREAD * scsiMgrPhysDevRunnableThreadGet
- (
- SCSI_PHYS_DEV * pScsiPhysDev
- )
- {
- BOOL canStart;
- SCSI_THREAD * pThread;
-
- if (pScsiPhysDev->connected)
- return (NULL);
- pThread = (SCSI_THREAD *) lstFirst (&pScsiPhysDev->waitingThreads);
- if (pThread == 0)
- return (NULL);
-
- switch (pScsiPhysDev->nexus)
- {
- case SCSI_NEXUS_NONE:
- canStart = TRUE;
- break;
- case SCSI_NEXUS_ITL:
- canStart = FALSE;
- break;
- case SCSI_NEXUS_ITLQ:
- canStart = (pScsiPhysDev->nFreeTags != 0) ||
- (pThread->tagType == SCSI_TAG_SENSE_RECOVERY);
- break;
- default:
- logMsg ("scsiMgrPhysDevRunnableThreadGet: device 0x%08x: "
- "invalid nexus (%d)n",
- (int) pScsiPhysDev, pScsiPhysDev->nexus, 0, 0, 0, 0);
- canStart = FALSE;
- break;
- }
- return (canStart ? pThread : 0);
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevActiveQAdd - add a thread to a phys dev's active queue
- *
- * Simply insert the thread onto the list. The list is unordered.
- *
- * RETURNS: N/A
- */
- LOCAL void scsiMgrPhysDevActiveQAdd
- (
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_THREAD * pThread
- )
- {
- lstInsert (&pScsiPhysDev->activeThreads, 0, (NODE *) pThread);
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevActiveQRemove - remove a thread from a phys dev's active queue
- *
- * NOTE:
- * It would be nice if "lstDelete()" told you whether or not the target node
- * was on the list - then three would be no need to have the "lstFind" first.
- *
- * RETURNS: OK, or ERROR if the thread was not on the queue.
- */
- LOCAL STATUS scsiMgrPhysDevActiveQRemove
- (
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_THREAD * pThread
- )
- {
- if (lstFind (&pScsiPhysDev->activeThreads, (NODE *) pThread) == ERROR)
- return (ERROR);
-
- lstDelete (&pScsiPhysDev->activeThreads, (NODE *) pThread);
- return (OK);
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevActiveThreadFind - find thread corresponding to tag on phys dev
- *
- * Search the physical device's active thread list to find one corresponding to
- * the given tag number.
- *
- * NOTE:
- * This function should not be called by application programs.
- *
- * RETURNS: thread ptr, or 0 if no active thread corresponding to this tag
- *
- * NOMANUAL
- */
- SCSI_THREAD * scsiMgrPhysDevActiveThreadFind
- (
- SCSI_PHYS_DEV * pScsiPhysDev, /* physical device to search */
- SCSI_TAG tagNum /* tag number, or SCSI_TAG_NONE */
- )
- {
- SCSI_THREAD * pThread;
- for (pThread = (SCSI_THREAD *) lstFirst (&pScsiPhysDev->activeThreads);
- pThread != 0;
- pThread = (SCSI_THREAD *) lstNext ((NODE *) pThread))
- {
- if (pThread->tagNumber == tagNum)
- return (pThread);
- }
- return (0);
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevTagInit - initialise tag table for a physical device
- *
- * Set all tags not in use; reset the free tag count and next free tag number.
- *
- * RETURNS: N/A
- *
- * NOMANUAL
- */
- VOID scsiMgrPhysDevTagInit
- (
- SCSI_PHYS_DEV * pScsiPhysDev
- )
- {
- UINT nTags = pScsiPhysDev->nTags;
- SCSI_TAG tagNum;
- /*
- * Initialise "dummy" tag used for untagged threads
- */
- pScsiPhysDev->untagged.inUse = FALSE;
- /*
- * Set all real tags to "available"
- */
- for (tagNum = 0; tagNum < nTags; ++tagNum)
- {
- SCSI_TAG_INFO * pTag = pScsiPhysDev->pTagInfo + tagNum;
- pTag->inUse = FALSE;
- }
- pScsiPhysDev->nFreeTags = nTags;
- pScsiPhysDev->nextTag = (nTags == 0) ? SCSI_TAG_NONE : 0;
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevTagAllocate - allocate a tag number on the physical device
- *
- * Validate the requested tag type; if it is tagged, allocate a tag number
- * on the physical device.
- *
- * The actual tag number allocated has no significance other than that it
- * identifies an active thread on the physical device. In particular, it has
- * no bearing on the order in which the target executes the command. Thus the
- * choice of tag number is somewhat arbitrary; however, this routine attempts
- * to use tags in rotation rather than continually re-using the same tag
- * numbers. (This is primarily because it makes debugging somewhat easier.)
- *
- * NOTE:
- * An incorrect tag type can result from a bad choice by the client, however
- * unavailability of free tags can only result from software failure (the SCSI
- * manager should not be activating a thread on a device which has no tags
- * free !)
- *
- * The search for a free tag could get quite slow when the device supports a
- * large number of tags and they are nearly all in use. In practice this is
- * not likely to cause problems even with the simple scheme used currently.
- * A more sophisticated approach would be to store free tag numbers on a
- * list or queue, however this would use substantial amounts of memory (up to
- * 256 free tags per physical device).
- *
- * RETURNS: OK, or ERROR if no tags available
- */
- LOCAL STATUS scsiMgrPhysDevTagAllocate
- (
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG_TYPE tagType,
- SCSI_TAG * pTagNum
- )
- {
- BOOL tagged;
- SCSI_TAG tagNum;
- SCSI_TAG_INFO * pTag = 0; /* initialise to avoid warning */
- if (scsiMgrPhysDevTagTypeValidate (pScsiPhysDev, tagType, &tagged) != OK)
- return (ERROR);
-
- if (!tagged)
- {
- tagNum = SCSI_TAG_NONE;
- pTag = &pScsiPhysDev->untagged;
- }
- else
- {
- int i;
- if (pScsiPhysDev->nFreeTags == 0)
- {
- logMsg ("scsiMgrPhysDevTagAllocate: device 0x%08x: no free tagsn",
- (int) pScsiPhysDev, 0, 0, 0, 0, 0);
- errnoSet (S_scsiLib_SOFTWARE_ERROR);
- return (ERROR);
- }
- --pScsiPhysDev->nFreeTags;
- /*
- * Search for a free tag; starting at one after we last used
- */
- tagNum = pScsiPhysDev->nextTag;
- for (i = 0; i < pScsiPhysDev->nTags; ++i)
- {
- pTag = (pScsiPhysDev->pTagInfo + tagNum);
- if (!pTag->inUse)
- break;
- if (++tagNum >= pScsiPhysDev->nTags)
- tagNum = 0;
- }
- if (i == pScsiPhysDev->nTags)
- {
- logMsg ("scsiMgrPhysDevTagAllocate: device 0x%08x: "
- "tag not found (%d free)n",
- (int) pScsiPhysDev, pScsiPhysDev->nTags, 0, 0, 0, 0);
- errnoSet (S_scsiLib_SOFTWARE_ERROR);
- return (ERROR);
- }
- if ((pScsiPhysDev->nextTag = tagNum + 1) == pScsiPhysDev->nTags)
- pScsiPhysDev->nextTag = 0;
- }
-
- pTag->inUse = TRUE;
- *pTagNum = tagNum;
- return (OK);
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevTagFree - free a tag for the physical device.
- *
- * Mark the specified tag not in use.
- *
- * RETURNS: OK, or ERROR if no tags available
- */
- LOCAL void scsiMgrPhysDevTagFree
- (
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG tagNum
- )
- {
- SCSI_TAG_INFO * pTag;
- if (tagNum == SCSI_TAG_NONE)
- {
- pTag = &pScsiPhysDev->untagged;
- }
- else
- {
- ++pScsiPhysDev->nFreeTags;
- pTag = (pScsiPhysDev->pTagInfo + tagNum);
- }
- pTag->inUse = FALSE;
- }
- /*******************************************************************************
- *
- * scsiMgrPhysDevTagTypeValidate - validate requested tag type for phys dev
- *
- * Check the requested tag type is valid given the current nexus type of the
- * device. Note the special cases which allow an untagged sense recovery
- * command to be started when the device has tagged nexuses active, provided
- * it is in a contingent allegiance condition.
- *
- * RETURNS: OK, or ERROR if the requested tag type is (currently) invalid
- */
- LOCAL STATUS scsiMgrPhysDevTagTypeValidate
- (
- SCSI_PHYS_DEV * pScsiPhysDev,
- SCSI_TAG_TYPE tagType,
- BOOL * pTagged
- )
- {
- BOOL valid;
- BOOL tagged;
-
- switch (tagType)
- {
- case SCSI_TAG_UNTAGGED:
- valid = (pScsiPhysDev->nexus != SCSI_NEXUS_ITLQ);
- tagged = FALSE;
- break;
- case SCSI_TAG_SENSE_RECOVERY:
- valid = (pScsiPhysDev->nexus != SCSI_NEXUS_ITLQ) ||
- pScsiPhysDev->pendingCA;
- tagged = FALSE;
- break;
- case SCSI_TAG_SIMPLE:
- case SCSI_TAG_ORDERED:
- case SCSI_TAG_HEAD_OF_Q:
- valid = (pScsiPhysDev->nexus != SCSI_NEXUS_ITL) &&
- (pScsiPhysDev->tagType != SCSI_TAG_UNTAGGED);
- tagged = TRUE;
- break;
-
- default:
- valid = FALSE;
- tagged = FALSE;
- break;
- }
- if (!valid)
- {
- SCSI_DEBUG_MSG ("scsiPhysDevTagTypeValidate: invalid tag type (%d) "
- "for device 0x%08xn",
- tagType, (int) pScsiPhysDev, 0, 0, 0, 0);
- errnoSet (S_scsiLib_INVALID_TAG_TYPE);
- return (ERROR);
- }
-
- *pTagged = tagged;
-
- return (OK);
- }
- /*******************************************************************************
- *
- * scsiMgrShow - show status information for the SCSI manager
- *
- * This routine shows the current state of the SCSI manager for the specified
- * controller, including the total number of threads created and the number of
- * threads currently free.
- *
- * Optionally, this routine also shows details for all created physical devices
- * on this controller and all threads for which SCSI requests are outstanding.
- * It also shows the IDs of all free threads.
- *
- * NOTE: The information displayed is volatile; this routine is best used when
- * there is no activity on the SCSI bus. Threads allocated by a client but
- * for which there are no outstanding SCSI requests are not shown.
- *
- * RETURNS: N/A
- */
- void scsiMgrShow
- (
- SCSI_CTRL * pScsiCtrl, /* SCSI controller to use */
- BOOL showPhysDevs, /* TRUE => show phys dev details */
- BOOL showThreads, /* TRUE => show thread details */
- BOOL showFreeThreads /* TRUE => show free thread IDs */
- )
- {
- char * state;
- int nFree;
-
- if (pScsiCtrl == NULL)
- pScsiCtrl = pSysScsiCtrl;
-
- if (pScsiCtrl == NULL)
- {
- printf ("No SCSI controller specified.n");
- return;
- }
- state = pScsiCtrl->active ? "active" : "inactive";
-
- nFree = lstCount (&pScsiCtrl->freeThreads);
-
- printf ("Controller State # Threads (tot/free)n");
- printf ("---------- -------- --------------------n");
- printf ("0x%08x %-8s %3d %3dn",
- (int) pScsiCtrl,
- state,
- pScsiCtrl->nThreads,
- nFree);
- if (showPhysDevs)
- {
- BOOL noHeader = FALSE;
- int i;
- printf ("n");
- for (i = 0; i < SCSI_MAX_PHYS_DEVS; ++i)
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pScsiCtrl->physDevArr[i];
- if (pScsiPhysDev != 0)
- {
- scsiPhysDevShow (pScsiPhysDev, FALSE, noHeader);
- noHeader = TRUE;
- }
- }
- }
- if (showThreads)
- {
- /*
- * This is crude. A better approach would be to collect all
- * the thread IDs into a buffer, then sort by priority before
- * calling "scsiThreadShow" for each thread.
- */
- int i;
- printf ("n");
- for (i = 0; i < SCSI_MAX_PHYS_DEVS; ++i)
- {
- SCSI_PHYS_DEV * pScsiPhysDev = pScsiCtrl->physDevArr[i];
- if (pScsiPhysDev != 0)
- {
- scsiThreadListShow (&pScsiPhysDev->activeThreads);
- scsiThreadListShow (&pScsiPhysDev->waitingThreads);
- }
- }
- }
- if (showFreeThreads && (nFree != 0))
- {
- printf ("nFree threads:n");
- scsiThreadListIdShow (&pScsiCtrl->freeThreads);
- }
- }