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 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 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 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; 4824c06356bSdh142964 /* 4834c06356bSdh142964 * If the target still has a PHY pointer, break the linkage 4844c06356bSdh142964 */ 4854c06356bSdh142964 if (phyp) { 4864c06356bSdh142964 phyp->target = NULL; 4874c06356bSdh142964 } 4884c06356bSdh142964 target->phy = NULL; 489ee13933aSSrikanth Suravajhala if (phyp) { 490ee13933aSSrikanth Suravajhala mutex_exit(&phyp->phy_lock); 491ee13933aSSrikanth Suravajhala } 492*654ea33cSSrikanth Suravajhala pmcs_destroy_target(target); 493*654ea33cSSrikanth Suravajhala } else { 494*654ea33cSSrikanth Suravajhala mutex_exit(&target->statlock); 495*654ea33cSSrikanth Suravajhala if (phyp) { 496*654ea33cSSrikanth Suravajhala mutex_exit(&phyp->phy_lock); 497*654ea33cSSrikanth Suravajhala } 498*654ea33cSSrikanth Suravajhala } 499*654ea33cSSrikanth Suravajhala 5004c06356bSdh142964 mutex_exit(&pwp->lock); 5014c06356bSdh142964 } 5024c06356bSdh142964 5034c06356bSdh142964 static int 5044c06356bSdh142964 pmcs_scsa_start(struct scsi_address *ap, struct scsi_pkt *pkt) 5054c06356bSdh142964 { 5064c06356bSdh142964 pmcs_cmd_t *sp = PKT2CMD(pkt); 5074c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap); 5084c06356bSdh142964 pmcs_xscsi_t *xp; 5094c06356bSdh142964 boolean_t blocked; 5104c06356bSdh142964 uint32_t hba_state; 5114c06356bSdh142964 512c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 513c3bc407cSdh142964 "%s: pkt %p sd %p cdb0=0x%02x dl=%lu", __func__, (void *)pkt, 5144c06356bSdh142964 (void *)scsi_address_device(&pkt->pkt_address), 5154c06356bSdh142964 pkt->pkt_cdbp[0] & 0xff, pkt->pkt_dma_len); 5164c06356bSdh142964 5174c06356bSdh142964 if (pkt->pkt_flags & FLAG_NOINTR) { 518c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL, 519c3bc407cSdh142964 "%s: nointr pkt", __func__); 5204c06356bSdh142964 return (TRAN_BADPKT); 5214c06356bSdh142964 } 5224c06356bSdh142964 5234c06356bSdh142964 sp->cmd_tag = 0; 5244c06356bSdh142964 pkt->pkt_state = pkt->pkt_statistics = 0; 5254c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE; 5264c06356bSdh142964 5274c06356bSdh142964 mutex_enter(&pwp->lock); 5284c06356bSdh142964 hba_state = pwp->state; 5294c06356bSdh142964 blocked = pwp->blocked; 5304c06356bSdh142964 mutex_exit(&pwp->lock); 5314c06356bSdh142964 5324c06356bSdh142964 if (hba_state != STATE_RUNNING) { 533c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 534c3bc407cSdh142964 "%s: hba dead", __func__); 5354c06356bSdh142964 return (TRAN_FATAL_ERROR); 5364c06356bSdh142964 } 5374c06356bSdh142964 5384c06356bSdh142964 xp = pmcs_addr2xp(ap, NULL, sp); 5394c06356bSdh142964 if (xp == NULL) { 540c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 5414c06356bSdh142964 "%s: dropping due to null target", __func__); 542b18a19c2SJesse Butler goto dead_target; 5434c06356bSdh142964 } 5444c06356bSdh142964 ASSERT(mutex_owned(&xp->statlock)); 5454c06356bSdh142964 5464c06356bSdh142964 /* 547b18a19c2SJesse Butler * First, check to see if the device is gone. 5484c06356bSdh142964 */ 549b18a19c2SJesse Butler if (xp->dev_gone) { 550601c90f1SSrikanth, Ramana xp->actv_pkts++; 5514c06356bSdh142964 mutex_exit(&xp->statlock); 552c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, xp, 553b18a19c2SJesse Butler "%s: dropping due to dead target 0x%p", 5544c06356bSdh142964 __func__, (void *)xp); 555b18a19c2SJesse Butler goto dead_target; 5564c06356bSdh142964 } 5574c06356bSdh142964 5584c06356bSdh142964 /* 5594c06356bSdh142964 * If we're blocked (quiesced) just return. 5604c06356bSdh142964 */ 5614c06356bSdh142964 if (blocked) { 562c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 563c3bc407cSdh142964 "%s: hba blocked", __func__); 564601c90f1SSrikanth, Ramana xp->actv_pkts++; 5654c06356bSdh142964 mutex_exit(&xp->statlock); 5664c06356bSdh142964 mutex_enter(&xp->wqlock); 5674c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next); 5684c06356bSdh142964 mutex_exit(&xp->wqlock); 5694c06356bSdh142964 return (TRAN_ACCEPT); 5704c06356bSdh142964 } 5714c06356bSdh142964 5724c06356bSdh142964 /* 5734c06356bSdh142964 * If we're draining or resetting, queue and return. 5744c06356bSdh142964 */ 5754c06356bSdh142964 if (xp->draining || xp->resetting || xp->recover_wait) { 576601c90f1SSrikanth, Ramana xp->actv_pkts++; 5774c06356bSdh142964 mutex_exit(&xp->statlock); 5784c06356bSdh142964 mutex_enter(&xp->wqlock); 5794c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next); 5804c06356bSdh142964 mutex_exit(&xp->wqlock); 581c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp, 5824c06356bSdh142964 "%s: draining/resetting/recovering (cnt %u)", 5834c06356bSdh142964 __func__, xp->actv_cnt); 5844c06356bSdh142964 /* 5854c06356bSdh142964 * By the time we get here, draining or 5864c06356bSdh142964 * resetting may have come and gone, not 5874c06356bSdh142964 * yet noticing that we had put something 5884c06356bSdh142964 * on the wait queue, so schedule a worker 5894c06356bSdh142964 * to look at this later. 5904c06356bSdh142964 */ 5914c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 5924c06356bSdh142964 return (TRAN_ACCEPT); 5934c06356bSdh142964 } 594601c90f1SSrikanth, Ramana 595601c90f1SSrikanth, Ramana xp->actv_pkts++; 5964c06356bSdh142964 mutex_exit(&xp->statlock); 5974c06356bSdh142964 5984c06356bSdh142964 /* 5994c06356bSdh142964 * Queue this command to the tail of the wait queue. 6004c06356bSdh142964 * This keeps us getting commands out of order. 6014c06356bSdh142964 */ 6024c06356bSdh142964 mutex_enter(&xp->wqlock); 6034c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next); 6044c06356bSdh142964 mutex_exit(&xp->wqlock); 6054c06356bSdh142964 6064c06356bSdh142964 /* 6074c06356bSdh142964 * Now run the queue for this device. 6084c06356bSdh142964 */ 6094c06356bSdh142964 (void) pmcs_scsa_wq_run_one(pwp, xp); 6104c06356bSdh142964 6114c06356bSdh142964 return (TRAN_ACCEPT); 6124c06356bSdh142964 613b18a19c2SJesse Butler dead_target: 6144c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 6154c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE; 6164c06356bSdh142964 mutex_enter(&pwp->cq_lock); 6174c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 6184c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp); 6194c06356bSdh142964 mutex_exit(&pwp->cq_lock); 6204c06356bSdh142964 return (TRAN_ACCEPT); 6214c06356bSdh142964 } 6224c06356bSdh142964 62302b04f6eSSrikanth, Ramana /* Return code 1 = Success */ 6244c06356bSdh142964 static int 6254c06356bSdh142964 pmcs_scsa_abort(struct scsi_address *ap, struct scsi_pkt *pkt) 6264c06356bSdh142964 { 6274c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap); 62802b04f6eSSrikanth, Ramana pmcs_cmd_t *sp = NULL; 62902b04f6eSSrikanth, Ramana pmcs_xscsi_t *xp = NULL; 63002b04f6eSSrikanth, Ramana pmcs_phy_t *pptr = NULL; 63102b04f6eSSrikanth, Ramana pmcs_lun_t *pmcs_lun = (pmcs_lun_t *) 63202b04f6eSSrikanth, Ramana scsi_device_hba_private_get(scsi_address_device(ap)); 6334c06356bSdh142964 uint32_t tag; 6344c06356bSdh142964 uint64_t lun; 6354c06356bSdh142964 pmcwork_t *pwrk; 6364c06356bSdh142964 6374c06356bSdh142964 mutex_enter(&pwp->lock); 6384c06356bSdh142964 if (pwp->state != STATE_RUNNING) { 6394c06356bSdh142964 mutex_exit(&pwp->lock); 640c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 641c3bc407cSdh142964 "%s: hba dead", __func__); 6424c06356bSdh142964 return (0); 6434c06356bSdh142964 } 6444c06356bSdh142964 mutex_exit(&pwp->lock); 6454c06356bSdh142964 64602b04f6eSSrikanth, Ramana if (pkt == NULL) { 64702b04f6eSSrikanth, Ramana if (pmcs_lun == NULL) { 64802b04f6eSSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: " 64902b04f6eSSrikanth, Ramana "No pmcs_lun_t struct to do ABORT_ALL", __func__); 65002b04f6eSSrikanth, Ramana return (0); 65102b04f6eSSrikanth, Ramana } 65202b04f6eSSrikanth, Ramana xp = pmcs_lun->target; 65302b04f6eSSrikanth, Ramana if (xp != NULL) { 65402b04f6eSSrikanth, Ramana pptr = xp->phy; 65502b04f6eSSrikanth, Ramana } 65602b04f6eSSrikanth, Ramana if (pptr == NULL) { 65702b04f6eSSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: pkt is " 65802b04f6eSSrikanth, Ramana "NULL. No tgt/phy to do ABORT_ALL", __func__); 65902b04f6eSSrikanth, Ramana return (0); 66002b04f6eSSrikanth, Ramana } 66102b04f6eSSrikanth, Ramana pmcs_lock_phy(pptr); 66202b04f6eSSrikanth, Ramana if (pmcs_abort(pwp, pptr, 0, 1, 0)) { 66302b04f6eSSrikanth, Ramana pptr->abort_pending = 1; 66402b04f6eSSrikanth, Ramana SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 66502b04f6eSSrikanth, Ramana } 66602b04f6eSSrikanth, Ramana pmcs_unlock_phy(pptr); 66702b04f6eSSrikanth, Ramana return (1); 66802b04f6eSSrikanth, Ramana } 66902b04f6eSSrikanth, Ramana 67002b04f6eSSrikanth, Ramana sp = PKT2CMD(pkt); 67102b04f6eSSrikanth, Ramana xp = sp->cmd_target; 67202b04f6eSSrikanth, Ramana 6734c06356bSdh142964 if (sp->cmd_lun) { 6744c06356bSdh142964 lun = sp->cmd_lun->lun_num; 6754c06356bSdh142964 } else { 6764c06356bSdh142964 lun = 0; 6774c06356bSdh142964 } 6784c06356bSdh142964 if (xp == NULL) { 6794c06356bSdh142964 return (0); 6804c06356bSdh142964 } 6814c06356bSdh142964 6824c06356bSdh142964 /* 6834c06356bSdh142964 * See if we have a real work structure associated with this cmd. 6844c06356bSdh142964 */ 685978d7443SSrikanth Suravajhala pwrk = pmcs_tag2wp(pwp, sp->cmd_tag, B_FALSE); 6864c06356bSdh142964 if (pwrk && pwrk->arg == sp) { 6874c06356bSdh142964 tag = pwrk->htag; 6884c06356bSdh142964 pptr = pwrk->phy; 6894c06356bSdh142964 pwrk->timer = 0; /* we don't time this here */ 6904c06356bSdh142964 ASSERT(pwrk->state == PMCS_WORK_STATE_ONCHIP); 6914c06356bSdh142964 mutex_exit(&pwrk->lock); 6924c06356bSdh142964 pmcs_lock_phy(pptr); 6934c06356bSdh142964 if (pptr->dtype == SAS) { 6944c06356bSdh142964 if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, tag, lun, 6954c06356bSdh142964 NULL)) { 6964c06356bSdh142964 pptr->abort_pending = 1; 6974c06356bSdh142964 pmcs_unlock_phy(pptr); 6984c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 6994c06356bSdh142964 return (0); 7004c06356bSdh142964 } 7014c06356bSdh142964 } else { 7024c06356bSdh142964 /* 7034c06356bSdh142964 * XXX: Was the command that was active an 7044c06356bSdh142964 * NCQ I/O command? 7054c06356bSdh142964 */ 7064c06356bSdh142964 pptr->need_rl_ext = 1; 7074c06356bSdh142964 if (pmcs_sata_abort_ncq(pwp, pptr)) { 7084c06356bSdh142964 pptr->abort_pending = 1; 7094c06356bSdh142964 pmcs_unlock_phy(pptr); 7104c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 7114c06356bSdh142964 return (0); 7124c06356bSdh142964 } 7134c06356bSdh142964 } 7144c06356bSdh142964 pptr->abort_pending = 1; 7154c06356bSdh142964 pmcs_unlock_phy(pptr); 7164c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 7174c06356bSdh142964 return (1); 7184c06356bSdh142964 } 7194c06356bSdh142964 if (pwrk) { 7204c06356bSdh142964 mutex_exit(&pwrk->lock); 7214c06356bSdh142964 } 7224c06356bSdh142964 /* 7234c06356bSdh142964 * Okay, those weren't the droids we were looking for. 7244c06356bSdh142964 * See if the command is on any of the wait queues. 7254c06356bSdh142964 */ 7264c06356bSdh142964 mutex_enter(&xp->wqlock); 7274c06356bSdh142964 sp = NULL; 7284c06356bSdh142964 STAILQ_FOREACH(sp, &xp->wq, cmd_next) { 7294c06356bSdh142964 if (sp == PKT2CMD(pkt)) { 7304c06356bSdh142964 STAILQ_REMOVE(&xp->wq, sp, pmcs_cmd, cmd_next); 7314c06356bSdh142964 break; 7324c06356bSdh142964 } 7334c06356bSdh142964 } 7344c06356bSdh142964 mutex_exit(&xp->wqlock); 7354c06356bSdh142964 if (sp) { 7364c06356bSdh142964 pkt->pkt_reason = CMD_ABORTED; 7374c06356bSdh142964 pkt->pkt_statistics |= STAT_ABORTED; 7384c06356bSdh142964 mutex_enter(&pwp->cq_lock); 7394c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 7404c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp); 7414c06356bSdh142964 mutex_exit(&pwp->cq_lock); 7424c06356bSdh142964 return (1); 7434c06356bSdh142964 } 7444c06356bSdh142964 return (0); 7454c06356bSdh142964 } 7464c06356bSdh142964 7474c06356bSdh142964 /* 7484c06356bSdh142964 * SCSA reset functions 7494c06356bSdh142964 */ 7504c06356bSdh142964 static int 7514c06356bSdh142964 pmcs_scsa_reset(struct scsi_address *ap, int level) 7524c06356bSdh142964 { 7534c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap); 7544c06356bSdh142964 pmcs_phy_t *pptr; 7554c06356bSdh142964 pmcs_xscsi_t *xp; 7564c06356bSdh142964 uint64_t lun = (uint64_t)-1, *lp = NULL; 7574c06356bSdh142964 int rval; 7584c06356bSdh142964 7594c06356bSdh142964 mutex_enter(&pwp->lock); 7604c06356bSdh142964 if (pwp->state != STATE_RUNNING) { 7614c06356bSdh142964 mutex_exit(&pwp->lock); 762c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 763c3bc407cSdh142964 "%s: hba dead", __func__); 7644c06356bSdh142964 return (0); 7654c06356bSdh142964 } 7664c06356bSdh142964 mutex_exit(&pwp->lock); 7674c06356bSdh142964 7684c06356bSdh142964 switch (level) { 7694c06356bSdh142964 case RESET_ALL: 7704c06356bSdh142964 rval = 0; 7714c06356bSdh142964 break; 7724c06356bSdh142964 case RESET_LUN: 7734c06356bSdh142964 /* 7744c06356bSdh142964 * Point lp at lun so that pmcs_addr2xp 7754c06356bSdh142964 * will fill out the 64 bit lun number. 7764c06356bSdh142964 */ 7774c06356bSdh142964 lp = &lun; 7784c06356bSdh142964 /* FALLTHROUGH */ 7794c06356bSdh142964 case RESET_TARGET: 7804c06356bSdh142964 xp = pmcs_addr2xp(ap, lp, NULL); 7814c06356bSdh142964 if (xp == NULL) { 782c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7834c06356bSdh142964 "%s: no xp found for this scsi address", __func__); 7844c06356bSdh142964 return (0); 7854c06356bSdh142964 } 7864c06356bSdh142964 787b18a19c2SJesse Butler if (xp->dev_gone) { 7884c06356bSdh142964 mutex_exit(&xp->statlock); 789c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 7904c06356bSdh142964 "%s: Target 0x%p has gone away", __func__, 7914c06356bSdh142964 (void *)xp); 7924c06356bSdh142964 return (0); 7934c06356bSdh142964 } 7944c06356bSdh142964 7954c06356bSdh142964 /* 7964c06356bSdh142964 * If we're already performing this action, or if device 7974c06356bSdh142964 * state recovery is already running, just return failure. 7984c06356bSdh142964 */ 7994c06356bSdh142964 if (xp->resetting || xp->recover_wait) { 8004c06356bSdh142964 mutex_exit(&xp->statlock); 8014c06356bSdh142964 return (0); 8024c06356bSdh142964 } 8034c06356bSdh142964 xp->reset_wait = 0; 8044c06356bSdh142964 xp->reset_success = 0; 8054c06356bSdh142964 xp->resetting = 1; 8064c06356bSdh142964 pptr = xp->phy; 8074c06356bSdh142964 mutex_exit(&xp->statlock); 8084c06356bSdh142964 8094c06356bSdh142964 if (pmcs_reset_dev(pwp, pptr, lun)) { 8104c06356bSdh142964 rval = 0; 8114c06356bSdh142964 } else { 8124c06356bSdh142964 rval = 1; 8134c06356bSdh142964 } 8144c06356bSdh142964 8154c06356bSdh142964 mutex_enter(&xp->statlock); 8164c06356bSdh142964 if (rval == 1) { 8174c06356bSdh142964 xp->reset_success = 1; 8184c06356bSdh142964 } 8194c06356bSdh142964 if (xp->reset_wait) { 8204c06356bSdh142964 xp->reset_wait = 0; 8214c06356bSdh142964 cv_signal(&xp->reset_cv); 8224c06356bSdh142964 } 8234c06356bSdh142964 xp->resetting = 0; 8244c06356bSdh142964 mutex_exit(&xp->statlock); 8254c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 8264c06356bSdh142964 break; 8274c06356bSdh142964 default: 8284c06356bSdh142964 rval = 0; 8294c06356bSdh142964 break; 8304c06356bSdh142964 } 8314c06356bSdh142964 8324c06356bSdh142964 return (rval); 8334c06356bSdh142964 } 8344c06356bSdh142964 8354c06356bSdh142964 static int 8364c06356bSdh142964 pmcs_scsi_reset_notify(struct scsi_address *ap, int flag, 8374c06356bSdh142964 void (*callback)(caddr_t), caddr_t arg) 8384c06356bSdh142964 { 8394c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap); 8404c06356bSdh142964 return (scsi_hba_reset_notify_setup(ap, flag, callback, arg, 8414c06356bSdh142964 &pwp->lock, &pwp->reset_notify_listf)); 8424c06356bSdh142964 } 8434c06356bSdh142964 8444c06356bSdh142964 8454c06356bSdh142964 static int 8464c06356bSdh142964 pmcs_cap(struct scsi_address *ap, char *cap, int val, int tonly, int set) 8474c06356bSdh142964 { 8484c06356bSdh142964 _NOTE(ARGUNUSED(val, tonly)); 8494c06356bSdh142964 int cidx, rval = 0; 8504c06356bSdh142964 pmcs_xscsi_t *xp; 8514c06356bSdh142964 8524c06356bSdh142964 cidx = scsi_hba_lookup_capstr(cap); 8534c06356bSdh142964 if (cidx == -1) { 8544c06356bSdh142964 return (-1); 8554c06356bSdh142964 } 8564c06356bSdh142964 8574c06356bSdh142964 xp = pmcs_addr2xp(ap, NULL, NULL); 8584c06356bSdh142964 if (xp == NULL) { 8594c06356bSdh142964 return (-1); 8604c06356bSdh142964 } 8614c06356bSdh142964 8624c06356bSdh142964 switch (cidx) { 8634c06356bSdh142964 case SCSI_CAP_DMA_MAX: 8644c06356bSdh142964 case SCSI_CAP_INITIATOR_ID: 8654c06356bSdh142964 if (set == 0) { 8664c06356bSdh142964 rval = INT_MAX; /* argh */ 8674c06356bSdh142964 } 8684c06356bSdh142964 break; 8694c06356bSdh142964 case SCSI_CAP_DISCONNECT: 8704c06356bSdh142964 case SCSI_CAP_SYNCHRONOUS: 8714c06356bSdh142964 case SCSI_CAP_WIDE_XFER: 8724c06356bSdh142964 case SCSI_CAP_PARITY: 8734c06356bSdh142964 case SCSI_CAP_ARQ: 8744c06356bSdh142964 case SCSI_CAP_UNTAGGED_QING: 8754c06356bSdh142964 if (set == 0) { 8764c06356bSdh142964 rval = 1; 8774c06356bSdh142964 } 8784c06356bSdh142964 break; 8794c06356bSdh142964 8804c06356bSdh142964 case SCSI_CAP_TAGGED_QING: 8814c06356bSdh142964 rval = 1; 8824c06356bSdh142964 break; 8834c06356bSdh142964 8844c06356bSdh142964 case SCSI_CAP_MSG_OUT: 8854c06356bSdh142964 case SCSI_CAP_RESET_NOTIFICATION: 8864c06356bSdh142964 case SCSI_CAP_QFULL_RETRIES: 8874c06356bSdh142964 case SCSI_CAP_QFULL_RETRY_INTERVAL: 8884c06356bSdh142964 break; 8894c06356bSdh142964 case SCSI_CAP_SCSI_VERSION: 8904c06356bSdh142964 if (set == 0) { 8914c06356bSdh142964 rval = SCSI_VERSION_3; 8924c06356bSdh142964 } 8934c06356bSdh142964 break; 8944c06356bSdh142964 case SCSI_CAP_INTERCONNECT_TYPE: 8954c06356bSdh142964 if (set) { 8964c06356bSdh142964 break; 8974c06356bSdh142964 } 8984c06356bSdh142964 if (xp->phy_addressable) { 8994c06356bSdh142964 rval = INTERCONNECT_SATA; 9004c06356bSdh142964 } else { 9014c06356bSdh142964 rval = INTERCONNECT_SAS; 9024c06356bSdh142964 } 9034c06356bSdh142964 break; 9044c06356bSdh142964 case SCSI_CAP_CDB_LEN: 9054c06356bSdh142964 if (set == 0) { 9064c06356bSdh142964 rval = 16; 9074c06356bSdh142964 } 9084c06356bSdh142964 break; 9094c06356bSdh142964 case SCSI_CAP_LUN_RESET: 9104c06356bSdh142964 if (set) { 9114c06356bSdh142964 break; 9124c06356bSdh142964 } 9134c06356bSdh142964 if (xp->dtype == SATA) { 9144c06356bSdh142964 rval = 0; 9154c06356bSdh142964 } else { 9164c06356bSdh142964 rval = 1; 9174c06356bSdh142964 } 9184c06356bSdh142964 break; 9194c06356bSdh142964 default: 9204c06356bSdh142964 rval = -1; 9214c06356bSdh142964 break; 9224c06356bSdh142964 } 9234c06356bSdh142964 mutex_exit(&xp->statlock); 924c3bc407cSdh142964 pmcs_prt(ADDR2PMC(ap), PMCS_PRT_DEBUG3, NULL, NULL, 9254c06356bSdh142964 "%s: cap %s val %d set %d rval %d", 9264c06356bSdh142964 __func__, cap, val, set, rval); 9274c06356bSdh142964 return (rval); 9284c06356bSdh142964 } 9294c06356bSdh142964 9304c06356bSdh142964 /* 9314c06356bSdh142964 * Returns with statlock held if the xp is found. 9324c06356bSdh142964 * Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL. 9334c06356bSdh142964 */ 9344c06356bSdh142964 static pmcs_xscsi_t * 9354c06356bSdh142964 pmcs_addr2xp(struct scsi_address *ap, uint64_t *lp, pmcs_cmd_t *sp) 9364c06356bSdh142964 { 9374c06356bSdh142964 pmcs_xscsi_t *xp; 9384c06356bSdh142964 pmcs_lun_t *lun = (pmcs_lun_t *) 9394c06356bSdh142964 scsi_device_hba_private_get(scsi_address_device(ap)); 9404c06356bSdh142964 9414c06356bSdh142964 if ((lun == NULL) || (lun->target == NULL)) { 9424c06356bSdh142964 return (NULL); 9434c06356bSdh142964 } 9444c06356bSdh142964 xp = lun->target; 9454c06356bSdh142964 mutex_enter(&xp->statlock); 9464c06356bSdh142964 947b18a19c2SJesse Butler if (xp->dev_gone || (xp->phy == NULL)) { 948601c90f1SSrikanth, Ramana /* 949601c90f1SSrikanth, Ramana * This may be a retried packet, so it's possible cmd_target 950601c90f1SSrikanth, Ramana * and cmd_lun may still be populated. Clear them. 951601c90f1SSrikanth, Ramana */ 952601c90f1SSrikanth, Ramana if (sp != NULL) { 953601c90f1SSrikanth, Ramana sp->cmd_target = NULL; 954601c90f1SSrikanth, Ramana sp->cmd_lun = NULL; 955601c90f1SSrikanth, Ramana } 9564c06356bSdh142964 mutex_exit(&xp->statlock); 9574c06356bSdh142964 return (NULL); 9584c06356bSdh142964 } 9594c06356bSdh142964 9604c06356bSdh142964 if (sp != NULL) { 9614c06356bSdh142964 sp->cmd_target = xp; 9624c06356bSdh142964 sp->cmd_lun = lun; 9634c06356bSdh142964 } 9644c06356bSdh142964 if (lp) { 9654c06356bSdh142964 *lp = lun->lun_num; 9664c06356bSdh142964 } 9674c06356bSdh142964 return (xp); 9684c06356bSdh142964 } 9694c06356bSdh142964 9704c06356bSdh142964 static int 9714c06356bSdh142964 pmcs_scsa_getcap(struct scsi_address *ap, char *cap, int whom) 9724c06356bSdh142964 { 9734c06356bSdh142964 int r; 9744c06356bSdh142964 if (cap == NULL) { 9754c06356bSdh142964 return (-1); 9764c06356bSdh142964 } 9774c06356bSdh142964 r = pmcs_cap(ap, cap, 0, whom, 0); 9784c06356bSdh142964 return (r); 9794c06356bSdh142964 } 9804c06356bSdh142964 9814c06356bSdh142964 static int 9824c06356bSdh142964 pmcs_scsa_setcap(struct scsi_address *ap, char *cap, int value, int whom) 9834c06356bSdh142964 { 9844c06356bSdh142964 int r; 9854c06356bSdh142964 if (cap == NULL) { 9864c06356bSdh142964 return (-1); 9874c06356bSdh142964 } 9884c06356bSdh142964 r = pmcs_cap(ap, cap, value, whom, 1); 9894c06356bSdh142964 return (r); 9904c06356bSdh142964 } 9914c06356bSdh142964 9924c06356bSdh142964 static int 9934c06356bSdh142964 pmcs_scsa_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t), 9944c06356bSdh142964 caddr_t cbarg) 9954c06356bSdh142964 { 9964c06356bSdh142964 _NOTE(ARGUNUSED(callback, cbarg)); 9974c06356bSdh142964 pmcs_cmd_t *sp = pkt->pkt_ha_private; 9984c06356bSdh142964 9994c06356bSdh142964 bzero(sp, sizeof (pmcs_cmd_t)); 10004c06356bSdh142964 sp->cmd_pkt = pkt; 10014c06356bSdh142964 return (0); 10024c06356bSdh142964 } 10034c06356bSdh142964 10044c06356bSdh142964 static void 10054c06356bSdh142964 pmcs_scsa_teardown_pkt(struct scsi_pkt *pkt) 10064c06356bSdh142964 { 10074c06356bSdh142964 pmcs_cmd_t *sp = pkt->pkt_ha_private; 10084c06356bSdh142964 sp->cmd_target = NULL; 10094c06356bSdh142964 sp->cmd_lun = NULL; 10104c06356bSdh142964 } 10114c06356bSdh142964 10124c06356bSdh142964 static int 101396c4a178SChris Horne pmcs_smp_start(struct smp_pkt *smp_pkt) 10144c06356bSdh142964 { 10154c06356bSdh142964 struct pmcwork *pwrk; 10163be32c0fSJesse Butler pmcs_iport_t *iport; 10174c06356bSdh142964 const uint_t rdoff = SAS_SMP_MAX_PAYLOAD; 10184c06356bSdh142964 uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status; 10194c06356bSdh142964 uint64_t wwn; 102096c4a178SChris Horne pmcs_hw_t *pwp; 10214c06356bSdh142964 pmcs_phy_t *pptr; 10224c06356bSdh142964 pmcs_xscsi_t *xp; 10234c06356bSdh142964 uint_t reqsz, rspsz, will_retry; 10244c06356bSdh142964 int result; 10254c06356bSdh142964 102696c4a178SChris Horne pwp = smp_pkt->smp_pkt_address->smp_a_hba_tran->smp_tran_hba_private; 102796c4a178SChris Horne bcopy(smp_pkt->smp_pkt_address->smp_a_wwn, &wwn, SAS_WWN_BYTE_SIZE); 10284c06356bSdh142964 1029c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL, 1030c3bc407cSdh142964 "%s: starting for wwn 0x%" PRIx64, __func__, wwn); 10314c06356bSdh142964 103296c4a178SChris Horne will_retry = smp_pkt->smp_pkt_will_retry; 10334c06356bSdh142964 10344c06356bSdh142964 (void) pmcs_acquire_scratch(pwp, B_TRUE); 103596c4a178SChris Horne reqsz = smp_pkt->smp_pkt_reqsize; 10364c06356bSdh142964 if (reqsz > SAS_SMP_MAX_PAYLOAD) { 10374c06356bSdh142964 reqsz = SAS_SMP_MAX_PAYLOAD; 10384c06356bSdh142964 } 103996c4a178SChris Horne (void) memcpy(pwp->scratch, smp_pkt->smp_pkt_req, reqsz); 10404c06356bSdh142964 104196c4a178SChris Horne rspsz = smp_pkt->smp_pkt_rspsize; 10424c06356bSdh142964 if (rspsz > SAS_SMP_MAX_PAYLOAD) { 10434c06356bSdh142964 rspsz = SAS_SMP_MAX_PAYLOAD; 10444c06356bSdh142964 } 10454c06356bSdh142964 10464c06356bSdh142964 /* 10474c06356bSdh142964 * The request size from the SMP driver always includes 4 bytes 10484c06356bSdh142964 * for the CRC. The PMCS chip, however, doesn't want to see those 10494c06356bSdh142964 * counts as part of the transfer size. 10504c06356bSdh142964 */ 10514c06356bSdh142964 reqsz -= 4; 10524c06356bSdh142964 10534c06356bSdh142964 pptr = pmcs_find_phy_by_wwn(pwp, wwn); 10544c06356bSdh142964 /* PHY is now locked */ 10554c06356bSdh142964 if (pptr == NULL || pptr->dtype != EXPANDER) { 10564c06356bSdh142964 if (pptr) { 10574c06356bSdh142964 pmcs_unlock_phy(pptr); 10584c06356bSdh142964 } 10594c06356bSdh142964 pmcs_release_scratch(pwp); 10609aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 1061c3bc407cSdh142964 "%s: could not find phy", __func__); 106296c4a178SChris Horne smp_pkt->smp_pkt_reason = ENXIO; 10634c06356bSdh142964 return (DDI_FAILURE); 10644c06356bSdh142964 } 10654c06356bSdh142964 10669aed1621SDavid Hollister if ((pptr->iport == NULL) || !pptr->valid_device_id) { 10679aed1621SDavid Hollister pmcs_unlock_phy(pptr); 10689aed1621SDavid Hollister pmcs_release_scratch(pwp); 10699aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target, 10709aed1621SDavid Hollister "%s: Can't reach PHY %s", __func__, pptr->path); 10719aed1621SDavid Hollister smp_pkt->smp_pkt_reason = ENXIO; 10729aed1621SDavid Hollister return (DDI_FAILURE); 10739aed1621SDavid Hollister } 10749aed1621SDavid Hollister 10754c06356bSdh142964 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 10764c06356bSdh142964 if (pwrk == NULL) { 10774c06356bSdh142964 pmcs_unlock_phy(pptr); 10784c06356bSdh142964 pmcs_release_scratch(pwp); 1079c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 10804c06356bSdh142964 "%s: could not get work structure", __func__); 108196c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EBUSY; 10824c06356bSdh142964 return (DDI_FAILURE); 10834c06356bSdh142964 } 10844c06356bSdh142964 10854c06356bSdh142964 pwrk->arg = msg; 10864c06356bSdh142964 pwrk->dtype = EXPANDER; 10874c06356bSdh142964 mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 10884c06356bSdh142964 ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 10894c06356bSdh142964 if (ptr == NULL) { 10904c06356bSdh142964 pmcs_pwork(pwp, pwrk); 10914c06356bSdh142964 mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 10924c06356bSdh142964 pmcs_unlock_phy(pptr); 10934c06356bSdh142964 pmcs_release_scratch(pwp); 1094c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1095c3bc407cSdh142964 "%s: could not get IQ entry", __func__); 109696c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN :EBUSY; 10974c06356bSdh142964 return (DDI_FAILURE); 10984c06356bSdh142964 } 10994c06356bSdh142964 msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST)); 11004c06356bSdh142964 msg[1] = LE_32(pwrk->htag); 11014c06356bSdh142964 msg[2] = LE_32(pptr->device_id); 11024c06356bSdh142964 msg[3] = LE_32(SMP_INDIRECT_RESPONSE | SMP_INDIRECT_REQUEST); 11034c06356bSdh142964 msg[8] = LE_32(DWORD0(pwp->scratch_dma)); 11044c06356bSdh142964 msg[9] = LE_32(DWORD1(pwp->scratch_dma)); 11054c06356bSdh142964 msg[10] = LE_32(reqsz); 11064c06356bSdh142964 msg[11] = 0; 11074c06356bSdh142964 msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff)); 11084c06356bSdh142964 msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff)); 11094c06356bSdh142964 msg[14] = LE_32(rspsz); 11104c06356bSdh142964 msg[15] = 0; 11114c06356bSdh142964 11124c06356bSdh142964 COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 11136745c559SJesse Butler 11143be32c0fSJesse Butler pmcs_hold_iport(pptr->iport); 11153be32c0fSJesse Butler iport = pptr->iport; 11163be32c0fSJesse Butler pmcs_smp_acquire(iport); 11174c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ONCHIP; 11184c06356bSdh142964 htag = pwrk->htag; 11194c06356bSdh142964 INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 11204c06356bSdh142964 pmcs_unlock_phy(pptr); 112196c4a178SChris Horne WAIT_FOR(pwrk, smp_pkt->smp_pkt_timeout * 1000, result); 11224c06356bSdh142964 pmcs_pwork(pwp, pwrk); 11233be32c0fSJesse Butler pmcs_smp_release(iport); 11243be32c0fSJesse Butler pmcs_rele_iport(iport); 1125601c90f1SSrikanth, Ramana pmcs_lock_phy(pptr); 11266745c559SJesse Butler 11274c06356bSdh142964 if (result) { 11284c06356bSdh142964 pmcs_timed_out(pwp, htag, __func__); 11294c06356bSdh142964 if (pmcs_abort(pwp, pptr, htag, 0, 0)) { 11309aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target, 11314c06356bSdh142964 "%s: Unable to issue SMP ABORT for htag 0x%08x", 11324c06356bSdh142964 __func__, htag); 11334c06356bSdh142964 } else { 11349aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target, 11354c06356bSdh142964 "%s: Issuing SMP ABORT for htag 0x%08x", 11364c06356bSdh142964 __func__, htag); 11374c06356bSdh142964 } 11384c06356bSdh142964 pmcs_unlock_phy(pptr); 11394c06356bSdh142964 pmcs_release_scratch(pwp); 114096c4a178SChris Horne smp_pkt->smp_pkt_reason = ETIMEDOUT; 11414c06356bSdh142964 return (DDI_FAILURE); 11424c06356bSdh142964 } 11434c06356bSdh142964 status = LE_32(msg[2]); 11444c06356bSdh142964 if (status == PMCOUT_STATUS_OVERFLOW) { 11454c06356bSdh142964 status = PMCOUT_STATUS_OK; 114696c4a178SChris Horne smp_pkt->smp_pkt_reason = EOVERFLOW; 11474c06356bSdh142964 } 11484c06356bSdh142964 if (status != PMCOUT_STATUS_OK) { 11494c06356bSdh142964 const char *emsg = pmcs_status_str(status); 11504c06356bSdh142964 if (emsg == NULL) { 11519aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target, 11524c06356bSdh142964 "SMP operation failed (0x%x)", status); 11534c06356bSdh142964 } else { 11549aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target, 11554c06356bSdh142964 "SMP operation failed (%s)", emsg); 11564c06356bSdh142964 } 11574c06356bSdh142964 11584c06356bSdh142964 if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) || 11594c06356bSdh142964 (status == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT)) { 116096c4a178SChris Horne smp_pkt->smp_pkt_reason = 116196c4a178SChris Horne will_retry ? EAGAIN : ETIMEDOUT; 11624c06356bSdh142964 result = DDI_FAILURE; 11634c06356bSdh142964 } else if (status == 11644c06356bSdh142964 PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) { 11654c06356bSdh142964 xp = pptr->target; 11664c06356bSdh142964 if (xp == NULL) { 116796c4a178SChris Horne smp_pkt->smp_pkt_reason = EIO; 11684c06356bSdh142964 result = DDI_FAILURE; 11694c06356bSdh142964 goto out; 11704c06356bSdh142964 } 11714c06356bSdh142964 if (xp->dev_state != 11724c06356bSdh142964 PMCS_DEVICE_STATE_NON_OPERATIONAL) { 11734c06356bSdh142964 xp->dev_state = 11744c06356bSdh142964 PMCS_DEVICE_STATE_NON_OPERATIONAL; 11759aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, xp->phy, 11769aed1621SDavid Hollister xp, "%s: Got _IT_NEXUS_LOSS SMP status. " 11774c06356bSdh142964 "Tgt(0x%p) dev_state set to " 11784c06356bSdh142964 "_NON_OPERATIONAL", __func__, 11794c06356bSdh142964 (void *)xp); 11804c06356bSdh142964 } 11814c06356bSdh142964 /* ABORT any pending commands related to this device */ 11824c06356bSdh142964 if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) != 0) { 11834c06356bSdh142964 pptr->abort_pending = 1; 118496c4a178SChris Horne smp_pkt->smp_pkt_reason = EIO; 11854c06356bSdh142964 result = DDI_FAILURE; 11864c06356bSdh142964 } 11874c06356bSdh142964 } else { 118896c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EIO; 11894c06356bSdh142964 result = DDI_FAILURE; 11904c06356bSdh142964 } 11914c06356bSdh142964 } else { 119296c4a178SChris Horne (void) memcpy(smp_pkt->smp_pkt_rsp, 11934c06356bSdh142964 &((uint8_t *)pwp->scratch)[rdoff], rspsz); 119496c4a178SChris Horne if (smp_pkt->smp_pkt_reason == EOVERFLOW) { 11954c06356bSdh142964 result = DDI_FAILURE; 11964c06356bSdh142964 } else { 11974c06356bSdh142964 result = DDI_SUCCESS; 11984c06356bSdh142964 } 11994c06356bSdh142964 } 12004c06356bSdh142964 out: 12019aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, pptr->target, 12029aed1621SDavid Hollister "%s: done for wwn 0x%" PRIx64, __func__, wwn); 12039aed1621SDavid Hollister 12044c06356bSdh142964 pmcs_unlock_phy(pptr); 12054c06356bSdh142964 pmcs_release_scratch(pwp); 12064c06356bSdh142964 return (result); 12074c06356bSdh142964 } 12084c06356bSdh142964 12094c06356bSdh142964 static int 12104c06356bSdh142964 pmcs_smp_init(dev_info_t *self, dev_info_t *child, 121196c4a178SChris Horne smp_hba_tran_t *tran, smp_device_t *smp_sd) 12124c06356bSdh142964 { 121396c4a178SChris Horne _NOTE(ARGUNUSED(tran, smp_sd)); 12144c06356bSdh142964 pmcs_iport_t *iport; 12154c06356bSdh142964 pmcs_hw_t *pwp; 12164c06356bSdh142964 pmcs_xscsi_t *tgt; 12174c06356bSdh142964 pmcs_phy_t *phy, *pphy; 12184c06356bSdh142964 uint64_t wwn; 12194c06356bSdh142964 char *addr, *tgt_port; 12204c06356bSdh142964 int ua_form = 1; 12214c06356bSdh142964 12224c06356bSdh142964 iport = ddi_get_soft_state(pmcs_iport_softstate, 12234c06356bSdh142964 ddi_get_instance(self)); 12244c06356bSdh142964 ASSERT(iport); 12254c06356bSdh142964 if (iport == NULL) 12264c06356bSdh142964 return (DDI_FAILURE); 12274c06356bSdh142964 pwp = iport->pwp; 12284c06356bSdh142964 ASSERT(pwp); 12294c06356bSdh142964 if (pwp == NULL) 12304c06356bSdh142964 return (DDI_FAILURE); 12314c06356bSdh142964 12324c06356bSdh142964 /* Get "target-port" prop from devinfo node */ 12334c06356bSdh142964 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, 12344c06356bSdh142964 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 12354c06356bSdh142964 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) { 1236c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to " 1237c3bc407cSdh142964 "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__); 12384c06356bSdh142964 /* Dont fail _smp_init() because we couldnt get/set a prop */ 12394c06356bSdh142964 return (DDI_SUCCESS); 12404c06356bSdh142964 } 12414c06356bSdh142964 12424c06356bSdh142964 /* 12434c06356bSdh142964 * Validate that this tran_tgt_init is for an active iport. 12444c06356bSdh142964 */ 12454c06356bSdh142964 if (iport->ua_state == UA_INACTIVE) { 1246c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1247c3bc407cSdh142964 "%s: Init on inactive iport for '%s'", __func__, tgt_port); 12484c06356bSdh142964 ddi_prop_free(tgt_port); 12494c06356bSdh142964 return (DDI_FAILURE); 12504c06356bSdh142964 } 12514c06356bSdh142964 12524c06356bSdh142964 mutex_enter(&pwp->lock); 12534c06356bSdh142964 12544c06356bSdh142964 /* Retrieve softstate using unit-address */ 12555c45adf0SJesse Butler tgt = pmcs_get_target(iport, tgt_port, B_TRUE); 12564c06356bSdh142964 if (tgt == NULL) { 1257c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1258c3bc407cSdh142964 "%s: tgt softstate not found", __func__); 12594c06356bSdh142964 ddi_prop_free(tgt_port); 12604c06356bSdh142964 mutex_exit(&pwp->lock); 12614c06356bSdh142964 return (DDI_FAILURE); 12624c06356bSdh142964 } 12634c06356bSdh142964 126473a3eccdSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)", 126573a3eccdSDavid Hollister __func__, ddi_get_name(child), tgt_port); 126673a3eccdSDavid Hollister 126773a3eccdSDavid Hollister mutex_enter(&tgt->statlock); 12684c06356bSdh142964 phy = tgt->phy; 12694c06356bSdh142964 ASSERT(mutex_owned(&phy->phy_lock)); 12704c06356bSdh142964 12714c06356bSdh142964 if (IS_ROOT_PHY(phy)) { 12724c06356bSdh142964 /* Expander attached to HBA - don't ref_count it */ 12734c06356bSdh142964 wwn = pwp->sas_wwns[0]; 12744c06356bSdh142964 } else { 12754c06356bSdh142964 pmcs_inc_phy_ref_count(phy); 12764c06356bSdh142964 12774c06356bSdh142964 /* 12784c06356bSdh142964 * Parent (in topology) is also an expander 12794c06356bSdh142964 * Now that we've increased the ref count on phy, it's OK 12804c06356bSdh142964 * to drop the lock so we can acquire the parent's lock. 12814c06356bSdh142964 */ 12824c06356bSdh142964 pphy = phy->parent; 128373a3eccdSDavid Hollister mutex_exit(&tgt->statlock); 12844c06356bSdh142964 pmcs_unlock_phy(phy); 12854c06356bSdh142964 pmcs_lock_phy(pphy); 12864c06356bSdh142964 wwn = pmcs_barray2wwn(pphy->sas_address); 12874c06356bSdh142964 pmcs_unlock_phy(pphy); 12884c06356bSdh142964 pmcs_lock_phy(phy); 128973a3eccdSDavid Hollister mutex_enter(&tgt->statlock); 12904c06356bSdh142964 } 12914c06356bSdh142964 12924c06356bSdh142964 /* 12934c06356bSdh142964 * If this is the 1st smp_init, add this to our list. 12944c06356bSdh142964 */ 12954c06356bSdh142964 if (tgt->target_num == PMCS_INVALID_TARGET_NUM) { 12964c06356bSdh142964 int target; 12974c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) { 12984c06356bSdh142964 if (pwp->targets[target] != NULL) { 12994c06356bSdh142964 continue; 13004c06356bSdh142964 } 13014c06356bSdh142964 13024c06356bSdh142964 pwp->targets[target] = tgt; 13034c06356bSdh142964 tgt->target_num = (uint16_t)target; 13044c06356bSdh142964 tgt->assigned = 1; 13054c06356bSdh142964 tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL; 13064c06356bSdh142964 break; 13074c06356bSdh142964 } 13084c06356bSdh142964 13094c06356bSdh142964 if (target == pwp->max_dev) { 1310c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 13114c06356bSdh142964 "Target list full."); 13124c06356bSdh142964 goto smp_init_fail; 13134c06356bSdh142964 } 13144c06356bSdh142964 } 13154c06356bSdh142964 13164c06356bSdh142964 if (!pmcs_assign_device(pwp, tgt)) { 13174c06356bSdh142964 pwp->targets[tgt->target_num] = NULL; 1318c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, 13194c06356bSdh142964 "%s: pmcs_assign_device failed for target 0x%p", 13204c06356bSdh142964 __func__, (void *)tgt); 13214c06356bSdh142964 goto smp_init_fail; 13224c06356bSdh142964 } 13234c06356bSdh142964 1324499cfd15SDavid Hollister /* 1325499cfd15SDavid Hollister * Update the attached port and target port pm properties 1326499cfd15SDavid Hollister */ 1327499cfd15SDavid Hollister tgt->smpd = smp_sd; 1328499cfd15SDavid Hollister 13294c06356bSdh142964 pmcs_unlock_phy(phy); 13304c06356bSdh142964 mutex_exit(&pwp->lock); 13314c06356bSdh142964 13324c06356bSdh142964 tgt->ref_count++; 13334c06356bSdh142964 tgt->dtype = phy->dtype; 133473a3eccdSDavid Hollister mutex_exit(&tgt->statlock); 133573a3eccdSDavid Hollister 133673a3eccdSDavid Hollister pmcs_update_phy_pm_props(phy, 0, 0, B_TRUE); 13374c06356bSdh142964 13384c06356bSdh142964 addr = scsi_wwn_to_wwnstr(wwn, ua_form, NULL); 1339499cfd15SDavid Hollister if (smp_device_prop_update_string(smp_sd, SCSI_ADDR_PROP_ATTACHED_PORT, 1340499cfd15SDavid Hollister addr) != DDI_SUCCESS) { 1341c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to set " 1342c3bc407cSdh142964 "prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", __func__); 13434c06356bSdh142964 } 13444c06356bSdh142964 (void) scsi_free_wwnstr(addr); 13454c06356bSdh142964 ddi_prop_free(tgt_port); 13464c06356bSdh142964 return (DDI_SUCCESS); 13474c06356bSdh142964 13484c06356bSdh142964 smp_init_fail: 13494c06356bSdh142964 tgt->phy = NULL; 13504c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM; 13514c06356bSdh142964 phy->target = NULL; 13524c06356bSdh142964 if (!IS_ROOT_PHY(phy)) { 13534c06356bSdh142964 pmcs_dec_phy_ref_count(phy); 13544c06356bSdh142964 } 135573a3eccdSDavid Hollister mutex_exit(&tgt->statlock); 13564c06356bSdh142964 pmcs_unlock_phy(phy); 13574c06356bSdh142964 mutex_exit(&pwp->lock); 13584c06356bSdh142964 ddi_soft_state_bystr_free(iport->tgt_sstate, tgt->unit_address); 13594c06356bSdh142964 ddi_prop_free(tgt_port); 13604c06356bSdh142964 return (DDI_FAILURE); 13614c06356bSdh142964 } 13624c06356bSdh142964 13634c06356bSdh142964 static void 13644c06356bSdh142964 pmcs_smp_free(dev_info_t *self, dev_info_t *child, 136596c4a178SChris Horne smp_hba_tran_t *tran, smp_device_t *smp) 13664c06356bSdh142964 { 13674c06356bSdh142964 _NOTE(ARGUNUSED(tran, smp)); 13684c06356bSdh142964 pmcs_iport_t *iport; 13694c06356bSdh142964 pmcs_hw_t *pwp; 13704c06356bSdh142964 pmcs_xscsi_t *tgt; 1371ee13933aSSrikanth Suravajhala pmcs_phy_t *phyp; 13724c06356bSdh142964 char *tgt_port; 13734c06356bSdh142964 13744c06356bSdh142964 iport = ddi_get_soft_state(pmcs_iport_softstate, 13754c06356bSdh142964 ddi_get_instance(self)); 13764c06356bSdh142964 ASSERT(iport); 13774c06356bSdh142964 if (iport == NULL) 13784c06356bSdh142964 return; 13794c06356bSdh142964 13804c06356bSdh142964 pwp = iport->pwp; 13814c06356bSdh142964 if (pwp == NULL) 13824c06356bSdh142964 return; 13834c06356bSdh142964 ASSERT(pwp); 13844c06356bSdh142964 13854c06356bSdh142964 /* Get "target-port" prop from devinfo node */ 13864c06356bSdh142964 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, 13874c06356bSdh142964 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 13884c06356bSdh142964 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) { 1389c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to " 1390c3bc407cSdh142964 "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__); 13914c06356bSdh142964 return; 13924c06356bSdh142964 } 13939aed1621SDavid Hollister 13944c06356bSdh142964 /* Retrieve softstate using unit-address */ 13959aed1621SDavid Hollister mutex_enter(&pwp->lock); 13964c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port); 13979aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)", __func__, 13989aed1621SDavid Hollister ddi_get_name(child), tgt_port); 13994c06356bSdh142964 ddi_prop_free(tgt_port); 14004c06356bSdh142964 14014c06356bSdh142964 if (tgt == NULL) { 1402c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1403c3bc407cSdh142964 "%s: tgt softstate not found", __func__); 14049aed1621SDavid Hollister mutex_exit(&pwp->lock); 14054c06356bSdh142964 return; 14064c06356bSdh142964 } 14074c06356bSdh142964 1408ee13933aSSrikanth Suravajhala phyp = tgt->phy; 1409ee13933aSSrikanth Suravajhala if (phyp) { 1410ee13933aSSrikanth Suravajhala mutex_enter(&phyp->phy_lock); 1411ee13933aSSrikanth Suravajhala if (!IS_ROOT_PHY(phyp)) { 1412ee13933aSSrikanth Suravajhala pmcs_dec_phy_ref_count(phyp); 1413ee13933aSSrikanth Suravajhala } 1414ee13933aSSrikanth Suravajhala } 14154c06356bSdh142964 mutex_enter(&tgt->statlock); 14164c06356bSdh142964 14174c06356bSdh142964 if (--tgt->ref_count == 0) { 14184c06356bSdh142964 /* 14194c06356bSdh142964 * Remove this target from our list. The softstate 14204c06356bSdh142964 * will remain, and the device will remain registered 14214c06356bSdh142964 * with the hardware unless/until we're told that the 14224c06356bSdh142964 * device physically went away. 14234c06356bSdh142964 */ 1424c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, 14254c06356bSdh142964 "Removing target 0x%p (vtgt %d) from target list", 14264c06356bSdh142964 (void *)tgt, tgt->target_num); 14274c06356bSdh142964 pwp->targets[tgt->target_num] = NULL; 14284c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM; 1429ee13933aSSrikanth Suravajhala if (phyp) { 1430ee13933aSSrikanth Suravajhala phyp->target = NULL; 14315c45adf0SJesse Butler } 1432ee13933aSSrikanth Suravajhala tgt->phy = NULL; 14339aed1621SDavid Hollister pmcs_destroy_target(tgt); 14349aed1621SDavid Hollister } else { 14359aed1621SDavid Hollister mutex_exit(&tgt->statlock); 14364c06356bSdh142964 } 14374c06356bSdh142964 1438ee13933aSSrikanth Suravajhala if (phyp) { 1439ee13933aSSrikanth Suravajhala mutex_exit(&phyp->phy_lock); 1440ee13933aSSrikanth Suravajhala } 14414c06356bSdh142964 mutex_exit(&pwp->lock); 14424c06356bSdh142964 } 14434c06356bSdh142964 14444c06356bSdh142964 static int 14454c06356bSdh142964 pmcs_scsi_quiesce(dev_info_t *dip) 14464c06356bSdh142964 { 14474c06356bSdh142964 pmcs_hw_t *pwp; 14484c06356bSdh142964 int totactive = -1; 14494c06356bSdh142964 pmcs_xscsi_t *xp; 14504c06356bSdh142964 uint16_t target; 14514c06356bSdh142964 14524c06356bSdh142964 if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip))) 14534c06356bSdh142964 return (0); /* iport */ 14544c06356bSdh142964 14554c06356bSdh142964 pwp = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip)); 14564c06356bSdh142964 if (pwp == NULL) { 14574c06356bSdh142964 return (-1); 14584c06356bSdh142964 } 14594c06356bSdh142964 mutex_enter(&pwp->lock); 14604c06356bSdh142964 if (pwp->state != STATE_RUNNING) { 14614c06356bSdh142964 mutex_exit(&pwp->lock); 14624c06356bSdh142964 return (-1); 14634c06356bSdh142964 } 14644c06356bSdh142964 1465c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__); 14665c45adf0SJesse Butler pwp->quiesced = pwp->blocked = 1; 14674c06356bSdh142964 while (totactive) { 14684c06356bSdh142964 totactive = 0; 14694c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) { 14704c06356bSdh142964 xp = pwp->targets[target]; 14714c06356bSdh142964 if (xp == NULL) { 14724c06356bSdh142964 continue; 14734c06356bSdh142964 } 14744c06356bSdh142964 mutex_enter(&xp->statlock); 14754c06356bSdh142964 if (xp->actv_cnt) { 14764c06356bSdh142964 totactive += xp->actv_cnt; 14774c06356bSdh142964 xp->draining = 1; 14784c06356bSdh142964 } 14794c06356bSdh142964 mutex_exit(&xp->statlock); 14804c06356bSdh142964 } 14814c06356bSdh142964 if (totactive) { 14824c06356bSdh142964 cv_wait(&pwp->drain_cv, &pwp->lock); 14834c06356bSdh142964 } 14844c06356bSdh142964 /* 14854c06356bSdh142964 * The pwp->blocked may have been reset. e.g a SCSI bus reset 14864c06356bSdh142964 */ 14874c06356bSdh142964 pwp->blocked = 1; 14884c06356bSdh142964 } 14894c06356bSdh142964 14904c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) { 14914c06356bSdh142964 xp = pwp->targets[target]; 14924c06356bSdh142964 if (xp == NULL) { 14934c06356bSdh142964 continue; 14944c06356bSdh142964 } 14954c06356bSdh142964 mutex_enter(&xp->statlock); 14964c06356bSdh142964 xp->draining = 0; 14974c06356bSdh142964 mutex_exit(&xp->statlock); 14984c06356bSdh142964 } 14994c06356bSdh142964 15004c06356bSdh142964 mutex_exit(&pwp->lock); 15014c06356bSdh142964 if (totactive == 0) { 1502c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 1503c3bc407cSdh142964 "%s drain complete", __func__); 15044c06356bSdh142964 } 15054c06356bSdh142964 return (0); 15064c06356bSdh142964 } 15074c06356bSdh142964 15084c06356bSdh142964 static int 15094c06356bSdh142964 pmcs_scsi_unquiesce(dev_info_t *dip) 15104c06356bSdh142964 { 15114c06356bSdh142964 pmcs_hw_t *pwp; 15124c06356bSdh142964 15134c06356bSdh142964 if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip))) 15144c06356bSdh142964 return (0); /* iport */ 15154c06356bSdh142964 15164c06356bSdh142964 pwp = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip)); 15174c06356bSdh142964 if (pwp == NULL) { 15184c06356bSdh142964 return (-1); 15194c06356bSdh142964 } 15204c06356bSdh142964 mutex_enter(&pwp->lock); 15214c06356bSdh142964 if (pwp->state != STATE_RUNNING) { 15224c06356bSdh142964 mutex_exit(&pwp->lock); 15234c06356bSdh142964 return (-1); 15244c06356bSdh142964 } 1525c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__); 15265c45adf0SJesse Butler pwp->blocked = pwp->quiesced = 0; 15274c06356bSdh142964 mutex_exit(&pwp->lock); 15284c06356bSdh142964 15294c06356bSdh142964 /* 15304c06356bSdh142964 * Run all pending commands. 15314c06356bSdh142964 */ 15324c06356bSdh142964 pmcs_scsa_wq_run(pwp); 15334c06356bSdh142964 15344c06356bSdh142964 /* 15354c06356bSdh142964 * Complete all completed commands. 15364c06356bSdh142964 * This also unlocks us. 15374c06356bSdh142964 */ 15384c06356bSdh142964 PMCS_CQ_RUN(pwp); 15394c06356bSdh142964 return (0); 15404c06356bSdh142964 } 15414c06356bSdh142964 15424c06356bSdh142964 /* 15434c06356bSdh142964 * Start commands for a particular device 15444c06356bSdh142964 * If the actual start of a command fails, return B_FALSE. Any other result 15454c06356bSdh142964 * is a B_TRUE return. 15464c06356bSdh142964 */ 15474c06356bSdh142964 boolean_t 15484c06356bSdh142964 pmcs_scsa_wq_run_one(pmcs_hw_t *pwp, pmcs_xscsi_t *xp) 15494c06356bSdh142964 { 15504c06356bSdh142964 pmcs_cmd_t *sp; 15514c06356bSdh142964 pmcs_phy_t *phyp; 15524c06356bSdh142964 pmcwork_t *pwrk; 15534c06356bSdh142964 boolean_t run_one, blocked; 15544c06356bSdh142964 int rval; 15554c06356bSdh142964 15564c06356bSdh142964 /* 15574c06356bSdh142964 * First, check to see if we're blocked or resource limited 15584c06356bSdh142964 */ 15594c06356bSdh142964 mutex_enter(&pwp->lock); 15604c06356bSdh142964 blocked = pwp->blocked; 15614c06356bSdh142964 /* 15624c06356bSdh142964 * If resource_limited is set, we're resource constrained and 15634c06356bSdh142964 * we will run only one work request for this target. 15644c06356bSdh142964 */ 15654c06356bSdh142964 run_one = pwp->resource_limited; 15664c06356bSdh142964 mutex_exit(&pwp->lock); 15674c06356bSdh142964 15684c06356bSdh142964 if (blocked) { 15694c06356bSdh142964 /* Queues will get restarted when we get unblocked */ 15704c06356bSdh142964 return (B_TRUE); 15714c06356bSdh142964 } 15724c06356bSdh142964 15734c06356bSdh142964 /* 15744c06356bSdh142964 * Might as well verify the queue is not empty before moving on 15754c06356bSdh142964 */ 15764c06356bSdh142964 mutex_enter(&xp->wqlock); 15774c06356bSdh142964 if (STAILQ_EMPTY(&xp->wq)) { 15784c06356bSdh142964 mutex_exit(&xp->wqlock); 15794c06356bSdh142964 return (B_TRUE); 15804c06356bSdh142964 } 15814c06356bSdh142964 mutex_exit(&xp->wqlock); 15824c06356bSdh142964 15834c06356bSdh142964 /* 15844c06356bSdh142964 * If we're draining or resetting, just reschedule work queue and bail. 15854c06356bSdh142964 */ 15864c06356bSdh142964 mutex_enter(&xp->statlock); 15874c06356bSdh142964 if (xp->draining || xp->resetting || xp->special_running || 15884c06356bSdh142964 xp->special_needed) { 15894c06356bSdh142964 mutex_exit(&xp->statlock); 15904c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 15914c06356bSdh142964 return (B_TRUE); 15924c06356bSdh142964 } 15934c06356bSdh142964 15944c06356bSdh142964 /* 1595b18a19c2SJesse Butler * Next, check to see if the target is gone. 15964c06356bSdh142964 */ 1597b18a19c2SJesse Butler if (xp->dev_gone) { 1598c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 1599b18a19c2SJesse Butler "%s: Flushing wait queue for dead tgt 0x%p", __func__, 16004c06356bSdh142964 (void *)xp); 16014c06356bSdh142964 pmcs_flush_target_queues(pwp, xp, PMCS_TGT_WAIT_QUEUE); 16024c06356bSdh142964 mutex_exit(&xp->statlock); 16034c06356bSdh142964 return (B_TRUE); 16044c06356bSdh142964 } 16054c06356bSdh142964 16064c06356bSdh142964 /* 16074c06356bSdh142964 * Increment the PHY's ref_count now so we know it won't go away 16084c06356bSdh142964 * after we drop the target lock. Drop it before returning. If the 16094c06356bSdh142964 * PHY dies, the commands we attempt to send will fail, but at least 16104c06356bSdh142964 * we know we have a real PHY pointer. 16114c06356bSdh142964 */ 16124c06356bSdh142964 phyp = xp->phy; 16134c06356bSdh142964 pmcs_inc_phy_ref_count(phyp); 16144c06356bSdh142964 mutex_exit(&xp->statlock); 16154c06356bSdh142964 16164c06356bSdh142964 mutex_enter(&xp->wqlock); 16174c06356bSdh142964 while ((sp = STAILQ_FIRST(&xp->wq)) != NULL) { 16184c06356bSdh142964 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_CBACK, phyp); 16194c06356bSdh142964 if (pwrk == NULL) { 1620c280a92bSDavid Hollister mutex_exit(&xp->wqlock); 1621c280a92bSDavid Hollister mutex_enter(&pwp->lock); 1622c280a92bSDavid Hollister if (pwp->resource_limited == 0) { 1623c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 16244c06356bSdh142964 "%s: out of work structures", __func__); 1625c280a92bSDavid Hollister } 1626c280a92bSDavid Hollister pwp->resource_limited = 1; 16274c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 1628c280a92bSDavid Hollister mutex_exit(&pwp->lock); 1629c280a92bSDavid Hollister return (B_FALSE); 16304c06356bSdh142964 } 16314c06356bSdh142964 STAILQ_REMOVE_HEAD(&xp->wq, cmd_next); 16324c06356bSdh142964 mutex_exit(&xp->wqlock); 16334c06356bSdh142964 16344c06356bSdh142964 pwrk->xp = xp; 16354c06356bSdh142964 pwrk->arg = sp; 16364c06356bSdh142964 sp->cmd_tag = pwrk->htag; 16374c06356bSdh142964 pwrk->timer = US2WT(CMD2PKT(sp)->pkt_time * 1000000); 16384c06356bSdh142964 if (pwrk->timer == 0) { 16394c06356bSdh142964 pwrk->timer = US2WT(1000000); 16404c06356bSdh142964 } 16414c06356bSdh142964 16424c06356bSdh142964 pwrk->dtype = xp->dtype; 16434c06356bSdh142964 16444c06356bSdh142964 if (xp->dtype == SAS) { 16454c06356bSdh142964 pwrk->ptr = (void *) pmcs_SAS_done; 16464c06356bSdh142964 if ((rval = pmcs_SAS_run(sp, pwrk)) != 0) { 16474c06356bSdh142964 sp->cmd_tag = NULL; 16484c06356bSdh142964 pmcs_dec_phy_ref_count(phyp); 16494c06356bSdh142964 pmcs_pwork(pwp, pwrk); 16504c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 16514c06356bSdh142964 if (rval == PMCS_WQ_RUN_FAIL_RES) { 16524c06356bSdh142964 return (B_FALSE); 16534c06356bSdh142964 } else { 16544c06356bSdh142964 return (B_TRUE); 16554c06356bSdh142964 } 16564c06356bSdh142964 } 16574c06356bSdh142964 } else { 16584c06356bSdh142964 ASSERT(xp->dtype == SATA); 16594c06356bSdh142964 pwrk->ptr = (void *) pmcs_SATA_done; 16604c06356bSdh142964 if ((rval = pmcs_SATA_run(sp, pwrk)) != 0) { 16614c06356bSdh142964 sp->cmd_tag = NULL; 16624c06356bSdh142964 pmcs_dec_phy_ref_count(phyp); 16634c06356bSdh142964 pmcs_pwork(pwp, pwrk); 16644c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 16654c06356bSdh142964 if (rval == PMCS_WQ_RUN_FAIL_RES) { 16664c06356bSdh142964 return (B_FALSE); 16674c06356bSdh142964 } else { 16684c06356bSdh142964 return (B_TRUE); 16694c06356bSdh142964 } 16704c06356bSdh142964 } 16714c06356bSdh142964 } 16724c06356bSdh142964 16734c06356bSdh142964 if (run_one) { 16744c06356bSdh142964 goto wq_out; 16754c06356bSdh142964 } 16764c06356bSdh142964 mutex_enter(&xp->wqlock); 16774c06356bSdh142964 } 16784c06356bSdh142964 16794c06356bSdh142964 mutex_exit(&xp->wqlock); 16804c06356bSdh142964 16814c06356bSdh142964 wq_out: 16824c06356bSdh142964 pmcs_dec_phy_ref_count(phyp); 16834c06356bSdh142964 return (B_TRUE); 16844c06356bSdh142964 } 16854c06356bSdh142964 16864c06356bSdh142964 /* 16874c06356bSdh142964 * Start commands for all devices. 16884c06356bSdh142964 */ 16894c06356bSdh142964 void 16904c06356bSdh142964 pmcs_scsa_wq_run(pmcs_hw_t *pwp) 16914c06356bSdh142964 { 16924c06356bSdh142964 pmcs_xscsi_t *xp; 16934c06356bSdh142964 uint16_t target_start, target; 16944c06356bSdh142964 boolean_t rval = B_TRUE; 16954c06356bSdh142964 16964c06356bSdh142964 mutex_enter(&pwp->lock); 16974c06356bSdh142964 target_start = pwp->last_wq_dev; 16984c06356bSdh142964 target = target_start; 16994c06356bSdh142964 17004c06356bSdh142964 do { 17014c06356bSdh142964 xp = pwp->targets[target]; 1702429adc13SSrikanth, Ramana if ((xp == NULL) || (STAILQ_EMPTY(&xp->wq))) { 17034c06356bSdh142964 if (++target == pwp->max_dev) { 17044c06356bSdh142964 target = 0; 17054c06356bSdh142964 } 17064c06356bSdh142964 continue; 17074c06356bSdh142964 } 17084c06356bSdh142964 17094c06356bSdh142964 mutex_exit(&pwp->lock); 17104c06356bSdh142964 rval = pmcs_scsa_wq_run_one(pwp, xp); 17114c06356bSdh142964 mutex_enter(&pwp->lock); 1712429adc13SSrikanth, Ramana 1713429adc13SSrikanth, Ramana if (rval == B_FALSE) { 17144c06356bSdh142964 break; 17154c06356bSdh142964 } 1716429adc13SSrikanth, Ramana 17174c06356bSdh142964 if (++target == pwp->max_dev) { 17184c06356bSdh142964 target = 0; 17194c06356bSdh142964 } 17204c06356bSdh142964 } while (target != target_start); 17214c06356bSdh142964 17224c06356bSdh142964 if (rval) { 1723c280a92bSDavid Hollister /* 1724c280a92bSDavid Hollister * If we were resource limited, but apparently are not now, 1725c280a92bSDavid Hollister * reschedule the work queues anyway. 1726c280a92bSDavid Hollister */ 1727c280a92bSDavid Hollister if (pwp->resource_limited) { 1728c280a92bSDavid Hollister SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 1729c280a92bSDavid Hollister } 17304c06356bSdh142964 pwp->resource_limited = 0; /* Not resource-constrained */ 17314c06356bSdh142964 } else { 1732c280a92bSDavid Hollister /* 1733c280a92bSDavid Hollister * Give everybody a chance, and reschedule to run the queues 1734c280a92bSDavid Hollister * again as long as we're limited. 1735c280a92bSDavid Hollister */ 1736c280a92bSDavid Hollister pwp->resource_limited = 1; 1737c280a92bSDavid Hollister SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 17384c06356bSdh142964 } 17394c06356bSdh142964 17404c06356bSdh142964 pwp->last_wq_dev = target; 17414c06356bSdh142964 mutex_exit(&pwp->lock); 17424c06356bSdh142964 } 17434c06356bSdh142964 17444c06356bSdh142964 /* 17454c06356bSdh142964 * Pull the completion queue, drop the lock and complete all elements. 17464c06356bSdh142964 */ 17474c06356bSdh142964 17484c06356bSdh142964 void 17494c06356bSdh142964 pmcs_scsa_cq_run(void *arg) 17504c06356bSdh142964 { 17514c06356bSdh142964 pmcs_cq_thr_info_t *cqti = (pmcs_cq_thr_info_t *)arg; 17524c06356bSdh142964 pmcs_hw_t *pwp = cqti->cq_pwp; 17534c06356bSdh142964 pmcs_cmd_t *sp, *nxt; 17544c06356bSdh142964 struct scsi_pkt *pkt; 1755601c90f1SSrikanth, Ramana pmcs_xscsi_t *tgt; 17564c06356bSdh142964 pmcs_iocomp_cb_t *ioccb, *ioccb_next; 17574c06356bSdh142964 pmcs_cb_t callback; 17584c06356bSdh142964 17594c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__start, pmcs_cq_thr_info_t *, cqti); 17604c06356bSdh142964 17614c06356bSdh142964 mutex_enter(&pwp->cq_lock); 17624c06356bSdh142964 17634c06356bSdh142964 while (!pwp->cq_info.cq_stop) { 17644c06356bSdh142964 /* 17654c06356bSdh142964 * First, check the I/O completion callback queue. 17664c06356bSdh142964 */ 17674c06356bSdh142964 ioccb = pwp->iocomp_cb_head; 17684c06356bSdh142964 pwp->iocomp_cb_head = NULL; 17694c06356bSdh142964 pwp->iocomp_cb_tail = NULL; 17704c06356bSdh142964 mutex_exit(&pwp->cq_lock); 17714c06356bSdh142964 17724c06356bSdh142964 while (ioccb) { 17734c06356bSdh142964 /* 17744c06356bSdh142964 * Grab the lock on the work structure. The callback 17754c06356bSdh142964 * routine is responsible for clearing it. 17764c06356bSdh142964 */ 17774c06356bSdh142964 mutex_enter(&ioccb->pwrk->lock); 17784c06356bSdh142964 ioccb_next = ioccb->next; 17794c06356bSdh142964 callback = (pmcs_cb_t)ioccb->pwrk->ptr; 17804c06356bSdh142964 (*callback)(pwp, ioccb->pwrk, 17814c06356bSdh142964 (uint32_t *)((void *)ioccb->iomb)); 17824c06356bSdh142964 kmem_cache_free(pwp->iocomp_cb_cache, ioccb); 17834c06356bSdh142964 ioccb = ioccb_next; 17844c06356bSdh142964 } 17854c06356bSdh142964 17864c06356bSdh142964 /* 17874c06356bSdh142964 * Next, run the completion queue 17884c06356bSdh142964 */ 17894c06356bSdh142964 mutex_enter(&pwp->cq_lock); 17904c06356bSdh142964 sp = STAILQ_FIRST(&pwp->cq); 17914c06356bSdh142964 STAILQ_INIT(&pwp->cq); 17924c06356bSdh142964 mutex_exit(&pwp->cq_lock); 17934c06356bSdh142964 17944c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__start__loop, 17954c06356bSdh142964 pmcs_cq_thr_info_t *, cqti); 17964c06356bSdh142964 17974c06356bSdh142964 if (sp && pmcs_check_acc_dma_handle(pwp)) { 17984c06356bSdh142964 ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED); 17994c06356bSdh142964 } 18004c06356bSdh142964 18014c06356bSdh142964 while (sp) { 18024c06356bSdh142964 nxt = STAILQ_NEXT(sp, cmd_next); 18034c06356bSdh142964 pkt = CMD2PKT(sp); 1804601c90f1SSrikanth, Ramana tgt = sp->cmd_target; 1805601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, tgt, 18064c06356bSdh142964 "%s: calling completion on %p for tgt %p", __func__, 1807601c90f1SSrikanth, Ramana (void *)sp, (void *)tgt); 1808601c90f1SSrikanth, Ramana if (tgt) { 1809601c90f1SSrikanth, Ramana mutex_enter(&tgt->statlock); 1810601c90f1SSrikanth, Ramana ASSERT(tgt->actv_pkts != 0); 1811601c90f1SSrikanth, Ramana tgt->actv_pkts--; 1812601c90f1SSrikanth, Ramana mutex_exit(&tgt->statlock); 1813601c90f1SSrikanth, Ramana } 18144c06356bSdh142964 scsi_hba_pkt_comp(pkt); 18154c06356bSdh142964 sp = nxt; 18164c06356bSdh142964 } 18174c06356bSdh142964 18184c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__end__loop, 18194c06356bSdh142964 pmcs_cq_thr_info_t *, cqti); 18204c06356bSdh142964 18218f514e74SDavid Hollister /* 18228f514e74SDavid Hollister * Check if there are more completions to do. If so, and we've 18238f514e74SDavid Hollister * not been told to stop, skip the wait and cycle through again. 18248f514e74SDavid Hollister */ 18258f514e74SDavid Hollister mutex_enter(&pwp->cq_lock); 18268f514e74SDavid Hollister if ((pwp->iocomp_cb_head == NULL) && STAILQ_EMPTY(&pwp->cq) && 18278f514e74SDavid Hollister !pwp->cq_info.cq_stop) { 18288f514e74SDavid Hollister mutex_exit(&pwp->cq_lock); 18294c06356bSdh142964 mutex_enter(&cqti->cq_thr_lock); 18304c06356bSdh142964 cv_wait(&cqti->cq_cv, &cqti->cq_thr_lock); 18314c06356bSdh142964 mutex_exit(&cqti->cq_thr_lock); 18324c06356bSdh142964 mutex_enter(&pwp->cq_lock); 18334c06356bSdh142964 } 18348f514e74SDavid Hollister } 18354c06356bSdh142964 18364c06356bSdh142964 mutex_exit(&pwp->cq_lock); 18374c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__stop, pmcs_cq_thr_info_t *, cqti); 18384c06356bSdh142964 thread_exit(); 18394c06356bSdh142964 } 18404c06356bSdh142964 18414c06356bSdh142964 /* 18424c06356bSdh142964 * Run a SAS command. Called with pwrk->lock held, returns unlocked. 18434c06356bSdh142964 */ 18444c06356bSdh142964 static int 18454c06356bSdh142964 pmcs_SAS_run(pmcs_cmd_t *sp, pmcwork_t *pwrk) 18464c06356bSdh142964 { 18474c06356bSdh142964 pmcs_hw_t *pwp = CMD2PMC(sp); 18484c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 18494c06356bSdh142964 pmcs_xscsi_t *xp = pwrk->xp; 18504c06356bSdh142964 uint32_t iq, *ptr; 18514c06356bSdh142964 sas_ssp_cmd_iu_t sc; 18524c06356bSdh142964 18533be32c0fSJesse Butler ASSERT(xp != NULL); 18544c06356bSdh142964 mutex_enter(&xp->statlock); 1855b18a19c2SJesse Butler if (!xp->assigned) { 18564c06356bSdh142964 mutex_exit(&xp->statlock); 18574c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 18584c06356bSdh142964 } 18594c06356bSdh142964 if ((xp->actv_cnt >= xp->qdepth) || xp->recover_wait) { 18604c06356bSdh142964 mutex_exit(&xp->statlock); 18614c06356bSdh142964 mutex_enter(&xp->wqlock); 18624c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 18634c06356bSdh142964 mutex_exit(&xp->wqlock); 18644c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 18654c06356bSdh142964 } 18664c06356bSdh142964 GET_IO_IQ_ENTRY(pwp, ptr, pwrk->phy->device_id, iq); 18674c06356bSdh142964 if (ptr == NULL) { 18684c06356bSdh142964 mutex_exit(&xp->statlock); 18694c06356bSdh142964 /* 18704c06356bSdh142964 * This is a temporary failure not likely to unblocked by 18714c06356bSdh142964 * commands completing as the test for scheduling the 18724c06356bSdh142964 * restart of work is a per-device test. 18734c06356bSdh142964 */ 18744c06356bSdh142964 mutex_enter(&xp->wqlock); 18754c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 18764c06356bSdh142964 mutex_exit(&xp->wqlock); 1877c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 18784c06356bSdh142964 "%s: Failed to get IO IQ entry for tgt %d", 18794c06356bSdh142964 __func__, xp->target_num); 18804c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES); 18814c06356bSdh142964 18824c06356bSdh142964 } 18834c06356bSdh142964 18844c06356bSdh142964 ptr[0] = 18854c06356bSdh142964 LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SSP_INI_IO_START)); 18864c06356bSdh142964 ptr[1] = LE_32(pwrk->htag); 18874c06356bSdh142964 ptr[2] = LE_32(pwrk->phy->device_id); 18884c06356bSdh142964 ptr[3] = LE_32(pkt->pkt_dma_len); 18894c06356bSdh142964 if (ptr[3]) { 18904c06356bSdh142964 ASSERT(pkt->pkt_numcookies); 18914c06356bSdh142964 if (pkt->pkt_dma_flags & DDI_DMA_READ) { 18924c06356bSdh142964 ptr[4] = LE_32(PMCIN_DATADIR_2_INI); 18934c06356bSdh142964 } else { 18944c06356bSdh142964 ptr[4] = LE_32(PMCIN_DATADIR_2_DEV); 18954c06356bSdh142964 } 18964c06356bSdh142964 if (pmcs_dma_load(pwp, sp, ptr)) { 18974c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]); 18984c06356bSdh142964 mutex_exit(&xp->statlock); 18994c06356bSdh142964 mutex_enter(&xp->wqlock); 19004c06356bSdh142964 if (STAILQ_EMPTY(&xp->wq)) { 19014c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 19024c06356bSdh142964 mutex_exit(&xp->wqlock); 19034c06356bSdh142964 } else { 19044c06356bSdh142964 mutex_exit(&xp->wqlock); 19054c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = STATUS_QFULL; 19064c06356bSdh142964 CMD2PKT(sp)->pkt_reason = CMD_CMPLT; 19074c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | 19084c06356bSdh142964 STATE_GOT_TARGET | STATE_SENT_CMD | 19094c06356bSdh142964 STATE_GOT_STATUS; 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); 19174c06356bSdh142964 } 19184c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES); 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; 19634c06356bSdh142964 mutex_exit(&pwrk->lock); 1964c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 19654c06356bSdh142964 "%s: giving pkt %p (tag %x) to the hardware", __func__, 19664c06356bSdh142964 (void *)pkt, pwrk->htag); 19674c06356bSdh142964 #ifdef DEBUG 19684c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SAS INI Message", ptr); 19694c06356bSdh142964 #endif 19704c06356bSdh142964 mutex_enter(&xp->aqlock); 19714c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next); 19724c06356bSdh142964 mutex_exit(&xp->aqlock); 19734c06356bSdh142964 INC_IQ_ENTRY(pwp, iq); 19744c06356bSdh142964 19754c06356bSdh142964 /* 19764c06356bSdh142964 * If we just submitted the last command queued from device state 19774c06356bSdh142964 * recovery, clear the wq_recovery_tail pointer. 19784c06356bSdh142964 */ 19794c06356bSdh142964 mutex_enter(&xp->wqlock); 19804c06356bSdh142964 if (xp->wq_recovery_tail == sp) { 19814c06356bSdh142964 xp->wq_recovery_tail = NULL; 19824c06356bSdh142964 } 19834c06356bSdh142964 mutex_exit(&xp->wqlock); 19844c06356bSdh142964 19854c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 19864c06356bSdh142964 } 19874c06356bSdh142964 19884c06356bSdh142964 /* 19894c06356bSdh142964 * Complete a SAS command 19904c06356bSdh142964 * 19914c06356bSdh142964 * Called with pwrk lock held. 19924c06356bSdh142964 * The free of pwrk releases the lock. 19934c06356bSdh142964 */ 19944c06356bSdh142964 19954c06356bSdh142964 static void 19964c06356bSdh142964 pmcs_SAS_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg) 19974c06356bSdh142964 { 19984c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg; 19994c06356bSdh142964 pmcs_phy_t *pptr = pwrk->phy; 20004c06356bSdh142964 pmcs_xscsi_t *xp = pwrk->xp; 20014c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 20024c06356bSdh142964 int dead; 20034c06356bSdh142964 uint32_t sts; 20044c06356bSdh142964 boolean_t aborted = B_FALSE; 20054c06356bSdh142964 boolean_t do_ds_recovery = B_FALSE; 20064c06356bSdh142964 20074c06356bSdh142964 ASSERT(xp != NULL); 20084c06356bSdh142964 ASSERT(sp != NULL); 20094c06356bSdh142964 ASSERT(pptr != NULL); 20104c06356bSdh142964 20114c06356bSdh142964 DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int, 20124c06356bSdh142964 (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start, 20134c06356bSdh142964 hrtime_t, gethrtime()); 20144c06356bSdh142964 20154c06356bSdh142964 dead = pwrk->dead; 20164c06356bSdh142964 20174c06356bSdh142964 if (msg) { 20184c06356bSdh142964 sts = LE_32(msg[2]); 20194c06356bSdh142964 } else { 20204c06356bSdh142964 sts = 0; 20214c06356bSdh142964 } 20224c06356bSdh142964 20234c06356bSdh142964 if (dead != 0) { 2024c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag " 2025c3bc407cSdh142964 "0x%x for %s", __func__, pwrk->htag, pptr->path); 20264c06356bSdh142964 goto out; 20274c06356bSdh142964 } 20284c06356bSdh142964 20294c06356bSdh142964 if (sts == PMCOUT_STATUS_ABORTED) { 20304c06356bSdh142964 aborted = B_TRUE; 20314c06356bSdh142964 } 20324c06356bSdh142964 20334c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) { 2034c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 20354c06356bSdh142964 "%s: cmd 0x%p (tag 0x%x) timed out for %s", 20364c06356bSdh142964 __func__, (void *)sp, pwrk->htag, pptr->path); 2037601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD; 2038601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | 2039601c90f1SSrikanth, Ramana STATE_SENT_CMD; 2040601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT; 20414c06356bSdh142964 goto out; 20424c06356bSdh142964 } 20434c06356bSdh142964 20444c06356bSdh142964 /* 20454c06356bSdh142964 * If the status isn't okay but not underflow, 20464c06356bSdh142964 * step to the side and parse the (possible) error. 20474c06356bSdh142964 */ 20484c06356bSdh142964 #ifdef DEBUG 20494c06356bSdh142964 if (msg) { 20504c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg); 20514c06356bSdh142964 } 20524c06356bSdh142964 #endif 20534c06356bSdh142964 if (!msg) { 20544c06356bSdh142964 goto out; 20554c06356bSdh142964 } 20564c06356bSdh142964 20574c06356bSdh142964 switch (sts) { 20584c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS: 20594c06356bSdh142964 case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: 20604c06356bSdh142964 case PMCOUT_STATUS_IO_DS_IN_RECOVERY: 2061c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 2062601c90f1SSrikanth, Ramana "%s: PHY %s requires DS recovery (status=%d)", 20634c06356bSdh142964 __func__, pptr->path, sts); 20644c06356bSdh142964 do_ds_recovery = B_TRUE; 20654c06356bSdh142964 break; 20664c06356bSdh142964 case PMCOUT_STATUS_UNDERFLOW: 20674c06356bSdh142964 (void) pmcs_set_resid(pkt, pkt->pkt_dma_len, LE_32(msg[3])); 2068c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, NULL, NULL, 20694c06356bSdh142964 "%s: underflow %u for cdb 0x%x", 20704c06356bSdh142964 __func__, LE_32(msg[3]), pkt->pkt_cdbp[0] & 0xff); 20714c06356bSdh142964 sts = PMCOUT_STATUS_OK; 20724c06356bSdh142964 msg[3] = 0; 20734c06356bSdh142964 break; 20744c06356bSdh142964 case PMCOUT_STATUS_OK: 20754c06356bSdh142964 pkt->pkt_resid = 0; 20764c06356bSdh142964 break; 20774c06356bSdh142964 } 20784c06356bSdh142964 20794c06356bSdh142964 if (sts != PMCOUT_STATUS_OK) { 2080658280b6SDavid Hollister pmcs_ioerror(pwp, SAS, pwrk, msg, sts); 20814c06356bSdh142964 } else { 20824c06356bSdh142964 if (msg[3]) { 20834c06356bSdh142964 uint8_t local[PMCS_QENTRY_SIZE << 1], *xd; 20844c06356bSdh142964 sas_ssp_rsp_iu_t *rptr = (void *)local; 20854c06356bSdh142964 const int lim = 20864c06356bSdh142964 (PMCS_QENTRY_SIZE << 1) - SAS_RSP_HDR_SIZE; 20874c06356bSdh142964 static const uint8_t ssp_rsp_evec[] = { 20884c06356bSdh142964 0x58, 0x61, 0x56, 0x72, 0x00 20894c06356bSdh142964 }; 20904c06356bSdh142964 20914c06356bSdh142964 /* 20924c06356bSdh142964 * Transform the the first part of the response 20934c06356bSdh142964 * to host canonical form. This gives us enough 20944c06356bSdh142964 * information to figure out what to do with the 20954c06356bSdh142964 * rest (which remains unchanged in the incoming 20964c06356bSdh142964 * message which can be up to two queue entries 20974c06356bSdh142964 * in length). 20984c06356bSdh142964 */ 20994c06356bSdh142964 pmcs_endian_transform(pwp, local, &msg[5], 21004c06356bSdh142964 ssp_rsp_evec); 21014c06356bSdh142964 xd = (uint8_t *)(&msg[5]); 21024c06356bSdh142964 xd += SAS_RSP_HDR_SIZE; 21034c06356bSdh142964 21044c06356bSdh142964 if (rptr->datapres == SAS_RSP_DATAPRES_RESPONSE_DATA) { 21054c06356bSdh142964 if (rptr->response_data_length != 4) { 21064c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 21074c06356bSdh142964 "Bad SAS RESPONSE DATA LENGTH", 21084c06356bSdh142964 msg); 21094c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 21104c06356bSdh142964 goto out; 21114c06356bSdh142964 } 21124c06356bSdh142964 (void) memcpy(&sts, xd, sizeof (uint32_t)); 21134c06356bSdh142964 sts = BE_32(sts); 21144c06356bSdh142964 /* 21154c06356bSdh142964 * The only response code we should legally get 21164c06356bSdh142964 * here is an INVALID FRAME response code. 21174c06356bSdh142964 */ 21184c06356bSdh142964 if (sts == SAS_RSP_INVALID_FRAME) { 2119c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 21204c06356bSdh142964 "%s: pkt %p tgt %u path %s " 21214c06356bSdh142964 "completed: INVALID FRAME response", 21224c06356bSdh142964 __func__, (void *)pkt, 21234c06356bSdh142964 xp->target_num, pptr->path); 21244c06356bSdh142964 } else { 2125c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 21264c06356bSdh142964 "%s: pkt %p tgt %u path %s " 21274c06356bSdh142964 "completed: illegal response 0x%x", 21284c06356bSdh142964 __func__, (void *)pkt, 21294c06356bSdh142964 xp->target_num, pptr->path, sts); 21304c06356bSdh142964 } 21314c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 21324c06356bSdh142964 goto out; 21334c06356bSdh142964 } 21344c06356bSdh142964 if (rptr->datapres == SAS_RSP_DATAPRES_SENSE_DATA) { 21354c06356bSdh142964 uint32_t slen; 21364c06356bSdh142964 slen = rptr->sense_data_length; 21374c06356bSdh142964 if (slen > lim) { 21384c06356bSdh142964 slen = lim; 21394c06356bSdh142964 } 21404c06356bSdh142964 pmcs_latch_status(pwp, sp, rptr->status, xd, 21414c06356bSdh142964 slen, pptr->path); 21424c06356bSdh142964 } else if (rptr->datapres == SAS_RSP_DATAPRES_NO_DATA) { 21434b456463SDavid Hollister pmcout_ssp_comp_t *sspcp; 21444b456463SDavid Hollister sspcp = (pmcout_ssp_comp_t *)msg; 21454b456463SDavid Hollister uint32_t *residp; 21464c06356bSdh142964 /* 21474c06356bSdh142964 * This is the case for a plain SCSI status. 21484b456463SDavid Hollister * Note: If RESC_V is set and we're here, there 21494b456463SDavid Hollister * is a residual. We need to find it and update 21504b456463SDavid Hollister * the packet accordingly. 21514c06356bSdh142964 */ 21524c06356bSdh142964 pmcs_latch_status(pwp, sp, rptr->status, NULL, 21534c06356bSdh142964 0, pptr->path); 21544b456463SDavid Hollister 21554b456463SDavid Hollister if (sspcp->resc_v) { 21564b456463SDavid Hollister /* 21574b456463SDavid Hollister * Point residual to the SSP_RESP_IU 21584b456463SDavid Hollister */ 21594b456463SDavid Hollister residp = (uint32_t *)(sspcp + 1); 21604b456463SDavid Hollister /* 21614b456463SDavid Hollister * param contains the number of bytes 21624b456463SDavid Hollister * between where the SSP_RESP_IU may 21634b456463SDavid Hollister * or may not be and the residual. 21644b456463SDavid Hollister * Increment residp by the appropriate 21654b456463SDavid Hollister * number of words: (param+resc_pad)/4). 21664b456463SDavid Hollister */ 21674b456463SDavid Hollister residp += (LE_32(sspcp->param) + 21684b456463SDavid Hollister sspcp->resc_pad) / 21694b456463SDavid Hollister sizeof (uint32_t); 21704b456463SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, 21714b456463SDavid Hollister pptr, xp, "%s: tgt 0x%p " 21724b456463SDavid Hollister "residual %d for pkt 0x%p", 21734b456463SDavid Hollister __func__, (void *) xp, *residp, 21744b456463SDavid Hollister (void *) pkt); 21754b456463SDavid Hollister ASSERT(LE_32(*residp) <= 21764b456463SDavid Hollister pkt->pkt_dma_len); 21774b456463SDavid Hollister (void) pmcs_set_resid(pkt, 21784b456463SDavid Hollister pkt->pkt_dma_len, LE_32(*residp)); 21794b456463SDavid Hollister } 21804c06356bSdh142964 } else { 21814c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 21824c06356bSdh142964 "illegal SAS response", msg); 21834c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 21844c06356bSdh142964 goto out; 21854c06356bSdh142964 } 21864c06356bSdh142964 } else { 21874c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, 21884c06356bSdh142964 pptr->path); 21894c06356bSdh142964 } 21904c06356bSdh142964 if (pkt->pkt_dma_len) { 21914c06356bSdh142964 pkt->pkt_state |= STATE_XFERRED_DATA; 21924c06356bSdh142964 } 21934c06356bSdh142964 } 2194c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, 21954c06356bSdh142964 "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x", 21964c06356bSdh142964 __func__, (void *)pkt, xp->target_num, pkt->pkt_reason, 21974c06356bSdh142964 pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]); 21984c06356bSdh142964 21994c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_ABORTED) { 2200c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 22014c06356bSdh142964 "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p", 22024c06356bSdh142964 __func__, (void *)pkt, pptr->path, (void *)pwrk); 22034c06356bSdh142964 aborted = B_TRUE; 22044c06356bSdh142964 } 22054c06356bSdh142964 22064c06356bSdh142964 out: 22074c06356bSdh142964 pmcs_dma_unload(pwp, sp); 22084c06356bSdh142964 mutex_enter(&xp->statlock); 22099aed1621SDavid Hollister 22109aed1621SDavid Hollister /* 22115c45adf0SJesse Butler * If the device no longer has a PHY pointer, clear the PHY pointer 22125c45adf0SJesse Butler * from the work structure before we free it. Otherwise, pmcs_pwork 22135c45adf0SJesse Butler * may decrement the ref_count on a PHY that's been freed. 22145c45adf0SJesse Butler */ 22155c45adf0SJesse Butler if (xp->phy == NULL) { 22165c45adf0SJesse Butler pwrk->phy = NULL; 22175c45adf0SJesse Butler } 22185c45adf0SJesse Butler 221939cd77a0SJesse Butler /* 222039cd77a0SJesse Butler * We may arrive here due to a command timing out, which in turn 222139cd77a0SJesse Butler * could be addressed in a different context. So, free the work 222239cd77a0SJesse Butler * back, but only after confirming it's not already been freed 222339cd77a0SJesse Butler * elsewhere. 222439cd77a0SJesse Butler */ 22253492a3feSJesse Butler if (pwrk->htag != PMCS_TAG_FREE) { 22265c45adf0SJesse Butler pmcs_pwork(pwp, pwrk); 222739cd77a0SJesse Butler } 22285c45adf0SJesse Butler 22295c45adf0SJesse Butler /* 22309aed1621SDavid Hollister * If the device is gone, we only put this command on the completion 22319aed1621SDavid Hollister * queue if the work structure is not marked dead. If it's marked 22329aed1621SDavid Hollister * dead, it will already have been put there. 22339aed1621SDavid Hollister */ 22344c06356bSdh142964 if (xp->dev_gone) { 22354c06356bSdh142964 mutex_exit(&xp->statlock); 22369aed1621SDavid Hollister if (!dead) { 2237429adc13SSrikanth, Ramana mutex_enter(&xp->aqlock); 2238429adc13SSrikanth, Ramana STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next); 2239429adc13SSrikanth, Ramana mutex_exit(&xp->aqlock); 22405c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp, 2241429adc13SSrikanth, Ramana "%s: Removing cmd 0x%p (htag 0x%x) from aq", 2242429adc13SSrikanth, Ramana __func__, (void *)sp, sp->cmd_tag); 22439aed1621SDavid Hollister mutex_enter(&pwp->cq_lock); 22449aed1621SDavid Hollister STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 22458f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp); 22469aed1621SDavid Hollister mutex_exit(&pwp->cq_lock); 2247c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, 22489aed1621SDavid Hollister "%s: Completing command for dead target 0x%p", 22499aed1621SDavid Hollister __func__, (void *)xp); 22509aed1621SDavid Hollister } 22514c06356bSdh142964 return; 22524c06356bSdh142964 } 22534c06356bSdh142964 22544c06356bSdh142964 ASSERT(xp->actv_cnt > 0); 22554c06356bSdh142964 if (--(xp->actv_cnt) == 0) { 22564c06356bSdh142964 if (xp->draining) { 2257c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, 22584c06356bSdh142964 "%s: waking up drain waiters", __func__); 22594c06356bSdh142964 cv_signal(&pwp->drain_cv); 22604c06356bSdh142964 } 22614c06356bSdh142964 } 22624c06356bSdh142964 mutex_exit(&xp->statlock); 2263c280a92bSDavid Hollister 2264c280a92bSDavid Hollister /* 2265c280a92bSDavid Hollister * If the status is other than OK, determine if it's something that 2266c280a92bSDavid Hollister * is worth re-attempting enumeration. If so, mark the PHY. 2267c280a92bSDavid Hollister */ 2268c280a92bSDavid Hollister if (sts != PMCOUT_STATUS_OK) { 2269c280a92bSDavid Hollister pmcs_status_disposition(pptr, sts); 2270c280a92bSDavid Hollister } 2271c280a92bSDavid Hollister 22724c06356bSdh142964 if (dead == 0) { 22734c06356bSdh142964 #ifdef DEBUG 22744c06356bSdh142964 pmcs_cmd_t *wp; 22754c06356bSdh142964 mutex_enter(&xp->aqlock); 22764c06356bSdh142964 STAILQ_FOREACH(wp, &xp->aq, cmd_next) { 22774c06356bSdh142964 if (wp == sp) { 22784c06356bSdh142964 break; 22794c06356bSdh142964 } 22804c06356bSdh142964 } 22814c06356bSdh142964 ASSERT(wp != NULL); 22824c06356bSdh142964 #else 22834c06356bSdh142964 mutex_enter(&xp->aqlock); 22844c06356bSdh142964 #endif 22855c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp, 22869aed1621SDavid Hollister "%s: Removing cmd 0x%p (htag 0x%x) from aq", __func__, 22879aed1621SDavid Hollister (void *)sp, sp->cmd_tag); 22884c06356bSdh142964 STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next); 22894c06356bSdh142964 if (aborted) { 2290c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 22914c06356bSdh142964 "%s: Aborted cmd for tgt 0x%p, signaling waiters", 22924c06356bSdh142964 __func__, (void *)xp); 22934c06356bSdh142964 cv_signal(&xp->abort_cv); 22944c06356bSdh142964 } 22954c06356bSdh142964 mutex_exit(&xp->aqlock); 22964c06356bSdh142964 } 22974c06356bSdh142964 22984c06356bSdh142964 /* 22994c06356bSdh142964 * If do_ds_recovery is set, we need to initiate device state 23004c06356bSdh142964 * recovery. In this case, we put this I/O back on the head of 23014c06356bSdh142964 * the wait queue to run again after recovery is complete 23024c06356bSdh142964 */ 23034c06356bSdh142964 if (do_ds_recovery) { 23044c06356bSdh142964 mutex_enter(&xp->statlock); 23054c06356bSdh142964 pmcs_start_dev_state_recovery(xp, pptr); 23064c06356bSdh142964 mutex_exit(&xp->statlock); 2307c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, "%s: Putting cmd 0x%p " 2308c3bc407cSdh142964 "back on wq during recovery for tgt 0x%p", __func__, 2309c3bc407cSdh142964 (void *)sp, (void *)xp); 23104c06356bSdh142964 mutex_enter(&xp->wqlock); 23114c06356bSdh142964 if (xp->wq_recovery_tail == NULL) { 23124c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 23134c06356bSdh142964 } else { 23144c06356bSdh142964 /* 23154c06356bSdh142964 * If there are other I/Os waiting at the head due to 23164c06356bSdh142964 * device state recovery, add this one in the right spot 23174c06356bSdh142964 * to maintain proper order. 23184c06356bSdh142964 */ 23194c06356bSdh142964 STAILQ_INSERT_AFTER(&xp->wq, xp->wq_recovery_tail, sp, 23204c06356bSdh142964 cmd_next); 23214c06356bSdh142964 } 23224c06356bSdh142964 xp->wq_recovery_tail = sp; 23234c06356bSdh142964 mutex_exit(&xp->wqlock); 23244c06356bSdh142964 } else { 23254c06356bSdh142964 /* 23264c06356bSdh142964 * If we're not initiating device state recovery and this 23274c06356bSdh142964 * command was not "dead", put it on the completion queue 23284c06356bSdh142964 */ 23294c06356bSdh142964 if (!dead) { 23304c06356bSdh142964 mutex_enter(&pwp->cq_lock); 23314c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 23328f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp); 23334c06356bSdh142964 mutex_exit(&pwp->cq_lock); 23344c06356bSdh142964 } 23354c06356bSdh142964 } 23364c06356bSdh142964 } 23374c06356bSdh142964 23384c06356bSdh142964 /* 23394c06356bSdh142964 * Run a SATA command (normal reads and writes), 23404c06356bSdh142964 * or block and schedule a SATL interpretation 23414c06356bSdh142964 * of the command. 23424c06356bSdh142964 * 23434c06356bSdh142964 * Called with pwrk lock held, returns unlocked. 23444c06356bSdh142964 */ 23454c06356bSdh142964 23464c06356bSdh142964 static int 23474c06356bSdh142964 pmcs_SATA_run(pmcs_cmd_t *sp, pmcwork_t *pwrk) 23484c06356bSdh142964 { 23494c06356bSdh142964 pmcs_hw_t *pwp = CMD2PMC(sp); 23504c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 23514c06356bSdh142964 pmcs_xscsi_t *xp; 23524c06356bSdh142964 uint8_t cdb_base, asc, tag; 23534c06356bSdh142964 uint32_t *ptr, iq, nblk, i, mtype; 23544c06356bSdh142964 fis_t fis; 23554c06356bSdh142964 size_t amt; 23564c06356bSdh142964 uint64_t lba; 23574c06356bSdh142964 23584c06356bSdh142964 xp = pwrk->xp; 23593be32c0fSJesse Butler ASSERT(xp != NULL); 23604c06356bSdh142964 23614c06356bSdh142964 /* 23624c06356bSdh142964 * First, see if this is just a plain read/write command. 23634c06356bSdh142964 * If not, we have to queue it up for processing, block 23644c06356bSdh142964 * any additional commands from coming in, and wake up 23654c06356bSdh142964 * the thread that will process this command. 23664c06356bSdh142964 */ 23674c06356bSdh142964 cdb_base = pkt->pkt_cdbp[0] & 0x1f; 23684c06356bSdh142964 if (cdb_base != SCMD_READ && cdb_base != SCMD_WRITE) { 2369c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL, 2370c3bc407cSdh142964 "%s: special SATA cmd %p", __func__, (void *)sp); 23714c06356bSdh142964 23724c06356bSdh142964 ASSERT(xp->phy != NULL); 23734c06356bSdh142964 pmcs_pwork(pwp, pwrk); 23744c06356bSdh142964 pmcs_lock_phy(xp->phy); 23754c06356bSdh142964 mutex_enter(&xp->statlock); 23764c06356bSdh142964 xp->special_needed = 1; /* Set the special_needed flag */ 23774c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->sq, sp, cmd_next); 23784c06356bSdh142964 if (pmcs_run_sata_special(pwp, xp)) { 23794c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN); 23804c06356bSdh142964 } 23814c06356bSdh142964 mutex_exit(&xp->statlock); 23824c06356bSdh142964 pmcs_unlock_phy(xp->phy); 23834c06356bSdh142964 23844c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 23854c06356bSdh142964 } 23864c06356bSdh142964 2387c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: regular cmd", __func__); 23884c06356bSdh142964 23894c06356bSdh142964 mutex_enter(&xp->statlock); 2390b18a19c2SJesse Butler if (!xp->assigned) { 23914c06356bSdh142964 mutex_exit(&xp->statlock); 23924c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 23934c06356bSdh142964 } 23944c06356bSdh142964 if (xp->special_running || xp->special_needed || xp->recover_wait) { 23954c06356bSdh142964 mutex_exit(&xp->statlock); 23964c06356bSdh142964 mutex_enter(&xp->wqlock); 23974c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 23984c06356bSdh142964 mutex_exit(&xp->wqlock); 23994c06356bSdh142964 /* 24004c06356bSdh142964 * By the time we get here the special 24014c06356bSdh142964 * commands running or waiting to be run 24024c06356bSdh142964 * may have come and gone, so kick our 24034c06356bSdh142964 * worker to run the waiting queues 24044c06356bSdh142964 * just in case. 24054c06356bSdh142964 */ 24064c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 24074c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 24084c06356bSdh142964 } 24094c06356bSdh142964 lba = xp->capacity; 24104c06356bSdh142964 mutex_exit(&xp->statlock); 24114c06356bSdh142964 24124c06356bSdh142964 /* 24134c06356bSdh142964 * Extract data length and lba parameters out of the command. The 24144c06356bSdh142964 * function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB 24154c06356bSdh142964 * values are considered illegal. 24164c06356bSdh142964 */ 24174c06356bSdh142964 asc = pmcs_SATA_rwparm(pkt->pkt_cdbp, &nblk, &lba, lba); 24184c06356bSdh142964 if (asc) { 24194c06356bSdh142964 uint8_t sns[18]; 24204c06356bSdh142964 bzero(sns, sizeof (sns)); 24214c06356bSdh142964 sns[0] = 0xf0; 24224c06356bSdh142964 sns[2] = 0x5; 24234c06356bSdh142964 sns[12] = asc; 24244c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, sns, sizeof (sns), 24254c06356bSdh142964 pwrk->phy->path); 24264c06356bSdh142964 pmcs_pwork(pwp, pwrk); 24274c06356bSdh142964 mutex_enter(&pwp->cq_lock); 24284c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 24294c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp); 24304c06356bSdh142964 mutex_exit(&pwp->cq_lock); 24314c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 24324c06356bSdh142964 } 24334c06356bSdh142964 24344c06356bSdh142964 /* 24354c06356bSdh142964 * If the command decodes as not moving any data, complete it here. 24364c06356bSdh142964 */ 24374c06356bSdh142964 amt = nblk; 24384c06356bSdh142964 amt <<= 9; 24394c06356bSdh142964 amt = pmcs_set_resid(pkt, amt, nblk << 9); 24404c06356bSdh142964 if (amt == 0) { 24414c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, 24424c06356bSdh142964 pwrk->phy->path); 24434c06356bSdh142964 pmcs_pwork(pwp, pwrk); 24444c06356bSdh142964 mutex_enter(&pwp->cq_lock); 24454c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 24464c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp); 24474c06356bSdh142964 mutex_exit(&pwp->cq_lock); 24484c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 24494c06356bSdh142964 } 24504c06356bSdh142964 24514c06356bSdh142964 /* 24524c06356bSdh142964 * Get an inbound queue entry for this I/O 24534c06356bSdh142964 */ 24544c06356bSdh142964 GET_IO_IQ_ENTRY(pwp, ptr, xp->phy->device_id, iq); 24554c06356bSdh142964 if (ptr == NULL) { 24564c06356bSdh142964 /* 24574c06356bSdh142964 * This is a temporary failure not likely to unblocked by 24584c06356bSdh142964 * commands completing as the test for scheduling the 24594c06356bSdh142964 * restart of work is a per-device test. 24604c06356bSdh142964 */ 24614c06356bSdh142964 mutex_enter(&xp->wqlock); 24624c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 24634c06356bSdh142964 mutex_exit(&xp->wqlock); 24644c06356bSdh142964 pmcs_dma_unload(pwp, sp); 24654c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 2466c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 24674c06356bSdh142964 "%s: Failed to get IO IQ entry for tgt %d", 24684c06356bSdh142964 __func__, xp->target_num); 24694c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES); 24704c06356bSdh142964 } 24714c06356bSdh142964 24724c06356bSdh142964 /* 24734c06356bSdh142964 * Get a tag. At this point, hold statlock until the tagmap is 24744c06356bSdh142964 * updated (just prior to sending the cmd to the hardware). 24754c06356bSdh142964 */ 24764c06356bSdh142964 mutex_enter(&xp->statlock); 24774c06356bSdh142964 for (tag = 0; tag < xp->qdepth; tag++) { 24784c06356bSdh142964 if ((xp->tagmap & (1 << tag)) == 0) { 24794c06356bSdh142964 break; 24804c06356bSdh142964 } 24814c06356bSdh142964 } 24824c06356bSdh142964 24834c06356bSdh142964 if (tag == xp->qdepth) { 24844c06356bSdh142964 mutex_exit(&xp->statlock); 24854c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]); 24864c06356bSdh142964 mutex_enter(&xp->wqlock); 24874c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 24884c06356bSdh142964 mutex_exit(&xp->wqlock); 24894c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 24904c06356bSdh142964 } 24914c06356bSdh142964 24924c06356bSdh142964 sp->cmd_satltag = (uint8_t)tag; 24934c06356bSdh142964 24944c06356bSdh142964 /* 24954c06356bSdh142964 * Set up the command 24964c06356bSdh142964 */ 24974c06356bSdh142964 bzero(fis, sizeof (fis)); 24984c06356bSdh142964 ptr[0] = 24994c06356bSdh142964 LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SATA_HOST_IO_START)); 25004c06356bSdh142964 ptr[1] = LE_32(pwrk->htag); 25014c06356bSdh142964 ptr[2] = LE_32(pwrk->phy->device_id); 25024c06356bSdh142964 ptr[3] = LE_32(amt); 25034c06356bSdh142964 25044c06356bSdh142964 if (xp->ncq) { 25054c06356bSdh142964 mtype = SATA_PROTOCOL_FPDMA | (tag << 16); 25064c06356bSdh142964 fis[0] = ((nblk & 0xff) << 24) | (C_BIT << 8) | FIS_REG_H2DEV; 25074c06356bSdh142964 if (cdb_base == SCMD_READ) { 25084c06356bSdh142964 fis[0] |= (READ_FPDMA_QUEUED << 16); 25094c06356bSdh142964 } else { 25104c06356bSdh142964 fis[0] |= (WRITE_FPDMA_QUEUED << 16); 25114c06356bSdh142964 } 25124c06356bSdh142964 fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff); 25134c06356bSdh142964 fis[2] = ((nblk & 0xff00) << 16) | ((lba >> 24) & 0xffffff); 25144c06356bSdh142964 fis[3] = tag << 3; 25154c06356bSdh142964 } else { 25164c06356bSdh142964 int op; 25174c06356bSdh142964 fis[0] = (C_BIT << 8) | FIS_REG_H2DEV; 25184c06356bSdh142964 if (xp->pio) { 25194c06356bSdh142964 mtype = SATA_PROTOCOL_PIO; 25204c06356bSdh142964 if (cdb_base == SCMD_READ) { 25214c06356bSdh142964 op = READ_SECTORS_EXT; 25224c06356bSdh142964 } else { 25234c06356bSdh142964 op = WRITE_SECTORS_EXT; 25244c06356bSdh142964 } 25254c06356bSdh142964 } else { 25264c06356bSdh142964 mtype = SATA_PROTOCOL_DMA; 25274c06356bSdh142964 if (cdb_base == SCMD_READ) { 25284c06356bSdh142964 op = READ_DMA_EXT; 25294c06356bSdh142964 } else { 25304c06356bSdh142964 op = WRITE_DMA_EXT; 25314c06356bSdh142964 } 25324c06356bSdh142964 } 25334c06356bSdh142964 fis[0] |= (op << 16); 25344c06356bSdh142964 fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff); 25354c06356bSdh142964 fis[2] = (lba >> 24) & 0xffffff; 25364c06356bSdh142964 fis[3] = nblk; 25374c06356bSdh142964 } 25384c06356bSdh142964 25394c06356bSdh142964 if (cdb_base == SCMD_READ) { 25404c06356bSdh142964 ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_INI); 25414c06356bSdh142964 } else { 25424c06356bSdh142964 ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_DEV); 25434c06356bSdh142964 } 25444c06356bSdh142964 #ifdef DEBUG 25454c06356bSdh142964 /* 25464c06356bSdh142964 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED 25474c06356bSdh142964 * event when this goes out on the wire. 25484c06356bSdh142964 */ 25494c06356bSdh142964 ptr[4] |= PMCIN_MESSAGE_REPORT; 25504c06356bSdh142964 #endif 25514c06356bSdh142964 for (i = 0; i < (sizeof (fis_t))/(sizeof (uint32_t)); i++) { 25524c06356bSdh142964 ptr[i+5] = LE_32(fis[i]); 25534c06356bSdh142964 } 25544c06356bSdh142964 if (pmcs_dma_load(pwp, sp, ptr)) { 25554c06356bSdh142964 mutex_exit(&xp->statlock); 25564c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]); 25574c06356bSdh142964 mutex_enter(&xp->wqlock); 25584c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 25594c06356bSdh142964 mutex_exit(&xp->wqlock); 2560c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 25614c06356bSdh142964 "%s: Failed to dma_load for tgt %d", 25624c06356bSdh142964 __func__, xp->target_num); 25634c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES); 25644c06356bSdh142964 25654c06356bSdh142964 } 25664c06356bSdh142964 25674c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ONCHIP; 25684c06356bSdh142964 mutex_exit(&pwrk->lock); 25694c06356bSdh142964 xp->tagmap |= (1 << tag); 25704c06356bSdh142964 xp->actv_cnt++; 25714c06356bSdh142964 if (xp->actv_cnt > xp->maxdepth) { 25724c06356bSdh142964 xp->maxdepth = xp->actv_cnt; 2573c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp, 2574c3bc407cSdh142964 "%s: max depth now %u", pwrk->phy->path, xp->maxdepth); 25754c06356bSdh142964 } 25764c06356bSdh142964 mutex_exit(&xp->statlock); 25774c06356bSdh142964 mutex_enter(&xp->aqlock); 25784c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next); 25794c06356bSdh142964 mutex_exit(&xp->aqlock); 2580c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 2581c3bc407cSdh142964 "%s: giving pkt %p to hardware", __func__, (void *)pkt); 25824c06356bSdh142964 #ifdef DEBUG 25834c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SATA INI Message", ptr); 25844c06356bSdh142964 #endif 25854c06356bSdh142964 INC_IQ_ENTRY(pwp, iq); 25864c06356bSdh142964 25874c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 25884c06356bSdh142964 } 25894c06356bSdh142964 25904c06356bSdh142964 /* 25914c06356bSdh142964 * Complete a SATA command. Called with pwrk lock held. 25924c06356bSdh142964 */ 25934c06356bSdh142964 void 25944c06356bSdh142964 pmcs_SATA_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg) 25954c06356bSdh142964 { 25964c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg; 25974c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 25984c06356bSdh142964 pmcs_phy_t *pptr = pwrk->phy; 25994c06356bSdh142964 int dead; 26004c06356bSdh142964 uint32_t sts; 26014c06356bSdh142964 pmcs_xscsi_t *xp; 26024c06356bSdh142964 boolean_t aborted = B_FALSE; 26034c06356bSdh142964 26044c06356bSdh142964 xp = pwrk->xp; 26054c06356bSdh142964 ASSERT(xp != NULL); 26064c06356bSdh142964 26074c06356bSdh142964 DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int, 26084c06356bSdh142964 (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start, 26094c06356bSdh142964 hrtime_t, gethrtime()); 26104c06356bSdh142964 26114c06356bSdh142964 dead = pwrk->dead; 26124c06356bSdh142964 26134c06356bSdh142964 if (msg) { 26144c06356bSdh142964 sts = LE_32(msg[2]); 26154c06356bSdh142964 } else { 26164c06356bSdh142964 sts = 0; 26174c06356bSdh142964 } 26184c06356bSdh142964 26194c06356bSdh142964 if (dead != 0) { 2620c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag " 2621c3bc407cSdh142964 "0x%x for %s", __func__, pwrk->htag, pptr->path); 26224c06356bSdh142964 goto out; 26234c06356bSdh142964 } 26244c06356bSdh142964 if ((pwrk->state == PMCS_WORK_STATE_TIMED_OUT) && 26254c06356bSdh142964 (sts != PMCOUT_STATUS_ABORTED)) { 2626c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 26274c06356bSdh142964 "%s: cmd 0x%p (tag 0x%x) timed out for %s", 26284c06356bSdh142964 __func__, (void *)sp, pwrk->htag, pptr->path); 26294c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD; 26304c06356bSdh142964 /* pkt_reason already set to CMD_TIMEOUT */ 26314c06356bSdh142964 ASSERT(CMD2PKT(sp)->pkt_reason == CMD_TIMEOUT); 26324c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | 26334c06356bSdh142964 STATE_SENT_CMD; 26344c06356bSdh142964 CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT; 26354c06356bSdh142964 goto out; 26364c06356bSdh142964 } 26374c06356bSdh142964 2638c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, "%s: pkt %p tgt %u done", 26394c06356bSdh142964 __func__, (void *)pkt, xp->target_num); 26404c06356bSdh142964 26414c06356bSdh142964 /* 26424c06356bSdh142964 * If the status isn't okay but not underflow, 26434c06356bSdh142964 * step to the side and parse the (possible) error. 26444c06356bSdh142964 */ 26454c06356bSdh142964 #ifdef DEBUG 26464c06356bSdh142964 if (msg) { 26474c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg); 26484c06356bSdh142964 } 26494c06356bSdh142964 #endif 26504c06356bSdh142964 if (!msg) { 26514c06356bSdh142964 goto out; 26524c06356bSdh142964 } 26534c06356bSdh142964 26544c06356bSdh142964 /* 26554c06356bSdh142964 * If the status isn't okay or we got a FIS response of some kind, 26564c06356bSdh142964 * step to the side and parse the (possible) error. 26574c06356bSdh142964 */ 26584c06356bSdh142964 if ((sts != PMCOUT_STATUS_OK) || (LE_32(msg[3]) != 0)) { 26594c06356bSdh142964 if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) { 26604c06356bSdh142964 mutex_exit(&pwrk->lock); 26614c06356bSdh142964 pmcs_lock_phy(pptr); 26624c06356bSdh142964 mutex_enter(&xp->statlock); 26634c06356bSdh142964 if ((xp->resetting == 0) && (xp->reset_success != 0) && 26644c06356bSdh142964 (xp->reset_wait == 0)) { 26654c06356bSdh142964 mutex_exit(&xp->statlock); 26664c06356bSdh142964 if (pmcs_reset_phy(pwp, pptr, 26674c06356bSdh142964 PMCS_PHYOP_LINK_RESET) != 0) { 2668c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 2669c3bc407cSdh142964 "%s: PHY (%s) Local Control/Link " 2670c3bc407cSdh142964 "Reset FAILED as part of error " 2671c3bc407cSdh142964 "recovery", __func__, pptr->path); 26724c06356bSdh142964 } 26734c06356bSdh142964 mutex_enter(&xp->statlock); 26744c06356bSdh142964 } 26754c06356bSdh142964 mutex_exit(&xp->statlock); 26764c06356bSdh142964 pmcs_unlock_phy(pptr); 26774c06356bSdh142964 mutex_enter(&pwrk->lock); 26784c06356bSdh142964 } 2679658280b6SDavid Hollister pmcs_ioerror(pwp, SATA, pwrk, msg, sts); 26804c06356bSdh142964 } else { 26814c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, 26824c06356bSdh142964 pwrk->phy->path); 26834c06356bSdh142964 pkt->pkt_state |= STATE_XFERRED_DATA; 26844c06356bSdh142964 pkt->pkt_resid = 0; 26854c06356bSdh142964 } 26864c06356bSdh142964 2687c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, 26884c06356bSdh142964 "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x", 26894c06356bSdh142964 __func__, (void *)pkt, xp->target_num, pkt->pkt_reason, 26904c06356bSdh142964 pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]); 26914c06356bSdh142964 26924c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_ABORTED) { 2693c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 26944c06356bSdh142964 "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p", 26954c06356bSdh142964 __func__, (void *)pkt, pptr->path, (void *)pwrk); 26964c06356bSdh142964 aborted = B_TRUE; 26974c06356bSdh142964 } 26984c06356bSdh142964 26994c06356bSdh142964 out: 27004c06356bSdh142964 pmcs_dma_unload(pwp, sp); 27014c06356bSdh142964 mutex_enter(&xp->statlock); 27024c06356bSdh142964 xp->tagmap &= ~(1 << sp->cmd_satltag); 27034c06356bSdh142964 27045c45adf0SJesse Butler /* 27055c45adf0SJesse Butler * If the device no longer has a PHY pointer, clear the PHY pointer 27065c45adf0SJesse Butler * from the work structure before we free it. Otherwise, pmcs_pwork 27075c45adf0SJesse Butler * may decrement the ref_count on a PHY that's been freed. 27085c45adf0SJesse Butler */ 27095c45adf0SJesse Butler if (xp->phy == NULL) { 27105c45adf0SJesse Butler pwrk->phy = NULL; 27115c45adf0SJesse Butler } 27125c45adf0SJesse Butler 271339cd77a0SJesse Butler /* 271439cd77a0SJesse Butler * We may arrive here due to a command timing out, which in turn 271539cd77a0SJesse Butler * could be addressed in a different context. So, free the work 271639cd77a0SJesse Butler * back, but only after confirming it's not already been freed 271739cd77a0SJesse Butler * elsewhere. 271839cd77a0SJesse Butler */ 27193492a3feSJesse Butler if (pwrk->htag != PMCS_TAG_FREE) { 27205c45adf0SJesse Butler pmcs_pwork(pwp, pwrk); 272139cd77a0SJesse Butler } 27225c45adf0SJesse Butler 27234c06356bSdh142964 if (xp->dev_gone) { 27244c06356bSdh142964 mutex_exit(&xp->statlock); 27259aed1621SDavid Hollister if (!dead) { 2726429adc13SSrikanth, Ramana mutex_enter(&xp->aqlock); 2727429adc13SSrikanth, Ramana STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next); 2728429adc13SSrikanth, Ramana mutex_exit(&xp->aqlock); 27295c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp, 2730429adc13SSrikanth, Ramana "%s: Removing cmd 0x%p (htag 0x%x) from aq", 2731429adc13SSrikanth, Ramana __func__, (void *)sp, sp->cmd_tag); 27329aed1621SDavid Hollister mutex_enter(&pwp->cq_lock); 27339aed1621SDavid Hollister STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 27348f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp); 27359aed1621SDavid Hollister mutex_exit(&pwp->cq_lock); 2736c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, 27379aed1621SDavid Hollister "%s: Completing command for dead target 0x%p", 27389aed1621SDavid Hollister __func__, (void *)xp); 27399aed1621SDavid Hollister } 27404c06356bSdh142964 return; 27414c06356bSdh142964 } 27424c06356bSdh142964 27434c06356bSdh142964 ASSERT(xp->actv_cnt > 0); 27444c06356bSdh142964 if (--(xp->actv_cnt) == 0) { 27454c06356bSdh142964 if (xp->draining) { 2746c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, 27474c06356bSdh142964 "%s: waking up drain waiters", __func__); 27484c06356bSdh142964 cv_signal(&pwp->drain_cv); 27494c06356bSdh142964 } else if (xp->special_needed) { 27504c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN); 27514c06356bSdh142964 } 27524c06356bSdh142964 } 27534c06356bSdh142964 mutex_exit(&xp->statlock); 27544c06356bSdh142964 2755c280a92bSDavid Hollister /* 2756c280a92bSDavid Hollister * If the status is other than OK, determine if it's something that 2757c280a92bSDavid Hollister * is worth re-attempting enumeration. If so, mark the PHY. 2758c280a92bSDavid Hollister */ 2759c280a92bSDavid Hollister if (sts != PMCOUT_STATUS_OK) { 2760c280a92bSDavid Hollister pmcs_status_disposition(pptr, sts); 2761c280a92bSDavid Hollister } 2762c280a92bSDavid Hollister 27634c06356bSdh142964 if (dead == 0) { 27644c06356bSdh142964 #ifdef DEBUG 27654c06356bSdh142964 pmcs_cmd_t *wp; 27664c06356bSdh142964 mutex_enter(&xp->aqlock); 27674c06356bSdh142964 STAILQ_FOREACH(wp, &xp->aq, cmd_next) { 27684c06356bSdh142964 if (wp == sp) { 27694c06356bSdh142964 break; 27704c06356bSdh142964 } 27714c06356bSdh142964 } 27724c06356bSdh142964 ASSERT(wp != NULL); 27734c06356bSdh142964 #else 27744c06356bSdh142964 mutex_enter(&xp->aqlock); 27754c06356bSdh142964 #endif 27764c06356bSdh142964 STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next); 27774c06356bSdh142964 if (aborted) { 2778c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 27794c06356bSdh142964 "%s: Aborted cmd for tgt 0x%p, signaling waiters", 27804c06356bSdh142964 __func__, (void *)xp); 27814c06356bSdh142964 cv_signal(&xp->abort_cv); 27824c06356bSdh142964 } 27834c06356bSdh142964 mutex_exit(&xp->aqlock); 27844c06356bSdh142964 mutex_enter(&pwp->cq_lock); 27854c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 27868f514e74SDavid Hollister PMCS_CQ_RUN_LOCKED(pwp); 27874c06356bSdh142964 mutex_exit(&pwp->cq_lock); 27884c06356bSdh142964 } 27894c06356bSdh142964 } 27904c06356bSdh142964 27914c06356bSdh142964 static uint8_t 27924c06356bSdh142964 pmcs_SATA_rwparm(uint8_t *cdb, uint32_t *xfr, uint64_t *lba, uint64_t lbamax) 27934c06356bSdh142964 { 27944c06356bSdh142964 uint8_t asc = 0; 27954c06356bSdh142964 switch (cdb[0]) { 27964c06356bSdh142964 case SCMD_READ_G5: 27974c06356bSdh142964 case SCMD_WRITE_G5: 27984c06356bSdh142964 *xfr = 27994c06356bSdh142964 (((uint32_t)cdb[10]) << 24) | 28004c06356bSdh142964 (((uint32_t)cdb[11]) << 16) | 28014c06356bSdh142964 (((uint32_t)cdb[12]) << 8) | 28024c06356bSdh142964 ((uint32_t)cdb[13]); 28034c06356bSdh142964 *lba = 28044c06356bSdh142964 (((uint64_t)cdb[2]) << 56) | 28054c06356bSdh142964 (((uint64_t)cdb[3]) << 48) | 28064c06356bSdh142964 (((uint64_t)cdb[4]) << 40) | 28074c06356bSdh142964 (((uint64_t)cdb[5]) << 32) | 28084c06356bSdh142964 (((uint64_t)cdb[6]) << 24) | 28094c06356bSdh142964 (((uint64_t)cdb[7]) << 16) | 28104c06356bSdh142964 (((uint64_t)cdb[8]) << 8) | 28114c06356bSdh142964 ((uint64_t)cdb[9]); 28124c06356bSdh142964 /* Check for illegal bits */ 28134c06356bSdh142964 if (cdb[15]) { 28144c06356bSdh142964 asc = 0x24; /* invalid field in cdb */ 28154c06356bSdh142964 } 28164c06356bSdh142964 break; 28174c06356bSdh142964 case SCMD_READ_G4: 28184c06356bSdh142964 case SCMD_WRITE_G4: 28194c06356bSdh142964 *xfr = 28204c06356bSdh142964 (((uint32_t)cdb[6]) << 16) | 28214c06356bSdh142964 (((uint32_t)cdb[7]) << 8) | 28224c06356bSdh142964 ((uint32_t)cdb[8]); 28234c06356bSdh142964 *lba = 28244c06356bSdh142964 (((uint32_t)cdb[2]) << 24) | 28254c06356bSdh142964 (((uint32_t)cdb[3]) << 16) | 28264c06356bSdh142964 (((uint32_t)cdb[4]) << 8) | 28274c06356bSdh142964 ((uint32_t)cdb[5]); 28284c06356bSdh142964 /* Check for illegal bits */ 28294c06356bSdh142964 if (cdb[11]) { 28304c06356bSdh142964 asc = 0x24; /* invalid field in cdb */ 28314c06356bSdh142964 } 28324c06356bSdh142964 break; 28334c06356bSdh142964 case SCMD_READ_G1: 28344c06356bSdh142964 case SCMD_WRITE_G1: 28354c06356bSdh142964 *xfr = (((uint32_t)cdb[7]) << 8) | ((uint32_t)cdb[8]); 28364c06356bSdh142964 *lba = 28374c06356bSdh142964 (((uint32_t)cdb[2]) << 24) | 28384c06356bSdh142964 (((uint32_t)cdb[3]) << 16) | 28394c06356bSdh142964 (((uint32_t)cdb[4]) << 8) | 28404c06356bSdh142964 ((uint32_t)cdb[5]); 28414c06356bSdh142964 /* Check for illegal bits */ 28424c06356bSdh142964 if (cdb[9]) { 28434c06356bSdh142964 asc = 0x24; /* invalid field in cdb */ 28444c06356bSdh142964 } 28454c06356bSdh142964 break; 28464c06356bSdh142964 case SCMD_READ: 28474c06356bSdh142964 case SCMD_WRITE: 28484c06356bSdh142964 *xfr = cdb[4]; 28494c06356bSdh142964 if (*xfr == 0) { 28504c06356bSdh142964 *xfr = 256; 28514c06356bSdh142964 } 28524c06356bSdh142964 *lba = 28534c06356bSdh142964 (((uint32_t)cdb[1] & 0x1f) << 16) | 28544c06356bSdh142964 (((uint32_t)cdb[2]) << 8) | 28554c06356bSdh142964 ((uint32_t)cdb[3]); 28564c06356bSdh142964 /* Check for illegal bits */ 28574c06356bSdh142964 if (cdb[5]) { 28584c06356bSdh142964 asc = 0x24; /* invalid field in cdb */ 28594c06356bSdh142964 } 28604c06356bSdh142964 break; 28614c06356bSdh142964 } 28624c06356bSdh142964 28634c06356bSdh142964 if (asc == 0) { 28644c06356bSdh142964 if ((*lba + *xfr) > lbamax) { 28654c06356bSdh142964 asc = 0x21; /* logical block out of range */ 28664c06356bSdh142964 } 28674c06356bSdh142964 } 28684c06356bSdh142964 return (asc); 28694c06356bSdh142964 } 28704c06356bSdh142964 28714c06356bSdh142964 /* 28724c06356bSdh142964 * Called with pwrk lock held. 28734c06356bSdh142964 */ 28744c06356bSdh142964 static void 2875658280b6SDavid Hollister pmcs_ioerror(pmcs_hw_t *pwp, pmcs_dtype_t t, pmcwork_t *pwrk, uint32_t *w, 2876658280b6SDavid Hollister uint32_t status) 28774c06356bSdh142964 { 28784c06356bSdh142964 static uint8_t por[] = { 28794c06356bSdh142964 0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28 28804c06356bSdh142964 }; 28814c06356bSdh142964 static uint8_t parity[] = { 28824c06356bSdh142964 0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5 28834c06356bSdh142964 }; 28844c06356bSdh142964 const char *msg; 28854c06356bSdh142964 char buf[20]; 28864c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg; 28874c06356bSdh142964 pmcs_phy_t *phyp = pwrk->phy; 28884c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 28894c06356bSdh142964 uint32_t resid; 28904c06356bSdh142964 28914c06356bSdh142964 ASSERT(w != NULL); 28924c06356bSdh142964 resid = LE_32(w[3]); 28934c06356bSdh142964 28944c06356bSdh142964 msg = pmcs_status_str(status); 28954c06356bSdh142964 if (msg == NULL) { 28964c06356bSdh142964 (void) snprintf(buf, sizeof (buf), "Error 0x%x", status); 28974c06356bSdh142964 msg = buf; 28984c06356bSdh142964 } 28994c06356bSdh142964 29004c06356bSdh142964 if (status != PMCOUT_STATUS_OK) { 29019aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, NULL, 29024c06356bSdh142964 "%s: device %s tag 0x%x status %s @ %llu", __func__, 29034c06356bSdh142964 phyp->path, pwrk->htag, msg, 29044c06356bSdh142964 (unsigned long long)gethrtime()); 29054c06356bSdh142964 } 29064c06356bSdh142964 29074c06356bSdh142964 pkt->pkt_reason = CMD_CMPLT; /* default reason */ 29084c06356bSdh142964 29094c06356bSdh142964 switch (status) { 29104c06356bSdh142964 case PMCOUT_STATUS_OK: 29114c06356bSdh142964 if (t == SATA) { 29124c06356bSdh142964 int i; 29134c06356bSdh142964 fis_t fis; 29144c06356bSdh142964 for (i = 0; i < sizeof (fis) / sizeof (fis[0]); i++) { 29154c06356bSdh142964 fis[i] = LE_32(w[4+i]); 29164c06356bSdh142964 } 29174c06356bSdh142964 if ((fis[0] & 0xff) != FIS_REG_D2H) { 2918c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 29194c06356bSdh142964 "unexpected fis code 0x%x", fis[0] & 0xff); 29204c06356bSdh142964 } else { 2921c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 2922c3bc407cSdh142964 "FIS ERROR"); 29234c06356bSdh142964 pmcs_fis_dump(pwp, fis); 29244c06356bSdh142964 } 29254c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 29264c06356bSdh142964 break; 29274c06356bSdh142964 } 29284c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path); 29294c06356bSdh142964 break; 29304c06356bSdh142964 29314c06356bSdh142964 case PMCOUT_STATUS_ABORTED: 29324c06356bSdh142964 /* 29334c06356bSdh142964 * Command successfully aborted. 29344c06356bSdh142964 */ 29354c06356bSdh142964 if (phyp->dead) { 29364c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE; 29374c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 29384c06356bSdh142964 } else if (pwrk->ssp_event != 0) { 29394c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 29404c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 29414c06356bSdh142964 } else if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) { 29424c06356bSdh142964 pkt->pkt_reason = CMD_TIMEOUT; 29434c06356bSdh142964 pkt->pkt_statistics |= STAT_TIMEOUT; 29444c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | 29454c06356bSdh142964 STATE_SENT_CMD; 29464c06356bSdh142964 } else { 29474c06356bSdh142964 pkt->pkt_reason = CMD_ABORTED; 29484c06356bSdh142964 pkt->pkt_statistics |= STAT_ABORTED; 29494c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | 29504c06356bSdh142964 STATE_SENT_CMD; 29514c06356bSdh142964 } 29524c06356bSdh142964 29534c06356bSdh142964 /* 29544c06356bSdh142964 * PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past 29554c06356bSdh142964 * this point, so go ahead and mark it as aborted. 29564c06356bSdh142964 */ 29574c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ABORTED; 29584c06356bSdh142964 break; 29594c06356bSdh142964 29604c06356bSdh142964 case PMCOUT_STATUS_UNDERFLOW: 29614c06356bSdh142964 /* 29624c06356bSdh142964 * This will only get called for SATA 29634c06356bSdh142964 */ 29644c06356bSdh142964 pkt->pkt_resid = resid; 29654c06356bSdh142964 if (pkt->pkt_dma_len < pkt->pkt_resid) { 29664c06356bSdh142964 (void) pmcs_set_resid(pkt, pkt->pkt_dma_len, resid); 29674c06356bSdh142964 } 29684c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path); 29694c06356bSdh142964 break; 29704c06356bSdh142964 29714c06356bSdh142964 case PMCOUT_STATUS_NO_DEVICE: 29724c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT: 29734c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE; 29744c06356bSdh142964 break; 29754c06356bSdh142964 29764c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION: 29774c06356bSdh142964 /* 29784c06356bSdh142964 * Need to do rediscovery. We probably have 29794c06356bSdh142964 * the wrong device (disk swap), so kill 29804c06356bSdh142964 * this one. 29814c06356bSdh142964 */ 29824c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED: 29834c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION: 29844c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 29859aed1621SDavid Hollister case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR: 29864c06356bSdh142964 /* 29874c06356bSdh142964 * Need to do rediscovery. 29884c06356bSdh142964 */ 29894c06356bSdh142964 if (!phyp->dead) { 29904c06356bSdh142964 mutex_exit(&pwrk->lock); 29914c06356bSdh142964 pmcs_lock_phy(pwrk->phy); 29924c06356bSdh142964 pmcs_kill_changed(pwp, pwrk->phy, 0); 29934c06356bSdh142964 pmcs_unlock_phy(pwrk->phy); 29944c06356bSdh142964 mutex_enter(&pwrk->lock); 29954c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE; 29964c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 29974c06356bSdh142964 } else { 29984c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE; 29994c06356bSdh142964 } 30004c06356bSdh142964 break; 30014c06356bSdh142964 30024c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK: 30034c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS: 30044c06356bSdh142964 case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION: 30054c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED: 30064c06356bSdh142964 /* cmd is pending on the target */ 30074c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH: 30084c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE: 30094c06356bSdh142964 /* transitory - commands sent while in NCQ failure mode */ 30104c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE: 30114c06356bSdh142964 /* NCQ failure */ 30124c06356bSdh142964 case PMCOUT_STATUS_IO_PORT_IN_RESET: 30134c06356bSdh142964 case PMCOUT_STATUS_XFER_ERR_BREAK: 30144c06356bSdh142964 case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 30154c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE; 30164c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 30174c06356bSdh142964 break; 30184c06356bSdh142964 30194c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 3020658280b6SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target, 3021658280b6SDavid Hollister "STATUS_BUSY for htag 0x%08x", sp->cmd_tag); 30224c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_BUSY, NULL, 0, phyp->path); 30234c06356bSdh142964 break; 30244c06356bSdh142964 30254c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: 30264c06356bSdh142964 /* synthesize a RESERVATION CONFLICT */ 3027499cfd15SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target, 3028499cfd15SDavid Hollister "%s: Potential affiliation active on 0x%" PRIx64, __func__, 3029499cfd15SDavid Hollister pmcs_barray2wwn(phyp->sas_address)); 30304c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_RESERVATION_CONFLICT, NULL, 30314c06356bSdh142964 0, phyp->path); 30324c06356bSdh142964 break; 30334c06356bSdh142964 30344c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST: 30354c06356bSdh142964 /* synthesize a power-on/reset */ 30364c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, por, sizeof (por), 30374c06356bSdh142964 phyp->path); 30384c06356bSdh142964 break; 30394c06356bSdh142964 30404c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE: 30414c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN: 30424c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED: 30434c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT: 30444c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK: 30454c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK: 30464c06356bSdh142964 /* synthesize a PARITY ERROR */ 30474c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, parity, 30484c06356bSdh142964 sizeof (parity), phyp->path); 30494c06356bSdh142964 break; 30504c06356bSdh142964 30514c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_ERROR_DMA: 30524c06356bSdh142964 case PMCOUT_STATUS_IO_NOT_VALID: 30534c06356bSdh142964 case PMCOUT_STATUS_PROG_ERROR: 30544c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED: 30554c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */ 30564c06356bSdh142964 default: 30574c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 30584c06356bSdh142964 break; 30594c06356bSdh142964 } 30604c06356bSdh142964 } 30614c06356bSdh142964 30624c06356bSdh142964 /* 30634c06356bSdh142964 * Latch up SCSI status 30644c06356bSdh142964 */ 30654c06356bSdh142964 30664c06356bSdh142964 void 30674c06356bSdh142964 pmcs_latch_status(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint8_t status, 30684c06356bSdh142964 uint8_t *snsp, size_t snslen, char *path) 30694c06356bSdh142964 { 30704c06356bSdh142964 static const char c1[] = 30714c06356bSdh142964 "%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) " 30724c06356bSdh142964 "HTAG 0x%x @ %llu"; 30734c06356bSdh142964 static const char c2[] = 30744c06356bSdh142964 "%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu"; 30754c06356bSdh142964 30764c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | 30774c06356bSdh142964 STATE_SENT_CMD | STATE_GOT_STATUS; 30784c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = status; 30794c06356bSdh142964 30804c06356bSdh142964 if (status == STATUS_CHECK && snsp && 30814c06356bSdh142964 (size_t)SCSA_STSLEN(sp) >= sizeof (struct scsi_arq_status)) { 30824c06356bSdh142964 struct scsi_arq_status *aqp = 30834c06356bSdh142964 (void *) CMD2PKT(sp)->pkt_scbp; 30844c06356bSdh142964 size_t amt = sizeof (struct scsi_extended_sense); 30854c06356bSdh142964 uint8_t key = scsi_sense_key(snsp); 30864c06356bSdh142964 uint8_t asc = scsi_sense_asc(snsp); 30874c06356bSdh142964 uint8_t ascq = scsi_sense_ascq(snsp); 30884c06356bSdh142964 if (amt > snslen) { 30894c06356bSdh142964 amt = snslen; 30904c06356bSdh142964 } 3091c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c1, path, 3092c3bc407cSdh142964 status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, key, asc, ascq, 30934c06356bSdh142964 sp->cmd_tag, (unsigned long long)gethrtime()); 30944c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_ARQ_DONE; 30954c06356bSdh142964 (*(uint8_t *)&aqp->sts_rqpkt_status) = STATUS_GOOD; 30964c06356bSdh142964 aqp->sts_rqpkt_statistics = 0; 30974c06356bSdh142964 aqp->sts_rqpkt_reason = CMD_CMPLT; 30984c06356bSdh142964 aqp->sts_rqpkt_state = STATE_GOT_BUS | 30994c06356bSdh142964 STATE_GOT_TARGET | STATE_SENT_CMD | 31004c06356bSdh142964 STATE_XFERRED_DATA | STATE_GOT_STATUS; 31014c06356bSdh142964 (void) memcpy(&aqp->sts_sensedata, snsp, amt); 31024c06356bSdh142964 if (aqp->sts_sensedata.es_class != CLASS_EXTENDED_SENSE) { 31034c06356bSdh142964 aqp->sts_rqpkt_reason = CMD_TRAN_ERR; 31044c06356bSdh142964 aqp->sts_rqpkt_state = 0; 31054c06356bSdh142964 aqp->sts_rqpkt_resid = 31064c06356bSdh142964 sizeof (struct scsi_extended_sense); 31074c06356bSdh142964 } else { 31084c06356bSdh142964 aqp->sts_rqpkt_resid = 31094c06356bSdh142964 sizeof (struct scsi_extended_sense) - amt; 31104c06356bSdh142964 } 31114c06356bSdh142964 } else if (status) { 3112c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c2, 31134c06356bSdh142964 path, status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, 31144c06356bSdh142964 sp->cmd_tag, (unsigned long long)gethrtime()); 31154c06356bSdh142964 } 31164c06356bSdh142964 31174c06356bSdh142964 CMD2PKT(sp)->pkt_reason = CMD_CMPLT; 31184c06356bSdh142964 } 31194c06356bSdh142964 31204c06356bSdh142964 /* 31214c06356bSdh142964 * Calculate and set packet residual and return the amount 31224c06356bSdh142964 * left over after applying various filters. 31234c06356bSdh142964 */ 31244c06356bSdh142964 size_t 31254c06356bSdh142964 pmcs_set_resid(struct scsi_pkt *pkt, size_t amt, uint32_t cdbamt) 31264c06356bSdh142964 { 31274c06356bSdh142964 pkt->pkt_resid = cdbamt; 31284c06356bSdh142964 if (amt > pkt->pkt_resid) { 31294c06356bSdh142964 amt = pkt->pkt_resid; 31304c06356bSdh142964 } 31314c06356bSdh142964 if (amt > pkt->pkt_dma_len) { 31324c06356bSdh142964 amt = pkt->pkt_dma_len; 31334c06356bSdh142964 } 31344c06356bSdh142964 return (amt); 31354c06356bSdh142964 } 31364c06356bSdh142964 31374c06356bSdh142964 /* 3138c280a92bSDavid Hollister * Return the existing target softstate (unlocked) if there is one. If so, 3139c280a92bSDavid Hollister * the PHY is locked and that lock must be freed by the caller after the 3140c280a92bSDavid Hollister * target/PHY linkage is established. If there isn't one, and alloc_tgt is 3141c280a92bSDavid Hollister * TRUE, then allocate one. 31424c06356bSdh142964 */ 31434c06356bSdh142964 pmcs_xscsi_t * 31445c45adf0SJesse Butler pmcs_get_target(pmcs_iport_t *iport, char *tgt_port, boolean_t alloc_tgt) 31454c06356bSdh142964 { 31464c06356bSdh142964 pmcs_hw_t *pwp = iport->pwp; 31474c06356bSdh142964 pmcs_phy_t *phyp; 31484c06356bSdh142964 pmcs_xscsi_t *tgt; 31494c06356bSdh142964 uint64_t wwn; 31504c06356bSdh142964 char unit_address[PMCS_MAX_UA_SIZE]; 31514c06356bSdh142964 int ua_form = 1; 31524c06356bSdh142964 31534c06356bSdh142964 /* 31544c06356bSdh142964 * Find the PHY for this target 31554c06356bSdh142964 */ 31564c06356bSdh142964 phyp = pmcs_find_phy_by_sas_address(pwp, iport, NULL, tgt_port); 31574c06356bSdh142964 if (phyp == NULL) { 3158c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL, 3159c3bc407cSdh142964 "%s: No PHY for target @ %s", __func__, tgt_port); 31604c06356bSdh142964 return (NULL); 31614c06356bSdh142964 } 31624c06356bSdh142964 31634c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port); 31644c06356bSdh142964 31654c06356bSdh142964 if (tgt) { 3166c280a92bSDavid Hollister mutex_enter(&tgt->statlock); 31674c06356bSdh142964 /* 31684c06356bSdh142964 * There's already a target. Check its PHY pointer to see 31694c06356bSdh142964 * if we need to clear the old linkages 31704c06356bSdh142964 */ 31714c06356bSdh142964 if (tgt->phy && (tgt->phy != phyp)) { 3172c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 31734c06356bSdh142964 "%s: Target PHY updated from %p to %p", __func__, 31744c06356bSdh142964 (void *)tgt->phy, (void *)phyp); 31754c06356bSdh142964 if (!IS_ROOT_PHY(tgt->phy)) { 31764c06356bSdh142964 pmcs_dec_phy_ref_count(tgt->phy); 31774c06356bSdh142964 pmcs_inc_phy_ref_count(phyp); 31784c06356bSdh142964 } 31794c06356bSdh142964 tgt->phy->target = NULL; 31804c06356bSdh142964 } 31814c06356bSdh142964 3182c280a92bSDavid Hollister /* 3183c280a92bSDavid Hollister * If this target has no PHY pointer and alloc_tgt is FALSE, 3184c280a92bSDavid Hollister * that implies we expect the target to already exist. This 3185c280a92bSDavid Hollister * implies that there has already been a tran_tgt_init on at 3186c280a92bSDavid Hollister * least one LU. 3187c280a92bSDavid Hollister */ 3188c280a92bSDavid Hollister if ((tgt->phy == NULL) && !alloc_tgt) { 3189c280a92bSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, tgt, 3190c280a92bSDavid Hollister "%s: Establish linkage from new PHY to old target @" 3191c280a92bSDavid Hollister "%s", __func__, tgt->unit_address); 3192c280a92bSDavid Hollister for (int idx = 0; idx < tgt->ref_count; idx++) { 3193c280a92bSDavid Hollister pmcs_inc_phy_ref_count(phyp); 3194c280a92bSDavid Hollister } 3195c280a92bSDavid Hollister } 3196c280a92bSDavid Hollister 31974c06356bSdh142964 tgt->phy = phyp; 31984c06356bSdh142964 phyp->target = tgt; 3199c280a92bSDavid Hollister 3200c280a92bSDavid Hollister mutex_exit(&tgt->statlock); 32014c06356bSdh142964 return (tgt); 32024c06356bSdh142964 } 32034c06356bSdh142964 32044c06356bSdh142964 /* 32054c06356bSdh142964 * Make sure the PHY we found is on the correct iport 32064c06356bSdh142964 */ 32074c06356bSdh142964 if (phyp->iport != iport) { 3208c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 32094c06356bSdh142964 "%s: No target at %s on this iport", __func__, tgt_port); 32104c06356bSdh142964 pmcs_unlock_phy(phyp); 32114c06356bSdh142964 return (NULL); 32124c06356bSdh142964 } 32134c06356bSdh142964 32144c06356bSdh142964 /* 32155c45adf0SJesse Butler * If this was just a lookup (i.e. alloc_tgt is false), return now. 32165c45adf0SJesse Butler */ 32175c45adf0SJesse Butler if (alloc_tgt == B_FALSE) { 32185c45adf0SJesse Butler pmcs_unlock_phy(phyp); 32195c45adf0SJesse Butler return (NULL); 32205c45adf0SJesse Butler } 32215c45adf0SJesse Butler 32225c45adf0SJesse Butler /* 32234c06356bSdh142964 * Allocate the new softstate 32244c06356bSdh142964 */ 32254c06356bSdh142964 wwn = pmcs_barray2wwn(phyp->sas_address); 32264c06356bSdh142964 (void) scsi_wwn_to_wwnstr(wwn, ua_form, unit_address); 32274c06356bSdh142964 32284c06356bSdh142964 if (ddi_soft_state_bystr_zalloc(iport->tgt_sstate, unit_address) != 32294c06356bSdh142964 DDI_SUCCESS) { 3230c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 32314c06356bSdh142964 "%s: Couldn't alloc softstate for device at %s", 32324c06356bSdh142964 __func__, unit_address); 32334c06356bSdh142964 pmcs_unlock_phy(phyp); 32344c06356bSdh142964 return (NULL); 32354c06356bSdh142964 } 32364c06356bSdh142964 32374c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, unit_address); 3238a25672a1SDavid Hollister ASSERT(tgt != NULL); 32394c06356bSdh142964 STAILQ_INIT(&tgt->wq); 32404c06356bSdh142964 STAILQ_INIT(&tgt->aq); 32414c06356bSdh142964 STAILQ_INIT(&tgt->sq); 32424c06356bSdh142964 mutex_init(&tgt->statlock, NULL, MUTEX_DRIVER, 32434c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri)); 32444c06356bSdh142964 mutex_init(&tgt->wqlock, NULL, MUTEX_DRIVER, 32454c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri)); 32464c06356bSdh142964 mutex_init(&tgt->aqlock, NULL, MUTEX_DRIVER, 32474c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri)); 32484c06356bSdh142964 cv_init(&tgt->reset_cv, NULL, CV_DRIVER, NULL); 32494c06356bSdh142964 cv_init(&tgt->abort_cv, NULL, CV_DRIVER, NULL); 325073a3eccdSDavid Hollister list_create(&tgt->lun_list, sizeof (pmcs_lun_t), 325173a3eccdSDavid Hollister offsetof(pmcs_lun_t, lun_list_next)); 32524c06356bSdh142964 tgt->qdepth = 1; 32534c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM; 32544c06356bSdh142964 bcopy(unit_address, tgt->unit_address, PMCS_MAX_UA_SIZE); 32554c06356bSdh142964 tgt->pwp = pwp; 32564c06356bSdh142964 tgt->ua = strdup(iport->ua); 32574c06356bSdh142964 tgt->phy = phyp; 32584c06356bSdh142964 ASSERT((phyp->target == NULL) || (phyp->target == tgt)); 32594c06356bSdh142964 if (phyp->target == NULL) { 32604c06356bSdh142964 phyp->target = tgt; 32614c06356bSdh142964 } 32624c06356bSdh142964 32634c06356bSdh142964 /* 32644c06356bSdh142964 * Don't allocate LUN softstate for SMP targets 32654c06356bSdh142964 */ 32664c06356bSdh142964 if (phyp->dtype == EXPANDER) { 32674c06356bSdh142964 return (tgt); 32684c06356bSdh142964 } 32694c06356bSdh142964 32704c06356bSdh142964 if (ddi_soft_state_bystr_init(&tgt->lun_sstate, 32714c06356bSdh142964 sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) { 3272c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 32734c06356bSdh142964 "%s: LUN soft_state_bystr_init failed", __func__); 32744c06356bSdh142964 ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port); 32754c06356bSdh142964 pmcs_unlock_phy(phyp); 32764c06356bSdh142964 return (NULL); 32774c06356bSdh142964 } 32784c06356bSdh142964 32794c06356bSdh142964 return (tgt); 32804c06356bSdh142964 } 3281