/*
* 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
*/
/*
*/
/*
* SCSI (SCSA) midlayer interface for PMC drier.
*/
scsi_hba_tran_t *, struct scsi_device *);
scsi_hba_tran_t *, struct scsi_device *);
static int pmcs_scsa_reset(struct scsi_address *, int);
static int pmcs_scsi_reset_notify(struct scsi_address *, int,
static int pmcs_scsa_getcap(struct scsi_address *, char *, int);
static int pmcs_scsa_setcap(struct scsi_address *, char *, int, int);
static void pmcs_scsa_teardown_pkt(struct scsi_pkt *);
smp_device_t *);
smp_device_t *);
static int pmcs_smp_start(struct smp_pkt *);
static int pmcs_scsi_quiesce(dev_info_t *);
static int pmcs_scsi_unquiesce(dev_info_t *);
static int pmcs_cap(struct scsi_address *, char *, int, int, int);
static pmcs_xscsi_t *
int
{
int flags;
/*
* Allocate a transport structure
*/
"scsi_hba_tran_alloc failed");
return (DDI_FAILURE);
}
/*
* Attach this instance of the hba
*/
"scsi_hba_attach failed");
return (DDI_FAILURE);
}
/*
* Attach the SMP part of this hba
*/
"smp_hba_attach failed");
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
}
/*
* SCSA entry points
*/
static int
{
int rval;
/*
* First, make sure we're an iport and get the pointer to the HBA
* node's softstate
*/
goto tgt_init_fail;
}
/*
* Get the unit-address
*/
"%s: Couldn't get UA", __func__);
goto tgt_init_fail;
}
"got ua '%s'", ua);
/*
* Get the target address
*/
if (rval != DDI_PROP_SUCCESS) {
"Couldn't get target UA");
goto tgt_init_fail;
}
"got tgt_port '%s'", tgt_port);
/*
* Validate that this tran_tgt_init is for an active iport.
*/
"%s: Got tran_tgt_init on inactive iport for '%s'",
goto tgt_init_fail;
}
/*
* Since we're going to wait for scratch, be sure to acquire it while
* we're not holding any other locks
*/
/*
* See if there's already a target softstate. If not, allocate one.
*/
goto tgt_init_fail;
}
if (!IS_ROOT_PHY(phyp)) {
}
/* Now get the lun */
if (lun_num == SCSI_LUN64_ILLEGAL) {
"No LUN for tgt %p", (void *)tgt);
goto tgt_init_fail;
}
"PHY 0x%p went away?", (void *)phyp);
goto tgt_init_fail;
}
/* We don't support SATA devices at LUN > 0. */
"%s: No support for SATA devices at LUN > 0 "
goto tgt_init_fail;
}
/*
* Allocate LU soft state. We use ddi_soft_state_bystr_zalloc instead
* of kmem_alloc because ddi_soft_state_bystr_zalloc allows us to
* verify that the framework never tries to initialize two scsi_device
* structures with the same unit-address at the same time.
*/
"Couldn't allocate LU soft state");
goto tgt_init_fail;
}
"Couldn't get LU soft state");
goto tgt_init_fail;
}
/* convert the scsi_lun64_t value to SCSI standard form */
/*
* If this is the first tran_tgt_init, add this target to our list
*/
int target;
continue;
}
break;
}
"Target list full.");
goto tgt_init_fail;
}
}
"%s: pmcs_assign_device failed for target 0x%p",
goto tgt_init_fail;
}
/* SM-HBA */
/* TCR in PSARC/1997/281 opinion */
(void) scsi_device_prop_update_string(sd,
}
if (tgt->phy_addressable) {
}
/* SM-HBA */
/*
* Make sure attached port and target port pm props are updated
* By passing in 0s, we're not actually updating any values, but
* the properties should now get updated on the node.
*/
return (DDI_SUCCESS);
if (got_scratch) {
}
if (lun) {
}
if (phyp) {
/*
* phyp's ref count was incremented in pmcs_new_tport.
* We're failing configuration, we now need to decrement it.
*/
if (!IS_ROOT_PHY(phyp)) {
}
}
}
if (pwp) {
}
if (tgt_port) {
}
return (DDI_FAILURE);
}
static void
{
char *unit_address;
"%s: We don't enumerate devices on the HBA node", __func__);
return;
}
if (phyp) {
}
if (target->recover_wait) {
if (phyp) {
}
"Target 0x%p in device state recovery, fail tran_tgt_free",
return;
}
/*
* If this target still has a PHY pointer and that PHY's target pointer
* has been cleared, then that PHY has been reaped. In that case, there
* would be no need to decrement the reference count
*/
}
/*
* Remove this target from our list. The target soft
* state will remain, and the device will remain registered
* physically went away.
*/
target->target_num);
/* If the PHY has a pointer to this target, clear it */
}
if (phyp) {
}
} else {
if (phyp) {
}
}
}
static int
{
"%s: nointr pkt", __func__);
return (TRAN_BADPKT);
}
if (hba_state != STATE_RUNNING) {
"%s: hba dead", __func__);
return (TRAN_FATAL_ERROR);
}
"%s: dropping due to null target", __func__);
goto dead_target;
}
/*
* First, check to see if the device is gone.
*/
"%s: dropping due to dead target 0x%p",
goto dead_target;
}
/*
* If we're blocked (quiesced) just return.
*/
if (blocked) {
"%s: hba blocked", __func__);
return (TRAN_ACCEPT);
}
/*
* If we're draining or resetting, queue and return.
*/
"%s: draining/resetting/recovering (cnt %u)",
/*
* By the time we get here, draining or
* resetting may have come and gone, not
* yet noticing that we had put something
* on the wait queue, so schedule a worker
* to look at this later.
*/
return (TRAN_ACCEPT);
}
/*
* Queue this command to the tail of the wait queue.
* This keeps us getting commands out of order.
*/
/*
* Now run the queue for this device.
*/
return (TRAN_ACCEPT);
return (TRAN_ACCEPT);
}
/* Return code 1 = Success */
static int
{
"%s: hba dead", __func__);
return (0);
}
"No pmcs_lun_t struct to do ABORT_ALL", __func__);
return (0);
}
}
return (0);
}
}
return (1);
}
} else {
lun = 0;
}
return (0);
}
/*
* See if we have a real work structure associated with this cmd.
*/
NULL)) {
return (0);
}
} else {
/*
* XXX: Was the command that was active an
* NCQ I/O command?
*/
return (0);
}
}
return (1);
}
if (pwrk) {
}
/*
* Okay, those weren't the droids we were looking for.
* See if the command is on any of the wait queues.
*/
break;
}
}
if (sp) {
return (1);
}
return (0);
}
/*
* SCSA reset functions
*/
static int
{
int rval;
"%s: hba dead", __func__);
return (0);
}
switch (level) {
case RESET_ALL:
rval = 0;
break;
case RESET_LUN:
/*
* Point lp at lun so that pmcs_addr2xp
* will fill out the 64 bit lun number.
*/
/* FALLTHROUGH */
case RESET_TARGET:
"%s: no xp found for this scsi address", __func__);
return (0);
}
"%s: Target 0x%p has gone away", __func__,
(void *)xp);
return (0);
}
/*
* If we're already performing this action, or if device
* state recovery is already running, just return failure.
*/
return (0);
}
xp->reset_wait = 0;
xp->reset_success = 0;
rval = 0;
} else {
rval = 1;
}
if (rval == 1) {
}
if (xp->reset_wait) {
xp->reset_wait = 0;
}
break;
default:
rval = 0;
break;
}
return (rval);
}
static int
{
}
static int
{
if (cidx == -1) {
return (-1);
}
return (-1);
}
switch (cidx) {
case SCSI_CAP_DMA_MAX:
case SCSI_CAP_INITIATOR_ID:
if (set == 0) {
}
break;
case SCSI_CAP_DISCONNECT:
case SCSI_CAP_SYNCHRONOUS:
case SCSI_CAP_WIDE_XFER:
case SCSI_CAP_PARITY:
case SCSI_CAP_ARQ:
case SCSI_CAP_UNTAGGED_QING:
if (set == 0) {
rval = 1;
}
break;
case SCSI_CAP_TAGGED_QING:
rval = 1;
break;
case SCSI_CAP_MSG_OUT:
case SCSI_CAP_QFULL_RETRIES:
break;
case SCSI_CAP_SCSI_VERSION:
if (set == 0) {
}
break;
if (set) {
break;
}
if (xp->phy_addressable) {
} else {
}
break;
case SCSI_CAP_CDB_LEN:
if (set == 0) {
rval = 16;
}
break;
case SCSI_CAP_LUN_RESET:
if (set) {
break;
}
rval = 0;
} else {
rval = 1;
}
break;
default:
rval = -1;
break;
}
"%s: cap %s val %d set %d rval %d",
return (rval);
}
/*
* Returns with statlock held if the xp is found.
* Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL.
*/
static pmcs_xscsi_t *
{
return (NULL);
}
/*
* This may be a retried packet, so it's possible cmd_target
* and cmd_lun may still be populated. Clear them.
*/
}
return (NULL);
}
}
if (lp) {
}
return (xp);
}
static int
{
int r;
return (-1);
}
return (r);
}
static int
{
int r;
return (-1);
}
return (r);
}
static int
{
return (0);
}
static void
{
}
static int
{
int result;
if (reqsz > SAS_SMP_MAX_PAYLOAD) {
}
if (rspsz > SAS_SMP_MAX_PAYLOAD) {
}
/*
* The request size from the SMP driver always includes 4 bytes
* for the CRC. The PMCS chip, however, doesn't want to see those
* counts as part of the transfer size.
*/
reqsz -= 4;
/* PHY is now locked */
if (pptr) {
}
"%s: could not find phy", __func__);
return (DDI_FAILURE);
}
return (DDI_FAILURE);
}
"%s: could not get work structure", __func__);
return (DDI_FAILURE);
}
"%s: could not get IQ entry", __func__);
return (DDI_FAILURE);
}
msg[11] = 0;
msg[15] = 0;
if (result) {
"%s: Unable to issue SMP ABORT for htag 0x%08x",
} else {
"%s: Issuing SMP ABORT for htag 0x%08x",
}
return (DDI_FAILURE);
}
if (status == PMCOUT_STATUS_OVERFLOW) {
}
if (status != PMCOUT_STATUS_OK) {
"SMP operation failed (0x%x)", status);
} else {
"SMP operation failed (%s)", emsg);
}
if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) ||
} else if (status ==
goto out;
}
xp, "%s: Got _IT_NEXUS_LOSS SMP status. "
"Tgt(0x%p) dev_state set to "
"_NON_OPERATIONAL", __func__,
(void *)xp);
}
/* ABORT any pending commands related to this device */
}
} else {
}
} else {
} else {
}
}
out:
return (result);
}
static int
{
return (DDI_FAILURE);
return (DDI_FAILURE);
/* Get "target-port" prop from devinfo node */
return (DDI_SUCCESS);
}
/*
* Validate that this tran_tgt_init is for an active iport.
*/
return (DDI_FAILURE);
}
/* Retrieve softstate using unit-address */
"%s: tgt softstate not found", __func__);
return (DDI_FAILURE);
}
if (IS_ROOT_PHY(phy)) {
/* Expander attached to HBA - don't ref_count it */
} else {
/*
* Parent (in topology) is also an expander
* Now that we've increased the ref count on phy, it's OK
* to drop the lock so we can acquire the parent's lock.
*/
}
/*
* If this is the 1st smp_init, add this to our list.
*/
int target;
continue;
}
break;
}
"Target list full.");
goto smp_init_fail;
}
}
"%s: pmcs_assign_device failed for target 0x%p",
goto smp_init_fail;
}
/*
* Update the attached port and target port pm properties
*/
addr) != DDI_SUCCESS) {
}
(void) scsi_free_wwnstr(addr);
return (DDI_SUCCESS);
if (!IS_ROOT_PHY(phy)) {
}
return (DDI_FAILURE);
}
static void
{
char *tgt_port;
return;
return;
/* Get "target-port" prop from devinfo node */
return;
}
/* Retrieve softstate using unit-address */
"%s: tgt softstate not found", __func__);
return;
}
if (phyp) {
if (!IS_ROOT_PHY(phyp)) {
}
}
/*
* Remove this target from our list. The softstate
* will remain, and the device will remain registered
* device physically went away.
*/
"Removing target 0x%p (vtgt %d) from target list",
/* If the PHY has a pointer to this target, clear it */
}
} else {
}
if (phyp) {
}
}
static int
{
return (0); /* iport */
return (-1);
}
return (-1);
}
while (totactive) {
totactive = 0;
continue;
}
}
}
if (totactive) {
}
/*
* The pwp->blocked may have been reset. e.g a SCSI bus reset
*/
}
continue;
}
}
if (totactive == 0) {
"%s drain complete", __func__);
}
return (0);
}
static int
{
return (0); /* iport */
return (-1);
}
return (-1);
}
/*
* Run all pending commands.
*/
/*
* Complete all completed commands.
* This also unlocks us.
*/
return (0);
}
/*
* Start commands for a particular device
* If the actual start of a command fails, return B_FALSE. Any other result
* is a B_TRUE return.
*/
{
int rval;
/*
* First, check to see if we're blocked or resource limited
*/
/*
* If resource_limited is set, we're resource constrained and
* we will run only one work request for this target.
*/
if (blocked) {
/* Queues will get restarted when we get unblocked */
return (B_TRUE);
}
/*
* Might as well verify the queue is not empty before moving on
*/
return (B_TRUE);
}
/*
* If we're draining or resetting, just reschedule work queue and bail.
*/
xp->special_needed) {
return (B_TRUE);
}
/*
* Next, check to see if the target is gone.
*/
"%s: Flushing wait queue for dead tgt 0x%p", __func__,
(void *)xp);
return (B_TRUE);
}
/*
* Increment the PHY's ref_count now so we know it won't go away
* after we drop the target lock. Drop it before returning. If the
* PHY dies, the commands we attempt to send will fail, but at least
* we know we have a real PHY pointer.
*/
if (pwp->resource_limited == 0) {
"%s: out of work structures", __func__);
}
return (B_FALSE);
}
if (rval != PMCS_WQ_RUN_FAIL_RES_CMP) {
}
if (rval == PMCS_WQ_RUN_FAIL_RES) {
return (B_FALSE);
} else {
return (B_TRUE);
}
}
} else {
if (rval == PMCS_WQ_RUN_FAIL_RES) {
return (B_FALSE);
} else {
return (B_TRUE);
}
}
}
if (run_one) {
goto wq_out;
}
}
return (B_TRUE);
}
/*
* Start commands for all devices.
*/
void
{
do {
target = 0;
}
continue;
}
break;
}
target = 0;
}
} while (target != target_start);
if (rval) {
/*
* If we were resource limited, but apparently are not now,
* reschedule the work queues anyway.
*/
if (pwp->resource_limited) {
}
} else {
/*
* Give everybody a chance, and reschedule to run the queues
* again as long as we're limited.
*/
}
}
/*
* Pull the completion queue, drop the lock and complete all elements.
*/
void
{
/*
* First, check the I/O completion callback queue.
*/
while (ioccb) {
/*
* Grab the lock on the work structure. The callback
* routine is responsible for clearing it.
*/
ioccb = ioccb_next;
}
/*
* Next, run the completion queue
*/
pmcs_cq_thr_info_t *, cqti);
}
while (sp) {
"%s: calling completion on %p for tgt %p", __func__,
if (tgt) {
}
}
pmcs_cq_thr_info_t *, cqti);
/*
* Check if there are more completions to do. If so, and we've
* not been told to stop, skip the wait and cycle through again.
*/
}
}
thread_exit();
}
/*
* Run a SAS command. Called with pwrk->lock held, returns unlocked.
*/
static int
{
int sp_pkt_time = 0;
return (PMCS_WQ_RUN_FAIL_OTHER);
}
return (PMCS_WQ_RUN_FAIL_OTHER);
}
/*
* This is a temporary failure not likely to unblocked by
* commands completing as the test for scheduling the
* restart of work is a per-device test.
*/
"%s: Failed to get IO IQ entry for tgt %d",
return (PMCS_WQ_RUN_FAIL_RES);
}
ptr[0] =
if (ptr[3]) {
} else {
}
return (PMCS_WQ_RUN_FAIL_RES);
} else {
"%s: Failed to dma_load for tgt %d (QF)",
return (PMCS_WQ_RUN_FAIL_RES_CMP);
}
}
} else {
}
}
#ifdef DEBUG
/*
* Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
* event when this goes out on the wire.
*/
#endif
/*
* Fill in the SSP IU
*/
case FLAG_HTAG:
break;
case FLAG_OTAG:
break;
case FLAG_STAG:
default:
break;
}
"%s: giving pkt %p (tag %x) to the hardware", __func__,
#ifdef DEBUG
#endif
}
}
/*
* If we just submitted the last command queued from device state
* recovery, clear the wq_recovery_tail pointer.
*/
}
return (PMCS_WQ_RUN_SUCCESS);
}
/*
* Complete a SAS command
*
* Called with pwrk lock held.
* The free of pwrk releases the lock.
*/
static void
{
int dead;
if (msg) {
} else {
sts = 0;
}
if (dead != 0) {
goto out;
}
if (sts == PMCOUT_STATUS_ABORTED) {
}
"%s: cmd 0x%p (tag 0x%x) timed out for %s",
goto out;
}
/*
* If the status isn't okay but not underflow,
* step to the side and parse the (possible) error.
*/
#ifdef DEBUG
if (msg) {
}
#endif
if (!msg) {
goto out;
}
switch (sts) {
"%s: PHY %s requires DS recovery (status=%d)",
break;
case PMCOUT_STATUS_UNDERFLOW:
"%s: underflow %u for cdb 0x%x",
msg[3] = 0;
break;
case PMCOUT_STATUS_OK:
break;
}
if (sts != PMCOUT_STATUS_OK) {
} else {
if (msg[3]) {
const int lim =
0x58, 0x61, 0x56, 0x72, 0x00
};
/*
* Transform the the first part of the response
* to host canonical form. This gives us enough
* information to figure out what to do with the
* rest (which remains unchanged in the incoming
* message which can be up to two queue entries
* in length).
*/
xd += SAS_RSP_HDR_SIZE;
"Bad SAS RESPONSE DATA LENGTH",
msg);
goto out;
}
/*
* The only response code we should legally get
* here is an INVALID FRAME response code.
*/
if (sts == SAS_RSP_INVALID_FRAME) {
"%s: pkt %p tgt %u path %s "
"completed: INVALID FRAME response",
} else {
"%s: pkt %p tgt %u path %s "
"completed: illegal response 0x%x",
}
goto out;
}
}
/*
* This is the case for a plain SCSI status.
* Note: If RESC_V is set and we're here, there
* is a residual. We need to find it and update
* the packet accordingly.
*/
/*
* Point residual to the SSP_RESP_IU
*/
/*
* param contains the number of bytes
* between where the SSP_RESP_IU may
* or may not be and the residual.
* Increment residp by the appropriate
* number of words: (param+resc_pad)/4).
*/
sizeof (uint32_t);
"residual %d for pkt 0x%p",
(void *) pkt);
pkt->pkt_dma_len);
(void) pmcs_set_resid(pkt,
}
} else {
"illegal SAS response", msg);
goto out;
}
} else {
}
if (pkt->pkt_dma_len) {
}
}
"%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
"%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
}
out:
/*
* If the device no longer has a PHY pointer, clear the PHY pointer
* from the work structure before we free it. Otherwise, pmcs_pwork
* may decrement the ref_count on a PHY that's been freed.
*/
}
/*
* We may arrive here due to a command timing out, which in turn
* could be addressed in a different context. So, free the work
* back, but only after confirming it's not already been freed
* elsewhere.
*/
}
/*
* If the device is gone, we only put this command on the completion
* queue if the work structure is not marked dead. If it's marked
* dead, it will already have been put there.
*/
if (!dead) {
"%s: Removing cmd 0x%p (htag 0x%x) from aq",
"%s: Completing command for dead target 0x%p",
}
return;
}
"%s: waking up drain waiters", __func__);
}
}
/*
* If the status is other than OK, determine if it's something that
* is worth re-attempting enumeration. If so, mark the PHY.
*/
if (sts != PMCOUT_STATUS_OK) {
}
if (dead == 0) {
#ifdef DEBUG
break;
}
}
#else
#endif
"%s: Removing cmd 0x%p (htag 0x%x) from aq", __func__,
if (aborted) {
"%s: Aborted cmd for tgt 0x%p, signaling waiters",
}
}
/*
* If do_ds_recovery is set, we need to initiate device state
* recovery. In this case, we put this I/O back on the head of
* the wait queue to run again after recovery is complete
*/
if (do_ds_recovery) {
"back on wq during recovery for tgt 0x%p", __func__,
} else {
/*
* device state recovery, add this one in the right spot
* to maintain proper order.
*/
cmd_next);
}
} else {
/*
* If we're not initiating device state recovery and this
* command was not "dead", put it on the completion queue
*/
if (!dead) {
}
}
}
/*
* Run a SATA command (normal reads and writes),
* or block and schedule a SATL interpretation
* of the command.
*
* Called with pwrk lock held, returns unlocked.
*/
static int
{
int sp_pkt_time = 0;
/*
* If not, we have to queue it up for processing, block
* any additional commands from coming in, and wake up
* the thread that will process this command.
*/
}
return (PMCS_WQ_RUN_SUCCESS);
}
return (PMCS_WQ_RUN_FAIL_OTHER);
}
/*
* By the time we get here the special
* commands running or waiting to be run
* may have come and gone, so kick our
* worker to run the waiting queues
* just in case.
*/
return (PMCS_WQ_RUN_FAIL_OTHER);
}
/*
* Extract data length and lba parameters out of the command. The
* function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB
* values are considered illegal.
*/
if (asc) {
sns[0] = 0xf0;
return (PMCS_WQ_RUN_SUCCESS);
}
/*
* If the command decodes as not moving any data, complete it here.
*/
amt <<= 9;
if (amt == 0) {
return (PMCS_WQ_RUN_SUCCESS);
}
/*
* Get an inbound queue entry for this I/O
*/
/*
* This is a temporary failure not likely to unblocked by
* commands completing as the test for scheduling the
* restart of work is a per-device test.
*/
"%s: Failed to get IO IQ entry for tgt %d",
return (PMCS_WQ_RUN_FAIL_RES);
}
/*
* Get a tag. At this point, hold statlock until the tagmap is
* updated (just prior to sending the cmd to the hardware).
*/
break;
}
}
return (PMCS_WQ_RUN_FAIL_OTHER);
}
/*
* Set up the command
*/
ptr[0] =
} else {
}
} else {
int op;
} else {
}
} else {
op = READ_DMA_EXT;
} else {
op = WRITE_DMA_EXT;
}
}
}
} else {
}
#ifdef DEBUG
/*
* Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
* event when this goes out on the wire.
*/
#endif
}
"%s: Failed to dma_load for tgt %d",
return (PMCS_WQ_RUN_FAIL_RES);
}
}
#ifdef DEBUG
#endif
}
}
return (PMCS_WQ_RUN_SUCCESS);
}
/*
* Complete a SATA command. Called with pwrk lock held.
*/
void
{
int dead;
if (msg) {
} else {
sts = 0;
}
if (dead != 0) {
goto out;
}
(sts != PMCOUT_STATUS_ABORTED)) {
"%s: cmd 0x%p (tag 0x%x) timed out for %s",
/* pkt_reason already set to CMD_TIMEOUT */
goto out;
}
/*
* If the status isn't okay but not underflow,
* step to the side and parse the (possible) error.
*/
#ifdef DEBUG
if (msg) {
}
#endif
if (!msg) {
goto out;
}
/*
* If the status isn't okay or we got a FIS response of some kind,
* step to the side and parse the (possible) error.
*/
if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) {
(xp->reset_wait == 0)) {
PMCS_PHYOP_LINK_RESET) != 0) {
"Reset FAILED as part of error "
}
}
}
} else {
}
"%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
"%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
}
out:
/*
* If the device no longer has a PHY pointer, clear the PHY pointer
* from the work structure before we free it. Otherwise, pmcs_pwork
* may decrement the ref_count on a PHY that's been freed.
*/
}
/*
* We may arrive here due to a command timing out, which in turn
* could be addressed in a different context. So, free the work
* back, but only after confirming it's not already been freed
* elsewhere.
*/
}
if (!dead) {
"%s: Removing cmd 0x%p (htag 0x%x) from aq",
"%s: Completing command for dead target 0x%p",
}
return;
}
"%s: waking up drain waiters", __func__);
} else if (xp->special_needed) {
}
}
/*
* If the status is other than OK, determine if it's something that
* is worth re-attempting enumeration. If so, mark the PHY.
*/
if (sts != PMCOUT_STATUS_OK) {
}
if (dead == 0) {
#ifdef DEBUG
break;
}
}
#else
#endif
if (aborted) {
"%s: Aborted cmd for tgt 0x%p, signaling waiters",
}
}
}
static uint8_t
{
switch (cdb[0]) {
case SCMD_READ_G5:
case SCMD_WRITE_G5:
*xfr =
*lba =
/* Check for illegal bits */
if (cdb[15]) {
}
break;
case SCMD_READ_G4:
case SCMD_WRITE_G4:
*xfr =
*lba =
/* Check for illegal bits */
if (cdb[11]) {
}
break;
case SCMD_READ_G1:
case SCMD_WRITE_G1:
*lba =
/* Check for illegal bits */
if (cdb[9]) {
}
break;
case SCMD_READ:
case SCMD_WRITE:
if (*xfr == 0) {
*xfr = 256;
}
*lba =
/* Check for illegal bits */
if (cdb[5]) {
}
break;
}
if (asc == 0) {
}
}
return (asc);
}
/*
* Called with pwrk lock held.
*/
static void
{
0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28
};
0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5
};
const char *msg;
}
if (status != PMCOUT_STATUS_OK) {
"%s: device %s tag 0x%x status %s @ %llu", __func__,
(unsigned long long)gethrtime());
}
switch (status) {
case PMCOUT_STATUS_OK:
if (t == SATA) {
int i;
}
} else {
"FIS ERROR");
}
break;
}
break;
case PMCOUT_STATUS_ABORTED:
/*
* Command successfully aborted.
*/
} else {
}
/*
* PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past
* this point, so go ahead and mark it as aborted.
*/
break;
case PMCOUT_STATUS_UNDERFLOW:
/*
* This will only get called for SATA
*/
}
break;
case PMCOUT_STATUS_NO_DEVICE:
break;
/*
* Need to do rediscovery. We probably have
* the wrong device (disk swap), so kill
* this one.
*/
/*
* Need to do rediscovery.
*/
} else {
}
break;
/* cmd is pending on the target */
/* transitory - commands sent while in NCQ failure mode */
/* NCQ failure */
break;
break;
/* synthesize a RESERVATION CONFLICT */
break;
break;
/* synthesize a PARITY ERROR */
break;
case PMCOUT_STATUS_PROG_ERROR:
case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */
default:
break;
}
}
/*
* Latch up SCSI status
*/
void
{
static const char c1[] =
"%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) "
"HTAG 0x%x @ %llu";
static const char c2[] =
"%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu";
}
aqp->sts_rqpkt_statistics = 0;
aqp->sts_rqpkt_state = 0;
sizeof (struct scsi_extended_sense);
} else {
sizeof (struct scsi_extended_sense) - amt;
}
} else if (status) {
}
}
/*
* Calculate and set packet residual and return the amount
* left over after applying various filters.
*/
{
}
}
return (amt);
}
/*
* Return the existing target softstate (unlocked) if there is one. If so,
* the PHY is locked and that lock must be freed by the caller after the
* TRUE, then allocate one.
*/
{
/*
* Find the PHY for this target
*/
return (NULL);
}
if (tgt) {
/*
* There's already a target. Check its PHY pointer to see
* if we need to clear the old linkages
*/
"%s: Target PHY updated from %p to %p", __func__,
}
}
/*
* If this target has no PHY pointer and alloc_tgt is FALSE,
* that implies we expect the target to already exist. This
* implies that there has already been a tran_tgt_init on at
* least one LU.
*/
"%s: Establish linkage from new PHY to old target @"
}
}
/*
* Set this target pointer back up, since it's been
* through pmcs_clear_xp().
*/
return (tgt);
}
/*
* Make sure the PHY we found is on the correct iport
*/
return (NULL);
}
/*
* If this was just a lookup (i.e. alloc_tgt is false), return now.
*/
return (NULL);
}
/*
* Allocate the new softstate
*/
DDI_SUCCESS) {
"%s: Couldn't alloc softstate for device at %s",
return (NULL);
}
}
/*
* Don't allocate LUN softstate for SMP targets
*/
return (tgt);
}
sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) {
"%s: LUN soft_state_bystr_init failed", __func__);
return (NULL);
}
return (tgt);
}