14c06356bSdh142964 /*
24c06356bSdh142964 * CDDL HEADER START
34c06356bSdh142964 *
44c06356bSdh142964 * The contents of this file are subject to the terms of the
54c06356bSdh142964 * Common Development and Distribution License (the "License").
64c06356bSdh142964 * You may not use this file except in compliance with the License.
74c06356bSdh142964 *
84c06356bSdh142964 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
94c06356bSdh142964 * or http://www.opensolaris.org/os/licensing.
104c06356bSdh142964 * See the License for the specific language governing permissions
114c06356bSdh142964 * and limitations under the License.
124c06356bSdh142964 *
134c06356bSdh142964 * When distributing Covered Code, include this CDDL HEADER in each
144c06356bSdh142964 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
154c06356bSdh142964 * If applicable, add the following below this CDDL HEADER, with the
164c06356bSdh142964 * fields enclosed by brackets "[]" replaced with your own identifying
174c06356bSdh142964 * information: Portions Copyright [yyyy] [name of copyright owner]
184c06356bSdh142964 *
194c06356bSdh142964 * CDDL HEADER END
20658280b6SDavid Hollister */
21658280b6SDavid Hollister /*
22658280b6SDavid Hollister * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
234c06356bSdh142964 */
244c06356bSdh142964 /*
254c06356bSdh142964 * SCSI (SCSA) midlayer interface for PMC drier.
264c06356bSdh142964 */
274c06356bSdh142964
284c06356bSdh142964 #include <sys/scsi/adapters/pmcs/pmcs.h>
294c06356bSdh142964
304c06356bSdh142964 extern scsi_lun_t scsi_lun64_to_lun(scsi_lun64_t lun64);
314c06356bSdh142964
324c06356bSdh142964 static int pmcs_scsa_tran_tgt_init(dev_info_t *, dev_info_t *,
334c06356bSdh142964 scsi_hba_tran_t *, struct scsi_device *);
344c06356bSdh142964 static void pmcs_scsa_tran_tgt_free(dev_info_t *, dev_info_t *,
354c06356bSdh142964 scsi_hba_tran_t *, struct scsi_device *);
364c06356bSdh142964 static int pmcs_scsa_start(struct scsi_address *, struct scsi_pkt *);
374c06356bSdh142964 static int pmcs_scsa_abort(struct scsi_address *, struct scsi_pkt *);
384c06356bSdh142964 static int pmcs_scsa_reset(struct scsi_address *, int);
394c06356bSdh142964 static int pmcs_scsi_reset_notify(struct scsi_address *, int,
404c06356bSdh142964 void (*)(caddr_t), caddr_t);
414c06356bSdh142964 static int pmcs_scsa_getcap(struct scsi_address *, char *, int);
424c06356bSdh142964 static int pmcs_scsa_setcap(struct scsi_address *, char *, int, int);
434c06356bSdh142964 static int pmcs_scsa_setup_pkt(struct scsi_pkt *, int (*)(caddr_t), caddr_t);
444c06356bSdh142964 static void pmcs_scsa_teardown_pkt(struct scsi_pkt *);
4596c4a178SChris Horne
4696c4a178SChris Horne static int pmcs_smp_init(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
4796c4a178SChris Horne smp_device_t *);
4896c4a178SChris Horne static void pmcs_smp_free(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
4996c4a178SChris Horne smp_device_t *);
504c06356bSdh142964 static int pmcs_smp_start(struct smp_pkt *);
514c06356bSdh142964
524c06356bSdh142964 static int pmcs_scsi_quiesce(dev_info_t *);
534c06356bSdh142964 static int pmcs_scsi_unquiesce(dev_info_t *);
544c06356bSdh142964
554c06356bSdh142964 static int pmcs_cap(struct scsi_address *, char *, int, int, int);
564c06356bSdh142964 static pmcs_xscsi_t *
574c06356bSdh142964 pmcs_addr2xp(struct scsi_address *, uint64_t *, pmcs_cmd_t *);
584c06356bSdh142964 static int pmcs_SAS_run(pmcs_cmd_t *, pmcwork_t *);
594c06356bSdh142964 static void pmcs_SAS_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
604c06356bSdh142964
614c06356bSdh142964 static int pmcs_SATA_run(pmcs_cmd_t *, pmcwork_t *);
624c06356bSdh142964 static void pmcs_SATA_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
634c06356bSdh142964 static uint8_t pmcs_SATA_rwparm(uint8_t *, uint32_t *, uint64_t *, uint64_t);
644c06356bSdh142964
654c06356bSdh142964 static void pmcs_ioerror(pmcs_hw_t *, pmcs_dtype_t pmcs_dtype,
66658280b6SDavid Hollister pmcwork_t *, uint32_t *, uint32_t);
674c06356bSdh142964
684c06356bSdh142964
694c06356bSdh142964 int
pmcs_scsa_init(pmcs_hw_t * pwp,const ddi_dma_attr_t * ap)704c06356bSdh142964 pmcs_scsa_init(pmcs_hw_t *pwp, const ddi_dma_attr_t *ap)
714c06356bSdh142964 {
724c06356bSdh142964 scsi_hba_tran_t *tran;
734c06356bSdh142964 ddi_dma_attr_t pmcs_scsa_dattr;
744c06356bSdh142964 int flags;
754c06356bSdh142964
764c06356bSdh142964 (void) memcpy(&pmcs_scsa_dattr, ap, sizeof (ddi_dma_attr_t));
774c06356bSdh142964 pmcs_scsa_dattr.dma_attr_sgllen =
784c06356bSdh142964 ((PMCS_SGL_NCHUNKS - 1) * (PMCS_MAX_CHUNKS - 1)) + PMCS_SGL_NCHUNKS;
794c06356bSdh142964 pmcs_scsa_dattr.dma_attr_flags = DDI_DMA_RELAXED_ORDERING;
804c06356bSdh142964 pmcs_scsa_dattr.dma_attr_flags |= DDI_DMA_FLAGERR;
814c06356bSdh142964
824c06356bSdh142964 /*
834c06356bSdh142964 * Allocate a transport structure
844c06356bSdh142964 */
854c06356bSdh142964 tran = scsi_hba_tran_alloc(pwp->dip, SCSI_HBA_CANSLEEP);
864c06356bSdh142964 if (tran == NULL) {
87c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
88c3bc407cSdh142964 "scsi_hba_tran_alloc failed");
894c06356bSdh142964 return (DDI_FAILURE);
904c06356bSdh142964 }
914c06356bSdh142964
924c06356bSdh142964 tran->tran_hba_private = pwp;
934c06356bSdh142964 tran->tran_tgt_init = pmcs_scsa_tran_tgt_init;
944c06356bSdh142964 tran->tran_tgt_free = pmcs_scsa_tran_tgt_free;
954c06356bSdh142964 tran->tran_start = pmcs_scsa_start;
964c06356bSdh142964 tran->tran_abort = pmcs_scsa_abort;
974c06356bSdh142964 tran->tran_reset = pmcs_scsa_reset;
984c06356bSdh142964 tran->tran_reset_notify = pmcs_scsi_reset_notify;
994c06356bSdh142964 tran->tran_getcap = pmcs_scsa_getcap;
1004c06356bSdh142964 tran->tran_setcap = pmcs_scsa_setcap;
1014c06356bSdh142964 tran->tran_setup_pkt = pmcs_scsa_setup_pkt;
1024c06356bSdh142964 tran->tran_teardown_pkt = pmcs_scsa_teardown_pkt;
1034c06356bSdh142964 tran->tran_quiesce = pmcs_scsi_quiesce;
1044c06356bSdh142964 tran->tran_unquiesce = pmcs_scsi_unquiesce;
1054c06356bSdh142964 tran->tran_interconnect_type = INTERCONNECT_SAS;
1064c06356bSdh142964 tran->tran_hba_len = sizeof (pmcs_cmd_t);
1074c06356bSdh142964
1084c06356bSdh142964 /*
1094c06356bSdh142964 * Attach this instance of the hba
1104c06356bSdh142964 */
1114c06356bSdh142964
1124c06356bSdh142964 flags = SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CDB | SCSI_HBA_ADDR_COMPLEX |
1134c06356bSdh142964 SCSI_HBA_TRAN_PHCI | SCSI_HBA_HBA;
1144c06356bSdh142964
1154c06356bSdh142964 if (scsi_hba_attach_setup(pwp->dip, &pmcs_scsa_dattr, tran, flags)) {
1164c06356bSdh142964 scsi_hba_tran_free(tran);
117c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
118c3bc407cSdh142964 "scsi_hba_attach failed");
1194c06356bSdh142964 return (DDI_FAILURE);
1204c06356bSdh142964 }
1214c06356bSdh142964 pwp->tran = tran;
1224c06356bSdh142964
1234c06356bSdh142964 /*
1244c06356bSdh142964 * Attach the SMP part of this hba
1254c06356bSdh142964 */
12696c4a178SChris Horne pwp->smp_tran = smp_hba_tran_alloc(pwp->dip);
1274c06356bSdh142964 ASSERT(pwp->smp_tran != NULL);
12896c4a178SChris Horne pwp->smp_tran->smp_tran_hba_private = pwp;
12996c4a178SChris Horne pwp->smp_tran->smp_tran_init = pmcs_smp_init;
13096c4a178SChris Horne pwp->smp_tran->smp_tran_free = pmcs_smp_free;
13196c4a178SChris Horne pwp->smp_tran->smp_tran_start = pmcs_smp_start;
1324c06356bSdh142964
13396c4a178SChris Horne if (smp_hba_attach_setup(pwp->dip, pwp->smp_tran) != DDI_SUCCESS) {
134c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
13596c4a178SChris Horne "smp_hba_attach failed");
13696c4a178SChris Horne smp_hba_tran_free(pwp->smp_tran);
1374c06356bSdh142964 pwp->smp_tran = NULL;
1384c06356bSdh142964 scsi_hba_tran_free(tran);
1394c06356bSdh142964 return (DDI_FAILURE);
1404c06356bSdh142964 }
1414c06356bSdh142964
1424c06356bSdh142964 return (DDI_SUCCESS);
1434c06356bSdh142964 }
1444c06356bSdh142964
1454c06356bSdh142964 /*
1464c06356bSdh142964 * SCSA entry points
1474c06356bSdh142964 */
1484c06356bSdh142964
1494c06356bSdh142964 static int
pmcs_scsa_tran_tgt_init(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * tran,struct scsi_device * sd)1504c06356bSdh142964 pmcs_scsa_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
1514c06356bSdh142964 scsi_hba_tran_t *tran, struct scsi_device *sd)
1524c06356bSdh142964 {
1534c06356bSdh142964 pmcs_hw_t *pwp = NULL;
1544c06356bSdh142964 int rval;
1554c06356bSdh142964 char *variant_prop = "sata";
1564c06356bSdh142964 char *tgt_port = NULL, *ua = NULL;
1574c06356bSdh142964 pmcs_xscsi_t *tgt = NULL;
1584c06356bSdh142964 pmcs_iport_t *iport;
1594c06356bSdh142964 pmcs_lun_t *lun = NULL;
1604c06356bSdh142964 pmcs_phy_t *phyp = NULL;
1614c06356bSdh142964 uint64_t lun_num;
1624c06356bSdh142964 boolean_t got_scratch = B_FALSE;
1634c06356bSdh142964
1644c06356bSdh142964 /*
1654c06356bSdh142964 * First, make sure we're an iport and get the pointer to the HBA
1664c06356bSdh142964 * node's softstate
1674c06356bSdh142964 */
1684c06356bSdh142964 if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
169c3bc407cSdh142964 pmcs_prt(TRAN2PMC(tran), PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
1704c06356bSdh142964 "%s: We don't enumerate devices on the HBA node", __func__);
1714c06356bSdh142964 goto tgt_init_fail;
1724c06356bSdh142964 }
1734c06356bSdh142964
1744c06356bSdh142964 pwp = ITRAN2PMC(tran);
1754c06356bSdh142964 iport = ITRAN2IPORT(tran);
1764c06356bSdh142964
1774c06356bSdh142964 /*
178c40ba10dSReed * Get the unit-address
179c40ba10dSReed */
180c40ba10dSReed ua = scsi_device_unit_address(sd);
181c40ba10dSReed if (ua == NULL) {
182c40ba10dSReed pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
183c40ba10dSReed "%s: Couldn't get UA", __func__);
184c40ba10dSReed pwp = NULL;
185c40ba10dSReed goto tgt_init_fail;
186c40ba10dSReed }
187c40ba10dSReed pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
188c40ba10dSReed "got ua '%s'", ua);
189c40ba10dSReed
190c40ba10dSReed /*
1914c06356bSdh142964 * Get the target address
1924c06356bSdh142964 */
1934c06356bSdh142964 rval = scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
1944c06356bSdh142964 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port);
1954c06356bSdh142964 if (rval != DDI_PROP_SUCCESS) {
196c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
197c3bc407cSdh142964 "Couldn't get target UA");
1984c06356bSdh142964 pwp = NULL;
1994c06356bSdh142964 goto tgt_init_fail;
2004c06356bSdh142964 }
201c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
202c3bc407cSdh142964 "got tgt_port '%s'", tgt_port);
2034c06356bSdh142964
2044c06356bSdh142964 /*
2054c06356bSdh142964 * Validate that this tran_tgt_init is for an active iport.
2064c06356bSdh142964 */
2074c06356bSdh142964 if (iport->ua_state == UA_INACTIVE) {
208c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
2094c06356bSdh142964 "%s: Got tran_tgt_init on inactive iport for '%s'",
2104c06356bSdh142964 __func__, tgt_port);
2114c06356bSdh142964 pwp = NULL;
2124c06356bSdh142964 goto tgt_init_fail;
2134c06356bSdh142964 }
2144c06356bSdh142964
2154c06356bSdh142964 /*
2164c06356bSdh142964 * Since we're going to wait for scratch, be sure to acquire it while
2174c06356bSdh142964 * we're not holding any other locks
2184c06356bSdh142964 */
2194c06356bSdh142964 (void) pmcs_acquire_scratch(pwp, B_TRUE);
2204c06356bSdh142964 got_scratch = B_TRUE;
2214c06356bSdh142964
2224c06356bSdh142964 mutex_enter(&pwp->lock);
2234c06356bSdh142964
2244c06356bSdh142964 /*
2254c06356bSdh142964 * See if there's already a target softstate. If not, allocate one.
2264c06356bSdh142964 */
2275c45adf0SJesse Butler tgt = pmcs_get_target(iport, tgt_port, B_TRUE);
2284c06356bSdh142964
2294c06356bSdh142964 if (tgt == NULL) {
230188eaed9SSrikanth Suravajhala pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: "
231188eaed9SSrikanth Suravajhala "No tgt for tgt_port (%s)", __func__, tgt_port);
2324c06356bSdh142964 goto tgt_init_fail;
2334c06356bSdh142964 }
2344c06356bSdh142964
2354c06356bSdh142964 phyp = tgt->phy;
2364c06356bSdh142964 if (!IS_ROOT_PHY(phyp)) {
2374c06356bSdh142964 pmcs_inc_phy_ref_count(phyp);
2384c06356bSdh142964 }
2394c06356bSdh142964 ASSERT(mutex_owned(&phyp->phy_lock));
2404c06356bSdh142964
241c40ba10dSReed pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt, "@%s tgt = 0x%p, dip = 0x%p",
242c40ba10dSReed ua, (void *)tgt, (void *)tgt_dip);
2434c06356bSdh142964
244c40ba10dSReed /* Now get the lun */
2454c06356bSdh142964 lun_num = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
2464c06356bSdh142964 SCSI_ADDR_PROP_LUN64, SCSI_LUN64_ILLEGAL);
2474c06356bSdh142964 if (lun_num == SCSI_LUN64_ILLEGAL) {
248c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
249c3bc407cSdh142964 "No LUN for tgt %p", (void *)tgt);
2504c06356bSdh142964 goto tgt_init_fail;
2514c06356bSdh142964 }
2524c06356bSdh142964
253c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, "%s: @%s tgt 0x%p phy "
254c3bc407cSdh142964 "0x%p (%s)", __func__, ua, (void *)tgt, (void *)phyp, phyp->path);
2554c06356bSdh142964
2564c06356bSdh142964 mutex_enter(&tgt->statlock);
2574c06356bSdh142964 tgt->dtype = phyp->dtype;
2584c06356bSdh142964 if (tgt->dtype != SAS && tgt->dtype != SATA) {
259c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
260c3bc407cSdh142964 "PHY 0x%p went away?", (void *)phyp);
2614c06356bSdh142964 goto tgt_init_fail;
2624c06356bSdh142964 }
2634c06356bSdh142964
2644c06356bSdh142964 /* We don't support SATA devices at LUN > 0. */
2654c06356bSdh142964 if ((tgt->dtype == SATA) && (lun_num > 0)) {
266c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
2674c06356bSdh142964 "%s: No support for SATA devices at LUN > 0 "
2684c06356bSdh142964 "(target = 0x%p)", __func__, (void *)tgt);
2694c06356bSdh142964 goto tgt_init_fail;
2704c06356bSdh142964 }
2714c06356bSdh142964
2724c06356bSdh142964 /*
2734c06356bSdh142964 * Allocate LU soft state. We use ddi_soft_state_bystr_zalloc instead
2744c06356bSdh142964 * of kmem_alloc because ddi_soft_state_bystr_zalloc allows us to
2754c06356bSdh142964 * verify that the framework never tries to initialize two scsi_device
2764c06356bSdh142964 * structures with the same unit-address at the same time.
2774c06356bSdh142964 */
2784c06356bSdh142964 if (ddi_soft_state_bystr_zalloc(tgt->lun_sstate, ua) != DDI_SUCCESS) {
279c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
2804c06356bSdh142964 "Couldn't allocate LU soft state");
2814c06356bSdh142964 goto tgt_init_fail;
2824c06356bSdh142964 }
2834c06356bSdh142964
2844c06356bSdh142964 lun = ddi_soft_state_bystr_get(tgt->lun_sstate, ua);
2854c06356bSdh142964 if (lun == NULL) {
286c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
287c3bc407cSdh142964 "Couldn't get LU soft state");
2884c06356bSdh142964 goto tgt_init_fail;
2894c06356bSdh142964 }
2904c06356bSdh142964 scsi_device_hba_private_set(sd, lun);
2914c06356bSdh142964 lun->lun_num = lun_num;
2924c06356bSdh142964
2934c06356bSdh142964 /* convert the scsi_lun64_t value to SCSI standard form */
2944c06356bSdh142964 lun->scsi_lun = scsi_lun64_to_lun(lun_num);
2954c06356bSdh142964
2964c06356bSdh142964 ASSERT(strlen(ua) < (PMCS_MAX_UA_SIZE - 1));
2974c06356bSdh142964 bcopy(ua, lun->unit_address, strnlen(ua, PMCS_MAX_UA_SIZE - 1));
2984c06356bSdh142964
2994c06356bSdh142964 lun->target = tgt;
3004c06356bSdh142964
3014c06356bSdh142964 /*
3024c06356bSdh142964 * If this is the first tran_tgt_init, add this target to our list
3034c06356bSdh142964 */
3044c06356bSdh142964 if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
3054c06356bSdh142964 int target;
3064c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) {
3074c06356bSdh142964 if (pwp->targets[target] != NULL) {
3084c06356bSdh142964 continue;
3094c06356bSdh142964 }
3104c06356bSdh142964
3114c06356bSdh142964 pwp->targets[target] = tgt;
3124c06356bSdh142964 tgt->target_num = (uint16_t)target;
3134c06356bSdh142964 break;
3144c06356bSdh142964 }
3154c06356bSdh142964
3164c06356bSdh142964 if (target == pwp->max_dev) {
317c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
3184c06356bSdh142964 "Target list full.");
3194c06356bSdh142964 goto tgt_init_fail;
3204c06356bSdh142964 }
3214c06356bSdh142964 }
3224c06356bSdh142964
3234c06356bSdh142964 tgt->dip = sd->sd_dev;
32473a3eccdSDavid Hollister lun->sd = sd;
32573a3eccdSDavid Hollister list_insert_tail(&tgt->lun_list, lun);
3264c06356bSdh142964
3274c06356bSdh142964 if (!pmcs_assign_device(pwp, tgt)) {
3284c06356bSdh142964 pmcs_release_scratch(pwp);
3294c06356bSdh142964 pwp->targets[tgt->target_num] = NULL;
3304c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM;
3314c06356bSdh142964 tgt->phy = NULL;
332c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
3334c06356bSdh142964 "%s: pmcs_assign_device failed for target 0x%p",
3344c06356bSdh142964 __func__, (void *)tgt);
3354c06356bSdh142964 goto tgt_init_fail;
3364c06356bSdh142964 }
3374c06356bSdh142964
3384c06356bSdh142964 pmcs_release_scratch(pwp);
3394c06356bSdh142964 tgt->ref_count++;
3404c06356bSdh142964
3414c06356bSdh142964 (void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
3424c06356bSdh142964 SCSI_ADDR_PROP_TARGET, (uint32_t)(tgt->target_num));
3434c06356bSdh142964
3444c06356bSdh142964 /* SM-HBA */
3454c06356bSdh142964 if (tgt->dtype == SATA) {
3464c06356bSdh142964 /* TCR in PSARC/1997/281 opinion */
3474c06356bSdh142964 (void) scsi_device_prop_update_string(sd,
3484c06356bSdh142964 SCSI_DEVICE_PROP_PATH, "variant", variant_prop);
3494c06356bSdh142964 }
3504c06356bSdh142964
3514c06356bSdh142964 tgt->phy_addressable = PMCS_PHY_ADDRESSABLE(phyp);
3524c06356bSdh142964
3534c06356bSdh142964 if (tgt->phy_addressable) {
3544c06356bSdh142964 (void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
3554c06356bSdh142964 SCSI_ADDR_PROP_SATA_PHY, phyp->phynum);
3564c06356bSdh142964 }
3574c06356bSdh142964
3584c06356bSdh142964 /* SM-HBA */
3594c06356bSdh142964 (void) pmcs_smhba_set_scsi_device_props(pwp, phyp, sd);
360499cfd15SDavid Hollister /*
361499cfd15SDavid Hollister * Make sure attached port and target port pm props are updated
362499cfd15SDavid Hollister * By passing in 0s, we're not actually updating any values, but
363499cfd15SDavid Hollister * the properties should now get updated on the node.
364499cfd15SDavid Hollister */
3654c06356bSdh142964
3664c06356bSdh142964 mutex_exit(&tgt->statlock);
36773a3eccdSDavid Hollister pmcs_update_phy_pm_props(phyp, 0, 0, B_TRUE);
3684c06356bSdh142964 pmcs_unlock_phy(phyp);
3694c06356bSdh142964 mutex_exit(&pwp->lock);
3704c06356bSdh142964 scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
3714c06356bSdh142964 return (DDI_SUCCESS);
3724c06356bSdh142964
3734c06356bSdh142964 tgt_init_fail:
374616875b4SDavid Hollister scsi_device_hba_private_set(sd, NULL);
3754c06356bSdh142964 if (got_scratch) {
3764c06356bSdh142964 pmcs_release_scratch(pwp);
3774c06356bSdh142964 }
3784c06356bSdh142964 if (lun) {
37973a3eccdSDavid Hollister list_remove(&tgt->lun_list, lun);
3804c06356bSdh142964 ddi_soft_state_bystr_free(tgt->lun_sstate, ua);
3814c06356bSdh142964 }
3824c06356bSdh142964 if (phyp) {
3834c06356bSdh142964 mutex_exit(&tgt->statlock);
3844c06356bSdh142964 pmcs_unlock_phy(phyp);
3854c06356bSdh142964 /*
3864c06356bSdh142964 * phyp's ref count was incremented in pmcs_new_tport.
3874c06356bSdh142964 * We're failing configuration, we now need to decrement it.
3884c06356bSdh142964 */
3894c06356bSdh142964 if (!IS_ROOT_PHY(phyp)) {
3904c06356bSdh142964 pmcs_dec_phy_ref_count(phyp);
3914c06356bSdh142964 }
3924c06356bSdh142964 phyp->target = NULL;
3934c06356bSdh142964 }
3944c06356bSdh142964 if (tgt && tgt->ref_count == 0) {
3954c06356bSdh142964 ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
3964c06356bSdh142964 }
3974c06356bSdh142964 if (pwp) {
3984c06356bSdh142964 mutex_exit(&pwp->lock);
399c40ba10dSReed pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
400c40ba10dSReed "%s: failed for @%s tgt 0x%p phy 0x%p", __func__, ua,
401c40ba10dSReed (void *)tgt, (void *)phyp);
4024c06356bSdh142964 }
4034c06356bSdh142964 if (tgt_port) {
4044c06356bSdh142964 scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
4054c06356bSdh142964 }
4064c06356bSdh142964 return (DDI_FAILURE);
4074c06356bSdh142964 }
4084c06356bSdh142964
4094c06356bSdh142964 static void
pmcs_scsa_tran_tgt_free(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * tran,struct scsi_device * sd)4104c06356bSdh142964 pmcs_scsa_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
4114c06356bSdh142964 scsi_hba_tran_t *tran, struct scsi_device *sd)
4124c06356bSdh142964 {
4134c06356bSdh142964 _NOTE(ARGUNUSED(hba_dip, tgt_dip));
4144c06356bSdh142964 pmcs_hw_t *pwp;
4154c06356bSdh142964 pmcs_lun_t *lun;
4164c06356bSdh142964 pmcs_xscsi_t *target;
4174c06356bSdh142964 char *unit_address;
4184c06356bSdh142964 pmcs_phy_t *phyp;
4194c06356bSdh142964
4204c06356bSdh142964 if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
4214c06356bSdh142964 pwp = TRAN2PMC(tran);
422c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
4234c06356bSdh142964 "%s: We don't enumerate devices on the HBA node", __func__);
4244c06356bSdh142964 return;
4254c06356bSdh142964 }
4264c06356bSdh142964
4274c06356bSdh142964 lun = (pmcs_lun_t *)scsi_device_hba_private_get(sd);
4284c06356bSdh142964
4294c06356bSdh142964 ASSERT((lun != NULL) && (lun->target != NULL));
4304c06356bSdh142964 ASSERT(lun->target->ref_count > 0);
4314c06356bSdh142964
4324c06356bSdh142964 target = lun->target;
4334c06356bSdh142964 unit_address = lun->unit_address;
43473a3eccdSDavid Hollister list_remove(&target->lun_list, lun);
4354c06356bSdh142964
4364c06356bSdh142964 pwp = ITRAN2PMC(tran);
4374c06356bSdh142964 mutex_enter(&pwp->lock);
4384c06356bSdh142964 phyp = target->phy;
439ee13933aSSrikanth Suravajhala if (phyp) {
440ee13933aSSrikanth Suravajhala mutex_enter(&phyp->phy_lock);
441ee13933aSSrikanth Suravajhala }
442ee13933aSSrikanth Suravajhala mutex_enter(&target->statlock);
4434c06356bSdh142964
44473a3eccdSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target,
44573a3eccdSDavid Hollister "%s: for @%s tgt 0x%p phy 0x%p", __func__, unit_address,
44673a3eccdSDavid Hollister (void *)target, (void *)phyp);
44773a3eccdSDavid Hollister ddi_soft_state_bystr_free(lun->target->lun_sstate, unit_address);
44873a3eccdSDavid Hollister
449af685682SSrikanth, Ramana if (target->recover_wait) {
450af685682SSrikanth, Ramana mutex_exit(&target->statlock);
451ee13933aSSrikanth Suravajhala if (phyp) {
452ee13933aSSrikanth Suravajhala mutex_exit(&phyp->phy_lock);
453ee13933aSSrikanth Suravajhala }
454af685682SSrikanth, Ramana mutex_exit(&pwp->lock);
455af685682SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target, "%s: "
456af685682SSrikanth, Ramana "Target 0x%p in device state recovery, fail tran_tgt_free",
457af685682SSrikanth, Ramana __func__, (void *)target);
458af685682SSrikanth, Ramana return;
459af685682SSrikanth, Ramana }
460af685682SSrikanth, Ramana
4614c06356bSdh142964 /*
4624c06356bSdh142964 * If this target still has a PHY pointer and that PHY's target pointer
4634c06356bSdh142964 * has been cleared, then that PHY has been reaped. In that case, there
4644c06356bSdh142964 * would be no need to decrement the reference count
4654c06356bSdh142964 */
4664c06356bSdh142964 if (phyp && !IS_ROOT_PHY(phyp) && phyp->target) {
4674c06356bSdh142964 pmcs_dec_phy_ref_count(phyp);
4684c06356bSdh142964 }
4694c06356bSdh142964
4704c06356bSdh142964 if (--target->ref_count == 0) {
4714c06356bSdh142964 /*
4724c06356bSdh142964 * Remove this target from our list. The target soft
4734c06356bSdh142964 * state will remain, and the device will remain registered
4744c06356bSdh142964 * with the hardware unless/until we're told the device
4754c06356bSdh142964 * physically went away.
4764c06356bSdh142964 */
477c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target,
4784c06356bSdh142964 "%s: Free target 0x%p (vtgt %d)", __func__, (void *)target,
4794c06356bSdh142964 target->target_num);
4804c06356bSdh142964 pwp->targets[target->target_num] = NULL;
4814c06356bSdh142964 target->target_num = PMCS_INVALID_TARGET_NUM;
4826c87a171SJesse Butler /* If the PHY has a pointer to this target, clear it */
4836c87a171SJesse Butler if (phyp && (phyp->target == target)) {
4844c06356bSdh142964 phyp->target = NULL;
4854c06356bSdh142964 }
4864c06356bSdh142964 target->phy = NULL;
487ee13933aSSrikanth Suravajhala if (phyp) {
488ee13933aSSrikanth Suravajhala mutex_exit(&phyp->phy_lock);
489ee13933aSSrikanth Suravajhala }
490654ea33cSSrikanth Suravajhala pmcs_destroy_target(target);
491654ea33cSSrikanth Suravajhala } else {
492654ea33cSSrikanth Suravajhala mutex_exit(&target->statlock);
493654ea33cSSrikanth Suravajhala if (phyp) {
494654ea33cSSrikanth Suravajhala mutex_exit(&phyp->phy_lock);
495654ea33cSSrikanth Suravajhala }
496654ea33cSSrikanth Suravajhala }
497654ea33cSSrikanth Suravajhala
4984c06356bSdh142964 mutex_exit(&pwp->lock);
4994c06356bSdh142964 }
5004c06356bSdh142964
5014c06356bSdh142964 static int
pmcs_scsa_start(struct scsi_address * ap,struct scsi_pkt * pkt)5024c06356bSdh142964 pmcs_scsa_start(struct scsi_address *ap, struct scsi_pkt *pkt)
5034c06356bSdh142964 {
5044c06356bSdh142964 pmcs_cmd_t *sp = PKT2CMD(pkt);
5054c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap);
5064c06356bSdh142964 pmcs_xscsi_t *xp;
5074c06356bSdh142964 boolean_t blocked;
5084c06356bSdh142964 uint32_t hba_state;
5094c06356bSdh142964
510c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
511c3bc407cSdh142964 "%s: pkt %p sd %p cdb0=0x%02x dl=%lu", __func__, (void *)pkt,
5124c06356bSdh142964 (void *)scsi_address_device(&pkt->pkt_address),
5134c06356bSdh142964 pkt->pkt_cdbp[0] & 0xff, pkt->pkt_dma_len);
5144c06356bSdh142964
5154c06356bSdh142964 if (pkt->pkt_flags & FLAG_NOINTR) {
516c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
517c3bc407cSdh142964 "%s: nointr pkt", __func__);
5184c06356bSdh142964 return (TRAN_BADPKT);
5194c06356bSdh142964 }
5204c06356bSdh142964
5214c06356bSdh142964 sp->cmd_tag = 0;
5224c06356bSdh142964 pkt->pkt_state = pkt->pkt_statistics = 0;
5234c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE;
5244c06356bSdh142964
5254c06356bSdh142964 mutex_enter(&pwp->lock);
5264c06356bSdh142964 hba_state = pwp->state;
5274c06356bSdh142964 blocked = pwp->blocked;
5284c06356bSdh142964 mutex_exit(&pwp->lock);
5294c06356bSdh142964
5304c06356bSdh142964 if (hba_state != STATE_RUNNING) {
531c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
532c3bc407cSdh142964 "%s: hba dead", __func__);
5334c06356bSdh142964 return (TRAN_FATAL_ERROR);
5344c06356bSdh142964 }
5354c06356bSdh142964
5364c06356bSdh142964 xp = pmcs_addr2xp(ap, NULL, sp);
5374c06356bSdh142964 if (xp == NULL) {
538c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
5394c06356bSdh142964 "%s: dropping due to null target", __func__);
540b18a19c2SJesse Butler goto dead_target;
5414c06356bSdh142964 }
5424c06356bSdh142964 ASSERT(mutex_owned(&xp->statlock));
5434c06356bSdh142964
5444c06356bSdh142964 /*
545b18a19c2SJesse Butler * First, check to see if the device is gone.
5464c06356bSdh142964 */
547b18a19c2SJesse Butler if (xp->dev_gone) {
548601c90f1SSrikanth, Ramana xp->actv_pkts++;
5494c06356bSdh142964 mutex_exit(&xp->statlock);
550c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, xp,
551b18a19c2SJesse Butler "%s: dropping due to dead target 0x%p",
5524c06356bSdh142964 __func__, (void *)xp);
553b18a19c2SJesse Butler goto dead_target;
5544c06356bSdh142964 }
5554c06356bSdh142964
5564c06356bSdh142964 /*
5574c06356bSdh142964 * If we're blocked (quiesced) just return.
5584c06356bSdh142964 */
5594c06356bSdh142964 if (blocked) {
560c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
561c3bc407cSdh142964 "%s: hba blocked", __func__);
562601c90f1SSrikanth, Ramana xp->actv_pkts++;
5634c06356bSdh142964 mutex_exit(&xp->statlock);
5644c06356bSdh142964 mutex_enter(&xp->wqlock);
5654c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5664c06356bSdh142964 mutex_exit(&xp->wqlock);
5674c06356bSdh142964 return (TRAN_ACCEPT);
5684c06356bSdh142964 }
5694c06356bSdh142964
5704c06356bSdh142964 /*
5714c06356bSdh142964 * If we're draining or resetting, queue and return.
5724c06356bSdh142964 */
5734c06356bSdh142964 if (xp->draining || xp->resetting || xp->recover_wait) {
574601c90f1SSrikanth, Ramana xp->actv_pkts++;
5754c06356bSdh142964 mutex_exit(&xp->statlock);
5764c06356bSdh142964 mutex_enter(&xp->wqlock);
5774c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
5784c06356bSdh142964 mutex_exit(&xp->wqlock);
579c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
5804c06356bSdh142964 "%s: draining/resetting/recovering (cnt %u)",
5814c06356bSdh142964 __func__, xp->actv_cnt);
5824c06356bSdh142964 /*
5834c06356bSdh142964 * By the time we get here, draining or
5844c06356bSdh142964 * resetting may have come and gone, not
5854c06356bSdh142964 * yet noticing that we had put something
5864c06356bSdh142964 * on the wait queue, so schedule a worker
5874c06356bSdh142964 * to look at this later.
5884c06356bSdh142964 */
5894c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
5904c06356bSdh142964 return (TRAN_ACCEPT);
5914c06356bSdh142964 }
592601c90f1SSrikanth, Ramana
593601c90f1SSrikanth, Ramana xp->actv_pkts++;
5944c06356bSdh142964 mutex_exit(&xp->statlock);
5954c06356bSdh142964
5964c06356bSdh142964 /*
5974c06356bSdh142964 * Queue this command to the tail of the wait queue.
5984c06356bSdh142964 * This keeps us getting commands out of order.
5994c06356bSdh142964 */
6004c06356bSdh142964 mutex_enter(&xp->wqlock);
6014c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
6024c06356bSdh142964 mutex_exit(&xp->wqlock);
6034c06356bSdh142964
6044c06356bSdh142964 /*
6054c06356bSdh142964 * Now run the queue for this device.
6064c06356bSdh142964 */
6074c06356bSdh142964 (void) pmcs_scsa_wq_run_one(pwp, xp);
6084c06356bSdh142964
6094c06356bSdh142964 return (TRAN_ACCEPT);
6104c06356bSdh142964
611b18a19c2SJesse Butler dead_target:
6124c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS;
6134c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE;
6144c06356bSdh142964 mutex_enter(&pwp->cq_lock);
6154c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
6164c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp);
6174c06356bSdh142964 mutex_exit(&pwp->cq_lock);
6184c06356bSdh142964 return (TRAN_ACCEPT);
6194c06356bSdh142964 }
6204c06356bSdh142964
62102b04f6eSSrikanth, Ramana /* Return code 1 = Success */
6224c06356bSdh142964 static int
pmcs_scsa_abort(struct scsi_address * ap,struct scsi_pkt * pkt)6234c06356bSdh142964 pmcs_scsa_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
6244c06356bSdh142964 {
6254c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap);
62602b04f6eSSrikanth, Ramana pmcs_cmd_t *sp = NULL;
62702b04f6eSSrikanth, Ramana pmcs_xscsi_t *xp = NULL;
62802b04f6eSSrikanth, Ramana pmcs_phy_t *pptr = NULL;
62902b04f6eSSrikanth, Ramana pmcs_lun_t *pmcs_lun = (pmcs_lun_t *)
63002b04f6eSSrikanth, Ramana scsi_device_hba_private_get(scsi_address_device(ap));
6314c06356bSdh142964 uint32_t tag;
6324c06356bSdh142964 uint64_t lun;
6334c06356bSdh142964 pmcwork_t *pwrk;
6344c06356bSdh142964
6354c06356bSdh142964 mutex_enter(&pwp->lock);
6364c06356bSdh142964 if (pwp->state != STATE_RUNNING) {
6374c06356bSdh142964 mutex_exit(&pwp->lock);
638c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
639c3bc407cSdh142964 "%s: hba dead", __func__);
6404c06356bSdh142964 return (0);
6414c06356bSdh142964 }
6424c06356bSdh142964 mutex_exit(&pwp->lock);
6434c06356bSdh142964
64402b04f6eSSrikanth, Ramana if (pkt == NULL) {
64502b04f6eSSrikanth, Ramana if (pmcs_lun == NULL) {
64602b04f6eSSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: "
64702b04f6eSSrikanth, Ramana "No pmcs_lun_t struct to do ABORT_ALL", __func__);
64802b04f6eSSrikanth, Ramana return (0);
64902b04f6eSSrikanth, Ramana }
65002b04f6eSSrikanth, Ramana xp = pmcs_lun->target;
65102b04f6eSSrikanth, Ramana if (xp != NULL) {
65202b04f6eSSrikanth, Ramana pptr = xp->phy;
65302b04f6eSSrikanth, Ramana }
65402b04f6eSSrikanth, Ramana if (pptr == NULL) {
65502b04f6eSSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: pkt is "
65602b04f6eSSrikanth, Ramana "NULL. No tgt/phy to do ABORT_ALL", __func__);
65702b04f6eSSrikanth, Ramana return (0);
65802b04f6eSSrikanth, Ramana }
65902b04f6eSSrikanth, Ramana pmcs_lock_phy(pptr);
66002b04f6eSSrikanth, Ramana if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
66102b04f6eSSrikanth, Ramana pptr->abort_pending = 1;
66202b04f6eSSrikanth, Ramana SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
66302b04f6eSSrikanth, Ramana }
66402b04f6eSSrikanth, Ramana pmcs_unlock_phy(pptr);
66502b04f6eSSrikanth, Ramana return (1);
66602b04f6eSSrikanth, Ramana }
66702b04f6eSSrikanth, Ramana
66802b04f6eSSrikanth, Ramana sp = PKT2CMD(pkt);
66902b04f6eSSrikanth, Ramana xp = sp->cmd_target;
67002b04f6eSSrikanth, Ramana
6714c06356bSdh142964 if (sp->cmd_lun) {
6724c06356bSdh142964 lun = sp->cmd_lun->lun_num;
6734c06356bSdh142964 } else {
6744c06356bSdh142964 lun = 0;
6754c06356bSdh142964 }
6764c06356bSdh142964 if (xp == NULL) {
6774c06356bSdh142964 return (0);
6784c06356bSdh142964 }
6794c06356bSdh142964
6804c06356bSdh142964 /*
6814c06356bSdh142964 * See if we have a real work structure associated with this cmd.
6824c06356bSdh142964 */
683978d7443SSrikanth Suravajhala pwrk = pmcs_tag2wp(pwp, sp->cmd_tag, B_FALSE);
6844c06356bSdh142964 if (pwrk && pwrk->arg == sp) {
6854c06356bSdh142964 tag = pwrk->htag;
6864c06356bSdh142964 pptr = pwrk->phy;
6874c06356bSdh142964 pwrk->timer = 0; /* we don't time this here */
6884c06356bSdh142964 ASSERT(pwrk->state == PMCS_WORK_STATE_ONCHIP);
6894c06356bSdh142964 mutex_exit(&pwrk->lock);
6904c06356bSdh142964 pmcs_lock_phy(pptr);
6914c06356bSdh142964 if (pptr->dtype == SAS) {
6924c06356bSdh142964 if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, tag, lun,
6934c06356bSdh142964 NULL)) {
6944c06356bSdh142964 pptr->abort_pending = 1;
6954c06356bSdh142964 pmcs_unlock_phy(pptr);
6964c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
6974c06356bSdh142964 return (0);
6984c06356bSdh142964 }
6994c06356bSdh142964 } else {
7004c06356bSdh142964 /*
7014c06356bSdh142964 * XXX: Was the command that was active an
7024c06356bSdh142964 * NCQ I/O command?
7034c06356bSdh142964 */
7044c06356bSdh142964 pptr->need_rl_ext = 1;
7054c06356bSdh142964 if (pmcs_sata_abort_ncq(pwp, pptr)) {
7064c06356bSdh142964 pptr->abort_pending = 1;
7074c06356bSdh142964 pmcs_unlock_phy(pptr);
7084c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
7094c06356bSdh142964 return (0);
7104c06356bSdh142964 }
7114c06356bSdh142964 }
7124c06356bSdh142964 pptr->abort_pending = 1;
7134c06356bSdh142964 pmcs_unlock_phy(pptr);
7144c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
7154c06356bSdh142964 return (1);
7164c06356bSdh142964 }
7174c06356bSdh142964 if (pwrk) {
7184c06356bSdh142964 mutex_exit(&pwrk->lock);
7194c06356bSdh142964 }
7204c06356bSdh142964 /*
7214c06356bSdh142964 * Okay, those weren't the droids we were looking for.
7224c06356bSdh142964 * See if the command is on any of the wait queues.
7234c06356bSdh142964 */
7244c06356bSdh142964 mutex_enter(&xp->wqlock);
7254c06356bSdh142964 sp = NULL;
7264c06356bSdh142964 STAILQ_FOREACH(sp, &xp->wq, cmd_next) {
7274c06356bSdh142964 if (sp == PKT2CMD(pkt)) {
7284c06356bSdh142964 STAILQ_REMOVE(&xp->wq, sp, pmcs_cmd, cmd_next);
7294c06356bSdh142964 break;
7304c06356bSdh142964 }
7314c06356bSdh142964 }
7324c06356bSdh142964 mutex_exit(&xp->wqlock);
7334c06356bSdh142964 if (sp) {
7344c06356bSdh142964 pkt->pkt_reason = CMD_ABORTED;
7354c06356bSdh142964 pkt->pkt_statistics |= STAT_ABORTED;
7364c06356bSdh142964 mutex_enter(&pwp->cq_lock);
7374c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
7384c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp);
7394c06356bSdh142964 mutex_exit(&pwp->cq_lock);
7404c06356bSdh142964 return (1);
7414c06356bSdh142964 }
7424c06356bSdh142964 return (0);
7434c06356bSdh142964 }
7444c06356bSdh142964
7454c06356bSdh142964 /*
7464c06356bSdh142964 * SCSA reset functions
7474c06356bSdh142964 */
7484c06356bSdh142964 static int
pmcs_scsa_reset(struct scsi_address * ap,int level)7494c06356bSdh142964 pmcs_scsa_reset(struct scsi_address *ap, int level)
7504c06356bSdh142964 {
7514c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap);
7524c06356bSdh142964 pmcs_phy_t *pptr;
7534c06356bSdh142964 pmcs_xscsi_t *xp;
7544c06356bSdh142964 uint64_t lun = (uint64_t)-1, *lp = NULL;
7554c06356bSdh142964 int rval;
7564c06356bSdh142964
7574c06356bSdh142964 mutex_enter(&pwp->lock);
7584c06356bSdh142964 if (pwp->state != STATE_RUNNING) {
7594c06356bSdh142964 mutex_exit(&pwp->lock);
760c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
761c3bc407cSdh142964 "%s: hba dead", __func__);
7624c06356bSdh142964 return (0);
7634c06356bSdh142964 }
7644c06356bSdh142964 mutex_exit(&pwp->lock);
7654c06356bSdh142964
7664c06356bSdh142964 switch (level) {
7674c06356bSdh142964 case RESET_ALL:
7684c06356bSdh142964 rval = 0;
7694c06356bSdh142964 break;
7704c06356bSdh142964 case RESET_LUN:
7714c06356bSdh142964 /*
7724c06356bSdh142964 * Point lp at lun so that pmcs_addr2xp
7734c06356bSdh142964 * will fill out the 64 bit lun number.
7744c06356bSdh142964 */
7754c06356bSdh142964 lp = &lun;
7764c06356bSdh142964 /* FALLTHROUGH */
7774c06356bSdh142964 case RESET_TARGET:
7784c06356bSdh142964 xp = pmcs_addr2xp(ap, lp, NULL);
7794c06356bSdh142964 if (xp == NULL) {
780c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
7814c06356bSdh142964 "%s: no xp found for this scsi address", __func__);
7824c06356bSdh142964 return (0);
7834c06356bSdh142964 }
7844c06356bSdh142964
785b18a19c2SJesse Butler if (xp->dev_gone) {
7864c06356bSdh142964 mutex_exit(&xp->statlock);
787c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
7884c06356bSdh142964 "%s: Target 0x%p has gone away", __func__,
7894c06356bSdh142964 (void *)xp);
7904c06356bSdh142964 return (0);
7914c06356bSdh142964 }
7924c06356bSdh142964
7934c06356bSdh142964 /*
7944c06356bSdh142964 * If we're already performing this action, or if device
7954c06356bSdh142964 * state recovery is already running, just return failure.
7964c06356bSdh142964 */
7974c06356bSdh142964 if (xp->resetting || xp->recover_wait) {
7984c06356bSdh142964 mutex_exit(&xp->statlock);
7994c06356bSdh142964 return (0);
8004c06356bSdh142964 }
8014c06356bSdh142964 xp->reset_wait = 0;
8024c06356bSdh142964 xp->reset_success = 0;
8034c06356bSdh142964 xp->resetting = 1;
8044c06356bSdh142964 pptr = xp->phy;
8054c06356bSdh142964 mutex_exit(&xp->statlock);
8064c06356bSdh142964
8074c06356bSdh142964 if (pmcs_reset_dev(pwp, pptr, lun)) {
8084c06356bSdh142964 rval = 0;
8094c06356bSdh142964 } else {
8104c06356bSdh142964 rval = 1;
8114c06356bSdh142964 }
8124c06356bSdh142964
8134c06356bSdh142964 mutex_enter(&xp->statlock);
8144c06356bSdh142964 if (rval == 1) {
8154c06356bSdh142964 xp->reset_success = 1;
8164c06356bSdh142964 }
8174c06356bSdh142964 if (xp->reset_wait) {
8184c06356bSdh142964 xp->reset_wait = 0;
8194c06356bSdh142964 cv_signal(&xp->reset_cv);
8204c06356bSdh142964 }
8214c06356bSdh142964 xp->resetting = 0;
8224c06356bSdh142964 mutex_exit(&xp->statlock);
8234c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
8244c06356bSdh142964 break;
8254c06356bSdh142964 default:
8264c06356bSdh142964 rval = 0;
8274c06356bSdh142964 break;
8284c06356bSdh142964 }
8294c06356bSdh142964
8304c06356bSdh142964 return (rval);
8314c06356bSdh142964 }
8324c06356bSdh142964
8334c06356bSdh142964 static int
pmcs_scsi_reset_notify(struct scsi_address * ap,int flag,void (* callback)(caddr_t),caddr_t arg)8344c06356bSdh142964 pmcs_scsi_reset_notify(struct scsi_address *ap, int flag,
8354c06356bSdh142964 void (*callback)(caddr_t), caddr_t arg)
8364c06356bSdh142964 {
8374c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap);
8384c06356bSdh142964 return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
8394c06356bSdh142964 &pwp->lock, &pwp->reset_notify_listf));
8404c06356bSdh142964 }
8414c06356bSdh142964
8424c06356bSdh142964
8434c06356bSdh142964 static int
pmcs_cap(struct scsi_address * ap,char * cap,int val,int tonly,int set)8444c06356bSdh142964 pmcs_cap(struct scsi_address *ap, char *cap, int val, int tonly, int set)
8454c06356bSdh142964 {
8464c06356bSdh142964 _NOTE(ARGUNUSED(val, tonly));
8474c06356bSdh142964 int cidx, rval = 0;
8484c06356bSdh142964 pmcs_xscsi_t *xp;
8494c06356bSdh142964
8504c06356bSdh142964 cidx = scsi_hba_lookup_capstr(cap);
8514c06356bSdh142964 if (cidx == -1) {
8524c06356bSdh142964 return (-1);
8534c06356bSdh142964 }
8544c06356bSdh142964
8554c06356bSdh142964 xp = pmcs_addr2xp(ap, NULL, NULL);
8564c06356bSdh142964 if (xp == NULL) {
8574c06356bSdh142964 return (-1);
8584c06356bSdh142964 }
8594c06356bSdh142964
8604c06356bSdh142964 switch (cidx) {
8614c06356bSdh142964 case SCSI_CAP_DMA_MAX:
8624c06356bSdh142964 case SCSI_CAP_INITIATOR_ID:
8634c06356bSdh142964 if (set == 0) {
8644c06356bSdh142964 rval = INT_MAX; /* argh */
8654c06356bSdh142964 }
8664c06356bSdh142964 break;
8674c06356bSdh142964 case SCSI_CAP_DISCONNECT:
8684c06356bSdh142964 case SCSI_CAP_SYNCHRONOUS:
8694c06356bSdh142964 case SCSI_CAP_WIDE_XFER:
8704c06356bSdh142964 case SCSI_CAP_PARITY:
8714c06356bSdh142964 case SCSI_CAP_ARQ:
8724c06356bSdh142964 case SCSI_CAP_UNTAGGED_QING:
8734c06356bSdh142964 if (set == 0) {
8744c06356bSdh142964 rval = 1;
8754c06356bSdh142964 }
8764c06356bSdh142964 break;
8774c06356bSdh142964
8784c06356bSdh142964 case SCSI_CAP_TAGGED_QING:
8794c06356bSdh142964 rval = 1;
8804c06356bSdh142964 break;
8814c06356bSdh142964
8824c06356bSdh142964 case SCSI_CAP_MSG_OUT:
8834c06356bSdh142964 case SCSI_CAP_RESET_NOTIFICATION:
8844c06356bSdh142964 case SCSI_CAP_QFULL_RETRIES:
8854c06356bSdh142964 case SCSI_CAP_QFULL_RETRY_INTERVAL:
8864c06356bSdh142964 break;
8874c06356bSdh142964 case SCSI_CAP_SCSI_VERSION:
8884c06356bSdh142964 if (set == 0) {
8894c06356bSdh142964 rval = SCSI_VERSION_3;
8904c06356bSdh142964 }
8914c06356bSdh142964 break;
8924c06356bSdh142964 case SCSI_CAP_INTERCONNECT_TYPE:
8934c06356bSdh142964 if (set) {
8944c06356bSdh142964 break;
8954c06356bSdh142964 }
8964c06356bSdh142964 if (xp->phy_addressable) {
8974c06356bSdh142964 rval = INTERCONNECT_SATA;
8984c06356bSdh142964 } else {
8994c06356bSdh142964 rval = INTERCONNECT_SAS;
9004c06356bSdh142964 }
9014c06356bSdh142964 break;
9024c06356bSdh142964 case SCSI_CAP_CDB_LEN:
9034c06356bSdh142964 if (set == 0) {
9044c06356bSdh142964 rval = 16;
9054c06356bSdh142964 }
9064c06356bSdh142964 break;
9074c06356bSdh142964 case SCSI_CAP_LUN_RESET:
9084c06356bSdh142964 if (set) {
9094c06356bSdh142964 break;
9104c06356bSdh142964 }
9114c06356bSdh142964 if (xp->dtype == SATA) {
9124c06356bSdh142964 rval = 0;
9134c06356bSdh142964 } else {
9144c06356bSdh142964 rval = 1;
9154c06356bSdh142964 }
9164c06356bSdh142964 break;
9174c06356bSdh142964 default:
9184c06356bSdh142964 rval = -1;
9194c06356bSdh142964 break;
9204c06356bSdh142964 }
9214c06356bSdh142964 mutex_exit(&xp->statlock);
922c3bc407cSdh142964 pmcs_prt(ADDR2PMC(ap), PMCS_PRT_DEBUG3, NULL, NULL,
9234c06356bSdh142964 "%s: cap %s val %d set %d rval %d",
9244c06356bSdh142964 __func__, cap, val, set, rval);
9254c06356bSdh142964 return (rval);
9264c06356bSdh142964 }
9274c06356bSdh142964
9284c06356bSdh142964 /*
9294c06356bSdh142964 * Returns with statlock held if the xp is found.
9304c06356bSdh142964 * Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL.
9314c06356bSdh142964 */
9324c06356bSdh142964 static pmcs_xscsi_t *
pmcs_addr2xp(struct scsi_address * ap,uint64_t * lp,pmcs_cmd_t * sp)9334c06356bSdh142964 pmcs_addr2xp(struct scsi_address *ap, uint64_t *lp, pmcs_cmd_t *sp)
9344c06356bSdh142964 {
9354c06356bSdh142964 pmcs_xscsi_t *xp;
9364c06356bSdh142964 pmcs_lun_t *lun = (pmcs_lun_t *)
9374c06356bSdh142964 scsi_device_hba_private_get(scsi_address_device(ap));
9384c06356bSdh142964
9394c06356bSdh142964 if ((lun == NULL) || (lun->target == NULL)) {
9404c06356bSdh142964 return (NULL);
9414c06356bSdh142964 }
9424c06356bSdh142964 xp = lun->target;
9434c06356bSdh142964 mutex_enter(&xp->statlock);
9444c06356bSdh142964
945b18a19c2SJesse Butler if (xp->dev_gone || (xp->phy == NULL)) {
946601c90f1SSrikanth, Ramana /*
947601c90f1SSrikanth, Ramana * This may be a retried packet, so it's possible cmd_target
948601c90f1SSrikanth, Ramana * and cmd_lun may still be populated. Clear them.
949601c90f1SSrikanth, Ramana */
950601c90f1SSrikanth, Ramana if (sp != NULL) {
951601c90f1SSrikanth, Ramana sp->cmd_target = NULL;
952601c90f1SSrikanth, Ramana sp->cmd_lun = NULL;
953601c90f1SSrikanth, Ramana }
9544c06356bSdh142964 mutex_exit(&xp->statlock);
9554c06356bSdh142964 return (NULL);
9564c06356bSdh142964 }
9574c06356bSdh142964
9584c06356bSdh142964 if (sp != NULL) {
9594c06356bSdh142964 sp->cmd_target = xp;
9604c06356bSdh142964 sp->cmd_lun = lun;
9614c06356bSdh142964 }
9624c06356bSdh142964 if (lp) {
9634c06356bSdh142964 *lp = lun->lun_num;
9644c06356bSdh142964 }
9654c06356bSdh142964 return (xp);
9664c06356bSdh142964 }
9674c06356bSdh142964
9684c06356bSdh142964 static int
pmcs_scsa_getcap(struct scsi_address * ap,char * cap,int whom)9694c06356bSdh142964 pmcs_scsa_getcap(struct scsi_address *ap, char *cap, int whom)
9704c06356bSdh142964 {
9714c06356bSdh142964 int r;
9724c06356bSdh142964 if (cap == NULL) {
9734c06356bSdh142964 return (-1);
9744c06356bSdh142964 }
9754c06356bSdh142964 r = pmcs_cap(ap, cap, 0, whom, 0);
9764c06356bSdh142964 return (r);
9774c06356bSdh142964 }
9784c06356bSdh142964
9794c06356bSdh142964 static int
pmcs_scsa_setcap(struct scsi_address * ap,char * cap,int value,int whom)9804c06356bSdh142964 pmcs_scsa_setcap(struct scsi_address *ap, char *cap, int value, int whom)
9814c06356bSdh142964 {
9824c06356bSdh142964 int r;
9834c06356bSdh142964 if (cap == NULL) {
9844c06356bSdh142964 return (-1);
9854c06356bSdh142964 }
9864c06356bSdh142964 r = pmcs_cap(ap, cap, value, whom, 1);
9874c06356bSdh142964 return (r);
9884c06356bSdh142964 }
9894c06356bSdh142964
9904c06356bSdh142964 static int
pmcs_scsa_setup_pkt(struct scsi_pkt * pkt,int (* callback)(caddr_t),caddr_t cbarg)9914c06356bSdh142964 pmcs_scsa_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t),
9924c06356bSdh142964 caddr_t cbarg)
9934c06356bSdh142964 {
9944c06356bSdh142964 _NOTE(ARGUNUSED(callback, cbarg));
9954c06356bSdh142964 pmcs_cmd_t *sp = pkt->pkt_ha_private;
9964c06356bSdh142964
9974c06356bSdh142964 bzero(sp, sizeof (pmcs_cmd_t));
9984c06356bSdh142964 sp->cmd_pkt = pkt;
9994c06356bSdh142964 return (0);
10004c06356bSdh142964 }
10014c06356bSdh142964
10024c06356bSdh142964 static void
pmcs_scsa_teardown_pkt(struct scsi_pkt * pkt)10034c06356bSdh142964 pmcs_scsa_teardown_pkt(struct scsi_pkt *pkt)
10044c06356bSdh142964 {
10054c06356bSdh142964 pmcs_cmd_t *sp = pkt->pkt_ha_private;
10064c06356bSdh142964 sp->cmd_target = NULL;
10074c06356bSdh142964 sp->cmd_lun = NULL;
10084c06356bSdh142964 }
10094c06356bSdh142964
10104c06356bSdh142964 static int
pmcs_smp_start(struct smp_pkt * smp_pkt)101196c4a178SChris Horne pmcs_smp_start(struct smp_pkt *smp_pkt)
10124c06356bSdh142964 {
10134c06356bSdh142964 struct pmcwork *pwrk;
10143be32c0fSJesse Butler pmcs_iport_t *iport;
10154c06356bSdh142964 const uint_t rdoff = SAS_SMP_MAX_PAYLOAD;
10164c06356bSdh142964 uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status;
10174c06356bSdh142964 uint64_t wwn;
101896c4a178SChris Horne pmcs_hw_t *pwp;
10194c06356bSdh142964 pmcs_phy_t *pptr;
10204c06356bSdh142964 pmcs_xscsi_t *xp;
10214c06356bSdh142964 uint_t reqsz, rspsz, will_retry;
10224c06356bSdh142964 int result;
10234c06356bSdh142964
102496c4a178SChris Horne pwp = smp_pkt->smp_pkt_address->smp_a_hba_tran->smp_tran_hba_private;
102596c4a178SChris Horne bcopy(smp_pkt->smp_pkt_address->smp_a_wwn, &wwn, SAS_WWN_BYTE_SIZE);
10264c06356bSdh142964
1027c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
1028c3bc407cSdh142964 "%s: starting for wwn 0x%" PRIx64, __func__, wwn);
10294c06356bSdh142964
103096c4a178SChris Horne will_retry = smp_pkt->smp_pkt_will_retry;
10314c06356bSdh142964
10324c06356bSdh142964 (void) pmcs_acquire_scratch(pwp, B_TRUE);
103396c4a178SChris Horne reqsz = smp_pkt->smp_pkt_reqsize;
10344c06356bSdh142964 if (reqsz > SAS_SMP_MAX_PAYLOAD) {
10354c06356bSdh142964 reqsz = SAS_SMP_MAX_PAYLOAD;
10364c06356bSdh142964 }
103796c4a178SChris Horne (void) memcpy(pwp->scratch, smp_pkt->smp_pkt_req, reqsz);
10384c06356bSdh142964
103996c4a178SChris Horne rspsz = smp_pkt->smp_pkt_rspsize;
10404c06356bSdh142964 if (rspsz > SAS_SMP_MAX_PAYLOAD) {
10414c06356bSdh142964 rspsz = SAS_SMP_MAX_PAYLOAD;
10424c06356bSdh142964 }
10434c06356bSdh142964
10444c06356bSdh142964 /*
10454c06356bSdh142964 * The request size from the SMP driver always includes 4 bytes
10464c06356bSdh142964 * for the CRC. The PMCS chip, however, doesn't want to see those
10474c06356bSdh142964 * counts as part of the transfer size.
10484c06356bSdh142964 */
10494c06356bSdh142964 reqsz -= 4;
10504c06356bSdh142964
10514c06356bSdh142964 pptr = pmcs_find_phy_by_wwn(pwp, wwn);
10524c06356bSdh142964 /* PHY is now locked */
10534c06356bSdh142964 if (pptr == NULL || pptr->dtype != EXPANDER) {
10544c06356bSdh142964 if (pptr) {
10554c06356bSdh142964 pmcs_unlock_phy(pptr);
10564c06356bSdh142964 }
10574c06356bSdh142964 pmcs_release_scratch(pwp);
10589aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
1059c3bc407cSdh142964 "%s: could not find phy", __func__);
106096c4a178SChris Horne smp_pkt->smp_pkt_reason = ENXIO;
10614c06356bSdh142964 return (DDI_FAILURE);
10624c06356bSdh142964 }
10634c06356bSdh142964
10649aed1621SDavid Hollister if ((pptr->iport == NULL) || !pptr->valid_device_id) {
10659aed1621SDavid Hollister pmcs_unlock_phy(pptr);
10669aed1621SDavid Hollister pmcs_release_scratch(pwp);
10679aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
10689aed1621SDavid Hollister "%s: Can't reach PHY %s", __func__, pptr->path);
10699aed1621SDavid Hollister smp_pkt->smp_pkt_reason = ENXIO;
10709aed1621SDavid Hollister return (DDI_FAILURE);
10719aed1621SDavid Hollister }
10729aed1621SDavid Hollister
10734c06356bSdh142964 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
10744c06356bSdh142964 if (pwrk == NULL) {
10754c06356bSdh142964 pmcs_unlock_phy(pptr);
10764c06356bSdh142964 pmcs_release_scratch(pwp);
1077c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
10784c06356bSdh142964 "%s: could not get work structure", __func__);
107996c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EBUSY;
10804c06356bSdh142964 return (DDI_FAILURE);
10814c06356bSdh142964 }
10824c06356bSdh142964
10834c06356bSdh142964 pwrk->arg = msg;
10844c06356bSdh142964 pwrk->dtype = EXPANDER;
10854c06356bSdh142964 mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10864c06356bSdh142964 ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
10874c06356bSdh142964 if (ptr == NULL) {
10884c06356bSdh142964 pmcs_pwork(pwp, pwrk);
10894c06356bSdh142964 mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
10904c06356bSdh142964 pmcs_unlock_phy(pptr);
10914c06356bSdh142964 pmcs_release_scratch(pwp);
1092c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1093c3bc407cSdh142964 "%s: could not get IQ entry", __func__);
109496c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN :EBUSY;
10954c06356bSdh142964 return (DDI_FAILURE);
10964c06356bSdh142964 }
10974c06356bSdh142964 msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
10984c06356bSdh142964 msg[1] = LE_32(pwrk->htag);
10994c06356bSdh142964 msg[2] = LE_32(pptr->device_id);
11004c06356bSdh142964 msg[3] = LE_32(SMP_INDIRECT_RESPONSE | SMP_INDIRECT_REQUEST);
11014c06356bSdh142964 msg[8] = LE_32(DWORD0(pwp->scratch_dma));
11024c06356bSdh142964 msg[9] = LE_32(DWORD1(pwp->scratch_dma));
11034c06356bSdh142964 msg[10] = LE_32(reqsz);
11044c06356bSdh142964 msg[11] = 0;
11054c06356bSdh142964 msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
11064c06356bSdh142964 msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
11074c06356bSdh142964 msg[14] = LE_32(rspsz);
11084c06356bSdh142964 msg[15] = 0;
11094c06356bSdh142964
11104c06356bSdh142964 COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
11116745c559SJesse Butler
11123be32c0fSJesse Butler pmcs_hold_iport(pptr->iport);
11133be32c0fSJesse Butler iport = pptr->iport;
11143be32c0fSJesse Butler pmcs_smp_acquire(iport);
11154c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ONCHIP;
11164c06356bSdh142964 htag = pwrk->htag;
11174c06356bSdh142964 INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
11184c06356bSdh142964 pmcs_unlock_phy(pptr);
111996c4a178SChris Horne WAIT_FOR(pwrk, smp_pkt->smp_pkt_timeout * 1000, result);
11204c06356bSdh142964 pmcs_pwork(pwp, pwrk);
1121827ab345SJesse Butler pmcs_smp_release(iport);
1122827ab345SJesse Butler pmcs_rele_iport(iport);
1123601c90f1SSrikanth, Ramana pmcs_lock_phy(pptr);
11244c06356bSdh142964 if (result) {
11254c06356bSdh142964 pmcs_timed_out(pwp, htag, __func__);
11264c06356bSdh142964 if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
11279aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11284c06356bSdh142964 "%s: Unable to issue SMP ABORT for htag 0x%08x",
11294c06356bSdh142964 __func__, htag);
11304c06356bSdh142964 } else {
11319aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11324c06356bSdh142964 "%s: Issuing SMP ABORT for htag 0x%08x",
11334c06356bSdh142964 __func__, htag);
11344c06356bSdh142964 }
11354c06356bSdh142964 pmcs_unlock_phy(pptr);
11364c06356bSdh142964 pmcs_release_scratch(pwp);
113796c4a178SChris Horne smp_pkt->smp_pkt_reason = ETIMEDOUT;
11384c06356bSdh142964 return (DDI_FAILURE);
11394c06356bSdh142964 }
11404c06356bSdh142964 status = LE_32(msg[2]);
11414c06356bSdh142964 if (status == PMCOUT_STATUS_OVERFLOW) {
11424c06356bSdh142964 status = PMCOUT_STATUS_OK;
114396c4a178SChris Horne smp_pkt->smp_pkt_reason = EOVERFLOW;
11444c06356bSdh142964 }
11454c06356bSdh142964 if (status != PMCOUT_STATUS_OK) {
11464c06356bSdh142964 const char *emsg = pmcs_status_str(status);
11474c06356bSdh142964 if (emsg == NULL) {
11489aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11494c06356bSdh142964 "SMP operation failed (0x%x)", status);
11504c06356bSdh142964 } else {
11519aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
11524c06356bSdh142964 "SMP operation failed (%s)", emsg);
11534c06356bSdh142964 }
11544c06356bSdh142964
11554c06356bSdh142964 if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) ||
11564c06356bSdh142964 (status == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT)) {
115796c4a178SChris Horne smp_pkt->smp_pkt_reason =
115896c4a178SChris Horne will_retry ? EAGAIN : ETIMEDOUT;
11594c06356bSdh142964 result = DDI_FAILURE;
11604c06356bSdh142964 } else if (status ==
11614c06356bSdh142964 PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
11624c06356bSdh142964 xp = pptr->target;
11634c06356bSdh142964 if (xp == NULL) {
116496c4a178SChris Horne smp_pkt->smp_pkt_reason = EIO;
11654c06356bSdh142964 result = DDI_FAILURE;
11664c06356bSdh142964 goto out;
11674c06356bSdh142964 }
11684c06356bSdh142964 if (xp->dev_state !=
11694c06356bSdh142964 PMCS_DEVICE_STATE_NON_OPERATIONAL) {
11704c06356bSdh142964 xp->dev_state =
11714c06356bSdh142964 PMCS_DEVICE_STATE_NON_OPERATIONAL;
11729aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, xp->phy,
11739aed1621SDavid Hollister xp, "%s: Got _IT_NEXUS_LOSS SMP status. "
11744c06356bSdh142964 "Tgt(0x%p) dev_state set to "
11754c06356bSdh142964 "_NON_OPERATIONAL", __func__,
11764c06356bSdh142964 (void *)xp);
11774c06356bSdh142964 }
11784c06356bSdh142964 /* ABORT any pending commands related to this device */
11794c06356bSdh142964 if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) != 0) {
11804c06356bSdh142964 pptr->abort_pending = 1;
118196c4a178SChris Horne smp_pkt->smp_pkt_reason = EIO;
11824c06356bSdh142964 result = DDI_FAILURE;
11834c06356bSdh142964 }
11844c06356bSdh142964 } else {
118596c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EIO;
11864c06356bSdh142964 result = DDI_FAILURE;
11874c06356bSdh142964 }
11884c06356bSdh142964 } else {
118996c4a178SChris Horne (void) memcpy(smp_pkt->smp_pkt_rsp,
11904c06356bSdh142964 &((uint8_t *)pwp->scratch)[rdoff], rspsz);
119196c4a178SChris Horne if (smp_pkt->smp_pkt_reason == EOVERFLOW) {
11924c06356bSdh142964 result = DDI_FAILURE;
11934c06356bSdh142964 } else {
11944c06356bSdh142964 result = DDI_SUCCESS;
11954c06356bSdh142964 }
11964c06356bSdh142964 }
11974c06356bSdh142964 out:
11989aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, pptr->target,
11999aed1621SDavid Hollister "%s: done for wwn 0x%" PRIx64, __func__, wwn);
12009aed1621SDavid Hollister
12014c06356bSdh142964 pmcs_unlock_phy(pptr);
12024c06356bSdh142964 pmcs_release_scratch(pwp);
12034c06356bSdh142964 return (result);
12044c06356bSdh142964 }
12054c06356bSdh142964
12064c06356bSdh142964 static int
pmcs_smp_init(dev_info_t * self,dev_info_t * child,smp_hba_tran_t * tran,smp_device_t * smp_sd)12074c06356bSdh142964 pmcs_smp_init(dev_info_t *self, dev_info_t *child,
120896c4a178SChris Horne smp_hba_tran_t *tran, smp_device_t *smp_sd)
12094c06356bSdh142964 {
121096c4a178SChris Horne _NOTE(ARGUNUSED(tran, smp_sd));
12114c06356bSdh142964 pmcs_iport_t *iport;
12124c06356bSdh142964 pmcs_hw_t *pwp;
12134c06356bSdh142964 pmcs_xscsi_t *tgt;
12144c06356bSdh142964 pmcs_phy_t *phy, *pphy;
12154c06356bSdh142964 uint64_t wwn;
12164c06356bSdh142964 char *addr, *tgt_port;
12174c06356bSdh142964 int ua_form = 1;
12184c06356bSdh142964
12194c06356bSdh142964 iport = ddi_get_soft_state(pmcs_iport_softstate,
12204c06356bSdh142964 ddi_get_instance(self));
12214c06356bSdh142964 ASSERT(iport);
12224c06356bSdh142964 if (iport == NULL)
12234c06356bSdh142964 return (DDI_FAILURE);
12244c06356bSdh142964 pwp = iport->pwp;
12254c06356bSdh142964 ASSERT(pwp);
12264c06356bSdh142964 if (pwp == NULL)
12274c06356bSdh142964 return (DDI_FAILURE);
12284c06356bSdh142964
12294c06356bSdh142964 /* Get "target-port" prop from devinfo node */
12304c06356bSdh142964 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
12314c06356bSdh142964 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
12324c06356bSdh142964 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
1233c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
1234c3bc407cSdh142964 "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
12354c06356bSdh142964 /* Dont fail _smp_init() because we couldnt get/set a prop */
12364c06356bSdh142964 return (DDI_SUCCESS);
12374c06356bSdh142964 }
12384c06356bSdh142964
12394c06356bSdh142964 /*
12404c06356bSdh142964 * Validate that this tran_tgt_init is for an active iport.
12414c06356bSdh142964 */
12424c06356bSdh142964 if (iport->ua_state == UA_INACTIVE) {
1243c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1244c3bc407cSdh142964 "%s: Init on inactive iport for '%s'", __func__, tgt_port);
12454c06356bSdh142964 ddi_prop_free(tgt_port);
12464c06356bSdh142964 return (DDI_FAILURE);
12474c06356bSdh142964 }
12484c06356bSdh142964
12494c06356bSdh142964 mutex_enter(&pwp->lock);
12504c06356bSdh142964
12514c06356bSdh142964 /* Retrieve softstate using unit-address */
12525c45adf0SJesse Butler tgt = pmcs_get_target(iport, tgt_port, B_TRUE);
12534c06356bSdh142964 if (tgt == NULL) {
1254c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1255c3bc407cSdh142964 "%s: tgt softstate not found", __func__);
12564c06356bSdh142964 ddi_prop_free(tgt_port);
12574c06356bSdh142964 mutex_exit(&pwp->lock);
12584c06356bSdh142964 return (DDI_FAILURE);
12594c06356bSdh142964 }
12604c06356bSdh142964
126173a3eccdSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)",
126273a3eccdSDavid Hollister __func__, ddi_get_name(child), tgt_port);
126373a3eccdSDavid Hollister
126473a3eccdSDavid Hollister mutex_enter(&tgt->statlock);
12654c06356bSdh142964 phy = tgt->phy;
12664c06356bSdh142964 ASSERT(mutex_owned(&phy->phy_lock));
12674c06356bSdh142964
12684c06356bSdh142964 if (IS_ROOT_PHY(phy)) {
12694c06356bSdh142964 /* Expander attached to HBA - don't ref_count it */
12704c06356bSdh142964 wwn = pwp->sas_wwns[0];
12714c06356bSdh142964 } else {
12724c06356bSdh142964 pmcs_inc_phy_ref_count(phy);
12734c06356bSdh142964
12744c06356bSdh142964 /*
12754c06356bSdh142964 * Parent (in topology) is also an expander
12764c06356bSdh142964 * Now that we've increased the ref count on phy, it's OK
12774c06356bSdh142964 * to drop the lock so we can acquire the parent's lock.
12784c06356bSdh142964 */
12794c06356bSdh142964 pphy = phy->parent;
128073a3eccdSDavid Hollister mutex_exit(&tgt->statlock);
12814c06356bSdh142964 pmcs_unlock_phy(phy);
12824c06356bSdh142964 pmcs_lock_phy(pphy);
12834c06356bSdh142964 wwn = pmcs_barray2wwn(pphy->sas_address);
12844c06356bSdh142964 pmcs_unlock_phy(pphy);
12854c06356bSdh142964 pmcs_lock_phy(phy);
128673a3eccdSDavid Hollister mutex_enter(&tgt->statlock);
12874c06356bSdh142964 }
12884c06356bSdh142964
12894c06356bSdh142964 /*
12904c06356bSdh142964 * If this is the 1st smp_init, add this to our list.
12914c06356bSdh142964 */
12924c06356bSdh142964 if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
12934c06356bSdh142964 int target;
12944c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) {
12954c06356bSdh142964 if (pwp->targets[target] != NULL) {
12964c06356bSdh142964 continue;
12974c06356bSdh142964 }
12984c06356bSdh142964
12994c06356bSdh142964 pwp->targets[target] = tgt;
13004c06356bSdh142964 tgt->target_num = (uint16_t)target;
13014c06356bSdh142964 tgt->assigned = 1;
13024c06356bSdh142964 tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
13034c06356bSdh142964 break;
13044c06356bSdh142964 }
13054c06356bSdh142964
13064c06356bSdh142964 if (target == pwp->max_dev) {
1307c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
13084c06356bSdh142964 "Target list full.");
13094c06356bSdh142964 goto smp_init_fail;
13104c06356bSdh142964 }
13114c06356bSdh142964 }
13124c06356bSdh142964
13134c06356bSdh142964 if (!pmcs_assign_device(pwp, tgt)) {
13144c06356bSdh142964 pwp->targets[tgt->target_num] = NULL;
1315c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
13164c06356bSdh142964 "%s: pmcs_assign_device failed for target 0x%p",
13174c06356bSdh142964 __func__, (void *)tgt);
13184c06356bSdh142964 goto smp_init_fail;
13194c06356bSdh142964 }
13204c06356bSdh142964
1321499cfd15SDavid Hollister /*
1322499cfd15SDavid Hollister * Update the attached port and target port pm properties
1323499cfd15SDavid Hollister */
1324499cfd15SDavid Hollister tgt->smpd = smp_sd;
1325499cfd15SDavid Hollister
13264c06356bSdh142964 pmcs_unlock_phy(phy);
13274c06356bSdh142964 mutex_exit(&pwp->lock);
13284c06356bSdh142964
13294c06356bSdh142964 tgt->ref_count++;
13304c06356bSdh142964 tgt->dtype = phy->dtype;
133173a3eccdSDavid Hollister mutex_exit(&tgt->statlock);
133273a3eccdSDavid Hollister
133373a3eccdSDavid Hollister pmcs_update_phy_pm_props(phy, 0, 0, B_TRUE);
13344c06356bSdh142964
13354c06356bSdh142964 addr = scsi_wwn_to_wwnstr(wwn, ua_form, NULL);
1336499cfd15SDavid Hollister if (smp_device_prop_update_string(smp_sd, SCSI_ADDR_PROP_ATTACHED_PORT,
1337499cfd15SDavid Hollister addr) != DDI_SUCCESS) {
1338c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to set "
1339c3bc407cSdh142964 "prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", __func__);
13404c06356bSdh142964 }
13414c06356bSdh142964 (void) scsi_free_wwnstr(addr);
13424c06356bSdh142964 ddi_prop_free(tgt_port);
13434c06356bSdh142964 return (DDI_SUCCESS);
13444c06356bSdh142964
13454c06356bSdh142964 smp_init_fail:
13464c06356bSdh142964 tgt->phy = NULL;
13474c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM;
13484c06356bSdh142964 phy->target = NULL;
13494c06356bSdh142964 if (!IS_ROOT_PHY(phy)) {
13504c06356bSdh142964 pmcs_dec_phy_ref_count(phy);
13514c06356bSdh142964 }
135273a3eccdSDavid Hollister mutex_exit(&tgt->statlock);
13534c06356bSdh142964 pmcs_unlock_phy(phy);
13544c06356bSdh142964 mutex_exit(&pwp->lock);
13554c06356bSdh142964 ddi_soft_state_bystr_free(iport->tgt_sstate, tgt->unit_address);
13564c06356bSdh142964 ddi_prop_free(tgt_port);
13574c06356bSdh142964 return (DDI_FAILURE);
13584c06356bSdh142964 }
13594c06356bSdh142964
13604c06356bSdh142964 static void
pmcs_smp_free(dev_info_t * self,dev_info_t * child,smp_hba_tran_t * tran,smp_device_t * smp)13614c06356bSdh142964 pmcs_smp_free(dev_info_t *self, dev_info_t *child,
136296c4a178SChris Horne smp_hba_tran_t *tran, smp_device_t *smp)
13634c06356bSdh142964 {
13644c06356bSdh142964 _NOTE(ARGUNUSED(tran, smp));
13654c06356bSdh142964 pmcs_iport_t *iport;
13664c06356bSdh142964 pmcs_hw_t *pwp;
13674c06356bSdh142964 pmcs_xscsi_t *tgt;
1368ee13933aSSrikanth Suravajhala pmcs_phy_t *phyp;
13694c06356bSdh142964 char *tgt_port;
13704c06356bSdh142964
13714c06356bSdh142964 iport = ddi_get_soft_state(pmcs_iport_softstate,
13724c06356bSdh142964 ddi_get_instance(self));
13734c06356bSdh142964 ASSERT(iport);
13744c06356bSdh142964 if (iport == NULL)
13754c06356bSdh142964 return;
13764c06356bSdh142964
13774c06356bSdh142964 pwp = iport->pwp;
13784c06356bSdh142964 if (pwp == NULL)
13794c06356bSdh142964 return;
13804c06356bSdh142964 ASSERT(pwp);
13814c06356bSdh142964
13824c06356bSdh142964 /* Get "target-port" prop from devinfo node */
13834c06356bSdh142964 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
13844c06356bSdh142964 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
13854c06356bSdh142964 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
1386c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
1387c3bc407cSdh142964 "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
13884c06356bSdh142964 return;
13894c06356bSdh142964 }
13909aed1621SDavid Hollister
13914c06356bSdh142964 /* Retrieve softstate using unit-address */
13929aed1621SDavid Hollister mutex_enter(&pwp->lock);
13934c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
13949aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)", __func__,
13959aed1621SDavid Hollister ddi_get_name(child), tgt_port);
13964c06356bSdh142964 ddi_prop_free(tgt_port);
13974c06356bSdh142964
13984c06356bSdh142964 if (tgt == NULL) {
1399c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
1400c3bc407cSdh142964 "%s: tgt softstate not found", __func__);
14019aed1621SDavid Hollister mutex_exit(&pwp->lock);
14024c06356bSdh142964 return;
14034c06356bSdh142964 }
14044c06356bSdh142964
1405ee13933aSSrikanth Suravajhala phyp = tgt->phy;
1406ee13933aSSrikanth Suravajhala if (phyp) {
1407ee13933aSSrikanth Suravajhala mutex_enter(&phyp->phy_lock);
1408ee13933aSSrikanth Suravajhala if (!IS_ROOT_PHY(phyp)) {
1409ee13933aSSrikanth Suravajhala pmcs_dec_phy_ref_count(phyp);
1410ee13933aSSrikanth Suravajhala }
1411ee13933aSSrikanth Suravajhala }
14124c06356bSdh142964 mutex_enter(&tgt->statlock);
14134c06356bSdh142964
14144c06356bSdh142964 if (--tgt->ref_count == 0) {
14154c06356bSdh142964 /*
14164c06356bSdh142964 * Remove this target from our list. The softstate
14174c06356bSdh142964 * will remain, and the device will remain registered
14184c06356bSdh142964 * with the hardware unless/until we're told that the
14194c06356bSdh142964 * device physically went away.
14204c06356bSdh142964 */
1421c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
14224c06356bSdh142964 "Removing target 0x%p (vtgt %d) from target list",
14234c06356bSdh142964 (void *)tgt, tgt->target_num);
14244c06356bSdh142964 pwp->targets[tgt->target_num] = NULL;
14254c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM;
14266c87a171SJesse Butler /* If the PHY has a pointer to this target, clear it */
14276c87a171SJesse Butler if (phyp && (phyp->target == tgt)) {
1428ee13933aSSrikanth Suravajhala phyp->target = NULL;
14295c45adf0SJesse Butler }
1430ee13933aSSrikanth Suravajhala tgt->phy = NULL;
14319aed1621SDavid Hollister pmcs_destroy_target(tgt);
14329aed1621SDavid Hollister } else {
14339aed1621SDavid Hollister mutex_exit(&tgt->statlock);
14344c06356bSdh142964 }
14354c06356bSdh142964
1436ee13933aSSrikanth Suravajhala if (phyp) {
1437ee13933aSSrikanth Suravajhala mutex_exit(&phyp->phy_lock);
1438ee13933aSSrikanth Suravajhala }
14394c06356bSdh142964 mutex_exit(&pwp->lock);
14404c06356bSdh142964 }
14414c06356bSdh142964
14424c06356bSdh142964 static int
pmcs_scsi_quiesce(dev_info_t * dip)14434c06356bSdh142964 pmcs_scsi_quiesce(dev_info_t *dip)
14444c06356bSdh142964 {
14454c06356bSdh142964 pmcs_hw_t *pwp;
14464c06356bSdh142964 int totactive = -1;
14474c06356bSdh142964 pmcs_xscsi_t *xp;
14484c06356bSdh142964 uint16_t target;
14494c06356bSdh142964
14504c06356bSdh142964 if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
14514c06356bSdh142964 return (0); /* iport */
14524c06356bSdh142964
14534c06356bSdh142964 pwp = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
14544c06356bSdh142964 if (pwp == NULL) {
14554c06356bSdh142964 return (-1);
14564c06356bSdh142964 }
14574c06356bSdh142964 mutex_enter(&pwp->lock);
14584c06356bSdh142964 if (pwp->state != STATE_RUNNING) {
14594c06356bSdh142964 mutex_exit(&pwp->lock);
14604c06356bSdh142964 return (-1);
14614c06356bSdh142964 }
14624c06356bSdh142964
1463c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
14645c45adf0SJesse Butler pwp->quiesced = pwp->blocked = 1;
14654c06356bSdh142964 while (totactive) {
14664c06356bSdh142964 totactive = 0;
14674c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) {
14684c06356bSdh142964 xp = pwp->targets[target];
14694c06356bSdh142964 if (xp == NULL) {
14704c06356bSdh142964 continue;
14714c06356bSdh142964 }
14724c06356bSdh142964 mutex_enter(&xp->statlock);
14734c06356bSdh142964 if (xp->actv_cnt) {
14744c06356bSdh142964 totactive += xp->actv_cnt;
14754c06356bSdh142964 xp->draining = 1;
14764c06356bSdh142964 }
14774c06356bSdh142964 mutex_exit(&xp->statlock);
14784c06356bSdh142964 }
14794c06356bSdh142964 if (totactive) {
14804c06356bSdh142964 cv_wait(&pwp->drain_cv, &pwp->lock);
14814c06356bSdh142964 }
14824c06356bSdh142964 /*
14834c06356bSdh142964 * The pwp->blocked may have been reset. e.g a SCSI bus reset
14844c06356bSdh142964 */
14854c06356bSdh142964 pwp->blocked = 1;
14864c06356bSdh142964 }
14874c06356bSdh142964
14884c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) {
14894c06356bSdh142964 xp = pwp->targets[target];
14904c06356bSdh142964 if (xp == NULL) {
14914c06356bSdh142964 continue;
14924c06356bSdh142964 }
14934c06356bSdh142964 mutex_enter(&xp->statlock);
14944c06356bSdh142964 xp->draining = 0;
14954c06356bSdh142964 mutex_exit(&xp->statlock);
14964c06356bSdh142964 }
14974c06356bSdh142964
14984c06356bSdh142964 mutex_exit(&pwp->lock);
14994c06356bSdh142964 if (totactive == 0) {
1500c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
1501c3bc407cSdh142964 "%s drain complete", __func__);
15024c06356bSdh142964 }
15034c06356bSdh142964 return (0);
15044c06356bSdh142964 }
15054c06356bSdh142964
15064c06356bSdh142964 static int
pmcs_scsi_unquiesce(dev_info_t * dip)15074c06356bSdh142964 pmcs_scsi_unquiesce(dev_info_t *dip)
15084c06356bSdh142964 {
15094c06356bSdh142964 pmcs_hw_t *pwp;
15104c06356bSdh142964
15114c06356bSdh142964 if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
15124c06356bSdh142964 return (0); /* iport */
15134c06356bSdh142964
15144c06356bSdh142964 pwp = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
15154c06356bSdh142964 if (pwp == NULL) {
15164c06356bSdh142964 return (-1);
15174c06356bSdh142964 }
15184c06356bSdh142964 mutex_enter(&pwp->lock);
15194c06356bSdh142964 if (pwp->state != STATE_RUNNING) {
15204c06356bSdh142964 mutex_exit(&pwp->lock);
15214c06356bSdh142964 return (-1);
15224c06356bSdh142964 }
1523c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
15245c45adf0SJesse Butler pwp->blocked = pwp->quiesced = 0;
15254c06356bSdh142964 mutex_exit(&pwp->lock);
15264c06356bSdh142964
15274c06356bSdh142964 /*
15284c06356bSdh142964 * Run all pending commands.
15294c06356bSdh142964 */
15304c06356bSdh142964 pmcs_scsa_wq_run(pwp);
15314c06356bSdh142964
15324c06356bSdh142964 /*
15334c06356bSdh142964 * Complete all completed commands.
15344c06356bSdh142964 * This also unlocks us.
15354c06356bSdh142964 */
15364c06356bSdh142964 PMCS_CQ_RUN(pwp);
15374c06356bSdh142964 return (0);
15384c06356bSdh142964 }
15394c06356bSdh142964
15404c06356bSdh142964 /*
15414c06356bSdh142964 * Start commands for a particular device
15424c06356bSdh142964 * If the actual start of a command fails, return B_FALSE. Any other result
15434c06356bSdh142964 * is a B_TRUE return.
15444c06356bSdh142964 */
15454c06356bSdh142964 boolean_t
pmcs_scsa_wq_run_one(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)15464c06356bSdh142964 pmcs_scsa_wq_run_one(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
15474c06356bSdh142964 {
15484c06356bSdh142964 pmcs_cmd_t *sp;
15494c06356bSdh142964 pmcs_phy_t *phyp;
15504c06356bSdh142964 pmcwork_t *pwrk;
15514c06356bSdh142964 boolean_t run_one, blocked;
15524c06356bSdh142964 int rval;
15534c06356bSdh142964
15544c06356bSdh142964 /*
15554c06356bSdh142964 * First, check to see if we're blocked or resource limited
15564c06356bSdh142964 */
15574c06356bSdh142964 mutex_enter(&pwp->lock);
15584c06356bSdh142964 blocked = pwp->blocked;
15594c06356bSdh142964 /*
15604c06356bSdh142964 * If resource_limited is set, we're resource constrained and
15614c06356bSdh142964 * we will run only one work request for this target.
15624c06356bSdh142964 */
15634c06356bSdh142964 run_one = pwp->resource_limited;
15644c06356bSdh142964 mutex_exit(&pwp->lock);
15654c06356bSdh142964
15664c06356bSdh142964 if (blocked) {
15674c06356bSdh142964 /* Queues will get restarted when we get unblocked */
15684c06356bSdh142964 return (B_TRUE);
15694c06356bSdh142964 }
15704c06356bSdh142964
15714c06356bSdh142964 /*
15724c06356bSdh142964 * Might as well verify the queue is not empty before moving on
15734c06356bSdh142964 */
15744c06356bSdh142964 mutex_enter(&xp->wqlock);
15754c06356bSdh142964 if (STAILQ_EMPTY(&xp->wq)) {
15764c06356bSdh142964 mutex_exit(&xp->wqlock);
15774c06356bSdh142964 return (B_TRUE);
15784c06356bSdh142964 }
15794c06356bSdh142964 mutex_exit(&xp->wqlock);
15804c06356bSdh142964
15814c06356bSdh142964 /*
15824c06356bSdh142964 * If we're draining or resetting, just reschedule work queue and bail.
15834c06356bSdh142964 */
15844c06356bSdh142964 mutex_enter(&xp->statlock);
15854c06356bSdh142964 if (xp->draining || xp->resetting || xp->special_running ||
15864c06356bSdh142964 xp->special_needed) {
15874c06356bSdh142964 mutex_exit(&xp->statlock);
15884c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
15894c06356bSdh142964 return (B_TRUE);
15904c06356bSdh142964 }
15914c06356bSdh142964
15924c06356bSdh142964 /*
1593b18a19c2SJesse Butler * Next, check to see if the target is gone.
15944c06356bSdh142964 */
1595b18a19c2SJesse Butler if (xp->dev_gone) {
1596c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
1597b18a19c2SJesse Butler "%s: Flushing wait queue for dead tgt 0x%p", __func__,
15984c06356bSdh142964 (void *)xp);
15994c06356bSdh142964 pmcs_flush_target_queues(pwp, xp, PMCS_TGT_WAIT_QUEUE);
16004c06356bSdh142964 mutex_exit(&xp->statlock);
16014c06356bSdh142964 return (B_TRUE);
16024c06356bSdh142964 }
16034c06356bSdh142964
16044c06356bSdh142964 /*
16054c06356bSdh142964 * Increment the PHY's ref_count now so we know it won't go away
16064c06356bSdh142964 * after we drop the target lock. Drop it before returning. If the
16074c06356bSdh142964 * PHY dies, the commands we attempt to send will fail, but at least
16084c06356bSdh142964 * we know we have a real PHY pointer.
16094c06356bSdh142964 */
16104c06356bSdh142964 phyp = xp->phy;
16114c06356bSdh142964 pmcs_inc_phy_ref_count(phyp);
16124c06356bSdh142964 mutex_exit(&xp->statlock);
16134c06356bSdh142964
16144c06356bSdh142964 mutex_enter(&xp->wqlock);
16154c06356bSdh142964 while ((sp = STAILQ_FIRST(&xp->wq)) != NULL) {
16164c06356bSdh142964 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_CBACK, phyp);
16174c06356bSdh142964 if (pwrk == NULL) {
1618c280a92bSDavid Hollister mutex_exit(&xp->wqlock);
1619c280a92bSDavid Hollister mutex_enter(&pwp->lock);
1620c280a92bSDavid Hollister if (pwp->resource_limited == 0) {
1621c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
16224c06356bSdh142964 "%s: out of work structures", __func__);
1623c280a92bSDavid Hollister }
1624c280a92bSDavid Hollister pwp->resource_limited = 1;
16254c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
1626c280a92bSDavid Hollister mutex_exit(&pwp->lock);
1627c280a92bSDavid Hollister return (B_FALSE);
16284c06356bSdh142964 }
16294c06356bSdh142964 STAILQ_REMOVE_HEAD(&xp->wq, cmd_next);
16304c06356bSdh142964 mutex_exit(&xp->wqlock);
16314c06356bSdh142964
16324c06356bSdh142964 pwrk->xp = xp;
16334c06356bSdh142964 pwrk->arg = sp;
1634b53bcbf6SSrikanth Suravajhala pwrk->timer = 0;
16354c06356bSdh142964 sp->cmd_tag = pwrk->htag;
16364c06356bSdh142964
16374c06356bSdh142964 pwrk->dtype = xp->dtype;
16384c06356bSdh142964
16394c06356bSdh142964 if (xp->dtype == SAS) {
16404c06356bSdh142964 pwrk->ptr = (void *) pmcs_SAS_done;
16414c06356bSdh142964 if ((rval = pmcs_SAS_run(sp, pwrk)) != 0) {
16425a7797ceSJesse Butler if (rval != PMCS_WQ_RUN_FAIL_RES_CMP) {
1643*b46556d0SToomas Soome sp->cmd_tag = 0;
16445a7797ceSJesse Butler }
16454c06356bSdh142964 pmcs_dec_phy_ref_count(phyp);
16464c06356bSdh142964 pmcs_pwork(pwp, pwrk);
16474c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
16484c06356bSdh142964 if (rval == PMCS_WQ_RUN_FAIL_RES) {
16494c06356bSdh142964 return (B_FALSE);
16504c06356bSdh142964 } else {
16514c06356bSdh142964 return (B_TRUE);
16524c06356bSdh142964 }
16534c06356bSdh142964 }
16544c06356bSdh142964 } else {
16554c06356bSdh142964 ASSERT(xp->dtype == SATA);
16564c06356bSdh142964 pwrk->ptr = (void *) pmcs_SATA_done;
16574c06356bSdh142964 if ((rval = pmcs_SATA_run(sp, pwrk)) != 0) {
1658*b46556d0SToomas Soome sp->cmd_tag = 0;
16594c06356bSdh142964 pmcs_dec_phy_ref_count(phyp);
16604c06356bSdh142964 pmcs_pwork(pwp, pwrk);
16614c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
16624c06356bSdh142964 if (rval == PMCS_WQ_RUN_FAIL_RES) {
16634c06356bSdh142964 return (B_FALSE);
16644c06356bSdh142964 } else {
16654c06356bSdh142964 return (B_TRUE);
16664c06356bSdh142964 }
16674c06356bSdh142964 }
16684c06356bSdh142964 }
16694c06356bSdh142964
16704c06356bSdh142964 if (run_one) {
16714c06356bSdh142964 goto wq_out;
16724c06356bSdh142964 }
16734c06356bSdh142964 mutex_enter(&xp->wqlock);
16744c06356bSdh142964 }
16754c06356bSdh142964
16764c06356bSdh142964 mutex_exit(&xp->wqlock);
16774c06356bSdh142964
16784c06356bSdh142964 wq_out:
16794c06356bSdh142964 pmcs_dec_phy_ref_count(phyp);
16804c06356bSdh142964 return (B_TRUE);
16814c06356bSdh142964 }
16824c06356bSdh142964
16834c06356bSdh142964 /*
16844c06356bSdh142964 * Start commands for all devices.
16854c06356bSdh142964 */
16864c06356bSdh142964 void
pmcs_scsa_wq_run(pmcs_hw_t * pwp)16874c06356bSdh142964 pmcs_scsa_wq_run(pmcs_hw_t *pwp)
16884c06356bSdh142964 {
16894c06356bSdh142964 pmcs_xscsi_t *xp;
16904c06356bSdh142964 uint16_t target_start, target;
16914c06356bSdh142964 boolean_t rval = B_TRUE;
16924c06356bSdh142964
16934c06356bSdh142964 mutex_enter(&pwp->lock);
16944c06356bSdh142964 target_start = pwp->last_wq_dev;
16954c06356bSdh142964 target = target_start;
16964c06356bSdh142964
16974c06356bSdh142964 do {
16984c06356bSdh142964 xp = pwp->targets[target];
1699429adc13SSrikanth, Ramana if ((xp == NULL) || (STAILQ_EMPTY(&xp->wq))) {
17004c06356bSdh142964 if (++target == pwp->max_dev) {
17014c06356bSdh142964 target = 0;
17024c06356bSdh142964 }
17034c06356bSdh142964 continue;
17044c06356bSdh142964 }
17054c06356bSdh142964
17064c06356bSdh142964 mutex_exit(&pwp->lock);
17074c06356bSdh142964 rval = pmcs_scsa_wq_run_one(pwp, xp);
17084c06356bSdh142964 mutex_enter(&pwp->lock);
1709429adc13SSrikanth, Ramana
1710429adc13SSrikanth, Ramana if (rval == B_FALSE) {
17114c06356bSdh142964 break;
17124c06356bSdh142964 }
1713429adc13SSrikanth, Ramana
17144c06356bSdh142964 if (++target == pwp->max_dev) {
17154c06356bSdh142964 target = 0;
17164c06356bSdh142964 }
17174c06356bSdh142964 } while (target != target_start);
17184c06356bSdh142964
17194c06356bSdh142964 if (rval) {
1720c280a92bSDavid Hollister /*
1721c280a92bSDavid Hollister * If we were resource limited, but apparently are not now,
1722c280a92bSDavid Hollister * reschedule the work queues anyway.
1723c280a92bSDavid Hollister */
1724c280a92bSDavid Hollister if (pwp->resource_limited) {
1725c280a92bSDavid Hollister SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
1726c280a92bSDavid Hollister }
17274c06356bSdh142964 pwp->resource_limited = 0; /* Not resource-constrained */
17284c06356bSdh142964 } else {
1729c280a92bSDavid Hollister /*
1730c280a92bSDavid Hollister * Give everybody a chance, and reschedule to run the queues
1731c280a92bSDavid Hollister * again as long as we're limited.
1732c280a92bSDavid Hollister */
1733c280a92bSDavid Hollister pwp->resource_limited = 1;
1734c280a92bSDavid Hollister SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
17354c06356bSdh142964 }
17364c06356bSdh142964
17374c06356bSdh142964 pwp->last_wq_dev = target;
17384c06356bSdh142964 mutex_exit(&pwp->lock);
17394c06356bSdh142964 }
17404c06356bSdh142964
17414c06356bSdh142964 /*
17424c06356bSdh142964 * Pull the completion queue, drop the lock and complete all elements.
17434c06356bSdh142964 */
17444c06356bSdh142964
17454c06356bSdh142964 void
pmcs_scsa_cq_run(void * arg)17464c06356bSdh142964 pmcs_scsa_cq_run(void *arg)
17474c06356bSdh142964 {
17484c06356bSdh142964 pmcs_cq_thr_info_t *cqti = (pmcs_cq_thr_info_t *)arg;
17494c06356bSdh142964 pmcs_hw_t *pwp = cqti->cq_pwp;
17504c06356bSdh142964 pmcs_cmd_t *sp, *nxt;
17514c06356bSdh142964 struct scsi_pkt *pkt;
1752601c90f1SSrikanth, Ramana pmcs_xscsi_t *tgt;
17534c06356bSdh142964 pmcs_iocomp_cb_t *ioccb, *ioccb_next;
17544c06356bSdh142964 pmcs_cb_t callback;
17554c06356bSdh142964
17564c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__start, pmcs_cq_thr_info_t *, cqti);
17574c06356bSdh142964
17584c06356bSdh142964 mutex_enter(&pwp->cq_lock);
17594c06356bSdh142964
17604c06356bSdh142964 while (!pwp->cq_info.cq_stop) {
17614c06356bSdh142964 /*
17624c06356bSdh142964 * First, check the I/O completion callback queue.
17634c06356bSdh142964 */
17644c06356bSdh142964 ioccb = pwp->iocomp_cb_head;
17654c06356bSdh142964 pwp->iocomp_cb_head = NULL;
17664c06356bSdh142964 pwp->iocomp_cb_tail = NULL;
17674c06356bSdh142964 mutex_exit(&pwp->cq_lock);
17684c06356bSdh142964
17694c06356bSdh142964 while (ioccb) {
17704c06356bSdh142964 /*
17714c06356bSdh142964 * Grab the lock on the work structure. The callback
17724c06356bSdh142964 * routine is responsible for clearing it.
17734c06356bSdh142964 */
17744c06356bSdh142964 mutex_enter(&ioccb->pwrk->lock);
17754c06356bSdh142964 ioccb_next = ioccb->next;
17764c06356bSdh142964 callback = (pmcs_cb_t)ioccb->pwrk->ptr;
17774c06356bSdh142964 (*callback)(pwp, ioccb->pwrk,
17784c06356bSdh142964 (uint32_t *)((void *)ioccb->iomb));
17794c06356bSdh142964 kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
17804c06356bSdh142964 ioccb = ioccb_next;
17814c06356bSdh142964 }
17824c06356bSdh142964
17834c06356bSdh142964 /*
17844c06356bSdh142964 * Next, run the completion queue
17854c06356bSdh142964 */
17864c06356bSdh142964 mutex_enter(&pwp->cq_lock);
17874c06356bSdh142964 sp = STAILQ_FIRST(&pwp->cq);
17884c06356bSdh142964 STAILQ_INIT(&pwp->cq);
17894c06356bSdh142964 mutex_exit(&pwp->cq_lock);
17904c06356bSdh142964
17914c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__start__loop,
17924c06356bSdh142964 pmcs_cq_thr_info_t *, cqti);
17934c06356bSdh142964
17944c06356bSdh142964 if (sp && pmcs_check_acc_dma_handle(pwp)) {
17954c06356bSdh142964 ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
17964c06356bSdh142964 }
17974c06356bSdh142964
17984c06356bSdh142964 while (sp) {
17994c06356bSdh142964 nxt = STAILQ_NEXT(sp, cmd_next);
18004c06356bSdh142964 pkt = CMD2PKT(sp);
1801601c90f1SSrikanth, Ramana tgt = sp->cmd_target;
1802601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, tgt,
18034c06356bSdh142964 "%s: calling completion on %p for tgt %p", __func__,
1804601c90f1SSrikanth, Ramana (void *)sp, (void *)tgt);
1805601c90f1SSrikanth, Ramana if (tgt) {
1806601c90f1SSrikanth, Ramana mutex_enter(&tgt->statlock);
1807601c90f1SSrikanth, Ramana ASSERT(tgt->actv_pkts != 0);
1808601c90f1SSrikanth, Ramana tgt->actv_pkts--;
1809601c90f1SSrikanth, Ramana mutex_exit(&tgt->statlock);
1810601c90f1SSrikanth, Ramana }
18114c06356bSdh142964 scsi_hba_pkt_comp(pkt);
18124c06356bSdh142964 sp = nxt;
18134c06356bSdh142964 }
18144c06356bSdh142964
18154c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__end__loop,
18164c06356bSdh142964 pmcs_cq_thr_info_t *, cqti);
18174c06356bSdh142964
18188f514e74SDavid Hollister /*
18198f514e74SDavid Hollister * Check if there are more completions to do. If so, and we've
18208f514e74SDavid Hollister * not been told to stop, skip the wait and cycle through again.
18218f514e74SDavid Hollister */
18228f514e74SDavid Hollister mutex_enter(&pwp->cq_lock);
18238f514e74SDavid Hollister if ((pwp->iocomp_cb_head == NULL) && STAILQ_EMPTY(&pwp->cq) &&
18248f514e74SDavid Hollister !pwp->cq_info.cq_stop) {
18258f514e74SDavid Hollister mutex_exit(&pwp->cq_lock);
18264c06356bSdh142964 mutex_enter(&cqti->cq_thr_lock);
18274c06356bSdh142964 cv_wait(&cqti->cq_cv, &cqti->cq_thr_lock);
18284c06356bSdh142964 mutex_exit(&cqti->cq_thr_lock);
18294c06356bSdh142964 mutex_enter(&pwp->cq_lock);
18304c06356bSdh142964 }
18318f514e74SDavid Hollister }
18324c06356bSdh142964
18334c06356bSdh142964 mutex_exit(&pwp->cq_lock);
18344c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__stop, pmcs_cq_thr_info_t *, cqti);
18354c06356bSdh142964 thread_exit();
18364c06356bSdh142964 }
18374c06356bSdh142964
18384c06356bSdh142964 /*
18394c06356bSdh142964 * Run a SAS command. Called with pwrk->lock held, returns unlocked.
18404c06356bSdh142964 */
18414c06356bSdh142964 static int
pmcs_SAS_run(pmcs_cmd_t * sp,pmcwork_t * pwrk)18424c06356bSdh142964 pmcs_SAS_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
18434c06356bSdh142964 {
18444c06356bSdh142964 pmcs_hw_t *pwp = CMD2PMC(sp);
18454c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp);
18464c06356bSdh142964 pmcs_xscsi_t *xp = pwrk->xp;
184769b2e231SSrikanth Suravajhala uint32_t iq, lhtag, *ptr;
18484c06356bSdh142964 sas_ssp_cmd_iu_t sc;
184969b2e231SSrikanth Suravajhala int sp_pkt_time = 0;
18504c06356bSdh142964
18513be32c0fSJesse Butler ASSERT(xp != NULL);
18524c06356bSdh142964 mutex_enter(&xp->statlock);
1853b18a19c2SJesse Butler if (!xp->assigned) {
18544c06356bSdh142964 mutex_exit(&xp->statlock);
18554c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER);
18564c06356bSdh142964 }
18574c06356bSdh142964 if ((xp->actv_cnt >= xp->qdepth) || xp->recover_wait) {
18584c06356bSdh142964 mutex_exit(&xp->statlock);
18594c06356bSdh142964 mutex_enter(&xp->wqlock);
18604c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
18614c06356bSdh142964 mutex_exit(&xp->wqlock);
18624c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER);
18634c06356bSdh142964 }
18644c06356bSdh142964 GET_IO_IQ_ENTRY(pwp, ptr, pwrk->phy->device_id, iq);
18654c06356bSdh142964 if (ptr == NULL) {
18664c06356bSdh142964 mutex_exit(&xp->statlock);
18674c06356bSdh142964 /*
18684c06356bSdh142964 * This is a temporary failure not likely to unblocked by
18694c06356bSdh142964 * commands completing as the test for scheduling the
18704c06356bSdh142964 * restart of work is a per-device test.
18714c06356bSdh142964 */
18724c06356bSdh142964 mutex_enter(&xp->wqlock);
18734c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
18744c06356bSdh142964 mutex_exit(&xp->wqlock);
1875c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
18764c06356bSdh142964 "%s: Failed to get IO IQ entry for tgt %d",
18774c06356bSdh142964 __func__, xp->target_num);
18784c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES);
18794c06356bSdh142964
18804c06356bSdh142964 }
18814c06356bSdh142964
18824c06356bSdh142964 ptr[0] =
18834c06356bSdh142964 LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SSP_INI_IO_START));
18844c06356bSdh142964 ptr[1] = LE_32(pwrk->htag);
18854c06356bSdh142964 ptr[2] = LE_32(pwrk->phy->device_id);
18864c06356bSdh142964 ptr[3] = LE_32(pkt->pkt_dma_len);
18874c06356bSdh142964 if (ptr[3]) {
18884c06356bSdh142964 ASSERT(pkt->pkt_numcookies);
18894c06356bSdh142964 if (pkt->pkt_dma_flags & DDI_DMA_READ) {
18904c06356bSdh142964 ptr[4] = LE_32(PMCIN_DATADIR_2_INI);
18914c06356bSdh142964 } else {
18924c06356bSdh142964 ptr[4] = LE_32(PMCIN_DATADIR_2_DEV);
18934c06356bSdh142964 }
18944c06356bSdh142964 if (pmcs_dma_load(pwp, sp, ptr)) {
18954c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]);
18964c06356bSdh142964 mutex_exit(&xp->statlock);
18974c06356bSdh142964 mutex_enter(&xp->wqlock);
18984c06356bSdh142964 if (STAILQ_EMPTY(&xp->wq)) {
18994c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
19004c06356bSdh142964 mutex_exit(&xp->wqlock);
19015a7797ceSJesse Butler return (PMCS_WQ_RUN_FAIL_RES);
19024c06356bSdh142964 } else {
19034c06356bSdh142964 mutex_exit(&xp->wqlock);
19044c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = STATUS_QFULL;
19054c06356bSdh142964 CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
19064c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS |
19074c06356bSdh142964 STATE_GOT_TARGET | STATE_SENT_CMD |
19084c06356bSdh142964 STATE_GOT_STATUS;
1909*b46556d0SToomas Soome sp->cmd_tag = 0;
19104c06356bSdh142964 mutex_enter(&pwp->cq_lock);
19114c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
19128f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp);
19134c06356bSdh142964 mutex_exit(&pwp->cq_lock);
1914c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
19154c06356bSdh142964 "%s: Failed to dma_load for tgt %d (QF)",
19164c06356bSdh142964 __func__, xp->target_num);
19175a7797ceSJesse Butler return (PMCS_WQ_RUN_FAIL_RES_CMP);
19184c06356bSdh142964 }
19194c06356bSdh142964 }
19204c06356bSdh142964 } else {
19214c06356bSdh142964 ptr[4] = LE_32(PMCIN_DATADIR_NONE);
19224c06356bSdh142964 CLEAN_MESSAGE(ptr, 12);
19234c06356bSdh142964 }
19244c06356bSdh142964 xp->actv_cnt++;
19254c06356bSdh142964 if (xp->actv_cnt > xp->maxdepth) {
19264c06356bSdh142964 xp->maxdepth = xp->actv_cnt;
1927c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp, "%s: max depth "
1928c3bc407cSdh142964 "now %u", pwrk->phy->path, xp->maxdepth);
19294c06356bSdh142964 }
19304c06356bSdh142964 mutex_exit(&xp->statlock);
19314c06356bSdh142964
19324c06356bSdh142964
19334c06356bSdh142964 #ifdef DEBUG
19344c06356bSdh142964 /*
19354c06356bSdh142964 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
19364c06356bSdh142964 * event when this goes out on the wire.
19374c06356bSdh142964 */
19384c06356bSdh142964 ptr[4] |= PMCIN_MESSAGE_REPORT;
19394c06356bSdh142964 #endif
19404c06356bSdh142964 /*
19414c06356bSdh142964 * Fill in the SSP IU
19424c06356bSdh142964 */
19434c06356bSdh142964
19444c06356bSdh142964 bzero(&sc, sizeof (sas_ssp_cmd_iu_t));
19454c06356bSdh142964 bcopy((uint8_t *)&sp->cmd_lun->scsi_lun, sc.lun, sizeof (scsi_lun_t));
19464c06356bSdh142964
19474c06356bSdh142964 switch (pkt->pkt_flags & FLAG_TAGMASK) {
19484c06356bSdh142964 case FLAG_HTAG:
19494c06356bSdh142964 sc.task_attribute = SAS_CMD_TASK_ATTR_HEAD;
19504c06356bSdh142964 break;
19514c06356bSdh142964 case FLAG_OTAG:
19524c06356bSdh142964 sc.task_attribute = SAS_CMD_TASK_ATTR_ORDERED;
19534c06356bSdh142964 break;
19544c06356bSdh142964 case FLAG_STAG:
19554c06356bSdh142964 default:
19564c06356bSdh142964 sc.task_attribute = SAS_CMD_TASK_ATTR_SIMPLE;
19574c06356bSdh142964 break;
19584c06356bSdh142964 }
19594c06356bSdh142964 (void) memcpy(sc.cdb, pkt->pkt_cdbp,
19604c06356bSdh142964 min(SCSA_CDBLEN(sp), sizeof (sc.cdb)));
19614c06356bSdh142964 (void) memcpy(&ptr[5], &sc, sizeof (sas_ssp_cmd_iu_t));
19624c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ONCHIP;
196369b2e231SSrikanth Suravajhala lhtag = pwrk->htag;
19644c06356bSdh142964 mutex_exit(&pwrk->lock);
1965c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
19664c06356bSdh142964 "%s: giving pkt %p (tag %x) to the hardware", __func__,
19674c06356bSdh142964 (void *)pkt, pwrk->htag);
19684c06356bSdh142964 #ifdef DEBUG
19694c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SAS INI Message", ptr);
19704c06356bSdh142964 #endif
19714c06356bSdh142964 mutex_enter(&xp->aqlock);
19724c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
19734c06356bSdh142964 mutex_exit(&xp->aqlock);
197469b2e231SSrikanth Suravajhala sp_pkt_time = CMD2PKT(sp)->pkt_time;
19754c06356bSdh142964 INC_IQ_ENTRY(pwp, iq);
1976b53bcbf6SSrikanth Suravajhala mutex_enter(&pwrk->lock);
197769b2e231SSrikanth Suravajhala if (lhtag == pwrk->htag) {
197869b2e231SSrikanth Suravajhala pwrk->timer = US2WT(sp_pkt_time * 1000000);
1979b53bcbf6SSrikanth Suravajhala if (pwrk->timer == 0) {
1980b53bcbf6SSrikanth Suravajhala pwrk->timer = US2WT(1000000);
1981b53bcbf6SSrikanth Suravajhala }
198269b2e231SSrikanth Suravajhala }
1983b53bcbf6SSrikanth Suravajhala mutex_exit(&pwrk->lock);
19844c06356bSdh142964
19854c06356bSdh142964 /*
19864c06356bSdh142964 * If we just submitted the last command queued from device state
19874c06356bSdh142964 * recovery, clear the wq_recovery_tail pointer.
19884c06356bSdh142964 */
19894c06356bSdh142964 mutex_enter(&xp->wqlock);
19904c06356bSdh142964 if (xp->wq_recovery_tail == sp) {
19914c06356bSdh142964 xp->wq_recovery_tail = NULL;
19924c06356bSdh142964 }
19934c06356bSdh142964 mutex_exit(&xp->wqlock);
19944c06356bSdh142964
19954c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS);
19964c06356bSdh142964 }
19974c06356bSdh142964
19984c06356bSdh142964 /*
19994c06356bSdh142964 * Complete a SAS command
20004c06356bSdh142964 *
20014c06356bSdh142964 * Called with pwrk lock held.
20024c06356bSdh142964 * The free of pwrk releases the lock.
20034c06356bSdh142964 */
20044c06356bSdh142964
20054c06356bSdh142964 static void
pmcs_SAS_done(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * msg)20064c06356bSdh142964 pmcs_SAS_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
20074c06356bSdh142964 {
20084c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg;
20094c06356bSdh142964 pmcs_phy_t *pptr = pwrk->phy;
20104c06356bSdh142964 pmcs_xscsi_t *xp = pwrk->xp;
20114c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp);
20124c06356bSdh142964 int dead;
20134c06356bSdh142964 uint32_t sts;
20144c06356bSdh142964 boolean_t aborted = B_FALSE;
20154c06356bSdh142964 boolean_t do_ds_recovery = B_FALSE;
20164c06356bSdh142964
20174c06356bSdh142964 ASSERT(xp != NULL);
20184c06356bSdh142964 ASSERT(sp != NULL);
20194c06356bSdh142964 ASSERT(pptr != NULL);
20204c06356bSdh142964
20214c06356bSdh142964 DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
20224c06356bSdh142964 (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
20234c06356bSdh142964 hrtime_t, gethrtime());
20244c06356bSdh142964
20254c06356bSdh142964 dead = pwrk->dead;
20264c06356bSdh142964
20274c06356bSdh142964 if (msg) {
20284c06356bSdh142964 sts = LE_32(msg[2]);
20294c06356bSdh142964 } else {
20304c06356bSdh142964 sts = 0;
20314c06356bSdh142964 }
20324c06356bSdh142964
20334c06356bSdh142964 if (dead != 0) {
2034c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
2035c3bc407cSdh142964 "0x%x for %s", __func__, pwrk->htag, pptr->path);
20364c06356bSdh142964 goto out;
20374c06356bSdh142964 }
20384c06356bSdh142964
20394c06356bSdh142964 if (sts == PMCOUT_STATUS_ABORTED) {
20404c06356bSdh142964 aborted = B_TRUE;
20414c06356bSdh142964 }
20424c06356bSdh142964
20434c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
2044c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
20454c06356bSdh142964 "%s: cmd 0x%p (tag 0x%x) timed out for %s",
20464c06356bSdh142964 __func__, (void *)sp, pwrk->htag, pptr->path);
2047601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
2048601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
2049601c90f1SSrikanth, Ramana STATE_SENT_CMD;
2050601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
20514c06356bSdh142964 goto out;
20524c06356bSdh142964 }
20534c06356bSdh142964
20544c06356bSdh142964 /*
20554c06356bSdh142964 * If the status isn't okay but not underflow,
20564c06356bSdh142964 * step to the side and parse the (possible) error.
20574c06356bSdh142964 */
20584c06356bSdh142964 #ifdef DEBUG
20594c06356bSdh142964 if (msg) {
20604c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
20614c06356bSdh142964 }
20624c06356bSdh142964 #endif
20634c06356bSdh142964 if (!msg) {
20644c06356bSdh142964 goto out;
20654c06356bSdh142964 }
20664c06356bSdh142964
20674c06356bSdh142964 switch (sts) {
20684c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
20694c06356bSdh142964 case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
20704c06356bSdh142964 case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
2071c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
2072601c90f1SSrikanth, Ramana "%s: PHY %s requires DS recovery (status=%d)",
20734c06356bSdh142964 __func__, pptr->path, sts);
20744c06356bSdh142964 do_ds_recovery = B_TRUE;
20754c06356bSdh142964 break;
20764c06356bSdh142964 case PMCOUT_STATUS_UNDERFLOW:
20774c06356bSdh142964 (void) pmcs_set_resid(pkt, pkt->pkt_dma_len, LE_32(msg[3]));
2078c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, NULL, NULL,
20794c06356bSdh142964 "%s: underflow %u for cdb 0x%x",
20804c06356bSdh142964 __func__, LE_32(msg[3]), pkt->pkt_cdbp[0] & 0xff);
20814c06356bSdh142964 sts = PMCOUT_STATUS_OK;
20824c06356bSdh142964 msg[3] = 0;
20834c06356bSdh142964 break;
20844c06356bSdh142964 case PMCOUT_STATUS_OK:
20854c06356bSdh142964 pkt->pkt_resid = 0;
20864c06356bSdh142964 break;
20874c06356bSdh142964 }
20884c06356bSdh142964
20894c06356bSdh142964 if (sts != PMCOUT_STATUS_OK) {
2090658280b6SDavid Hollister pmcs_ioerror(pwp, SAS, pwrk, msg, sts);
20914c06356bSdh142964 } else {
20924c06356bSdh142964 if (msg[3]) {
20934c06356bSdh142964 uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
20944c06356bSdh142964 sas_ssp_rsp_iu_t *rptr = (void *)local;
20954c06356bSdh142964 const int lim =
20964c06356bSdh142964 (PMCS_QENTRY_SIZE << 1) - SAS_RSP_HDR_SIZE;
20974c06356bSdh142964 static const uint8_t ssp_rsp_evec[] = {
20984c06356bSdh142964 0x58, 0x61, 0x56, 0x72, 0x00
20994c06356bSdh142964 };
21004c06356bSdh142964
21014c06356bSdh142964 /*
21024c06356bSdh142964 * Transform the the first part of the response
21034c06356bSdh142964 * to host canonical form. This gives us enough
21044c06356bSdh142964 * information to figure out what to do with the
21054c06356bSdh142964 * rest (which remains unchanged in the incoming
21064c06356bSdh142964 * message which can be up to two queue entries
21074c06356bSdh142964 * in length).
21084c06356bSdh142964 */
21094c06356bSdh142964 pmcs_endian_transform(pwp, local, &msg[5],
21104c06356bSdh142964 ssp_rsp_evec);
21114c06356bSdh142964 xd = (uint8_t *)(&msg[5]);
21124c06356bSdh142964 xd += SAS_RSP_HDR_SIZE;
21134c06356bSdh142964
21144c06356bSdh142964 if (rptr->datapres == SAS_RSP_DATAPRES_RESPONSE_DATA) {
21154c06356bSdh142964 if (rptr->response_data_length != 4) {
21164c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
21174c06356bSdh142964 "Bad SAS RESPONSE DATA LENGTH",
21184c06356bSdh142964 msg);
21194c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR;
21204c06356bSdh142964 goto out;
21214c06356bSdh142964 }
21224c06356bSdh142964 (void) memcpy(&sts, xd, sizeof (uint32_t));
21234c06356bSdh142964 sts = BE_32(sts);
21244c06356bSdh142964 /*
21254c06356bSdh142964 * The only response code we should legally get
21264c06356bSdh142964 * here is an INVALID FRAME response code.
21274c06356bSdh142964 */
21284c06356bSdh142964 if (sts == SAS_RSP_INVALID_FRAME) {
2129c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
21304c06356bSdh142964 "%s: pkt %p tgt %u path %s "
21314c06356bSdh142964 "completed: INVALID FRAME response",
21324c06356bSdh142964 __func__, (void *)pkt,
21334c06356bSdh142964 xp->target_num, pptr->path);
21344c06356bSdh142964 } else {
2135c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
21364c06356bSdh142964 "%s: pkt %p tgt %u path %s "
21374c06356bSdh142964 "completed: illegal response 0x%x",
21384c06356bSdh142964 __func__, (void *)pkt,
21394c06356bSdh142964 xp->target_num, pptr->path, sts);
21404c06356bSdh142964 }
21414c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR;
21424c06356bSdh142964 goto out;
21434c06356bSdh142964 }
21444c06356bSdh142964 if (rptr->datapres == SAS_RSP_DATAPRES_SENSE_DATA) {
21454c06356bSdh142964 uint32_t slen;
21464c06356bSdh142964 slen = rptr->sense_data_length;
21474c06356bSdh142964 if (slen > lim) {
21484c06356bSdh142964 slen = lim;
21494c06356bSdh142964 }
21504c06356bSdh142964 pmcs_latch_status(pwp, sp, rptr->status, xd,
21514c06356bSdh142964 slen, pptr->path);
21524c06356bSdh142964 } else if (rptr->datapres == SAS_RSP_DATAPRES_NO_DATA) {
21534b456463SDavid Hollister pmcout_ssp_comp_t *sspcp;
21544b456463SDavid Hollister sspcp = (pmcout_ssp_comp_t *)msg;
21554b456463SDavid Hollister uint32_t *residp;
21564c06356bSdh142964 /*
21574c06356bSdh142964 * This is the case for a plain SCSI status.
21584b456463SDavid Hollister * Note: If RESC_V is set and we're here, there
21594b456463SDavid Hollister * is a residual. We need to find it and update
21604b456463SDavid Hollister * the packet accordingly.
21614c06356bSdh142964 */
21624c06356bSdh142964 pmcs_latch_status(pwp, sp, rptr->status, NULL,
21634c06356bSdh142964 0, pptr->path);
21644b456463SDavid Hollister
21654b456463SDavid Hollister if (sspcp->resc_v) {
21664b456463SDavid Hollister /*
21674b456463SDavid Hollister * Point residual to the SSP_RESP_IU
21684b456463SDavid Hollister */
21694b456463SDavid Hollister residp = (uint32_t *)(sspcp + 1);
21704b456463SDavid Hollister /*
21714b456463SDavid Hollister * param contains the number of bytes
21724b456463SDavid Hollister * between where the SSP_RESP_IU may
21734b456463SDavid Hollister * or may not be and the residual.
21744b456463SDavid Hollister * Increment residp by the appropriate
21754b456463SDavid Hollister * number of words: (param+resc_pad)/4).
21764b456463SDavid Hollister */
21774b456463SDavid Hollister residp += (LE_32(sspcp->param) +
21784b456463SDavid Hollister sspcp->resc_pad) /
21794b456463SDavid Hollister sizeof (uint32_t);
21804b456463SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
21814b456463SDavid Hollister pptr, xp, "%s: tgt 0x%p "
21824b456463SDavid Hollister "residual %d for pkt 0x%p",
21834b456463SDavid Hollister __func__, (void *) xp, *residp,
21844b456463SDavid Hollister (void *) pkt);
21854b456463SDavid Hollister ASSERT(LE_32(*residp) <=
21864b456463SDavid Hollister pkt->pkt_dma_len);
21874b456463SDavid Hollister (void) pmcs_set_resid(pkt,
21884b456463SDavid Hollister pkt->pkt_dma_len, LE_32(*residp));
21894b456463SDavid Hollister }
21904c06356bSdh142964 } else {
21914c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
21924c06356bSdh142964 "illegal SAS response", msg);
21934c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR;
21944c06356bSdh142964 goto out;
21954c06356bSdh142964 }
21964c06356bSdh142964 } else {
21974c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
21984c06356bSdh142964 pptr->path);
21994c06356bSdh142964 }
22004c06356bSdh142964 if (pkt->pkt_dma_len) {
22014c06356bSdh142964 pkt->pkt_state |= STATE_XFERRED_DATA;
22024c06356bSdh142964 }
22034c06356bSdh142964 }
2204c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
22054c06356bSdh142964 "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
22064c06356bSdh142964 __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
22074c06356bSdh142964 pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
22084c06356bSdh142964
22094c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
2210c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
22114c06356bSdh142964 "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
22124c06356bSdh142964 __func__, (void *)pkt, pptr->path, (void *)pwrk);
22134c06356bSdh142964 aborted = B_TRUE;
22144c06356bSdh142964 }
22154c06356bSdh142964
22164c06356bSdh142964 out:
22174c06356bSdh142964 pmcs_dma_unload(pwp, sp);
22184c06356bSdh142964 mutex_enter(&xp->statlock);
22199aed1621SDavid Hollister
22209aed1621SDavid Hollister /*
22215c45adf0SJesse Butler * If the device no longer has a PHY pointer, clear the PHY pointer
22225c45adf0SJesse Butler * from the work structure before we free it. Otherwise, pmcs_pwork
22235c45adf0SJesse Butler * may decrement the ref_count on a PHY that's been freed.
22245c45adf0SJesse Butler */
22255c45adf0SJesse Butler if (xp->phy == NULL) {
22265c45adf0SJesse Butler pwrk->phy = NULL;
22275c45adf0SJesse Butler }
22285c45adf0SJesse Butler
222939cd77a0SJesse Butler /*
223039cd77a0SJesse Butler * We may arrive here due to a command timing out, which in turn
223139cd77a0SJesse Butler * could be addressed in a different context. So, free the work
223239cd77a0SJesse Butler * back, but only after confirming it's not already been freed
223339cd77a0SJesse Butler * elsewhere.
223439cd77a0SJesse Butler */
22353492a3feSJesse Butler if (pwrk->htag != PMCS_TAG_FREE) {
22365c45adf0SJesse Butler pmcs_pwork(pwp, pwrk);
223739cd77a0SJesse Butler }
22385c45adf0SJesse Butler
22395c45adf0SJesse Butler /*
22409aed1621SDavid Hollister * If the device is gone, we only put this command on the completion
22419aed1621SDavid Hollister * queue if the work structure is not marked dead. If it's marked
22429aed1621SDavid Hollister * dead, it will already have been put there.
22439aed1621SDavid Hollister */
22444c06356bSdh142964 if (xp->dev_gone) {
22454c06356bSdh142964 mutex_exit(&xp->statlock);
22469aed1621SDavid Hollister if (!dead) {
2247429adc13SSrikanth, Ramana mutex_enter(&xp->aqlock);
2248429adc13SSrikanth, Ramana STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
2249429adc13SSrikanth, Ramana mutex_exit(&xp->aqlock);
22505c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
2251429adc13SSrikanth, Ramana "%s: Removing cmd 0x%p (htag 0x%x) from aq",
2252429adc13SSrikanth, Ramana __func__, (void *)sp, sp->cmd_tag);
22539aed1621SDavid Hollister mutex_enter(&pwp->cq_lock);
22549aed1621SDavid Hollister STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
22558f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp);
22569aed1621SDavid Hollister mutex_exit(&pwp->cq_lock);
2257c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
22589aed1621SDavid Hollister "%s: Completing command for dead target 0x%p",
22599aed1621SDavid Hollister __func__, (void *)xp);
22609aed1621SDavid Hollister }
22614c06356bSdh142964 return;
22624c06356bSdh142964 }
22634c06356bSdh142964
22644c06356bSdh142964 ASSERT(xp->actv_cnt > 0);
22654c06356bSdh142964 if (--(xp->actv_cnt) == 0) {
22664c06356bSdh142964 if (xp->draining) {
2267c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
22684c06356bSdh142964 "%s: waking up drain waiters", __func__);
22694c06356bSdh142964 cv_signal(&pwp->drain_cv);
22704c06356bSdh142964 }
22714c06356bSdh142964 }
22724c06356bSdh142964 mutex_exit(&xp->statlock);
2273c280a92bSDavid Hollister
2274c280a92bSDavid Hollister /*
2275c280a92bSDavid Hollister * If the status is other than OK, determine if it's something that
2276c280a92bSDavid Hollister * is worth re-attempting enumeration. If so, mark the PHY.
2277c280a92bSDavid Hollister */
2278c280a92bSDavid Hollister if (sts != PMCOUT_STATUS_OK) {
2279c280a92bSDavid Hollister pmcs_status_disposition(pptr, sts);
2280c280a92bSDavid Hollister }
2281c280a92bSDavid Hollister
22824c06356bSdh142964 if (dead == 0) {
22834c06356bSdh142964 #ifdef DEBUG
22844c06356bSdh142964 pmcs_cmd_t *wp;
22854c06356bSdh142964 mutex_enter(&xp->aqlock);
22864c06356bSdh142964 STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
22874c06356bSdh142964 if (wp == sp) {
22884c06356bSdh142964 break;
22894c06356bSdh142964 }
22904c06356bSdh142964 }
22914c06356bSdh142964 ASSERT(wp != NULL);
22924c06356bSdh142964 #else
22934c06356bSdh142964 mutex_enter(&xp->aqlock);
22944c06356bSdh142964 #endif
22955c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
22969aed1621SDavid Hollister "%s: Removing cmd 0x%p (htag 0x%x) from aq", __func__,
22979aed1621SDavid Hollister (void *)sp, sp->cmd_tag);
22984c06356bSdh142964 STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
22994c06356bSdh142964 if (aborted) {
2300c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
23014c06356bSdh142964 "%s: Aborted cmd for tgt 0x%p, signaling waiters",
23024c06356bSdh142964 __func__, (void *)xp);
23034c06356bSdh142964 cv_signal(&xp->abort_cv);
23044c06356bSdh142964 }
23054c06356bSdh142964 mutex_exit(&xp->aqlock);
23064c06356bSdh142964 }
23074c06356bSdh142964
23084c06356bSdh142964 /*
23094c06356bSdh142964 * If do_ds_recovery is set, we need to initiate device state
23104c06356bSdh142964 * recovery. In this case, we put this I/O back on the head of
23114c06356bSdh142964 * the wait queue to run again after recovery is complete
23124c06356bSdh142964 */
23134c06356bSdh142964 if (do_ds_recovery) {
23144c06356bSdh142964 mutex_enter(&xp->statlock);
23154c06356bSdh142964 pmcs_start_dev_state_recovery(xp, pptr);
23164c06356bSdh142964 mutex_exit(&xp->statlock);
2317c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, "%s: Putting cmd 0x%p "
2318c3bc407cSdh142964 "back on wq during recovery for tgt 0x%p", __func__,
2319c3bc407cSdh142964 (void *)sp, (void *)xp);
23204c06356bSdh142964 mutex_enter(&xp->wqlock);
23214c06356bSdh142964 if (xp->wq_recovery_tail == NULL) {
23224c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
23234c06356bSdh142964 } else {
23244c06356bSdh142964 /*
23254c06356bSdh142964 * If there are other I/Os waiting at the head due to
23264c06356bSdh142964 * device state recovery, add this one in the right spot
23274c06356bSdh142964 * to maintain proper order.
23284c06356bSdh142964 */
23294c06356bSdh142964 STAILQ_INSERT_AFTER(&xp->wq, xp->wq_recovery_tail, sp,
23304c06356bSdh142964 cmd_next);
23314c06356bSdh142964 }
23324c06356bSdh142964 xp->wq_recovery_tail = sp;
23334c06356bSdh142964 mutex_exit(&xp->wqlock);
23344c06356bSdh142964 } else {
23354c06356bSdh142964 /*
23364c06356bSdh142964 * If we're not initiating device state recovery and this
23374c06356bSdh142964 * command was not "dead", put it on the completion queue
23384c06356bSdh142964 */
23394c06356bSdh142964 if (!dead) {
23404c06356bSdh142964 mutex_enter(&pwp->cq_lock);
23414c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
23428f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp);
23434c06356bSdh142964 mutex_exit(&pwp->cq_lock);
23444c06356bSdh142964 }
23454c06356bSdh142964 }
23464c06356bSdh142964 }
23474c06356bSdh142964
23484c06356bSdh142964 /*
23494c06356bSdh142964 * Run a SATA command (normal reads and writes),
23504c06356bSdh142964 * or block and schedule a SATL interpretation
23514c06356bSdh142964 * of the command.
23524c06356bSdh142964 *
23534c06356bSdh142964 * Called with pwrk lock held, returns unlocked.
23544c06356bSdh142964 */
23554c06356bSdh142964
23564c06356bSdh142964 static int
pmcs_SATA_run(pmcs_cmd_t * sp,pmcwork_t * pwrk)23574c06356bSdh142964 pmcs_SATA_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
23584c06356bSdh142964 {
23594c06356bSdh142964 pmcs_hw_t *pwp = CMD2PMC(sp);
23604c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp);
23614c06356bSdh142964 pmcs_xscsi_t *xp;
23624c06356bSdh142964 uint8_t cdb_base, asc, tag;
236369b2e231SSrikanth Suravajhala uint32_t *ptr, lhtag, iq, nblk, i, mtype;
23644c06356bSdh142964 fis_t fis;
23654c06356bSdh142964 size_t amt;
23664c06356bSdh142964 uint64_t lba;
236769b2e231SSrikanth Suravajhala int sp_pkt_time = 0;
23684c06356bSdh142964
23694c06356bSdh142964 xp = pwrk->xp;
23703be32c0fSJesse Butler ASSERT(xp != NULL);
23714c06356bSdh142964
23724c06356bSdh142964 /*
23734c06356bSdh142964 * First, see if this is just a plain read/write command.
23744c06356bSdh142964 * If not, we have to queue it up for processing, block
23754c06356bSdh142964 * any additional commands from coming in, and wake up
23764c06356bSdh142964 * the thread that will process this command.
23774c06356bSdh142964 */
23784c06356bSdh142964 cdb_base = pkt->pkt_cdbp[0] & 0x1f;
23794c06356bSdh142964 if (cdb_base != SCMD_READ && cdb_base != SCMD_WRITE) {
2380c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
2381c3bc407cSdh142964 "%s: special SATA cmd %p", __func__, (void *)sp);
23824c06356bSdh142964
23834c06356bSdh142964 ASSERT(xp->phy != NULL);
23844c06356bSdh142964 pmcs_pwork(pwp, pwrk);
23854c06356bSdh142964 pmcs_lock_phy(xp->phy);
23864c06356bSdh142964 mutex_enter(&xp->statlock);
23874c06356bSdh142964 xp->special_needed = 1; /* Set the special_needed flag */
23884c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->sq, sp, cmd_next);
23894c06356bSdh142964 if (pmcs_run_sata_special(pwp, xp)) {
23904c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
23914c06356bSdh142964 }
23924c06356bSdh142964 mutex_exit(&xp->statlock);
23934c06356bSdh142964 pmcs_unlock_phy(xp->phy);
23944c06356bSdh142964
23954c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS);
23964c06356bSdh142964 }
23974c06356bSdh142964
2398c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: regular cmd", __func__);
23994c06356bSdh142964
24004c06356bSdh142964 mutex_enter(&xp->statlock);
2401b18a19c2SJesse Butler if (!xp->assigned) {
24024c06356bSdh142964 mutex_exit(&xp->statlock);
24034c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER);
24044c06356bSdh142964 }
24054c06356bSdh142964 if (xp->special_running || xp->special_needed || xp->recover_wait) {
24064c06356bSdh142964 mutex_exit(&xp->statlock);
24074c06356bSdh142964 mutex_enter(&xp->wqlock);
24084c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24094c06356bSdh142964 mutex_exit(&xp->wqlock);
24104c06356bSdh142964 /*
24114c06356bSdh142964 * By the time we get here the special
24124c06356bSdh142964 * commands running or waiting to be run
24134c06356bSdh142964 * may have come and gone, so kick our
24144c06356bSdh142964 * worker to run the waiting queues
24154c06356bSdh142964 * just in case.
24164c06356bSdh142964 */
24174c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
24184c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER);
24194c06356bSdh142964 }
24204c06356bSdh142964 lba = xp->capacity;
24214c06356bSdh142964 mutex_exit(&xp->statlock);
24224c06356bSdh142964
24234c06356bSdh142964 /*
24244c06356bSdh142964 * Extract data length and lba parameters out of the command. The
24254c06356bSdh142964 * function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB
24264c06356bSdh142964 * values are considered illegal.
24274c06356bSdh142964 */
24284c06356bSdh142964 asc = pmcs_SATA_rwparm(pkt->pkt_cdbp, &nblk, &lba, lba);
24294c06356bSdh142964 if (asc) {
24304c06356bSdh142964 uint8_t sns[18];
24314c06356bSdh142964 bzero(sns, sizeof (sns));
24324c06356bSdh142964 sns[0] = 0xf0;
24334c06356bSdh142964 sns[2] = 0x5;
24344c06356bSdh142964 sns[12] = asc;
24354c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, sns, sizeof (sns),
24364c06356bSdh142964 pwrk->phy->path);
24374c06356bSdh142964 pmcs_pwork(pwp, pwrk);
24384c06356bSdh142964 mutex_enter(&pwp->cq_lock);
24394c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
24404c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp);
24414c06356bSdh142964 mutex_exit(&pwp->cq_lock);
24424c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS);
24434c06356bSdh142964 }
24444c06356bSdh142964
24454c06356bSdh142964 /*
24464c06356bSdh142964 * If the command decodes as not moving any data, complete it here.
24474c06356bSdh142964 */
24484c06356bSdh142964 amt = nblk;
24494c06356bSdh142964 amt <<= 9;
24504c06356bSdh142964 amt = pmcs_set_resid(pkt, amt, nblk << 9);
24514c06356bSdh142964 if (amt == 0) {
24524c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
24534c06356bSdh142964 pwrk->phy->path);
24544c06356bSdh142964 pmcs_pwork(pwp, pwrk);
24554c06356bSdh142964 mutex_enter(&pwp->cq_lock);
24564c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
24574c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp);
24584c06356bSdh142964 mutex_exit(&pwp->cq_lock);
24594c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS);
24604c06356bSdh142964 }
24614c06356bSdh142964
24624c06356bSdh142964 /*
24634c06356bSdh142964 * Get an inbound queue entry for this I/O
24644c06356bSdh142964 */
24654c06356bSdh142964 GET_IO_IQ_ENTRY(pwp, ptr, xp->phy->device_id, iq);
24664c06356bSdh142964 if (ptr == NULL) {
24674c06356bSdh142964 /*
24684c06356bSdh142964 * This is a temporary failure not likely to unblocked by
24694c06356bSdh142964 * commands completing as the test for scheduling the
24704c06356bSdh142964 * restart of work is a per-device test.
24714c06356bSdh142964 */
24724c06356bSdh142964 mutex_enter(&xp->wqlock);
24734c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24744c06356bSdh142964 mutex_exit(&xp->wqlock);
24754c06356bSdh142964 pmcs_dma_unload(pwp, sp);
24764c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
2477c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
24784c06356bSdh142964 "%s: Failed to get IO IQ entry for tgt %d",
24794c06356bSdh142964 __func__, xp->target_num);
24804c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES);
24814c06356bSdh142964 }
24824c06356bSdh142964
24834c06356bSdh142964 /*
24844c06356bSdh142964 * Get a tag. At this point, hold statlock until the tagmap is
24854c06356bSdh142964 * updated (just prior to sending the cmd to the hardware).
24864c06356bSdh142964 */
24874c06356bSdh142964 mutex_enter(&xp->statlock);
24884c06356bSdh142964 for (tag = 0; tag < xp->qdepth; tag++) {
24894c06356bSdh142964 if ((xp->tagmap & (1 << tag)) == 0) {
24904c06356bSdh142964 break;
24914c06356bSdh142964 }
24924c06356bSdh142964 }
24934c06356bSdh142964
24944c06356bSdh142964 if (tag == xp->qdepth) {
24954c06356bSdh142964 mutex_exit(&xp->statlock);
24964c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]);
24974c06356bSdh142964 mutex_enter(&xp->wqlock);
24984c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
24994c06356bSdh142964 mutex_exit(&xp->wqlock);
25004c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER);
25014c06356bSdh142964 }
25024c06356bSdh142964
25034c06356bSdh142964 sp->cmd_satltag = (uint8_t)tag;
25044c06356bSdh142964
25054c06356bSdh142964 /*
25064c06356bSdh142964 * Set up the command
25074c06356bSdh142964 */
25084c06356bSdh142964 bzero(fis, sizeof (fis));
25094c06356bSdh142964 ptr[0] =
25104c06356bSdh142964 LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SATA_HOST_IO_START));
25114c06356bSdh142964 ptr[1] = LE_32(pwrk->htag);
25124c06356bSdh142964 ptr[2] = LE_32(pwrk->phy->device_id);
25134c06356bSdh142964 ptr[3] = LE_32(amt);
25144c06356bSdh142964
25154c06356bSdh142964 if (xp->ncq) {
25164c06356bSdh142964 mtype = SATA_PROTOCOL_FPDMA | (tag << 16);
25174c06356bSdh142964 fis[0] = ((nblk & 0xff) << 24) | (C_BIT << 8) | FIS_REG_H2DEV;
25184c06356bSdh142964 if (cdb_base == SCMD_READ) {
25194c06356bSdh142964 fis[0] |= (READ_FPDMA_QUEUED << 16);
25204c06356bSdh142964 } else {
25214c06356bSdh142964 fis[0] |= (WRITE_FPDMA_QUEUED << 16);
25224c06356bSdh142964 }
25234c06356bSdh142964 fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
25244c06356bSdh142964 fis[2] = ((nblk & 0xff00) << 16) | ((lba >> 24) & 0xffffff);
25254c06356bSdh142964 fis[3] = tag << 3;
25264c06356bSdh142964 } else {
25274c06356bSdh142964 int op;
25284c06356bSdh142964 fis[0] = (C_BIT << 8) | FIS_REG_H2DEV;
25294c06356bSdh142964 if (xp->pio) {
25304c06356bSdh142964 mtype = SATA_PROTOCOL_PIO;
25314c06356bSdh142964 if (cdb_base == SCMD_READ) {
25324c06356bSdh142964 op = READ_SECTORS_EXT;
25334c06356bSdh142964 } else {
25344c06356bSdh142964 op = WRITE_SECTORS_EXT;
25354c06356bSdh142964 }
25364c06356bSdh142964 } else {
25374c06356bSdh142964 mtype = SATA_PROTOCOL_DMA;
25384c06356bSdh142964 if (cdb_base == SCMD_READ) {
25394c06356bSdh142964 op = READ_DMA_EXT;
25404c06356bSdh142964 } else {
25414c06356bSdh142964 op = WRITE_DMA_EXT;
25424c06356bSdh142964 }
25434c06356bSdh142964 }
25444c06356bSdh142964 fis[0] |= (op << 16);
25454c06356bSdh142964 fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
25464c06356bSdh142964 fis[2] = (lba >> 24) & 0xffffff;
25474c06356bSdh142964 fis[3] = nblk;
25484c06356bSdh142964 }
25494c06356bSdh142964
25504c06356bSdh142964 if (cdb_base == SCMD_READ) {
25514c06356bSdh142964 ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_INI);
25524c06356bSdh142964 } else {
25534c06356bSdh142964 ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_DEV);
25544c06356bSdh142964 }
25554c06356bSdh142964 #ifdef DEBUG
25564c06356bSdh142964 /*
25574c06356bSdh142964 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
25584c06356bSdh142964 * event when this goes out on the wire.
25594c06356bSdh142964 */
25604c06356bSdh142964 ptr[4] |= PMCIN_MESSAGE_REPORT;
25614c06356bSdh142964 #endif
25624c06356bSdh142964 for (i = 0; i < (sizeof (fis_t))/(sizeof (uint32_t)); i++) {
25634c06356bSdh142964 ptr[i+5] = LE_32(fis[i]);
25644c06356bSdh142964 }
25654c06356bSdh142964 if (pmcs_dma_load(pwp, sp, ptr)) {
25664c06356bSdh142964 mutex_exit(&xp->statlock);
25674c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]);
25684c06356bSdh142964 mutex_enter(&xp->wqlock);
25694c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
25704c06356bSdh142964 mutex_exit(&xp->wqlock);
2571c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
25724c06356bSdh142964 "%s: Failed to dma_load for tgt %d",
25734c06356bSdh142964 __func__, xp->target_num);
25744c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES);
25754c06356bSdh142964
25764c06356bSdh142964 }
25774c06356bSdh142964
25784c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ONCHIP;
257969b2e231SSrikanth Suravajhala lhtag = pwrk->htag;
25804c06356bSdh142964 mutex_exit(&pwrk->lock);
25814c06356bSdh142964 xp->tagmap |= (1 << tag);
25824c06356bSdh142964 xp->actv_cnt++;
25834c06356bSdh142964 if (xp->actv_cnt > xp->maxdepth) {
25844c06356bSdh142964 xp->maxdepth = xp->actv_cnt;
2585c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp,
2586c3bc407cSdh142964 "%s: max depth now %u", pwrk->phy->path, xp->maxdepth);
25874c06356bSdh142964 }
25884c06356bSdh142964 mutex_exit(&xp->statlock);
25894c06356bSdh142964 mutex_enter(&xp->aqlock);
25904c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
25914c06356bSdh142964 mutex_exit(&xp->aqlock);
2592c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
2593c3bc407cSdh142964 "%s: giving pkt %p to hardware", __func__, (void *)pkt);
25944c06356bSdh142964 #ifdef DEBUG
25954c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SATA INI Message", ptr);
25964c06356bSdh142964 #endif
259769b2e231SSrikanth Suravajhala sp_pkt_time = CMD2PKT(sp)->pkt_time;
25984c06356bSdh142964 INC_IQ_ENTRY(pwp, iq);
2599b53bcbf6SSrikanth Suravajhala mutex_enter(&pwrk->lock);
260069b2e231SSrikanth Suravajhala if (lhtag == pwrk->htag) {
260169b2e231SSrikanth Suravajhala pwrk->timer = US2WT(sp_pkt_time * 1000000);
2602b53bcbf6SSrikanth Suravajhala if (pwrk->timer == 0) {
2603b53bcbf6SSrikanth Suravajhala pwrk->timer = US2WT(1000000);
2604b53bcbf6SSrikanth Suravajhala }
260569b2e231SSrikanth Suravajhala }
2606b53bcbf6SSrikanth Suravajhala mutex_exit(&pwrk->lock);
26074c06356bSdh142964
26084c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS);
26094c06356bSdh142964 }
26104c06356bSdh142964
26114c06356bSdh142964 /*
26124c06356bSdh142964 * Complete a SATA command. Called with pwrk lock held.
26134c06356bSdh142964 */
26144c06356bSdh142964 void
pmcs_SATA_done(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * msg)26154c06356bSdh142964 pmcs_SATA_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
26164c06356bSdh142964 {
26174c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg;
26184c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp);
26194c06356bSdh142964 pmcs_phy_t *pptr = pwrk->phy;
26204c06356bSdh142964 int dead;
26214c06356bSdh142964 uint32_t sts;
26224c06356bSdh142964 pmcs_xscsi_t *xp;
26234c06356bSdh142964 boolean_t aborted = B_FALSE;
26244c06356bSdh142964
26254c06356bSdh142964 xp = pwrk->xp;
26264c06356bSdh142964 ASSERT(xp != NULL);
26274c06356bSdh142964
26284c06356bSdh142964 DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
26294c06356bSdh142964 (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
26304c06356bSdh142964 hrtime_t, gethrtime());
26314c06356bSdh142964
26324c06356bSdh142964 dead = pwrk->dead;
26334c06356bSdh142964
26344c06356bSdh142964 if (msg) {
26354c06356bSdh142964 sts = LE_32(msg[2]);
26364c06356bSdh142964 } else {
26374c06356bSdh142964 sts = 0;
26384c06356bSdh142964 }
26394c06356bSdh142964
26404c06356bSdh142964 if (dead != 0) {
2641c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
2642c3bc407cSdh142964 "0x%x for %s", __func__, pwrk->htag, pptr->path);
26434c06356bSdh142964 goto out;
26444c06356bSdh142964 }
26454c06356bSdh142964 if ((pwrk->state == PMCS_WORK_STATE_TIMED_OUT) &&
26464c06356bSdh142964 (sts != PMCOUT_STATUS_ABORTED)) {
2647c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
26484c06356bSdh142964 "%s: cmd 0x%p (tag 0x%x) timed out for %s",
26494c06356bSdh142964 __func__, (void *)sp, pwrk->htag, pptr->path);
26504c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
26514c06356bSdh142964 /* pkt_reason already set to CMD_TIMEOUT */
26524c06356bSdh142964 ASSERT(CMD2PKT(sp)->pkt_reason == CMD_TIMEOUT);
26534c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
26544c06356bSdh142964 STATE_SENT_CMD;
26554c06356bSdh142964 CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
26564c06356bSdh142964 goto out;
26574c06356bSdh142964 }
26584c06356bSdh142964
2659c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, "%s: pkt %p tgt %u done",
26604c06356bSdh142964 __func__, (void *)pkt, xp->target_num);
26614c06356bSdh142964
26624c06356bSdh142964 /*
26634c06356bSdh142964 * If the status isn't okay but not underflow,
26644c06356bSdh142964 * step to the side and parse the (possible) error.
26654c06356bSdh142964 */
26664c06356bSdh142964 #ifdef DEBUG
26674c06356bSdh142964 if (msg) {
26684c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
26694c06356bSdh142964 }
26704c06356bSdh142964 #endif
26714c06356bSdh142964 if (!msg) {
26724c06356bSdh142964 goto out;
26734c06356bSdh142964 }
26744c06356bSdh142964
26754c06356bSdh142964 /*
26764c06356bSdh142964 * If the status isn't okay or we got a FIS response of some kind,
26774c06356bSdh142964 * step to the side and parse the (possible) error.
26784c06356bSdh142964 */
26794c06356bSdh142964 if ((sts != PMCOUT_STATUS_OK) || (LE_32(msg[3]) != 0)) {
26804c06356bSdh142964 if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) {
26814c06356bSdh142964 mutex_exit(&pwrk->lock);
26824c06356bSdh142964 pmcs_lock_phy(pptr);
26834c06356bSdh142964 mutex_enter(&xp->statlock);
26844c06356bSdh142964 if ((xp->resetting == 0) && (xp->reset_success != 0) &&
26854c06356bSdh142964 (xp->reset_wait == 0)) {
26864c06356bSdh142964 mutex_exit(&xp->statlock);
26874c06356bSdh142964 if (pmcs_reset_phy(pwp, pptr,
26884c06356bSdh142964 PMCS_PHYOP_LINK_RESET) != 0) {
2689c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
2690c3bc407cSdh142964 "%s: PHY (%s) Local Control/Link "
2691c3bc407cSdh142964 "Reset FAILED as part of error "
2692c3bc407cSdh142964 "recovery", __func__, pptr->path);
26934c06356bSdh142964 }
26944c06356bSdh142964 mutex_enter(&xp->statlock);
26954c06356bSdh142964 }
26964c06356bSdh142964 mutex_exit(&xp->statlock);
26974c06356bSdh142964 pmcs_unlock_phy(pptr);
26984c06356bSdh142964 mutex_enter(&pwrk->lock);
26994c06356bSdh142964 }
2700658280b6SDavid Hollister pmcs_ioerror(pwp, SATA, pwrk, msg, sts);
27014c06356bSdh142964 } else {
27024c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
27034c06356bSdh142964 pwrk->phy->path);
27044c06356bSdh142964 pkt->pkt_state |= STATE_XFERRED_DATA;
27054c06356bSdh142964 pkt->pkt_resid = 0;
27064c06356bSdh142964 }
27074c06356bSdh142964
2708c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
27094c06356bSdh142964 "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
27104c06356bSdh142964 __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
27114c06356bSdh142964 pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
27124c06356bSdh142964
27134c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
2714c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
27154c06356bSdh142964 "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
27164c06356bSdh142964 __func__, (void *)pkt, pptr->path, (void *)pwrk);
27174c06356bSdh142964 aborted = B_TRUE;
27184c06356bSdh142964 }
27194c06356bSdh142964
27204c06356bSdh142964 out:
27214c06356bSdh142964 pmcs_dma_unload(pwp, sp);
27224c06356bSdh142964 mutex_enter(&xp->statlock);
27234c06356bSdh142964 xp->tagmap &= ~(1 << sp->cmd_satltag);
27244c06356bSdh142964
27255c45adf0SJesse Butler /*
27265c45adf0SJesse Butler * If the device no longer has a PHY pointer, clear the PHY pointer
27275c45adf0SJesse Butler * from the work structure before we free it. Otherwise, pmcs_pwork
27285c45adf0SJesse Butler * may decrement the ref_count on a PHY that's been freed.
27295c45adf0SJesse Butler */
27305c45adf0SJesse Butler if (xp->phy == NULL) {
27315c45adf0SJesse Butler pwrk->phy = NULL;
27325c45adf0SJesse Butler }
27335c45adf0SJesse Butler
273439cd77a0SJesse Butler /*
273539cd77a0SJesse Butler * We may arrive here due to a command timing out, which in turn
273639cd77a0SJesse Butler * could be addressed in a different context. So, free the work
273739cd77a0SJesse Butler * back, but only after confirming it's not already been freed
273839cd77a0SJesse Butler * elsewhere.
273939cd77a0SJesse Butler */
27403492a3feSJesse Butler if (pwrk->htag != PMCS_TAG_FREE) {
27415c45adf0SJesse Butler pmcs_pwork(pwp, pwrk);
274239cd77a0SJesse Butler }
27435c45adf0SJesse Butler
27444c06356bSdh142964 if (xp->dev_gone) {
27454c06356bSdh142964 mutex_exit(&xp->statlock);
27469aed1621SDavid Hollister if (!dead) {
2747429adc13SSrikanth, Ramana mutex_enter(&xp->aqlock);
2748429adc13SSrikanth, Ramana STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
2749429adc13SSrikanth, Ramana mutex_exit(&xp->aqlock);
27505c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
2751429adc13SSrikanth, Ramana "%s: Removing cmd 0x%p (htag 0x%x) from aq",
2752429adc13SSrikanth, Ramana __func__, (void *)sp, sp->cmd_tag);
27539aed1621SDavid Hollister mutex_enter(&pwp->cq_lock);
27549aed1621SDavid Hollister STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
27558f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp);
27569aed1621SDavid Hollister mutex_exit(&pwp->cq_lock);
2757c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
27589aed1621SDavid Hollister "%s: Completing command for dead target 0x%p",
27599aed1621SDavid Hollister __func__, (void *)xp);
27609aed1621SDavid Hollister }
27614c06356bSdh142964 return;
27624c06356bSdh142964 }
27634c06356bSdh142964
27644c06356bSdh142964 ASSERT(xp->actv_cnt > 0);
27654c06356bSdh142964 if (--(xp->actv_cnt) == 0) {
27664c06356bSdh142964 if (xp->draining) {
2767c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
27684c06356bSdh142964 "%s: waking up drain waiters", __func__);
27694c06356bSdh142964 cv_signal(&pwp->drain_cv);
27704c06356bSdh142964 } else if (xp->special_needed) {
27714c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
27724c06356bSdh142964 }
27734c06356bSdh142964 }
27744c06356bSdh142964 mutex_exit(&xp->statlock);
27754c06356bSdh142964
2776c280a92bSDavid Hollister /*
2777c280a92bSDavid Hollister * If the status is other than OK, determine if it's something that
2778c280a92bSDavid Hollister * is worth re-attempting enumeration. If so, mark the PHY.
2779c280a92bSDavid Hollister */
2780c280a92bSDavid Hollister if (sts != PMCOUT_STATUS_OK) {
2781c280a92bSDavid Hollister pmcs_status_disposition(pptr, sts);
2782c280a92bSDavid Hollister }
2783c280a92bSDavid Hollister
27844c06356bSdh142964 if (dead == 0) {
27854c06356bSdh142964 #ifdef DEBUG
27864c06356bSdh142964 pmcs_cmd_t *wp;
27874c06356bSdh142964 mutex_enter(&xp->aqlock);
27884c06356bSdh142964 STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
27894c06356bSdh142964 if (wp == sp) {
27904c06356bSdh142964 break;
27914c06356bSdh142964 }
27924c06356bSdh142964 }
27934c06356bSdh142964 ASSERT(wp != NULL);
27944c06356bSdh142964 #else
27954c06356bSdh142964 mutex_enter(&xp->aqlock);
27964c06356bSdh142964 #endif
27974c06356bSdh142964 STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
27984c06356bSdh142964 if (aborted) {
2799c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
28004c06356bSdh142964 "%s: Aborted cmd for tgt 0x%p, signaling waiters",
28014c06356bSdh142964 __func__, (void *)xp);
28024c06356bSdh142964 cv_signal(&xp->abort_cv);
28034c06356bSdh142964 }
28044c06356bSdh142964 mutex_exit(&xp->aqlock);
28054c06356bSdh142964 mutex_enter(&pwp->cq_lock);
28064c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
28078f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp);
28084c06356bSdh142964 mutex_exit(&pwp->cq_lock);
28094c06356bSdh142964 }
28104c06356bSdh142964 }
28114c06356bSdh142964
28124c06356bSdh142964 static uint8_t
pmcs_SATA_rwparm(uint8_t * cdb,uint32_t * xfr,uint64_t * lba,uint64_t lbamax)28134c06356bSdh142964 pmcs_SATA_rwparm(uint8_t *cdb, uint32_t *xfr, uint64_t *lba, uint64_t lbamax)
28144c06356bSdh142964 {
28154c06356bSdh142964 uint8_t asc = 0;
28164c06356bSdh142964 switch (cdb[0]) {
28174c06356bSdh142964 case SCMD_READ_G5:
28184c06356bSdh142964 case SCMD_WRITE_G5:
28194c06356bSdh142964 *xfr =
28204c06356bSdh142964 (((uint32_t)cdb[10]) << 24) |
28214c06356bSdh142964 (((uint32_t)cdb[11]) << 16) |
28224c06356bSdh142964 (((uint32_t)cdb[12]) << 8) |
28234c06356bSdh142964 ((uint32_t)cdb[13]);
28244c06356bSdh142964 *lba =
28254c06356bSdh142964 (((uint64_t)cdb[2]) << 56) |
28264c06356bSdh142964 (((uint64_t)cdb[3]) << 48) |
28274c06356bSdh142964 (((uint64_t)cdb[4]) << 40) |
28284c06356bSdh142964 (((uint64_t)cdb[5]) << 32) |
28294c06356bSdh142964 (((uint64_t)cdb[6]) << 24) |
28304c06356bSdh142964 (((uint64_t)cdb[7]) << 16) |
28314c06356bSdh142964 (((uint64_t)cdb[8]) << 8) |
28324c06356bSdh142964 ((uint64_t)cdb[9]);
28334c06356bSdh142964 /* Check for illegal bits */
28344c06356bSdh142964 if (cdb[15]) {
28354c06356bSdh142964 asc = 0x24; /* invalid field in cdb */
28364c06356bSdh142964 }
28374c06356bSdh142964 break;
28384c06356bSdh142964 case SCMD_READ_G4:
28394c06356bSdh142964 case SCMD_WRITE_G4:
28404c06356bSdh142964 *xfr =
28414c06356bSdh142964 (((uint32_t)cdb[6]) << 16) |
28424c06356bSdh142964 (((uint32_t)cdb[7]) << 8) |
28434c06356bSdh142964 ((uint32_t)cdb[8]);
28444c06356bSdh142964 *lba =
28454c06356bSdh142964 (((uint32_t)cdb[2]) << 24) |
28464c06356bSdh142964 (((uint32_t)cdb[3]) << 16) |
28474c06356bSdh142964 (((uint32_t)cdb[4]) << 8) |
28484c06356bSdh142964 ((uint32_t)cdb[5]);
28494c06356bSdh142964 /* Check for illegal bits */
28504c06356bSdh142964 if (cdb[11]) {
28514c06356bSdh142964 asc = 0x24; /* invalid field in cdb */
28524c06356bSdh142964 }
28534c06356bSdh142964 break;
28544c06356bSdh142964 case SCMD_READ_G1:
28554c06356bSdh142964 case SCMD_WRITE_G1:
28564c06356bSdh142964 *xfr = (((uint32_t)cdb[7]) << 8) | ((uint32_t)cdb[8]);
28574c06356bSdh142964 *lba =
28584c06356bSdh142964 (((uint32_t)cdb[2]) << 24) |
28594c06356bSdh142964 (((uint32_t)cdb[3]) << 16) |
28604c06356bSdh142964 (((uint32_t)cdb[4]) << 8) |
28614c06356bSdh142964 ((uint32_t)cdb[5]);
28624c06356bSdh142964 /* Check for illegal bits */
28634c06356bSdh142964 if (cdb[9]) {
28644c06356bSdh142964 asc = 0x24; /* invalid field in cdb */
28654c06356bSdh142964 }
28664c06356bSdh142964 break;
28674c06356bSdh142964 case SCMD_READ:
28684c06356bSdh142964 case SCMD_WRITE:
28694c06356bSdh142964 *xfr = cdb[4];
28704c06356bSdh142964 if (*xfr == 0) {
28714c06356bSdh142964 *xfr = 256;
28724c06356bSdh142964 }
28734c06356bSdh142964 *lba =
28744c06356bSdh142964 (((uint32_t)cdb[1] & 0x1f) << 16) |
28754c06356bSdh142964 (((uint32_t)cdb[2]) << 8) |
28764c06356bSdh142964 ((uint32_t)cdb[3]);
28774c06356bSdh142964 /* Check for illegal bits */
28784c06356bSdh142964 if (cdb[5]) {
28794c06356bSdh142964 asc = 0x24; /* invalid field in cdb */
28804c06356bSdh142964 }
28814c06356bSdh142964 break;
28824c06356bSdh142964 }
28834c06356bSdh142964
28844c06356bSdh142964 if (asc == 0) {
28854c06356bSdh142964 if ((*lba + *xfr) > lbamax) {
28864c06356bSdh142964 asc = 0x21; /* logical block out of range */
28874c06356bSdh142964 }
28884c06356bSdh142964 }
28894c06356bSdh142964 return (asc);
28904c06356bSdh142964 }
28914c06356bSdh142964
28924c06356bSdh142964 /*
28934c06356bSdh142964 * Called with pwrk lock held.
28944c06356bSdh142964 */
28954c06356bSdh142964 static void
pmcs_ioerror(pmcs_hw_t * pwp,pmcs_dtype_t t,pmcwork_t * pwrk,uint32_t * w,uint32_t status)2896658280b6SDavid Hollister pmcs_ioerror(pmcs_hw_t *pwp, pmcs_dtype_t t, pmcwork_t *pwrk, uint32_t *w,
2897658280b6SDavid Hollister uint32_t status)
28984c06356bSdh142964 {
28994c06356bSdh142964 static uint8_t por[] = {
29004c06356bSdh142964 0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28
29014c06356bSdh142964 };
29024c06356bSdh142964 static uint8_t parity[] = {
29034c06356bSdh142964 0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5
29044c06356bSdh142964 };
29054c06356bSdh142964 const char *msg;
29064c06356bSdh142964 char buf[20];
29074c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg;
29084c06356bSdh142964 pmcs_phy_t *phyp = pwrk->phy;
29094c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp);
29104c06356bSdh142964 uint32_t resid;
29114c06356bSdh142964
29124c06356bSdh142964 ASSERT(w != NULL);
29134c06356bSdh142964 resid = LE_32(w[3]);
29144c06356bSdh142964
29154c06356bSdh142964 msg = pmcs_status_str(status);
29164c06356bSdh142964 if (msg == NULL) {
29174c06356bSdh142964 (void) snprintf(buf, sizeof (buf), "Error 0x%x", status);
29184c06356bSdh142964 msg = buf;
29194c06356bSdh142964 }
29204c06356bSdh142964
29214c06356bSdh142964 if (status != PMCOUT_STATUS_OK) {
29229aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, NULL,
29234c06356bSdh142964 "%s: device %s tag 0x%x status %s @ %llu", __func__,
29244c06356bSdh142964 phyp->path, pwrk->htag, msg,
29254c06356bSdh142964 (unsigned long long)gethrtime());
29264c06356bSdh142964 }
29274c06356bSdh142964
29284c06356bSdh142964 pkt->pkt_reason = CMD_CMPLT; /* default reason */
29294c06356bSdh142964
29304c06356bSdh142964 switch (status) {
29314c06356bSdh142964 case PMCOUT_STATUS_OK:
29324c06356bSdh142964 if (t == SATA) {
29334c06356bSdh142964 int i;
29344c06356bSdh142964 fis_t fis;
29354c06356bSdh142964 for (i = 0; i < sizeof (fis) / sizeof (fis[0]); i++) {
29364c06356bSdh142964 fis[i] = LE_32(w[4+i]);
29374c06356bSdh142964 }
29384c06356bSdh142964 if ((fis[0] & 0xff) != FIS_REG_D2H) {
2939c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
29404c06356bSdh142964 "unexpected fis code 0x%x", fis[0] & 0xff);
29414c06356bSdh142964 } else {
2942c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
2943c3bc407cSdh142964 "FIS ERROR");
29444c06356bSdh142964 pmcs_fis_dump(pwp, fis);
29454c06356bSdh142964 }
29464c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR;
29474c06356bSdh142964 break;
29484c06356bSdh142964 }
29494c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
29504c06356bSdh142964 break;
29514c06356bSdh142964
29524c06356bSdh142964 case PMCOUT_STATUS_ABORTED:
29534c06356bSdh142964 /*
29544c06356bSdh142964 * Command successfully aborted.
29554c06356bSdh142964 */
29564c06356bSdh142964 if (phyp->dead) {
29574c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE;
29584c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS;
29594c06356bSdh142964 } else if (pwrk->ssp_event != 0) {
29604c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR;
29614c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS;
29624c06356bSdh142964 } else if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
29634c06356bSdh142964 pkt->pkt_reason = CMD_TIMEOUT;
29644c06356bSdh142964 pkt->pkt_statistics |= STAT_TIMEOUT;
29654c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
29664c06356bSdh142964 STATE_SENT_CMD;
29674c06356bSdh142964 } else {
29684c06356bSdh142964 pkt->pkt_reason = CMD_ABORTED;
29694c06356bSdh142964 pkt->pkt_statistics |= STAT_ABORTED;
29704c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
29714c06356bSdh142964 STATE_SENT_CMD;
29724c06356bSdh142964 }
29734c06356bSdh142964
29744c06356bSdh142964 /*
29754c06356bSdh142964 * PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past
29764c06356bSdh142964 * this point, so go ahead and mark it as aborted.
29774c06356bSdh142964 */
29784c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ABORTED;
29794c06356bSdh142964 break;
29804c06356bSdh142964
29814c06356bSdh142964 case PMCOUT_STATUS_UNDERFLOW:
29824c06356bSdh142964 /*
29834c06356bSdh142964 * This will only get called for SATA
29844c06356bSdh142964 */
29854c06356bSdh142964 pkt->pkt_resid = resid;
29864c06356bSdh142964 if (pkt->pkt_dma_len < pkt->pkt_resid) {
29874c06356bSdh142964 (void) pmcs_set_resid(pkt, pkt->pkt_dma_len, resid);
29884c06356bSdh142964 }
29894c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
29904c06356bSdh142964 break;
29914c06356bSdh142964
29924c06356bSdh142964 case PMCOUT_STATUS_NO_DEVICE:
29934c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
29944c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE;
29954c06356bSdh142964 break;
29964c06356bSdh142964
29974c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
29984c06356bSdh142964 /*
29994c06356bSdh142964 * Need to do rediscovery. We probably have
30004c06356bSdh142964 * the wrong device (disk swap), so kill
30014c06356bSdh142964 * this one.
30024c06356bSdh142964 */
30034c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
30044c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
30054c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
30069aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
30074c06356bSdh142964 /*
30084c06356bSdh142964 * Need to do rediscovery.
30094c06356bSdh142964 */
30104c06356bSdh142964 if (!phyp->dead) {
30114c06356bSdh142964 mutex_exit(&pwrk->lock);
30124c06356bSdh142964 pmcs_lock_phy(pwrk->phy);
30134c06356bSdh142964 pmcs_kill_changed(pwp, pwrk->phy, 0);
30144c06356bSdh142964 pmcs_unlock_phy(pwrk->phy);
30154c06356bSdh142964 mutex_enter(&pwrk->lock);
30164c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE;
30174c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS;
30184c06356bSdh142964 } else {
30194c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE;
30204c06356bSdh142964 }
30214c06356bSdh142964 break;
30224c06356bSdh142964
30234c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
30244c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
30254c06356bSdh142964 case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
30264c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
30274c06356bSdh142964 /* cmd is pending on the target */
30284c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
30294c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
30304c06356bSdh142964 /* transitory - commands sent while in NCQ failure mode */
30314c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
30324c06356bSdh142964 /* NCQ failure */
30334c06356bSdh142964 case PMCOUT_STATUS_IO_PORT_IN_RESET:
30344c06356bSdh142964 case PMCOUT_STATUS_XFER_ERR_BREAK:
30354c06356bSdh142964 case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
30364c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE;
30374c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS;
30384c06356bSdh142964 break;
30394c06356bSdh142964
30404c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
3041658280b6SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
3042658280b6SDavid Hollister "STATUS_BUSY for htag 0x%08x", sp->cmd_tag);
30434c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_BUSY, NULL, 0, phyp->path);
30444c06356bSdh142964 break;
30454c06356bSdh142964
30464c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
30474c06356bSdh142964 /* synthesize a RESERVATION CONFLICT */
3048499cfd15SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
3049499cfd15SDavid Hollister "%s: Potential affiliation active on 0x%" PRIx64, __func__,
3050499cfd15SDavid Hollister pmcs_barray2wwn(phyp->sas_address));
30514c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_RESERVATION_CONFLICT, NULL,
30524c06356bSdh142964 0, phyp->path);
30534c06356bSdh142964 break;
30544c06356bSdh142964
30554c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
30564c06356bSdh142964 /* synthesize a power-on/reset */
30574c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, por, sizeof (por),
30584c06356bSdh142964 phyp->path);
30594c06356bSdh142964 break;
30604c06356bSdh142964
30614c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
30624c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
30634c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
30644c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
30654c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
30664c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
30674c06356bSdh142964 /* synthesize a PARITY ERROR */
30684c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, parity,
30694c06356bSdh142964 sizeof (parity), phyp->path);
30704c06356bSdh142964 break;
30714c06356bSdh142964
30724c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
30734c06356bSdh142964 case PMCOUT_STATUS_IO_NOT_VALID:
30744c06356bSdh142964 case PMCOUT_STATUS_PROG_ERROR:
30754c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
30764c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */
30774c06356bSdh142964 default:
30784c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR;
30794c06356bSdh142964 break;
30804c06356bSdh142964 }
30814c06356bSdh142964 }
30824c06356bSdh142964
30834c06356bSdh142964 /*
30844c06356bSdh142964 * Latch up SCSI status
30854c06356bSdh142964 */
30864c06356bSdh142964
30874c06356bSdh142964 void
pmcs_latch_status(pmcs_hw_t * pwp,pmcs_cmd_t * sp,uint8_t status,uint8_t * snsp,size_t snslen,char * path)30884c06356bSdh142964 pmcs_latch_status(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint8_t status,
30894c06356bSdh142964 uint8_t *snsp, size_t snslen, char *path)
30904c06356bSdh142964 {
30914c06356bSdh142964 static const char c1[] =
30924c06356bSdh142964 "%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) "
30934c06356bSdh142964 "HTAG 0x%x @ %llu";
30944c06356bSdh142964 static const char c2[] =
30954c06356bSdh142964 "%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu";
30964c06356bSdh142964
30974c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
30984c06356bSdh142964 STATE_SENT_CMD | STATE_GOT_STATUS;
30994c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = status;
31004c06356bSdh142964
31014c06356bSdh142964 if (status == STATUS_CHECK && snsp &&
31024c06356bSdh142964 (size_t)SCSA_STSLEN(sp) >= sizeof (struct scsi_arq_status)) {
31034c06356bSdh142964 struct scsi_arq_status *aqp =
31044c06356bSdh142964 (void *) CMD2PKT(sp)->pkt_scbp;
31054c06356bSdh142964 size_t amt = sizeof (struct scsi_extended_sense);
31064c06356bSdh142964 uint8_t key = scsi_sense_key(snsp);
31074c06356bSdh142964 uint8_t asc = scsi_sense_asc(snsp);
31084c06356bSdh142964 uint8_t ascq = scsi_sense_ascq(snsp);
31094c06356bSdh142964 if (amt > snslen) {
31104c06356bSdh142964 amt = snslen;
31114c06356bSdh142964 }
3112c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c1, path,
3113c3bc407cSdh142964 status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, key, asc, ascq,
31144c06356bSdh142964 sp->cmd_tag, (unsigned long long)gethrtime());
31154c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_ARQ_DONE;
31164c06356bSdh142964 (*(uint8_t *)&aqp->sts_rqpkt_status) = STATUS_GOOD;
31174c06356bSdh142964 aqp->sts_rqpkt_statistics = 0;
31184c06356bSdh142964 aqp->sts_rqpkt_reason = CMD_CMPLT;
31194c06356bSdh142964 aqp->sts_rqpkt_state = STATE_GOT_BUS |
31204c06356bSdh142964 STATE_GOT_TARGET | STATE_SENT_CMD |
31214c06356bSdh142964 STATE_XFERRED_DATA | STATE_GOT_STATUS;
31224c06356bSdh142964 (void) memcpy(&aqp->sts_sensedata, snsp, amt);
31234c06356bSdh142964 if (aqp->sts_sensedata.es_class != CLASS_EXTENDED_SENSE) {
31244c06356bSdh142964 aqp->sts_rqpkt_reason = CMD_TRAN_ERR;
31254c06356bSdh142964 aqp->sts_rqpkt_state = 0;
31264c06356bSdh142964 aqp->sts_rqpkt_resid =
31274c06356bSdh142964 sizeof (struct scsi_extended_sense);
31284c06356bSdh142964 } else {
31294c06356bSdh142964 aqp->sts_rqpkt_resid =
31304c06356bSdh142964 sizeof (struct scsi_extended_sense) - amt;
31314c06356bSdh142964 }
31324c06356bSdh142964 } else if (status) {
3133c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c2,
31344c06356bSdh142964 path, status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff,
31354c06356bSdh142964 sp->cmd_tag, (unsigned long long)gethrtime());
31364c06356bSdh142964 }
31374c06356bSdh142964
31384c06356bSdh142964 CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
31394c06356bSdh142964 }
31404c06356bSdh142964
31414c06356bSdh142964 /*
31424c06356bSdh142964 * Calculate and set packet residual and return the amount
31434c06356bSdh142964 * left over after applying various filters.
31444c06356bSdh142964 */
31454c06356bSdh142964 size_t
pmcs_set_resid(struct scsi_pkt * pkt,size_t amt,uint32_t cdbamt)31464c06356bSdh142964 pmcs_set_resid(struct scsi_pkt *pkt, size_t amt, uint32_t cdbamt)
31474c06356bSdh142964 {
31484c06356bSdh142964 pkt->pkt_resid = cdbamt;
31494c06356bSdh142964 if (amt > pkt->pkt_resid) {
31504c06356bSdh142964 amt = pkt->pkt_resid;
31514c06356bSdh142964 }
31524c06356bSdh142964 if (amt > pkt->pkt_dma_len) {
31534c06356bSdh142964 amt = pkt->pkt_dma_len;
31544c06356bSdh142964 }
31554c06356bSdh142964 return (amt);
31564c06356bSdh142964 }
31574c06356bSdh142964
31584c06356bSdh142964 /*
3159c280a92bSDavid Hollister * Return the existing target softstate (unlocked) if there is one. If so,
3160c280a92bSDavid Hollister * the PHY is locked and that lock must be freed by the caller after the
3161c280a92bSDavid Hollister * target/PHY linkage is established. If there isn't one, and alloc_tgt is
3162c280a92bSDavid Hollister * TRUE, then allocate one.
31634c06356bSdh142964 */
31644c06356bSdh142964 pmcs_xscsi_t *
pmcs_get_target(pmcs_iport_t * iport,char * tgt_port,boolean_t alloc_tgt)31655c45adf0SJesse Butler pmcs_get_target(pmcs_iport_t *iport, char *tgt_port, boolean_t alloc_tgt)
31664c06356bSdh142964 {
31674c06356bSdh142964 pmcs_hw_t *pwp = iport->pwp;
31684c06356bSdh142964 pmcs_phy_t *phyp;
31694c06356bSdh142964 pmcs_xscsi_t *tgt;
31704c06356bSdh142964 uint64_t wwn;
31714c06356bSdh142964 char unit_address[PMCS_MAX_UA_SIZE];
31724c06356bSdh142964 int ua_form = 1;
31734c06356bSdh142964
31744c06356bSdh142964 /*
31754c06356bSdh142964 * Find the PHY for this target
31764c06356bSdh142964 */
31774c06356bSdh142964 phyp = pmcs_find_phy_by_sas_address(pwp, iport, NULL, tgt_port);
31784c06356bSdh142964 if (phyp == NULL) {
3179c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
3180c3bc407cSdh142964 "%s: No PHY for target @ %s", __func__, tgt_port);
31814c06356bSdh142964 return (NULL);
31824c06356bSdh142964 }
31834c06356bSdh142964
31844c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
31854c06356bSdh142964
31864c06356bSdh142964 if (tgt) {
3187c280a92bSDavid Hollister mutex_enter(&tgt->statlock);
31884c06356bSdh142964 /*
31894c06356bSdh142964 * There's already a target. Check its PHY pointer to see
31904c06356bSdh142964 * if we need to clear the old linkages
31914c06356bSdh142964 */
31924c06356bSdh142964 if (tgt->phy && (tgt->phy != phyp)) {
3193c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
31944c06356bSdh142964 "%s: Target PHY updated from %p to %p", __func__,
31954c06356bSdh142964 (void *)tgt->phy, (void *)phyp);
31964c06356bSdh142964 if (!IS_ROOT_PHY(tgt->phy)) {
31974c06356bSdh142964 pmcs_dec_phy_ref_count(tgt->phy);
31984c06356bSdh142964 pmcs_inc_phy_ref_count(phyp);
31994c06356bSdh142964 }
32004c06356bSdh142964 tgt->phy->target = NULL;
32014c06356bSdh142964 }
32024c06356bSdh142964
3203c280a92bSDavid Hollister /*
3204c280a92bSDavid Hollister * If this target has no PHY pointer and alloc_tgt is FALSE,
3205c280a92bSDavid Hollister * that implies we expect the target to already exist. This
3206c280a92bSDavid Hollister * implies that there has already been a tran_tgt_init on at
3207c280a92bSDavid Hollister * least one LU.
3208c280a92bSDavid Hollister */
3209c280a92bSDavid Hollister if ((tgt->phy == NULL) && !alloc_tgt) {
3210c280a92bSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, tgt,
3211c280a92bSDavid Hollister "%s: Establish linkage from new PHY to old target @"
3212c280a92bSDavid Hollister "%s", __func__, tgt->unit_address);
3213c280a92bSDavid Hollister for (int idx = 0; idx < tgt->ref_count; idx++) {
3214c280a92bSDavid Hollister pmcs_inc_phy_ref_count(phyp);
3215c280a92bSDavid Hollister }
3216c280a92bSDavid Hollister }
3217c280a92bSDavid Hollister
32186c87a171SJesse Butler /*
32196c87a171SJesse Butler * Set this target pointer back up, since it's been
32206c87a171SJesse Butler * through pmcs_clear_xp().
32216c87a171SJesse Butler */
32226c87a171SJesse Butler tgt->dev_gone = 0;
32236c87a171SJesse Butler tgt->assigned = 1;
32246c87a171SJesse Butler tgt->dtype = phyp->dtype;
32256c87a171SJesse Butler tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
32264c06356bSdh142964 tgt->phy = phyp;
32274c06356bSdh142964 phyp->target = tgt;
3228c280a92bSDavid Hollister
3229c280a92bSDavid Hollister mutex_exit(&tgt->statlock);
32304c06356bSdh142964 return (tgt);
32314c06356bSdh142964 }
32324c06356bSdh142964
32334c06356bSdh142964 /*
32344c06356bSdh142964 * Make sure the PHY we found is on the correct iport
32354c06356bSdh142964 */
32364c06356bSdh142964 if (phyp->iport != iport) {
3237c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
32384c06356bSdh142964 "%s: No target at %s on this iport", __func__, tgt_port);
32394c06356bSdh142964 pmcs_unlock_phy(phyp);
32404c06356bSdh142964 return (NULL);
32414c06356bSdh142964 }
32424c06356bSdh142964
32434c06356bSdh142964 /*
32445c45adf0SJesse Butler * If this was just a lookup (i.e. alloc_tgt is false), return now.
32455c45adf0SJesse Butler */
32465c45adf0SJesse Butler if (alloc_tgt == B_FALSE) {
32475c45adf0SJesse Butler pmcs_unlock_phy(phyp);
32485c45adf0SJesse Butler return (NULL);
32495c45adf0SJesse Butler }
32505c45adf0SJesse Butler
32515c45adf0SJesse Butler /*
32524c06356bSdh142964 * Allocate the new softstate
32534c06356bSdh142964 */
32544c06356bSdh142964 wwn = pmcs_barray2wwn(phyp->sas_address);
32554c06356bSdh142964 (void) scsi_wwn_to_wwnstr(wwn, ua_form, unit_address);
32564c06356bSdh142964
32574c06356bSdh142964 if (ddi_soft_state_bystr_zalloc(iport->tgt_sstate, unit_address) !=
32584c06356bSdh142964 DDI_SUCCESS) {
3259c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
32604c06356bSdh142964 "%s: Couldn't alloc softstate for device at %s",
32614c06356bSdh142964 __func__, unit_address);
32624c06356bSdh142964 pmcs_unlock_phy(phyp);
32634c06356bSdh142964 return (NULL);
32644c06356bSdh142964 }
32654c06356bSdh142964
32664c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, unit_address);
3267a25672a1SDavid Hollister ASSERT(tgt != NULL);
32684c06356bSdh142964 STAILQ_INIT(&tgt->wq);
32694c06356bSdh142964 STAILQ_INIT(&tgt->aq);
32704c06356bSdh142964 STAILQ_INIT(&tgt->sq);
32714c06356bSdh142964 mutex_init(&tgt->statlock, NULL, MUTEX_DRIVER,
32724c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri));
32734c06356bSdh142964 mutex_init(&tgt->wqlock, NULL, MUTEX_DRIVER,
32744c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri));
32754c06356bSdh142964 mutex_init(&tgt->aqlock, NULL, MUTEX_DRIVER,
32764c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri));
32774c06356bSdh142964 cv_init(&tgt->reset_cv, NULL, CV_DRIVER, NULL);
32784c06356bSdh142964 cv_init(&tgt->abort_cv, NULL, CV_DRIVER, NULL);
327973a3eccdSDavid Hollister list_create(&tgt->lun_list, sizeof (pmcs_lun_t),
328073a3eccdSDavid Hollister offsetof(pmcs_lun_t, lun_list_next));
32814c06356bSdh142964 tgt->qdepth = 1;
32824c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM;
32834c06356bSdh142964 bcopy(unit_address, tgt->unit_address, PMCS_MAX_UA_SIZE);
32844c06356bSdh142964 tgt->pwp = pwp;
32854c06356bSdh142964 tgt->ua = strdup(iport->ua);
32864c06356bSdh142964 tgt->phy = phyp;
32874c06356bSdh142964 ASSERT((phyp->target == NULL) || (phyp->target == tgt));
32884c06356bSdh142964 if (phyp->target == NULL) {
32894c06356bSdh142964 phyp->target = tgt;
32904c06356bSdh142964 }
32914c06356bSdh142964
32924c06356bSdh142964 /*
32934c06356bSdh142964 * Don't allocate LUN softstate for SMP targets
32944c06356bSdh142964 */
32954c06356bSdh142964 if (phyp->dtype == EXPANDER) {
32964c06356bSdh142964 return (tgt);
32974c06356bSdh142964 }
32984c06356bSdh142964
32994c06356bSdh142964 if (ddi_soft_state_bystr_init(&tgt->lun_sstate,
33004c06356bSdh142964 sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) {
3301c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
33024c06356bSdh142964 "%s: LUN soft_state_bystr_init failed", __func__);
33034c06356bSdh142964 ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
33044c06356bSdh142964 pmcs_unlock_phy(phyp);
33054c06356bSdh142964 return (NULL);
33064c06356bSdh142964 }
33074c06356bSdh142964
33084c06356bSdh142964 return (tgt);
33094c06356bSdh142964 }
3310