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 204c06356bSdh142964 * 214c06356bSdh142964 * 224c06356bSdh142964 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 234c06356bSdh142964 * Use is subject to license terms. 244c06356bSdh142964 */ 254c06356bSdh142964 /* 264c06356bSdh142964 * SCSI (SCSA) midlayer interface for PMC drier. 274c06356bSdh142964 */ 284c06356bSdh142964 294c06356bSdh142964 #include <sys/scsi/adapters/pmcs/pmcs.h> 304c06356bSdh142964 314c06356bSdh142964 extern scsi_lun_t scsi_lun64_to_lun(scsi_lun64_t lun64); 324c06356bSdh142964 334c06356bSdh142964 static int pmcs_scsa_tran_tgt_init(dev_info_t *, dev_info_t *, 344c06356bSdh142964 scsi_hba_tran_t *, struct scsi_device *); 354c06356bSdh142964 static void pmcs_scsa_tran_tgt_free(dev_info_t *, dev_info_t *, 364c06356bSdh142964 scsi_hba_tran_t *, struct scsi_device *); 374c06356bSdh142964 static int pmcs_scsa_start(struct scsi_address *, struct scsi_pkt *); 384c06356bSdh142964 static int pmcs_scsa_abort(struct scsi_address *, struct scsi_pkt *); 394c06356bSdh142964 static int pmcs_scsa_reset(struct scsi_address *, int); 404c06356bSdh142964 static int pmcs_scsi_reset_notify(struct scsi_address *, int, 414c06356bSdh142964 void (*)(caddr_t), caddr_t); 424c06356bSdh142964 static int pmcs_scsa_getcap(struct scsi_address *, char *, int); 434c06356bSdh142964 static int pmcs_scsa_setcap(struct scsi_address *, char *, int, int); 444c06356bSdh142964 static int pmcs_scsa_setup_pkt(struct scsi_pkt *, int (*)(caddr_t), caddr_t); 454c06356bSdh142964 static void pmcs_scsa_teardown_pkt(struct scsi_pkt *); 4696c4a178SChris Horne 4796c4a178SChris Horne static int pmcs_smp_init(dev_info_t *, dev_info_t *, smp_hba_tran_t *, 4896c4a178SChris Horne smp_device_t *); 4996c4a178SChris Horne static void pmcs_smp_free(dev_info_t *, dev_info_t *, smp_hba_tran_t *, 5096c4a178SChris Horne smp_device_t *); 514c06356bSdh142964 static int pmcs_smp_start(struct smp_pkt *); 524c06356bSdh142964 534c06356bSdh142964 static int pmcs_scsi_quiesce(dev_info_t *); 544c06356bSdh142964 static int pmcs_scsi_unquiesce(dev_info_t *); 554c06356bSdh142964 564c06356bSdh142964 static int pmcs_cap(struct scsi_address *, char *, int, int, int); 574c06356bSdh142964 static pmcs_xscsi_t * 584c06356bSdh142964 pmcs_addr2xp(struct scsi_address *, uint64_t *, pmcs_cmd_t *); 594c06356bSdh142964 static int pmcs_SAS_run(pmcs_cmd_t *, pmcwork_t *); 604c06356bSdh142964 static void pmcs_SAS_done(pmcs_hw_t *, pmcwork_t *, uint32_t *); 614c06356bSdh142964 624c06356bSdh142964 static int pmcs_SATA_run(pmcs_cmd_t *, pmcwork_t *); 634c06356bSdh142964 static void pmcs_SATA_done(pmcs_hw_t *, pmcwork_t *, uint32_t *); 644c06356bSdh142964 static uint8_t pmcs_SATA_rwparm(uint8_t *, uint32_t *, uint64_t *, uint64_t); 654c06356bSdh142964 664c06356bSdh142964 static void pmcs_ioerror(pmcs_hw_t *, pmcs_dtype_t pmcs_dtype, 674c06356bSdh142964 pmcwork_t *, uint32_t *); 684c06356bSdh142964 694c06356bSdh142964 704c06356bSdh142964 int 714c06356bSdh142964 pmcs_scsa_init(pmcs_hw_t *pwp, const ddi_dma_attr_t *ap) 724c06356bSdh142964 { 734c06356bSdh142964 scsi_hba_tran_t *tran; 744c06356bSdh142964 ddi_dma_attr_t pmcs_scsa_dattr; 754c06356bSdh142964 int flags; 764c06356bSdh142964 774c06356bSdh142964 (void) memcpy(&pmcs_scsa_dattr, ap, sizeof (ddi_dma_attr_t)); 784c06356bSdh142964 pmcs_scsa_dattr.dma_attr_sgllen = 794c06356bSdh142964 ((PMCS_SGL_NCHUNKS - 1) * (PMCS_MAX_CHUNKS - 1)) + PMCS_SGL_NCHUNKS; 804c06356bSdh142964 pmcs_scsa_dattr.dma_attr_flags = DDI_DMA_RELAXED_ORDERING; 814c06356bSdh142964 pmcs_scsa_dattr.dma_attr_flags |= DDI_DMA_FLAGERR; 824c06356bSdh142964 834c06356bSdh142964 /* 844c06356bSdh142964 * Allocate a transport structure 854c06356bSdh142964 */ 864c06356bSdh142964 tran = scsi_hba_tran_alloc(pwp->dip, SCSI_HBA_CANSLEEP); 874c06356bSdh142964 if (tran == NULL) { 88c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 89c3bc407cSdh142964 "scsi_hba_tran_alloc failed"); 904c06356bSdh142964 return (DDI_FAILURE); 914c06356bSdh142964 } 924c06356bSdh142964 934c06356bSdh142964 tran->tran_hba_private = pwp; 944c06356bSdh142964 tran->tran_tgt_init = pmcs_scsa_tran_tgt_init; 954c06356bSdh142964 tran->tran_tgt_free = pmcs_scsa_tran_tgt_free; 964c06356bSdh142964 tran->tran_start = pmcs_scsa_start; 974c06356bSdh142964 tran->tran_abort = pmcs_scsa_abort; 984c06356bSdh142964 tran->tran_reset = pmcs_scsa_reset; 994c06356bSdh142964 tran->tran_reset_notify = pmcs_scsi_reset_notify; 1004c06356bSdh142964 tran->tran_getcap = pmcs_scsa_getcap; 1014c06356bSdh142964 tran->tran_setcap = pmcs_scsa_setcap; 1024c06356bSdh142964 tran->tran_setup_pkt = pmcs_scsa_setup_pkt; 1034c06356bSdh142964 tran->tran_teardown_pkt = pmcs_scsa_teardown_pkt; 1044c06356bSdh142964 tran->tran_quiesce = pmcs_scsi_quiesce; 1054c06356bSdh142964 tran->tran_unquiesce = pmcs_scsi_unquiesce; 1064c06356bSdh142964 tran->tran_interconnect_type = INTERCONNECT_SAS; 1074c06356bSdh142964 tran->tran_hba_len = sizeof (pmcs_cmd_t); 1084c06356bSdh142964 1094c06356bSdh142964 /* 1104c06356bSdh142964 * Attach this instance of the hba 1114c06356bSdh142964 */ 1124c06356bSdh142964 1134c06356bSdh142964 flags = SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CDB | SCSI_HBA_ADDR_COMPLEX | 1144c06356bSdh142964 SCSI_HBA_TRAN_PHCI | SCSI_HBA_HBA; 1154c06356bSdh142964 1164c06356bSdh142964 if (scsi_hba_attach_setup(pwp->dip, &pmcs_scsa_dattr, tran, flags)) { 1174c06356bSdh142964 scsi_hba_tran_free(tran); 118c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 119c3bc407cSdh142964 "scsi_hba_attach failed"); 1204c06356bSdh142964 return (DDI_FAILURE); 1214c06356bSdh142964 } 1224c06356bSdh142964 pwp->tran = tran; 1234c06356bSdh142964 1244c06356bSdh142964 /* 1254c06356bSdh142964 * Attach the SMP part of this hba 1264c06356bSdh142964 */ 12796c4a178SChris Horne pwp->smp_tran = smp_hba_tran_alloc(pwp->dip); 1284c06356bSdh142964 ASSERT(pwp->smp_tran != NULL); 12996c4a178SChris Horne pwp->smp_tran->smp_tran_hba_private = pwp; 13096c4a178SChris Horne pwp->smp_tran->smp_tran_init = pmcs_smp_init; 13196c4a178SChris Horne pwp->smp_tran->smp_tran_free = pmcs_smp_free; 13296c4a178SChris Horne pwp->smp_tran->smp_tran_start = pmcs_smp_start; 1334c06356bSdh142964 13496c4a178SChris Horne if (smp_hba_attach_setup(pwp->dip, pwp->smp_tran) != DDI_SUCCESS) { 135c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 13696c4a178SChris Horne "smp_hba_attach failed"); 13796c4a178SChris Horne smp_hba_tran_free(pwp->smp_tran); 1384c06356bSdh142964 pwp->smp_tran = NULL; 1394c06356bSdh142964 scsi_hba_tran_free(tran); 1404c06356bSdh142964 return (DDI_FAILURE); 1414c06356bSdh142964 } 1424c06356bSdh142964 1434c06356bSdh142964 return (DDI_SUCCESS); 1444c06356bSdh142964 } 1454c06356bSdh142964 1464c06356bSdh142964 /* 1474c06356bSdh142964 * SCSA entry points 1484c06356bSdh142964 */ 1494c06356bSdh142964 1504c06356bSdh142964 static int 1514c06356bSdh142964 pmcs_scsa_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip, 1524c06356bSdh142964 scsi_hba_tran_t *tran, struct scsi_device *sd) 1534c06356bSdh142964 { 1544c06356bSdh142964 pmcs_hw_t *pwp = NULL; 1554c06356bSdh142964 int rval; 1564c06356bSdh142964 char *variant_prop = "sata"; 1574c06356bSdh142964 char *tgt_port = NULL, *ua = NULL; 1584c06356bSdh142964 pmcs_xscsi_t *tgt = NULL; 1594c06356bSdh142964 pmcs_iport_t *iport; 1604c06356bSdh142964 pmcs_lun_t *lun = NULL; 1614c06356bSdh142964 pmcs_phy_t *phyp = NULL; 1624c06356bSdh142964 uint64_t lun_num; 1634c06356bSdh142964 boolean_t got_scratch = B_FALSE; 1644c06356bSdh142964 1654c06356bSdh142964 /* 1664c06356bSdh142964 * First, make sure we're an iport and get the pointer to the HBA 1674c06356bSdh142964 * node's softstate 1684c06356bSdh142964 */ 1694c06356bSdh142964 if (scsi_hba_iport_unit_address(hba_dip) == NULL) { 170c3bc407cSdh142964 pmcs_prt(TRAN2PMC(tran), PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 1714c06356bSdh142964 "%s: We don't enumerate devices on the HBA node", __func__); 1724c06356bSdh142964 goto tgt_init_fail; 1734c06356bSdh142964 } 1744c06356bSdh142964 1754c06356bSdh142964 pwp = ITRAN2PMC(tran); 1764c06356bSdh142964 iport = ITRAN2IPORT(tran); 1774c06356bSdh142964 1784c06356bSdh142964 /* 1794c06356bSdh142964 * Get the target address 1804c06356bSdh142964 */ 1814c06356bSdh142964 rval = scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH, 1824c06356bSdh142964 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port); 1834c06356bSdh142964 if (rval != DDI_PROP_SUCCESS) { 184c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 185c3bc407cSdh142964 "Couldn't get target UA"); 1864c06356bSdh142964 pwp = NULL; 1874c06356bSdh142964 goto tgt_init_fail; 1884c06356bSdh142964 } 189c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL, 190c3bc407cSdh142964 "got tgt_port '%s'", tgt_port); 1914c06356bSdh142964 1924c06356bSdh142964 /* 1934c06356bSdh142964 * Validate that this tran_tgt_init is for an active iport. 1944c06356bSdh142964 */ 1954c06356bSdh142964 if (iport->ua_state == UA_INACTIVE) { 196c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1974c06356bSdh142964 "%s: Got tran_tgt_init on inactive iport for '%s'", 1984c06356bSdh142964 __func__, tgt_port); 1994c06356bSdh142964 pwp = NULL; 2004c06356bSdh142964 goto tgt_init_fail; 2014c06356bSdh142964 } 2024c06356bSdh142964 2034c06356bSdh142964 /* 2044c06356bSdh142964 * Since we're going to wait for scratch, be sure to acquire it while 2054c06356bSdh142964 * we're not holding any other locks 2064c06356bSdh142964 */ 2074c06356bSdh142964 (void) pmcs_acquire_scratch(pwp, B_TRUE); 2084c06356bSdh142964 got_scratch = B_TRUE; 2094c06356bSdh142964 2104c06356bSdh142964 mutex_enter(&pwp->lock); 2114c06356bSdh142964 2124c06356bSdh142964 /* 2134c06356bSdh142964 * See if there's already a target softstate. If not, allocate one. 2144c06356bSdh142964 */ 2154c06356bSdh142964 tgt = pmcs_get_target(iport, tgt_port); 2164c06356bSdh142964 2174c06356bSdh142964 if (tgt == NULL) { 2184c06356bSdh142964 goto tgt_init_fail; 2194c06356bSdh142964 } 2204c06356bSdh142964 2214c06356bSdh142964 phyp = tgt->phy; 2224c06356bSdh142964 if (!IS_ROOT_PHY(phyp)) { 2234c06356bSdh142964 pmcs_inc_phy_ref_count(phyp); 2244c06356bSdh142964 } 2254c06356bSdh142964 ASSERT(mutex_owned(&phyp->phy_lock)); 2264c06356bSdh142964 227c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt, "tgt = 0x%p, dip = 0x%p", 2284c06356bSdh142964 (void *)tgt, (void *)tgt_dip); 2294c06356bSdh142964 2304c06356bSdh142964 /* 2314c06356bSdh142964 * Now get the full "w<WWN>,LUN" unit-address (including LU). 2324c06356bSdh142964 */ 2334c06356bSdh142964 ua = scsi_device_unit_address(sd); 2344c06356bSdh142964 if (ua == NULL) { 235c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 2364c06356bSdh142964 "Couldn't get LU unit address"); 2374c06356bSdh142964 goto tgt_init_fail; 2384c06356bSdh142964 } 239c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, tgt, "got lun ua '%s'", ua); 2404c06356bSdh142964 2414c06356bSdh142964 lun_num = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH, 2424c06356bSdh142964 SCSI_ADDR_PROP_LUN64, SCSI_LUN64_ILLEGAL); 2434c06356bSdh142964 if (lun_num == SCSI_LUN64_ILLEGAL) { 244c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 245c3bc407cSdh142964 "No LUN for tgt %p", (void *)tgt); 2464c06356bSdh142964 goto tgt_init_fail; 2474c06356bSdh142964 } 2484c06356bSdh142964 249c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, "%s: @%s tgt 0x%p phy " 250c3bc407cSdh142964 "0x%p (%s)", __func__, ua, (void *)tgt, (void *)phyp, phyp->path); 2514c06356bSdh142964 2524c06356bSdh142964 mutex_enter(&tgt->statlock); 2534c06356bSdh142964 tgt->dtype = phyp->dtype; 2544c06356bSdh142964 if (tgt->dtype != SAS && tgt->dtype != SATA) { 255c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 256c3bc407cSdh142964 "PHY 0x%p went away?", (void *)phyp); 2574c06356bSdh142964 goto tgt_init_fail; 2584c06356bSdh142964 } 2594c06356bSdh142964 2604c06356bSdh142964 /* We don't support SATA devices at LUN > 0. */ 2614c06356bSdh142964 if ((tgt->dtype == SATA) && (lun_num > 0)) { 262c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 2634c06356bSdh142964 "%s: No support for SATA devices at LUN > 0 " 2644c06356bSdh142964 "(target = 0x%p)", __func__, (void *)tgt); 2654c06356bSdh142964 goto tgt_init_fail; 2664c06356bSdh142964 } 2674c06356bSdh142964 2684c06356bSdh142964 /* 2694c06356bSdh142964 * Allocate LU soft state. We use ddi_soft_state_bystr_zalloc instead 2704c06356bSdh142964 * of kmem_alloc because ddi_soft_state_bystr_zalloc allows us to 2714c06356bSdh142964 * verify that the framework never tries to initialize two scsi_device 2724c06356bSdh142964 * structures with the same unit-address at the same time. 2734c06356bSdh142964 */ 2744c06356bSdh142964 if (ddi_soft_state_bystr_zalloc(tgt->lun_sstate, ua) != DDI_SUCCESS) { 275c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt, 2764c06356bSdh142964 "Couldn't allocate LU soft state"); 2774c06356bSdh142964 goto tgt_init_fail; 2784c06356bSdh142964 } 2794c06356bSdh142964 2804c06356bSdh142964 lun = ddi_soft_state_bystr_get(tgt->lun_sstate, ua); 2814c06356bSdh142964 if (lun == NULL) { 282c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt, 283c3bc407cSdh142964 "Couldn't get LU soft state"); 2844c06356bSdh142964 goto tgt_init_fail; 2854c06356bSdh142964 } 2864c06356bSdh142964 scsi_device_hba_private_set(sd, lun); 2874c06356bSdh142964 lun->lun_num = lun_num; 2884c06356bSdh142964 2894c06356bSdh142964 /* convert the scsi_lun64_t value to SCSI standard form */ 2904c06356bSdh142964 lun->scsi_lun = scsi_lun64_to_lun(lun_num); 2914c06356bSdh142964 2924c06356bSdh142964 ASSERT(strlen(ua) < (PMCS_MAX_UA_SIZE - 1)); 2934c06356bSdh142964 bcopy(ua, lun->unit_address, strnlen(ua, PMCS_MAX_UA_SIZE - 1)); 2944c06356bSdh142964 2954c06356bSdh142964 lun->target = tgt; 2964c06356bSdh142964 2974c06356bSdh142964 /* 2984c06356bSdh142964 * If this is the first tran_tgt_init, add this target to our list 2994c06356bSdh142964 */ 3004c06356bSdh142964 if (tgt->target_num == PMCS_INVALID_TARGET_NUM) { 3014c06356bSdh142964 int target; 3024c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) { 3034c06356bSdh142964 if (pwp->targets[target] != NULL) { 3044c06356bSdh142964 continue; 3054c06356bSdh142964 } 3064c06356bSdh142964 3074c06356bSdh142964 pwp->targets[target] = tgt; 3084c06356bSdh142964 tgt->target_num = (uint16_t)target; 3094c06356bSdh142964 break; 3104c06356bSdh142964 } 3114c06356bSdh142964 3124c06356bSdh142964 if (target == pwp->max_dev) { 313c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 3144c06356bSdh142964 "Target list full."); 3154c06356bSdh142964 goto tgt_init_fail; 3164c06356bSdh142964 } 3174c06356bSdh142964 } 3184c06356bSdh142964 3194c06356bSdh142964 tgt->dip = sd->sd_dev; 3204c06356bSdh142964 3214c06356bSdh142964 if (!pmcs_assign_device(pwp, tgt)) { 3224c06356bSdh142964 pmcs_release_scratch(pwp); 3234c06356bSdh142964 pwp->targets[tgt->target_num] = NULL; 3244c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM; 3254c06356bSdh142964 tgt->phy = NULL; 326c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 3274c06356bSdh142964 "%s: pmcs_assign_device failed for target 0x%p", 3284c06356bSdh142964 __func__, (void *)tgt); 3294c06356bSdh142964 goto tgt_init_fail; 3304c06356bSdh142964 } 3314c06356bSdh142964 3324c06356bSdh142964 pmcs_release_scratch(pwp); 3334c06356bSdh142964 tgt->ref_count++; 3344c06356bSdh142964 3354c06356bSdh142964 (void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH, 3364c06356bSdh142964 SCSI_ADDR_PROP_TARGET, (uint32_t)(tgt->target_num)); 3374c06356bSdh142964 3384c06356bSdh142964 /* SM-HBA */ 3394c06356bSdh142964 if (tgt->dtype == SATA) { 3404c06356bSdh142964 /* TCR in PSARC/1997/281 opinion */ 3414c06356bSdh142964 (void) scsi_device_prop_update_string(sd, 3424c06356bSdh142964 SCSI_DEVICE_PROP_PATH, "variant", variant_prop); 3434c06356bSdh142964 } 3444c06356bSdh142964 3454c06356bSdh142964 tgt->phy_addressable = PMCS_PHY_ADDRESSABLE(phyp); 3464c06356bSdh142964 3474c06356bSdh142964 if (tgt->phy_addressable) { 3484c06356bSdh142964 (void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH, 3494c06356bSdh142964 SCSI_ADDR_PROP_SATA_PHY, phyp->phynum); 3504c06356bSdh142964 } 3514c06356bSdh142964 3524c06356bSdh142964 /* SM-HBA */ 3534c06356bSdh142964 (void) pmcs_smhba_set_scsi_device_props(pwp, phyp, sd); 3544c06356bSdh142964 3554c06356bSdh142964 mutex_exit(&tgt->statlock); 3564c06356bSdh142964 pmcs_unlock_phy(phyp); 3574c06356bSdh142964 mutex_exit(&pwp->lock); 3584c06356bSdh142964 scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port); 3594c06356bSdh142964 return (DDI_SUCCESS); 3604c06356bSdh142964 3614c06356bSdh142964 tgt_init_fail: 3624c06356bSdh142964 if (got_scratch) { 3634c06356bSdh142964 pmcs_release_scratch(pwp); 3644c06356bSdh142964 } 3654c06356bSdh142964 if (lun) { 3664c06356bSdh142964 ddi_soft_state_bystr_free(tgt->lun_sstate, ua); 3674c06356bSdh142964 } 3684c06356bSdh142964 if (phyp) { 3694c06356bSdh142964 mutex_exit(&tgt->statlock); 3704c06356bSdh142964 pmcs_unlock_phy(phyp); 3714c06356bSdh142964 /* 3724c06356bSdh142964 * phyp's ref count was incremented in pmcs_new_tport. 3734c06356bSdh142964 * We're failing configuration, we now need to decrement it. 3744c06356bSdh142964 */ 3754c06356bSdh142964 if (!IS_ROOT_PHY(phyp)) { 3764c06356bSdh142964 pmcs_dec_phy_ref_count(phyp); 3774c06356bSdh142964 } 3784c06356bSdh142964 phyp->target = NULL; 3794c06356bSdh142964 } 3804c06356bSdh142964 if (tgt && tgt->ref_count == 0) { 3814c06356bSdh142964 ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port); 3824c06356bSdh142964 } 3834c06356bSdh142964 if (pwp) { 3844c06356bSdh142964 mutex_exit(&pwp->lock); 3854c06356bSdh142964 } 3864c06356bSdh142964 if (tgt_port) { 3874c06356bSdh142964 scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port); 3884c06356bSdh142964 } 3894c06356bSdh142964 return (DDI_FAILURE); 3904c06356bSdh142964 } 3914c06356bSdh142964 3924c06356bSdh142964 static void 3934c06356bSdh142964 pmcs_scsa_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip, 3944c06356bSdh142964 scsi_hba_tran_t *tran, struct scsi_device *sd) 3954c06356bSdh142964 { 3964c06356bSdh142964 _NOTE(ARGUNUSED(hba_dip, tgt_dip)); 3974c06356bSdh142964 pmcs_hw_t *pwp; 3984c06356bSdh142964 pmcs_lun_t *lun; 3994c06356bSdh142964 pmcs_xscsi_t *target; 4004c06356bSdh142964 char *unit_address; 4014c06356bSdh142964 pmcs_phy_t *phyp; 4024c06356bSdh142964 4034c06356bSdh142964 if (scsi_hba_iport_unit_address(hba_dip) == NULL) { 4044c06356bSdh142964 pwp = TRAN2PMC(tran); 405c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 4064c06356bSdh142964 "%s: We don't enumerate devices on the HBA node", __func__); 4074c06356bSdh142964 return; 4084c06356bSdh142964 } 4094c06356bSdh142964 4104c06356bSdh142964 lun = (pmcs_lun_t *)scsi_device_hba_private_get(sd); 4114c06356bSdh142964 4124c06356bSdh142964 ASSERT((lun != NULL) && (lun->target != NULL)); 4134c06356bSdh142964 ASSERT(lun->target->ref_count > 0); 4144c06356bSdh142964 4154c06356bSdh142964 target = lun->target; 4164c06356bSdh142964 4174c06356bSdh142964 unit_address = lun->unit_address; 4184c06356bSdh142964 ddi_soft_state_bystr_free(lun->target->lun_sstate, unit_address); 4194c06356bSdh142964 4204c06356bSdh142964 pwp = ITRAN2PMC(tran); 4214c06356bSdh142964 mutex_enter(&pwp->lock); 4224c06356bSdh142964 mutex_enter(&target->statlock); 4234c06356bSdh142964 ASSERT(target->phy); 4244c06356bSdh142964 phyp = target->phy; 4254c06356bSdh142964 426af685682SSrikanth, Ramana if (target->recover_wait) { 427af685682SSrikanth, Ramana mutex_exit(&target->statlock); 428af685682SSrikanth, Ramana mutex_exit(&pwp->lock); 429af685682SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target, "%s: " 430af685682SSrikanth, Ramana "Target 0x%p in device state recovery, fail tran_tgt_free", 431af685682SSrikanth, Ramana __func__, (void *)target); 432af685682SSrikanth, Ramana return; 433af685682SSrikanth, Ramana } 434af685682SSrikanth, Ramana 4354c06356bSdh142964 /* 4364c06356bSdh142964 * If this target still has a PHY pointer and that PHY's target pointer 4374c06356bSdh142964 * has been cleared, then that PHY has been reaped. In that case, there 4384c06356bSdh142964 * would be no need to decrement the reference count 4394c06356bSdh142964 */ 4404c06356bSdh142964 if (phyp && !IS_ROOT_PHY(phyp) && phyp->target) { 4414c06356bSdh142964 pmcs_dec_phy_ref_count(phyp); 4424c06356bSdh142964 } 4434c06356bSdh142964 4444c06356bSdh142964 if (--target->ref_count == 0) { 4454c06356bSdh142964 /* 4464c06356bSdh142964 * Remove this target from our list. The target soft 4474c06356bSdh142964 * state will remain, and the device will remain registered 4484c06356bSdh142964 * with the hardware unless/until we're told the device 4494c06356bSdh142964 * physically went away. 4504c06356bSdh142964 */ 451c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target, 4524c06356bSdh142964 "%s: Free target 0x%p (vtgt %d)", __func__, (void *)target, 4534c06356bSdh142964 target->target_num); 4544c06356bSdh142964 pwp->targets[target->target_num] = NULL; 4554c06356bSdh142964 target->target_num = PMCS_INVALID_TARGET_NUM; 4564c06356bSdh142964 /* 4574c06356bSdh142964 * If the target still has a PHY pointer, break the linkage 4584c06356bSdh142964 */ 4594c06356bSdh142964 if (phyp) { 4604c06356bSdh142964 phyp->target = NULL; 4614c06356bSdh142964 } 4624c06356bSdh142964 target->phy = NULL; 4634c06356bSdh142964 pmcs_destroy_target(target); 4644c06356bSdh142964 } else { 4654c06356bSdh142964 mutex_exit(&target->statlock); 4664c06356bSdh142964 } 4674c06356bSdh142964 4684c06356bSdh142964 mutex_exit(&pwp->lock); 4694c06356bSdh142964 } 4704c06356bSdh142964 4714c06356bSdh142964 static int 4724c06356bSdh142964 pmcs_scsa_start(struct scsi_address *ap, struct scsi_pkt *pkt) 4734c06356bSdh142964 { 4744c06356bSdh142964 pmcs_cmd_t *sp = PKT2CMD(pkt); 4754c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap); 4764c06356bSdh142964 pmcs_xscsi_t *xp; 4774c06356bSdh142964 boolean_t blocked; 4784c06356bSdh142964 uint32_t hba_state; 4794c06356bSdh142964 480c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 481c3bc407cSdh142964 "%s: pkt %p sd %p cdb0=0x%02x dl=%lu", __func__, (void *)pkt, 4824c06356bSdh142964 (void *)scsi_address_device(&pkt->pkt_address), 4834c06356bSdh142964 pkt->pkt_cdbp[0] & 0xff, pkt->pkt_dma_len); 4844c06356bSdh142964 4854c06356bSdh142964 if (pkt->pkt_flags & FLAG_NOINTR) { 486c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL, 487c3bc407cSdh142964 "%s: nointr pkt", __func__); 4884c06356bSdh142964 return (TRAN_BADPKT); 4894c06356bSdh142964 } 4904c06356bSdh142964 4914c06356bSdh142964 sp->cmd_tag = 0; 4924c06356bSdh142964 pkt->pkt_state = pkt->pkt_statistics = 0; 4934c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE; 4944c06356bSdh142964 4954c06356bSdh142964 mutex_enter(&pwp->lock); 4964c06356bSdh142964 hba_state = pwp->state; 4974c06356bSdh142964 blocked = pwp->blocked; 4984c06356bSdh142964 mutex_exit(&pwp->lock); 4994c06356bSdh142964 5004c06356bSdh142964 if (hba_state != STATE_RUNNING) { 501c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 502c3bc407cSdh142964 "%s: hba dead", __func__); 5034c06356bSdh142964 return (TRAN_FATAL_ERROR); 5044c06356bSdh142964 } 5054c06356bSdh142964 5064c06356bSdh142964 xp = pmcs_addr2xp(ap, NULL, sp); 5074c06356bSdh142964 if (xp == NULL) { 508c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 5094c06356bSdh142964 "%s: dropping due to null target", __func__); 510b18a19c2SJesse Butler goto dead_target; 5114c06356bSdh142964 } 5124c06356bSdh142964 ASSERT(mutex_owned(&xp->statlock)); 5134c06356bSdh142964 5144c06356bSdh142964 /* 515b18a19c2SJesse Butler * First, check to see if the device is gone. 5164c06356bSdh142964 */ 517b18a19c2SJesse Butler if (xp->dev_gone) { 5184c06356bSdh142964 mutex_exit(&xp->statlock); 519c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, xp, 520b18a19c2SJesse Butler "%s: dropping due to dead target 0x%p", 5214c06356bSdh142964 __func__, (void *)xp); 522b18a19c2SJesse Butler goto dead_target; 5234c06356bSdh142964 } 5244c06356bSdh142964 5254c06356bSdh142964 /* 5264c06356bSdh142964 * If we're blocked (quiesced) just return. 5274c06356bSdh142964 */ 5284c06356bSdh142964 if (blocked) { 529c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 530c3bc407cSdh142964 "%s: hba blocked", __func__); 5314c06356bSdh142964 mutex_exit(&xp->statlock); 5324c06356bSdh142964 mutex_enter(&xp->wqlock); 5334c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next); 5344c06356bSdh142964 mutex_exit(&xp->wqlock); 5354c06356bSdh142964 return (TRAN_ACCEPT); 5364c06356bSdh142964 } 5374c06356bSdh142964 5384c06356bSdh142964 /* 5394c06356bSdh142964 * If we're draining or resetting, queue and return. 5404c06356bSdh142964 */ 5414c06356bSdh142964 if (xp->draining || xp->resetting || xp->recover_wait) { 5424c06356bSdh142964 mutex_exit(&xp->statlock); 5434c06356bSdh142964 mutex_enter(&xp->wqlock); 5444c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next); 5454c06356bSdh142964 mutex_exit(&xp->wqlock); 546c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp, 5474c06356bSdh142964 "%s: draining/resetting/recovering (cnt %u)", 5484c06356bSdh142964 __func__, xp->actv_cnt); 5494c06356bSdh142964 /* 5504c06356bSdh142964 * By the time we get here, draining or 5514c06356bSdh142964 * resetting may have come and gone, not 5524c06356bSdh142964 * yet noticing that we had put something 5534c06356bSdh142964 * on the wait queue, so schedule a worker 5544c06356bSdh142964 * to look at this later. 5554c06356bSdh142964 */ 5564c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 5574c06356bSdh142964 return (TRAN_ACCEPT); 5584c06356bSdh142964 } 5594c06356bSdh142964 mutex_exit(&xp->statlock); 5604c06356bSdh142964 5614c06356bSdh142964 /* 5624c06356bSdh142964 * Queue this command to the tail of the wait queue. 5634c06356bSdh142964 * This keeps us getting commands out of order. 5644c06356bSdh142964 */ 5654c06356bSdh142964 mutex_enter(&xp->wqlock); 5664c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next); 5674c06356bSdh142964 mutex_exit(&xp->wqlock); 5684c06356bSdh142964 5694c06356bSdh142964 /* 5704c06356bSdh142964 * Now run the queue for this device. 5714c06356bSdh142964 */ 5724c06356bSdh142964 (void) pmcs_scsa_wq_run_one(pwp, xp); 5734c06356bSdh142964 5744c06356bSdh142964 return (TRAN_ACCEPT); 5754c06356bSdh142964 576b18a19c2SJesse Butler dead_target: 5774c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 5784c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE; 5794c06356bSdh142964 mutex_enter(&pwp->cq_lock); 5804c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 5814c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp); 5824c06356bSdh142964 mutex_exit(&pwp->cq_lock); 5834c06356bSdh142964 return (TRAN_ACCEPT); 5844c06356bSdh142964 } 5854c06356bSdh142964 5864c06356bSdh142964 static int 5874c06356bSdh142964 pmcs_scsa_abort(struct scsi_address *ap, struct scsi_pkt *pkt) 5884c06356bSdh142964 { 5894c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap); 5904c06356bSdh142964 pmcs_cmd_t *sp = PKT2CMD(pkt); 5914c06356bSdh142964 pmcs_xscsi_t *xp = sp->cmd_target; 5924c06356bSdh142964 pmcs_phy_t *pptr; 5934c06356bSdh142964 uint32_t tag; 5944c06356bSdh142964 uint64_t lun; 5954c06356bSdh142964 pmcwork_t *pwrk; 5964c06356bSdh142964 5974c06356bSdh142964 mutex_enter(&pwp->lock); 5984c06356bSdh142964 if (pwp->state != STATE_RUNNING) { 5994c06356bSdh142964 mutex_exit(&pwp->lock); 600c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 601c3bc407cSdh142964 "%s: hba dead", __func__); 6024c06356bSdh142964 return (0); 6034c06356bSdh142964 } 6044c06356bSdh142964 mutex_exit(&pwp->lock); 6054c06356bSdh142964 6064c06356bSdh142964 if (sp->cmd_lun) { 6074c06356bSdh142964 lun = sp->cmd_lun->lun_num; 6084c06356bSdh142964 } else { 6094c06356bSdh142964 lun = 0; 6104c06356bSdh142964 } 6114c06356bSdh142964 if (xp == NULL) { 6124c06356bSdh142964 return (0); 6134c06356bSdh142964 } 6144c06356bSdh142964 6154c06356bSdh142964 /* 6164c06356bSdh142964 * See if we have a real work structure associated with this cmd. 6174c06356bSdh142964 */ 6184c06356bSdh142964 pwrk = pmcs_tag2wp(pwp, sp->cmd_tag); 6194c06356bSdh142964 if (pwrk && pwrk->arg == sp) { 6204c06356bSdh142964 tag = pwrk->htag; 6214c06356bSdh142964 pptr = pwrk->phy; 6224c06356bSdh142964 pwrk->timer = 0; /* we don't time this here */ 6234c06356bSdh142964 ASSERT(pwrk->state == PMCS_WORK_STATE_ONCHIP); 6244c06356bSdh142964 mutex_exit(&pwrk->lock); 6254c06356bSdh142964 pmcs_lock_phy(pptr); 6264c06356bSdh142964 if (pptr->dtype == SAS) { 6274c06356bSdh142964 if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, tag, lun, 6284c06356bSdh142964 NULL)) { 6294c06356bSdh142964 pptr->abort_pending = 1; 6304c06356bSdh142964 pmcs_unlock_phy(pptr); 6314c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 6324c06356bSdh142964 return (0); 6334c06356bSdh142964 } 6344c06356bSdh142964 } else { 6354c06356bSdh142964 /* 6364c06356bSdh142964 * XXX: Was the command that was active an 6374c06356bSdh142964 * NCQ I/O command? 6384c06356bSdh142964 */ 6394c06356bSdh142964 pptr->need_rl_ext = 1; 6404c06356bSdh142964 if (pmcs_sata_abort_ncq(pwp, pptr)) { 6414c06356bSdh142964 pptr->abort_pending = 1; 6424c06356bSdh142964 pmcs_unlock_phy(pptr); 6434c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 6444c06356bSdh142964 return (0); 6454c06356bSdh142964 } 6464c06356bSdh142964 } 6474c06356bSdh142964 pptr->abort_pending = 1; 6484c06356bSdh142964 pmcs_unlock_phy(pptr); 6494c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 6504c06356bSdh142964 return (1); 6514c06356bSdh142964 } 6524c06356bSdh142964 if (pwrk) { 6534c06356bSdh142964 mutex_exit(&pwrk->lock); 6544c06356bSdh142964 } 6554c06356bSdh142964 /* 6564c06356bSdh142964 * Okay, those weren't the droids we were looking for. 6574c06356bSdh142964 * See if the command is on any of the wait queues. 6584c06356bSdh142964 */ 6594c06356bSdh142964 mutex_enter(&xp->wqlock); 6604c06356bSdh142964 sp = NULL; 6614c06356bSdh142964 STAILQ_FOREACH(sp, &xp->wq, cmd_next) { 6624c06356bSdh142964 if (sp == PKT2CMD(pkt)) { 6634c06356bSdh142964 STAILQ_REMOVE(&xp->wq, sp, pmcs_cmd, cmd_next); 6644c06356bSdh142964 break; 6654c06356bSdh142964 } 6664c06356bSdh142964 } 6674c06356bSdh142964 mutex_exit(&xp->wqlock); 6684c06356bSdh142964 if (sp) { 6694c06356bSdh142964 pkt->pkt_reason = CMD_ABORTED; 6704c06356bSdh142964 pkt->pkt_statistics |= STAT_ABORTED; 6714c06356bSdh142964 mutex_enter(&pwp->cq_lock); 6724c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 6734c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp); 6744c06356bSdh142964 mutex_exit(&pwp->cq_lock); 6754c06356bSdh142964 return (1); 6764c06356bSdh142964 } 6774c06356bSdh142964 return (0); 6784c06356bSdh142964 } 6794c06356bSdh142964 6804c06356bSdh142964 /* 6814c06356bSdh142964 * SCSA reset functions 6824c06356bSdh142964 */ 6834c06356bSdh142964 static int 6844c06356bSdh142964 pmcs_scsa_reset(struct scsi_address *ap, int level) 6854c06356bSdh142964 { 6864c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap); 6874c06356bSdh142964 pmcs_phy_t *pptr; 6884c06356bSdh142964 pmcs_xscsi_t *xp; 6894c06356bSdh142964 uint64_t lun = (uint64_t)-1, *lp = NULL; 6904c06356bSdh142964 int rval; 6914c06356bSdh142964 6924c06356bSdh142964 mutex_enter(&pwp->lock); 6934c06356bSdh142964 if (pwp->state != STATE_RUNNING) { 6944c06356bSdh142964 mutex_exit(&pwp->lock); 695c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 696c3bc407cSdh142964 "%s: hba dead", __func__); 6974c06356bSdh142964 return (0); 6984c06356bSdh142964 } 6994c06356bSdh142964 mutex_exit(&pwp->lock); 7004c06356bSdh142964 7014c06356bSdh142964 switch (level) { 7024c06356bSdh142964 case RESET_ALL: 7034c06356bSdh142964 rval = 0; 7044c06356bSdh142964 break; 7054c06356bSdh142964 case RESET_LUN: 7064c06356bSdh142964 /* 7074c06356bSdh142964 * Point lp at lun so that pmcs_addr2xp 7084c06356bSdh142964 * will fill out the 64 bit lun number. 7094c06356bSdh142964 */ 7104c06356bSdh142964 lp = &lun; 7114c06356bSdh142964 /* FALLTHROUGH */ 7124c06356bSdh142964 case RESET_TARGET: 7134c06356bSdh142964 xp = pmcs_addr2xp(ap, lp, NULL); 7144c06356bSdh142964 if (xp == NULL) { 715c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7164c06356bSdh142964 "%s: no xp found for this scsi address", __func__); 7174c06356bSdh142964 return (0); 7184c06356bSdh142964 } 7194c06356bSdh142964 720b18a19c2SJesse Butler if (xp->dev_gone) { 7214c06356bSdh142964 mutex_exit(&xp->statlock); 722c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 7234c06356bSdh142964 "%s: Target 0x%p has gone away", __func__, 7244c06356bSdh142964 (void *)xp); 7254c06356bSdh142964 return (0); 7264c06356bSdh142964 } 7274c06356bSdh142964 7284c06356bSdh142964 /* 7294c06356bSdh142964 * If we're already performing this action, or if device 7304c06356bSdh142964 * state recovery is already running, just return failure. 7314c06356bSdh142964 */ 7324c06356bSdh142964 if (xp->resetting || xp->recover_wait) { 7334c06356bSdh142964 mutex_exit(&xp->statlock); 7344c06356bSdh142964 return (0); 7354c06356bSdh142964 } 7364c06356bSdh142964 xp->reset_wait = 0; 7374c06356bSdh142964 xp->reset_success = 0; 7384c06356bSdh142964 xp->resetting = 1; 7394c06356bSdh142964 pptr = xp->phy; 7404c06356bSdh142964 mutex_exit(&xp->statlock); 7414c06356bSdh142964 7424c06356bSdh142964 if (pmcs_reset_dev(pwp, pptr, lun)) { 7434c06356bSdh142964 rval = 0; 7444c06356bSdh142964 } else { 7454c06356bSdh142964 rval = 1; 7464c06356bSdh142964 } 7474c06356bSdh142964 7484c06356bSdh142964 mutex_enter(&xp->statlock); 7494c06356bSdh142964 if (rval == 1) { 7504c06356bSdh142964 xp->reset_success = 1; 7514c06356bSdh142964 } 7524c06356bSdh142964 if (xp->reset_wait) { 7534c06356bSdh142964 xp->reset_wait = 0; 7544c06356bSdh142964 cv_signal(&xp->reset_cv); 7554c06356bSdh142964 } 7564c06356bSdh142964 xp->resetting = 0; 7574c06356bSdh142964 mutex_exit(&xp->statlock); 7584c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 7594c06356bSdh142964 break; 7604c06356bSdh142964 default: 7614c06356bSdh142964 rval = 0; 7624c06356bSdh142964 break; 7634c06356bSdh142964 } 7644c06356bSdh142964 7654c06356bSdh142964 return (rval); 7664c06356bSdh142964 } 7674c06356bSdh142964 7684c06356bSdh142964 static int 7694c06356bSdh142964 pmcs_scsi_reset_notify(struct scsi_address *ap, int flag, 7704c06356bSdh142964 void (*callback)(caddr_t), caddr_t arg) 7714c06356bSdh142964 { 7724c06356bSdh142964 pmcs_hw_t *pwp = ADDR2PMC(ap); 7734c06356bSdh142964 return (scsi_hba_reset_notify_setup(ap, flag, callback, arg, 7744c06356bSdh142964 &pwp->lock, &pwp->reset_notify_listf)); 7754c06356bSdh142964 } 7764c06356bSdh142964 7774c06356bSdh142964 7784c06356bSdh142964 static int 7794c06356bSdh142964 pmcs_cap(struct scsi_address *ap, char *cap, int val, int tonly, int set) 7804c06356bSdh142964 { 7814c06356bSdh142964 _NOTE(ARGUNUSED(val, tonly)); 7824c06356bSdh142964 int cidx, rval = 0; 7834c06356bSdh142964 pmcs_xscsi_t *xp; 7844c06356bSdh142964 7854c06356bSdh142964 cidx = scsi_hba_lookup_capstr(cap); 7864c06356bSdh142964 if (cidx == -1) { 7874c06356bSdh142964 return (-1); 7884c06356bSdh142964 } 7894c06356bSdh142964 7904c06356bSdh142964 xp = pmcs_addr2xp(ap, NULL, NULL); 7914c06356bSdh142964 if (xp == NULL) { 7924c06356bSdh142964 return (-1); 7934c06356bSdh142964 } 7944c06356bSdh142964 7954c06356bSdh142964 switch (cidx) { 7964c06356bSdh142964 case SCSI_CAP_DMA_MAX: 7974c06356bSdh142964 case SCSI_CAP_INITIATOR_ID: 7984c06356bSdh142964 if (set == 0) { 7994c06356bSdh142964 rval = INT_MAX; /* argh */ 8004c06356bSdh142964 } 8014c06356bSdh142964 break; 8024c06356bSdh142964 case SCSI_CAP_DISCONNECT: 8034c06356bSdh142964 case SCSI_CAP_SYNCHRONOUS: 8044c06356bSdh142964 case SCSI_CAP_WIDE_XFER: 8054c06356bSdh142964 case SCSI_CAP_PARITY: 8064c06356bSdh142964 case SCSI_CAP_ARQ: 8074c06356bSdh142964 case SCSI_CAP_UNTAGGED_QING: 8084c06356bSdh142964 if (set == 0) { 8094c06356bSdh142964 rval = 1; 8104c06356bSdh142964 } 8114c06356bSdh142964 break; 8124c06356bSdh142964 8134c06356bSdh142964 case SCSI_CAP_TAGGED_QING: 8144c06356bSdh142964 rval = 1; 8154c06356bSdh142964 break; 8164c06356bSdh142964 8174c06356bSdh142964 case SCSI_CAP_MSG_OUT: 8184c06356bSdh142964 case SCSI_CAP_RESET_NOTIFICATION: 8194c06356bSdh142964 case SCSI_CAP_QFULL_RETRIES: 8204c06356bSdh142964 case SCSI_CAP_QFULL_RETRY_INTERVAL: 8214c06356bSdh142964 break; 8224c06356bSdh142964 case SCSI_CAP_SCSI_VERSION: 8234c06356bSdh142964 if (set == 0) { 8244c06356bSdh142964 rval = SCSI_VERSION_3; 8254c06356bSdh142964 } 8264c06356bSdh142964 break; 8274c06356bSdh142964 case SCSI_CAP_INTERCONNECT_TYPE: 8284c06356bSdh142964 if (set) { 8294c06356bSdh142964 break; 8304c06356bSdh142964 } 8314c06356bSdh142964 if (xp->phy_addressable) { 8324c06356bSdh142964 rval = INTERCONNECT_SATA; 8334c06356bSdh142964 } else { 8344c06356bSdh142964 rval = INTERCONNECT_SAS; 8354c06356bSdh142964 } 8364c06356bSdh142964 break; 8374c06356bSdh142964 case SCSI_CAP_CDB_LEN: 8384c06356bSdh142964 if (set == 0) { 8394c06356bSdh142964 rval = 16; 8404c06356bSdh142964 } 8414c06356bSdh142964 break; 8424c06356bSdh142964 case SCSI_CAP_LUN_RESET: 8434c06356bSdh142964 if (set) { 8444c06356bSdh142964 break; 8454c06356bSdh142964 } 8464c06356bSdh142964 if (xp->dtype == SATA) { 8474c06356bSdh142964 rval = 0; 8484c06356bSdh142964 } else { 8494c06356bSdh142964 rval = 1; 8504c06356bSdh142964 } 8514c06356bSdh142964 break; 8524c06356bSdh142964 default: 8534c06356bSdh142964 rval = -1; 8544c06356bSdh142964 break; 8554c06356bSdh142964 } 8564c06356bSdh142964 mutex_exit(&xp->statlock); 857c3bc407cSdh142964 pmcs_prt(ADDR2PMC(ap), PMCS_PRT_DEBUG3, NULL, NULL, 8584c06356bSdh142964 "%s: cap %s val %d set %d rval %d", 8594c06356bSdh142964 __func__, cap, val, set, rval); 8604c06356bSdh142964 return (rval); 8614c06356bSdh142964 } 8624c06356bSdh142964 8634c06356bSdh142964 /* 8644c06356bSdh142964 * Returns with statlock held if the xp is found. 8654c06356bSdh142964 * Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL. 8664c06356bSdh142964 */ 8674c06356bSdh142964 static pmcs_xscsi_t * 8684c06356bSdh142964 pmcs_addr2xp(struct scsi_address *ap, uint64_t *lp, pmcs_cmd_t *sp) 8694c06356bSdh142964 { 8704c06356bSdh142964 pmcs_xscsi_t *xp; 8714c06356bSdh142964 pmcs_lun_t *lun = (pmcs_lun_t *) 8724c06356bSdh142964 scsi_device_hba_private_get(scsi_address_device(ap)); 8734c06356bSdh142964 8744c06356bSdh142964 if ((lun == NULL) || (lun->target == NULL)) { 8754c06356bSdh142964 return (NULL); 8764c06356bSdh142964 } 8774c06356bSdh142964 xp = lun->target; 8784c06356bSdh142964 mutex_enter(&xp->statlock); 8794c06356bSdh142964 880b18a19c2SJesse Butler if (xp->dev_gone || (xp->phy == NULL)) { 8814c06356bSdh142964 mutex_exit(&xp->statlock); 8824c06356bSdh142964 return (NULL); 8834c06356bSdh142964 } 8844c06356bSdh142964 8854c06356bSdh142964 if (sp != NULL) { 8864c06356bSdh142964 sp->cmd_target = xp; 8874c06356bSdh142964 sp->cmd_lun = lun; 8884c06356bSdh142964 } 8894c06356bSdh142964 if (lp) { 8904c06356bSdh142964 *lp = lun->lun_num; 8914c06356bSdh142964 } 8924c06356bSdh142964 return (xp); 8934c06356bSdh142964 } 8944c06356bSdh142964 8954c06356bSdh142964 static int 8964c06356bSdh142964 pmcs_scsa_getcap(struct scsi_address *ap, char *cap, int whom) 8974c06356bSdh142964 { 8984c06356bSdh142964 int r; 8994c06356bSdh142964 if (cap == NULL) { 9004c06356bSdh142964 return (-1); 9014c06356bSdh142964 } 9024c06356bSdh142964 r = pmcs_cap(ap, cap, 0, whom, 0); 9034c06356bSdh142964 return (r); 9044c06356bSdh142964 } 9054c06356bSdh142964 9064c06356bSdh142964 static int 9074c06356bSdh142964 pmcs_scsa_setcap(struct scsi_address *ap, char *cap, int value, int whom) 9084c06356bSdh142964 { 9094c06356bSdh142964 int r; 9104c06356bSdh142964 if (cap == NULL) { 9114c06356bSdh142964 return (-1); 9124c06356bSdh142964 } 9134c06356bSdh142964 r = pmcs_cap(ap, cap, value, whom, 1); 9144c06356bSdh142964 return (r); 9154c06356bSdh142964 } 9164c06356bSdh142964 9174c06356bSdh142964 static int 9184c06356bSdh142964 pmcs_scsa_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t), 9194c06356bSdh142964 caddr_t cbarg) 9204c06356bSdh142964 { 9214c06356bSdh142964 _NOTE(ARGUNUSED(callback, cbarg)); 9224c06356bSdh142964 pmcs_cmd_t *sp = pkt->pkt_ha_private; 9234c06356bSdh142964 9244c06356bSdh142964 bzero(sp, sizeof (pmcs_cmd_t)); 9254c06356bSdh142964 sp->cmd_pkt = pkt; 9264c06356bSdh142964 return (0); 9274c06356bSdh142964 } 9284c06356bSdh142964 9294c06356bSdh142964 static void 9304c06356bSdh142964 pmcs_scsa_teardown_pkt(struct scsi_pkt *pkt) 9314c06356bSdh142964 { 9324c06356bSdh142964 pmcs_cmd_t *sp = pkt->pkt_ha_private; 9334c06356bSdh142964 sp->cmd_target = NULL; 9344c06356bSdh142964 sp->cmd_lun = NULL; 9354c06356bSdh142964 } 9364c06356bSdh142964 9374c06356bSdh142964 static int 93896c4a178SChris Horne pmcs_smp_start(struct smp_pkt *smp_pkt) 9394c06356bSdh142964 { 9404c06356bSdh142964 struct pmcwork *pwrk; 9414c06356bSdh142964 const uint_t rdoff = SAS_SMP_MAX_PAYLOAD; 9424c06356bSdh142964 uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status; 9434c06356bSdh142964 uint64_t wwn; 94496c4a178SChris Horne pmcs_hw_t *pwp; 9454c06356bSdh142964 pmcs_phy_t *pptr; 9464c06356bSdh142964 pmcs_xscsi_t *xp; 9474c06356bSdh142964 uint_t reqsz, rspsz, will_retry; 9484c06356bSdh142964 int result; 9494c06356bSdh142964 95096c4a178SChris Horne pwp = smp_pkt->smp_pkt_address->smp_a_hba_tran->smp_tran_hba_private; 95196c4a178SChris Horne bcopy(smp_pkt->smp_pkt_address->smp_a_wwn, &wwn, SAS_WWN_BYTE_SIZE); 9524c06356bSdh142964 953c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL, 954c3bc407cSdh142964 "%s: starting for wwn 0x%" PRIx64, __func__, wwn); 9554c06356bSdh142964 95696c4a178SChris Horne will_retry = smp_pkt->smp_pkt_will_retry; 9574c06356bSdh142964 9584c06356bSdh142964 (void) pmcs_acquire_scratch(pwp, B_TRUE); 95996c4a178SChris Horne reqsz = smp_pkt->smp_pkt_reqsize; 9604c06356bSdh142964 if (reqsz > SAS_SMP_MAX_PAYLOAD) { 9614c06356bSdh142964 reqsz = SAS_SMP_MAX_PAYLOAD; 9624c06356bSdh142964 } 96396c4a178SChris Horne (void) memcpy(pwp->scratch, smp_pkt->smp_pkt_req, reqsz); 9644c06356bSdh142964 96596c4a178SChris Horne rspsz = smp_pkt->smp_pkt_rspsize; 9664c06356bSdh142964 if (rspsz > SAS_SMP_MAX_PAYLOAD) { 9674c06356bSdh142964 rspsz = SAS_SMP_MAX_PAYLOAD; 9684c06356bSdh142964 } 9694c06356bSdh142964 9704c06356bSdh142964 /* 9714c06356bSdh142964 * The request size from the SMP driver always includes 4 bytes 9724c06356bSdh142964 * for the CRC. The PMCS chip, however, doesn't want to see those 9734c06356bSdh142964 * counts as part of the transfer size. 9744c06356bSdh142964 */ 9754c06356bSdh142964 reqsz -= 4; 9764c06356bSdh142964 9774c06356bSdh142964 pptr = pmcs_find_phy_by_wwn(pwp, wwn); 9784c06356bSdh142964 /* PHY is now locked */ 9794c06356bSdh142964 if (pptr == NULL || pptr->dtype != EXPANDER) { 9804c06356bSdh142964 if (pptr) { 9814c06356bSdh142964 pmcs_unlock_phy(pptr); 9824c06356bSdh142964 } 9834c06356bSdh142964 pmcs_release_scratch(pwp); 984c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 985c3bc407cSdh142964 "%s: could not find phy", __func__); 98696c4a178SChris Horne smp_pkt->smp_pkt_reason = ENXIO; 9874c06356bSdh142964 return (DDI_FAILURE); 9884c06356bSdh142964 } 9894c06356bSdh142964 9904c06356bSdh142964 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr); 9914c06356bSdh142964 if (pwrk == NULL) { 9924c06356bSdh142964 pmcs_unlock_phy(pptr); 9934c06356bSdh142964 pmcs_release_scratch(pwp); 994c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL, 9954c06356bSdh142964 "%s: could not get work structure", __func__); 99696c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EBUSY; 9974c06356bSdh142964 return (DDI_FAILURE); 9984c06356bSdh142964 } 9994c06356bSdh142964 10004c06356bSdh142964 pwrk->arg = msg; 10014c06356bSdh142964 pwrk->dtype = EXPANDER; 10024c06356bSdh142964 mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]); 10034c06356bSdh142964 ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 10044c06356bSdh142964 if (ptr == NULL) { 10054c06356bSdh142964 pmcs_pwork(pwp, pwrk); 10064c06356bSdh142964 mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]); 10074c06356bSdh142964 pmcs_unlock_phy(pptr); 10084c06356bSdh142964 pmcs_release_scratch(pwp); 1009c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1010c3bc407cSdh142964 "%s: could not get IQ entry", __func__); 101196c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN :EBUSY; 10124c06356bSdh142964 return (DDI_FAILURE); 10134c06356bSdh142964 } 10144c06356bSdh142964 msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST)); 10154c06356bSdh142964 msg[1] = LE_32(pwrk->htag); 10164c06356bSdh142964 msg[2] = LE_32(pptr->device_id); 10174c06356bSdh142964 msg[3] = LE_32(SMP_INDIRECT_RESPONSE | SMP_INDIRECT_REQUEST); 10184c06356bSdh142964 msg[8] = LE_32(DWORD0(pwp->scratch_dma)); 10194c06356bSdh142964 msg[9] = LE_32(DWORD1(pwp->scratch_dma)); 10204c06356bSdh142964 msg[10] = LE_32(reqsz); 10214c06356bSdh142964 msg[11] = 0; 10224c06356bSdh142964 msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff)); 10234c06356bSdh142964 msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff)); 10244c06356bSdh142964 msg[14] = LE_32(rspsz); 10254c06356bSdh142964 msg[15] = 0; 10264c06356bSdh142964 10274c06356bSdh142964 COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE); 1028*6745c559SJesse Butler 1029*6745c559SJesse Butler /* SMP serialization */ 1030*6745c559SJesse Butler pmcs_smp_acquire(pptr->iport); 1031*6745c559SJesse Butler 10324c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ONCHIP; 10334c06356bSdh142964 htag = pwrk->htag; 10344c06356bSdh142964 INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER); 10354c06356bSdh142964 10364c06356bSdh142964 pmcs_unlock_phy(pptr); 103796c4a178SChris Horne WAIT_FOR(pwrk, smp_pkt->smp_pkt_timeout * 1000, result); 10384c06356bSdh142964 pmcs_pwork(pwp, pwrk); 10394c06356bSdh142964 pmcs_lock_phy(pptr); 10404c06356bSdh142964 1041*6745c559SJesse Butler /* SMP serialization */ 1042*6745c559SJesse Butler pmcs_smp_release(pptr->iport); 1043*6745c559SJesse Butler 10444c06356bSdh142964 if (result) { 10454c06356bSdh142964 pmcs_timed_out(pwp, htag, __func__); 10464c06356bSdh142964 if (pmcs_abort(pwp, pptr, htag, 0, 0)) { 1047c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 10484c06356bSdh142964 "%s: Unable to issue SMP ABORT for htag 0x%08x", 10494c06356bSdh142964 __func__, htag); 10504c06356bSdh142964 } else { 1051c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, NULL, 10524c06356bSdh142964 "%s: Issuing SMP ABORT for htag 0x%08x", 10534c06356bSdh142964 __func__, htag); 10544c06356bSdh142964 } 10554c06356bSdh142964 pmcs_unlock_phy(pptr); 10564c06356bSdh142964 pmcs_release_scratch(pwp); 105796c4a178SChris Horne smp_pkt->smp_pkt_reason = ETIMEDOUT; 10584c06356bSdh142964 return (DDI_FAILURE); 10594c06356bSdh142964 } 10604c06356bSdh142964 status = LE_32(msg[2]); 10614c06356bSdh142964 if (status == PMCOUT_STATUS_OVERFLOW) { 10624c06356bSdh142964 status = PMCOUT_STATUS_OK; 106396c4a178SChris Horne smp_pkt->smp_pkt_reason = EOVERFLOW; 10644c06356bSdh142964 } 10654c06356bSdh142964 if (status != PMCOUT_STATUS_OK) { 10664c06356bSdh142964 const char *emsg = pmcs_status_str(status); 10674c06356bSdh142964 if (emsg == NULL) { 1068c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 10694c06356bSdh142964 "SMP operation failed (0x%x)", status); 10704c06356bSdh142964 } else { 1071c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 10724c06356bSdh142964 "SMP operation failed (%s)", emsg); 10734c06356bSdh142964 } 10744c06356bSdh142964 10754c06356bSdh142964 if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) || 10764c06356bSdh142964 (status == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT)) { 107796c4a178SChris Horne smp_pkt->smp_pkt_reason = 107896c4a178SChris Horne will_retry ? EAGAIN : ETIMEDOUT; 10794c06356bSdh142964 result = DDI_FAILURE; 10804c06356bSdh142964 } else if (status == 10814c06356bSdh142964 PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) { 10824c06356bSdh142964 xp = pptr->target; 10834c06356bSdh142964 if (xp == NULL) { 108496c4a178SChris Horne smp_pkt->smp_pkt_reason = EIO; 10854c06356bSdh142964 result = DDI_FAILURE; 10864c06356bSdh142964 goto out; 10874c06356bSdh142964 } 10884c06356bSdh142964 if (xp->dev_state != 10894c06356bSdh142964 PMCS_DEVICE_STATE_NON_OPERATIONAL) { 10904c06356bSdh142964 xp->dev_state = 10914c06356bSdh142964 PMCS_DEVICE_STATE_NON_OPERATIONAL; 1092c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, xp, 10934c06356bSdh142964 "%s: Got _IT_NEXUS_LOSS SMP status. " 10944c06356bSdh142964 "Tgt(0x%p) dev_state set to " 10954c06356bSdh142964 "_NON_OPERATIONAL", __func__, 10964c06356bSdh142964 (void *)xp); 10974c06356bSdh142964 } 10984c06356bSdh142964 /* ABORT any pending commands related to this device */ 10994c06356bSdh142964 if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) != 0) { 11004c06356bSdh142964 pptr->abort_pending = 1; 110196c4a178SChris Horne smp_pkt->smp_pkt_reason = EIO; 11024c06356bSdh142964 result = DDI_FAILURE; 11034c06356bSdh142964 } 11044c06356bSdh142964 } else { 110596c4a178SChris Horne smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EIO; 11064c06356bSdh142964 result = DDI_FAILURE; 11074c06356bSdh142964 } 11084c06356bSdh142964 } else { 110996c4a178SChris Horne (void) memcpy(smp_pkt->smp_pkt_rsp, 11104c06356bSdh142964 &((uint8_t *)pwp->scratch)[rdoff], rspsz); 111196c4a178SChris Horne if (smp_pkt->smp_pkt_reason == EOVERFLOW) { 11124c06356bSdh142964 result = DDI_FAILURE; 11134c06356bSdh142964 } else { 11144c06356bSdh142964 result = DDI_SUCCESS; 11154c06356bSdh142964 } 11164c06356bSdh142964 } 11174c06356bSdh142964 out: 11184c06356bSdh142964 pmcs_unlock_phy(pptr); 11194c06356bSdh142964 pmcs_release_scratch(pwp); 11204c06356bSdh142964 return (result); 11214c06356bSdh142964 } 11224c06356bSdh142964 11234c06356bSdh142964 static int 11244c06356bSdh142964 pmcs_smp_init(dev_info_t *self, dev_info_t *child, 112596c4a178SChris Horne smp_hba_tran_t *tran, smp_device_t *smp_sd) 11264c06356bSdh142964 { 112796c4a178SChris Horne _NOTE(ARGUNUSED(tran, smp_sd)); 11284c06356bSdh142964 pmcs_iport_t *iport; 11294c06356bSdh142964 pmcs_hw_t *pwp; 11304c06356bSdh142964 pmcs_xscsi_t *tgt; 11314c06356bSdh142964 pmcs_phy_t *phy, *pphy; 11324c06356bSdh142964 uint64_t wwn; 11334c06356bSdh142964 char *addr, *tgt_port; 11344c06356bSdh142964 int ua_form = 1; 11354c06356bSdh142964 11364c06356bSdh142964 iport = ddi_get_soft_state(pmcs_iport_softstate, 11374c06356bSdh142964 ddi_get_instance(self)); 11384c06356bSdh142964 ASSERT(iport); 11394c06356bSdh142964 if (iport == NULL) 11404c06356bSdh142964 return (DDI_FAILURE); 11414c06356bSdh142964 pwp = iport->pwp; 11424c06356bSdh142964 ASSERT(pwp); 11434c06356bSdh142964 if (pwp == NULL) 11444c06356bSdh142964 return (DDI_FAILURE); 1145c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: %s", __func__, 11464c06356bSdh142964 ddi_get_name(child)); 11474c06356bSdh142964 11484c06356bSdh142964 /* Get "target-port" prop from devinfo node */ 11494c06356bSdh142964 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, 11504c06356bSdh142964 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 11514c06356bSdh142964 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) { 1152c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to " 1153c3bc407cSdh142964 "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__); 11544c06356bSdh142964 /* Dont fail _smp_init() because we couldnt get/set a prop */ 11554c06356bSdh142964 return (DDI_SUCCESS); 11564c06356bSdh142964 } 11574c06356bSdh142964 11584c06356bSdh142964 /* 11594c06356bSdh142964 * Validate that this tran_tgt_init is for an active iport. 11604c06356bSdh142964 */ 11614c06356bSdh142964 if (iport->ua_state == UA_INACTIVE) { 1162c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1163c3bc407cSdh142964 "%s: Init on inactive iport for '%s'", __func__, tgt_port); 11644c06356bSdh142964 ddi_prop_free(tgt_port); 11654c06356bSdh142964 return (DDI_FAILURE); 11664c06356bSdh142964 } 11674c06356bSdh142964 11684c06356bSdh142964 mutex_enter(&pwp->lock); 11694c06356bSdh142964 11704c06356bSdh142964 /* Retrieve softstate using unit-address */ 11714c06356bSdh142964 tgt = pmcs_get_target(iport, tgt_port); 11724c06356bSdh142964 if (tgt == NULL) { 1173c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1174c3bc407cSdh142964 "%s: tgt softstate not found", __func__); 11754c06356bSdh142964 ddi_prop_free(tgt_port); 11764c06356bSdh142964 mutex_exit(&pwp->lock); 11774c06356bSdh142964 return (DDI_FAILURE); 11784c06356bSdh142964 } 11794c06356bSdh142964 11804c06356bSdh142964 phy = tgt->phy; 11814c06356bSdh142964 ASSERT(mutex_owned(&phy->phy_lock)); 11824c06356bSdh142964 11834c06356bSdh142964 if (IS_ROOT_PHY(phy)) { 11844c06356bSdh142964 /* Expander attached to HBA - don't ref_count it */ 11854c06356bSdh142964 wwn = pwp->sas_wwns[0]; 11864c06356bSdh142964 } else { 11874c06356bSdh142964 pmcs_inc_phy_ref_count(phy); 11884c06356bSdh142964 11894c06356bSdh142964 /* 11904c06356bSdh142964 * Parent (in topology) is also an expander 11914c06356bSdh142964 * Now that we've increased the ref count on phy, it's OK 11924c06356bSdh142964 * to drop the lock so we can acquire the parent's lock. 11934c06356bSdh142964 */ 11944c06356bSdh142964 11954c06356bSdh142964 pphy = phy->parent; 11964c06356bSdh142964 pmcs_unlock_phy(phy); 11974c06356bSdh142964 pmcs_lock_phy(pphy); 11984c06356bSdh142964 wwn = pmcs_barray2wwn(pphy->sas_address); 11994c06356bSdh142964 pmcs_unlock_phy(pphy); 12004c06356bSdh142964 pmcs_lock_phy(phy); 12014c06356bSdh142964 } 12024c06356bSdh142964 12034c06356bSdh142964 /* 12044c06356bSdh142964 * If this is the 1st smp_init, add this to our list. 12054c06356bSdh142964 */ 12064c06356bSdh142964 if (tgt->target_num == PMCS_INVALID_TARGET_NUM) { 12074c06356bSdh142964 int target; 12084c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) { 12094c06356bSdh142964 if (pwp->targets[target] != NULL) { 12104c06356bSdh142964 continue; 12114c06356bSdh142964 } 12124c06356bSdh142964 12134c06356bSdh142964 pwp->targets[target] = tgt; 12144c06356bSdh142964 tgt->target_num = (uint16_t)target; 12154c06356bSdh142964 tgt->assigned = 1; 12164c06356bSdh142964 tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL; 12174c06356bSdh142964 break; 12184c06356bSdh142964 } 12194c06356bSdh142964 12204c06356bSdh142964 if (target == pwp->max_dev) { 1221c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 12224c06356bSdh142964 "Target list full."); 12234c06356bSdh142964 goto smp_init_fail; 12244c06356bSdh142964 } 12254c06356bSdh142964 } 12264c06356bSdh142964 12274c06356bSdh142964 if (!pmcs_assign_device(pwp, tgt)) { 12284c06356bSdh142964 pwp->targets[tgt->target_num] = NULL; 1229c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, 12304c06356bSdh142964 "%s: pmcs_assign_device failed for target 0x%p", 12314c06356bSdh142964 __func__, (void *)tgt); 12324c06356bSdh142964 goto smp_init_fail; 12334c06356bSdh142964 } 12344c06356bSdh142964 12354c06356bSdh142964 pmcs_unlock_phy(phy); 12364c06356bSdh142964 mutex_exit(&pwp->lock); 12374c06356bSdh142964 12384c06356bSdh142964 tgt->ref_count++; 12394c06356bSdh142964 tgt->dtype = phy->dtype; 12404c06356bSdh142964 12414c06356bSdh142964 addr = scsi_wwn_to_wwnstr(wwn, ua_form, NULL); 12424c06356bSdh142964 /* XXX: Update smp devinfo node using ndi_xxx */ 12434c06356bSdh142964 if (ndi_prop_update_string(DDI_DEV_T_NONE, child, 12444c06356bSdh142964 SCSI_ADDR_PROP_ATTACHED_PORT, addr) != DDI_SUCCESS) { 1245c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to set " 1246c3bc407cSdh142964 "prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", __func__); 12474c06356bSdh142964 } 12484c06356bSdh142964 (void) scsi_free_wwnstr(addr); 12494c06356bSdh142964 ddi_prop_free(tgt_port); 12504c06356bSdh142964 return (DDI_SUCCESS); 12514c06356bSdh142964 12524c06356bSdh142964 smp_init_fail: 12534c06356bSdh142964 tgt->phy = NULL; 12544c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM; 12554c06356bSdh142964 phy->target = NULL; 12564c06356bSdh142964 if (!IS_ROOT_PHY(phy)) { 12574c06356bSdh142964 pmcs_dec_phy_ref_count(phy); 12584c06356bSdh142964 } 12594c06356bSdh142964 pmcs_unlock_phy(phy); 12604c06356bSdh142964 mutex_exit(&pwp->lock); 12614c06356bSdh142964 ddi_soft_state_bystr_free(iport->tgt_sstate, tgt->unit_address); 12624c06356bSdh142964 ddi_prop_free(tgt_port); 12634c06356bSdh142964 return (DDI_FAILURE); 12644c06356bSdh142964 } 12654c06356bSdh142964 12664c06356bSdh142964 static void 12674c06356bSdh142964 pmcs_smp_free(dev_info_t *self, dev_info_t *child, 126896c4a178SChris Horne smp_hba_tran_t *tran, smp_device_t *smp) 12694c06356bSdh142964 { 12704c06356bSdh142964 _NOTE(ARGUNUSED(tran, smp)); 12714c06356bSdh142964 pmcs_iport_t *iport; 12724c06356bSdh142964 pmcs_hw_t *pwp; 12734c06356bSdh142964 pmcs_xscsi_t *tgt; 12744c06356bSdh142964 char *tgt_port; 12754c06356bSdh142964 12764c06356bSdh142964 iport = ddi_get_soft_state(pmcs_iport_softstate, 12774c06356bSdh142964 ddi_get_instance(self)); 12784c06356bSdh142964 ASSERT(iport); 12794c06356bSdh142964 if (iport == NULL) 12804c06356bSdh142964 return; 12814c06356bSdh142964 12824c06356bSdh142964 pwp = iport->pwp; 12834c06356bSdh142964 if (pwp == NULL) 12844c06356bSdh142964 return; 12854c06356bSdh142964 ASSERT(pwp); 1286c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, "%s: %s", __func__, 12874c06356bSdh142964 ddi_get_name(child)); 12884c06356bSdh142964 12894c06356bSdh142964 /* Get "target-port" prop from devinfo node */ 12904c06356bSdh142964 if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child, 12914c06356bSdh142964 DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 12924c06356bSdh142964 SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) { 1293c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to " 1294c3bc407cSdh142964 "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__); 12954c06356bSdh142964 return; 12964c06356bSdh142964 } 12974c06356bSdh142964 /* Retrieve softstate using unit-address */ 12984c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port); 12994c06356bSdh142964 ddi_prop_free(tgt_port); 13004c06356bSdh142964 13014c06356bSdh142964 if (tgt == NULL) { 1302c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1303c3bc407cSdh142964 "%s: tgt softstate not found", __func__); 13044c06356bSdh142964 return; 13054c06356bSdh142964 } 13064c06356bSdh142964 13074c06356bSdh142964 mutex_enter(&pwp->lock); 13084c06356bSdh142964 mutex_enter(&tgt->statlock); 13094c06356bSdh142964 if (tgt->phy) { 13104c06356bSdh142964 if (!IS_ROOT_PHY(tgt->phy)) { 13114c06356bSdh142964 pmcs_dec_phy_ref_count(tgt->phy); 13124c06356bSdh142964 } 13134c06356bSdh142964 } 13144c06356bSdh142964 13154c06356bSdh142964 if (--tgt->ref_count == 0) { 13164c06356bSdh142964 /* 13174c06356bSdh142964 * Remove this target from our list. The softstate 13184c06356bSdh142964 * will remain, and the device will remain registered 13194c06356bSdh142964 * with the hardware unless/until we're told that the 13204c06356bSdh142964 * device physically went away. 13214c06356bSdh142964 */ 1322c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, 13234c06356bSdh142964 "Removing target 0x%p (vtgt %d) from target list", 13244c06356bSdh142964 (void *)tgt, tgt->target_num); 13254c06356bSdh142964 pwp->targets[tgt->target_num] = NULL; 13264c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM; 13274c06356bSdh142964 tgt->phy->target = NULL; 13284c06356bSdh142964 tgt->phy = NULL; 13294c06356bSdh142964 } 13304c06356bSdh142964 13314c06356bSdh142964 mutex_exit(&tgt->statlock); 13324c06356bSdh142964 mutex_exit(&pwp->lock); 13334c06356bSdh142964 } 13344c06356bSdh142964 13354c06356bSdh142964 static int 13364c06356bSdh142964 pmcs_scsi_quiesce(dev_info_t *dip) 13374c06356bSdh142964 { 13384c06356bSdh142964 pmcs_hw_t *pwp; 13394c06356bSdh142964 int totactive = -1; 13404c06356bSdh142964 pmcs_xscsi_t *xp; 13414c06356bSdh142964 uint16_t target; 13424c06356bSdh142964 13434c06356bSdh142964 if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip))) 13444c06356bSdh142964 return (0); /* iport */ 13454c06356bSdh142964 13464c06356bSdh142964 pwp = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip)); 13474c06356bSdh142964 if (pwp == NULL) { 13484c06356bSdh142964 return (-1); 13494c06356bSdh142964 } 13504c06356bSdh142964 mutex_enter(&pwp->lock); 13514c06356bSdh142964 if (pwp->state != STATE_RUNNING) { 13524c06356bSdh142964 mutex_exit(&pwp->lock); 13534c06356bSdh142964 return (-1); 13544c06356bSdh142964 } 13554c06356bSdh142964 1356c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__); 13574c06356bSdh142964 pwp->blocked = 1; 13584c06356bSdh142964 while (totactive) { 13594c06356bSdh142964 totactive = 0; 13604c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) { 13614c06356bSdh142964 xp = pwp->targets[target]; 13624c06356bSdh142964 if (xp == NULL) { 13634c06356bSdh142964 continue; 13644c06356bSdh142964 } 13654c06356bSdh142964 mutex_enter(&xp->statlock); 13664c06356bSdh142964 if (xp->actv_cnt) { 13674c06356bSdh142964 totactive += xp->actv_cnt; 13684c06356bSdh142964 xp->draining = 1; 13694c06356bSdh142964 } 13704c06356bSdh142964 mutex_exit(&xp->statlock); 13714c06356bSdh142964 } 13724c06356bSdh142964 if (totactive) { 13734c06356bSdh142964 cv_wait(&pwp->drain_cv, &pwp->lock); 13744c06356bSdh142964 } 13754c06356bSdh142964 /* 13764c06356bSdh142964 * The pwp->blocked may have been reset. e.g a SCSI bus reset 13774c06356bSdh142964 */ 13784c06356bSdh142964 pwp->blocked = 1; 13794c06356bSdh142964 } 13804c06356bSdh142964 13814c06356bSdh142964 for (target = 0; target < pwp->max_dev; target++) { 13824c06356bSdh142964 xp = pwp->targets[target]; 13834c06356bSdh142964 if (xp == NULL) { 13844c06356bSdh142964 continue; 13854c06356bSdh142964 } 13864c06356bSdh142964 mutex_enter(&xp->statlock); 13874c06356bSdh142964 xp->draining = 0; 13884c06356bSdh142964 mutex_exit(&xp->statlock); 13894c06356bSdh142964 } 13904c06356bSdh142964 13914c06356bSdh142964 mutex_exit(&pwp->lock); 13924c06356bSdh142964 if (totactive == 0) { 1393c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 1394c3bc407cSdh142964 "%s drain complete", __func__); 13954c06356bSdh142964 } 13964c06356bSdh142964 return (0); 13974c06356bSdh142964 } 13984c06356bSdh142964 13994c06356bSdh142964 static int 14004c06356bSdh142964 pmcs_scsi_unquiesce(dev_info_t *dip) 14014c06356bSdh142964 { 14024c06356bSdh142964 pmcs_hw_t *pwp; 14034c06356bSdh142964 14044c06356bSdh142964 if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip))) 14054c06356bSdh142964 return (0); /* iport */ 14064c06356bSdh142964 14074c06356bSdh142964 pwp = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip)); 14084c06356bSdh142964 if (pwp == NULL) { 14094c06356bSdh142964 return (-1); 14104c06356bSdh142964 } 14114c06356bSdh142964 mutex_enter(&pwp->lock); 14124c06356bSdh142964 if (pwp->state != STATE_RUNNING) { 14134c06356bSdh142964 mutex_exit(&pwp->lock); 14144c06356bSdh142964 return (-1); 14154c06356bSdh142964 } 1416c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__); 14174c06356bSdh142964 pwp->blocked = 0; 14184c06356bSdh142964 mutex_exit(&pwp->lock); 14194c06356bSdh142964 14204c06356bSdh142964 /* 14214c06356bSdh142964 * Run all pending commands. 14224c06356bSdh142964 */ 14234c06356bSdh142964 pmcs_scsa_wq_run(pwp); 14244c06356bSdh142964 14254c06356bSdh142964 /* 14264c06356bSdh142964 * Complete all completed commands. 14274c06356bSdh142964 * This also unlocks us. 14284c06356bSdh142964 */ 14294c06356bSdh142964 PMCS_CQ_RUN(pwp); 14304c06356bSdh142964 return (0); 14314c06356bSdh142964 } 14324c06356bSdh142964 14334c06356bSdh142964 /* 14344c06356bSdh142964 * Start commands for a particular device 14354c06356bSdh142964 * If the actual start of a command fails, return B_FALSE. Any other result 14364c06356bSdh142964 * is a B_TRUE return. 14374c06356bSdh142964 */ 14384c06356bSdh142964 boolean_t 14394c06356bSdh142964 pmcs_scsa_wq_run_one(pmcs_hw_t *pwp, pmcs_xscsi_t *xp) 14404c06356bSdh142964 { 14414c06356bSdh142964 pmcs_cmd_t *sp; 14424c06356bSdh142964 pmcs_phy_t *phyp; 14434c06356bSdh142964 pmcwork_t *pwrk; 14444c06356bSdh142964 boolean_t run_one, blocked; 14454c06356bSdh142964 int rval; 14464c06356bSdh142964 14474c06356bSdh142964 /* 14484c06356bSdh142964 * First, check to see if we're blocked or resource limited 14494c06356bSdh142964 */ 14504c06356bSdh142964 mutex_enter(&pwp->lock); 14514c06356bSdh142964 blocked = pwp->blocked; 14524c06356bSdh142964 /* 14534c06356bSdh142964 * If resource_limited is set, we're resource constrained and 14544c06356bSdh142964 * we will run only one work request for this target. 14554c06356bSdh142964 */ 14564c06356bSdh142964 run_one = pwp->resource_limited; 14574c06356bSdh142964 mutex_exit(&pwp->lock); 14584c06356bSdh142964 14594c06356bSdh142964 if (blocked) { 14604c06356bSdh142964 /* Queues will get restarted when we get unblocked */ 14614c06356bSdh142964 return (B_TRUE); 14624c06356bSdh142964 } 14634c06356bSdh142964 14644c06356bSdh142964 /* 14654c06356bSdh142964 * Might as well verify the queue is not empty before moving on 14664c06356bSdh142964 */ 14674c06356bSdh142964 mutex_enter(&xp->wqlock); 14684c06356bSdh142964 if (STAILQ_EMPTY(&xp->wq)) { 14694c06356bSdh142964 mutex_exit(&xp->wqlock); 14704c06356bSdh142964 return (B_TRUE); 14714c06356bSdh142964 } 14724c06356bSdh142964 mutex_exit(&xp->wqlock); 14734c06356bSdh142964 14744c06356bSdh142964 /* 14754c06356bSdh142964 * If we're draining or resetting, just reschedule work queue and bail. 14764c06356bSdh142964 */ 14774c06356bSdh142964 mutex_enter(&xp->statlock); 14784c06356bSdh142964 if (xp->draining || xp->resetting || xp->special_running || 14794c06356bSdh142964 xp->special_needed) { 14804c06356bSdh142964 mutex_exit(&xp->statlock); 14814c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 14824c06356bSdh142964 return (B_TRUE); 14834c06356bSdh142964 } 14844c06356bSdh142964 14854c06356bSdh142964 /* 1486b18a19c2SJesse Butler * Next, check to see if the target is gone. 14874c06356bSdh142964 */ 1488b18a19c2SJesse Butler if (xp->dev_gone) { 1489c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 1490b18a19c2SJesse Butler "%s: Flushing wait queue for dead tgt 0x%p", __func__, 14914c06356bSdh142964 (void *)xp); 14924c06356bSdh142964 pmcs_flush_target_queues(pwp, xp, PMCS_TGT_WAIT_QUEUE); 14934c06356bSdh142964 mutex_exit(&xp->statlock); 14944c06356bSdh142964 return (B_TRUE); 14954c06356bSdh142964 } 14964c06356bSdh142964 14974c06356bSdh142964 /* 14984c06356bSdh142964 * Increment the PHY's ref_count now so we know it won't go away 14994c06356bSdh142964 * after we drop the target lock. Drop it before returning. If the 15004c06356bSdh142964 * PHY dies, the commands we attempt to send will fail, but at least 15014c06356bSdh142964 * we know we have a real PHY pointer. 15024c06356bSdh142964 */ 15034c06356bSdh142964 phyp = xp->phy; 15044c06356bSdh142964 pmcs_inc_phy_ref_count(phyp); 15054c06356bSdh142964 mutex_exit(&xp->statlock); 15064c06356bSdh142964 15074c06356bSdh142964 mutex_enter(&xp->wqlock); 15084c06356bSdh142964 while ((sp = STAILQ_FIRST(&xp->wq)) != NULL) { 15094c06356bSdh142964 pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_CBACK, phyp); 15104c06356bSdh142964 if (pwrk == NULL) { 1511c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 15124c06356bSdh142964 "%s: out of work structures", __func__); 15134c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 15144c06356bSdh142964 break; 15154c06356bSdh142964 } 15164c06356bSdh142964 STAILQ_REMOVE_HEAD(&xp->wq, cmd_next); 15174c06356bSdh142964 mutex_exit(&xp->wqlock); 15184c06356bSdh142964 15194c06356bSdh142964 pwrk->xp = xp; 15204c06356bSdh142964 pwrk->arg = sp; 15214c06356bSdh142964 sp->cmd_tag = pwrk->htag; 15224c06356bSdh142964 pwrk->timer = US2WT(CMD2PKT(sp)->pkt_time * 1000000); 15234c06356bSdh142964 if (pwrk->timer == 0) { 15244c06356bSdh142964 pwrk->timer = US2WT(1000000); 15254c06356bSdh142964 } 15264c06356bSdh142964 15274c06356bSdh142964 pwrk->dtype = xp->dtype; 15284c06356bSdh142964 15294c06356bSdh142964 if (xp->dtype == SAS) { 15304c06356bSdh142964 pwrk->ptr = (void *) pmcs_SAS_done; 15314c06356bSdh142964 if ((rval = pmcs_SAS_run(sp, pwrk)) != 0) { 15324c06356bSdh142964 sp->cmd_tag = NULL; 15334c06356bSdh142964 pmcs_dec_phy_ref_count(phyp); 15344c06356bSdh142964 pmcs_pwork(pwp, pwrk); 15354c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 15364c06356bSdh142964 if (rval == PMCS_WQ_RUN_FAIL_RES) { 15374c06356bSdh142964 return (B_FALSE); 15384c06356bSdh142964 } else { 15394c06356bSdh142964 return (B_TRUE); 15404c06356bSdh142964 } 15414c06356bSdh142964 } 15424c06356bSdh142964 } else { 15434c06356bSdh142964 ASSERT(xp->dtype == SATA); 15444c06356bSdh142964 pwrk->ptr = (void *) pmcs_SATA_done; 15454c06356bSdh142964 if ((rval = pmcs_SATA_run(sp, pwrk)) != 0) { 15464c06356bSdh142964 sp->cmd_tag = NULL; 15474c06356bSdh142964 pmcs_dec_phy_ref_count(phyp); 15484c06356bSdh142964 pmcs_pwork(pwp, pwrk); 15494c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 15504c06356bSdh142964 if (rval == PMCS_WQ_RUN_FAIL_RES) { 15514c06356bSdh142964 return (B_FALSE); 15524c06356bSdh142964 } else { 15534c06356bSdh142964 return (B_TRUE); 15544c06356bSdh142964 } 15554c06356bSdh142964 } 15564c06356bSdh142964 } 15574c06356bSdh142964 15584c06356bSdh142964 if (run_one) { 15594c06356bSdh142964 goto wq_out; 15604c06356bSdh142964 } 15614c06356bSdh142964 mutex_enter(&xp->wqlock); 15624c06356bSdh142964 } 15634c06356bSdh142964 15644c06356bSdh142964 mutex_exit(&xp->wqlock); 15654c06356bSdh142964 15664c06356bSdh142964 wq_out: 15674c06356bSdh142964 pmcs_dec_phy_ref_count(phyp); 15684c06356bSdh142964 return (B_TRUE); 15694c06356bSdh142964 } 15704c06356bSdh142964 15714c06356bSdh142964 /* 15724c06356bSdh142964 * Start commands for all devices. 15734c06356bSdh142964 */ 15744c06356bSdh142964 void 15754c06356bSdh142964 pmcs_scsa_wq_run(pmcs_hw_t *pwp) 15764c06356bSdh142964 { 15774c06356bSdh142964 pmcs_xscsi_t *xp; 15784c06356bSdh142964 uint16_t target_start, target; 15794c06356bSdh142964 boolean_t rval = B_TRUE; 15804c06356bSdh142964 15814c06356bSdh142964 mutex_enter(&pwp->lock); 15824c06356bSdh142964 target_start = pwp->last_wq_dev; 15834c06356bSdh142964 target = target_start; 15844c06356bSdh142964 15854c06356bSdh142964 do { 15864c06356bSdh142964 xp = pwp->targets[target]; 15874c06356bSdh142964 if (xp == NULL) { 15884c06356bSdh142964 if (++target == pwp->max_dev) { 15894c06356bSdh142964 target = 0; 15904c06356bSdh142964 } 15914c06356bSdh142964 continue; 15924c06356bSdh142964 } 15934c06356bSdh142964 15944c06356bSdh142964 mutex_exit(&pwp->lock); 15954c06356bSdh142964 rval = pmcs_scsa_wq_run_one(pwp, xp); 15964c06356bSdh142964 if (rval == B_FALSE) { 15974c06356bSdh142964 mutex_enter(&pwp->lock); 15984c06356bSdh142964 break; 15994c06356bSdh142964 } 16004c06356bSdh142964 mutex_enter(&pwp->lock); 16014c06356bSdh142964 if (++target == pwp->max_dev) { 16024c06356bSdh142964 target = 0; 16034c06356bSdh142964 } 16044c06356bSdh142964 } while (target != target_start); 16054c06356bSdh142964 16064c06356bSdh142964 if (rval) { 16074c06356bSdh142964 pwp->resource_limited = 0; /* Not resource-constrained */ 16084c06356bSdh142964 } else { 16094c06356bSdh142964 pwp->resource_limited = 1; /* Give others a chance */ 16104c06356bSdh142964 } 16114c06356bSdh142964 16124c06356bSdh142964 pwp->last_wq_dev = target; 16134c06356bSdh142964 mutex_exit(&pwp->lock); 16144c06356bSdh142964 } 16154c06356bSdh142964 16164c06356bSdh142964 /* 16174c06356bSdh142964 * Pull the completion queue, drop the lock and complete all elements. 16184c06356bSdh142964 */ 16194c06356bSdh142964 16204c06356bSdh142964 void 16214c06356bSdh142964 pmcs_scsa_cq_run(void *arg) 16224c06356bSdh142964 { 16234c06356bSdh142964 pmcs_cq_thr_info_t *cqti = (pmcs_cq_thr_info_t *)arg; 16244c06356bSdh142964 pmcs_hw_t *pwp = cqti->cq_pwp; 16254c06356bSdh142964 pmcs_cmd_t *sp, *nxt; 16264c06356bSdh142964 struct scsi_pkt *pkt; 16274c06356bSdh142964 pmcs_iocomp_cb_t *ioccb, *ioccb_next; 16284c06356bSdh142964 pmcs_cb_t callback; 16294c06356bSdh142964 uint32_t niodone; 16304c06356bSdh142964 16314c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__start, pmcs_cq_thr_info_t *, cqti); 16324c06356bSdh142964 16334c06356bSdh142964 mutex_enter(&pwp->cq_lock); 16344c06356bSdh142964 16354c06356bSdh142964 while (!pwp->cq_info.cq_stop) { 16364c06356bSdh142964 /* 16374c06356bSdh142964 * First, check the I/O completion callback queue. 16384c06356bSdh142964 */ 16394c06356bSdh142964 16404c06356bSdh142964 ioccb = pwp->iocomp_cb_head; 16414c06356bSdh142964 pwp->iocomp_cb_head = NULL; 16424c06356bSdh142964 pwp->iocomp_cb_tail = NULL; 16434c06356bSdh142964 mutex_exit(&pwp->cq_lock); 16444c06356bSdh142964 16454c06356bSdh142964 niodone = 0; 16464c06356bSdh142964 16474c06356bSdh142964 while (ioccb) { 16484c06356bSdh142964 niodone++; 16494c06356bSdh142964 /* 16504c06356bSdh142964 * Grab the lock on the work structure. The callback 16514c06356bSdh142964 * routine is responsible for clearing it. 16524c06356bSdh142964 */ 16534c06356bSdh142964 mutex_enter(&ioccb->pwrk->lock); 16544c06356bSdh142964 ioccb_next = ioccb->next; 16554c06356bSdh142964 callback = (pmcs_cb_t)ioccb->pwrk->ptr; 16564c06356bSdh142964 (*callback)(pwp, ioccb->pwrk, 16574c06356bSdh142964 (uint32_t *)((void *)ioccb->iomb)); 16584c06356bSdh142964 kmem_cache_free(pwp->iocomp_cb_cache, ioccb); 16594c06356bSdh142964 ioccb = ioccb_next; 16604c06356bSdh142964 } 16614c06356bSdh142964 16624c06356bSdh142964 /* 16634c06356bSdh142964 * Next, run the completion queue 16644c06356bSdh142964 */ 16654c06356bSdh142964 16664c06356bSdh142964 mutex_enter(&pwp->cq_lock); 16674c06356bSdh142964 sp = STAILQ_FIRST(&pwp->cq); 16684c06356bSdh142964 STAILQ_INIT(&pwp->cq); 16694c06356bSdh142964 mutex_exit(&pwp->cq_lock); 16704c06356bSdh142964 16714c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__start__loop, 16724c06356bSdh142964 pmcs_cq_thr_info_t *, cqti); 16734c06356bSdh142964 16744c06356bSdh142964 if (sp && pmcs_check_acc_dma_handle(pwp)) { 16754c06356bSdh142964 ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED); 16764c06356bSdh142964 } 16774c06356bSdh142964 16784c06356bSdh142964 while (sp) { 16794c06356bSdh142964 nxt = STAILQ_NEXT(sp, cmd_next); 16804c06356bSdh142964 pkt = CMD2PKT(sp); 1681c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, sp->cmd_target, 16824c06356bSdh142964 "%s: calling completion on %p for tgt %p", __func__, 16834c06356bSdh142964 (void *)sp, (void *)sp->cmd_target); 16844c06356bSdh142964 scsi_hba_pkt_comp(pkt); 16854c06356bSdh142964 sp = nxt; 16864c06356bSdh142964 } 16874c06356bSdh142964 16884c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__end__loop, 16894c06356bSdh142964 pmcs_cq_thr_info_t *, cqti); 16904c06356bSdh142964 16914c06356bSdh142964 mutex_enter(&cqti->cq_thr_lock); 16924c06356bSdh142964 cv_wait(&cqti->cq_cv, &cqti->cq_thr_lock); 16934c06356bSdh142964 mutex_exit(&cqti->cq_thr_lock); 16944c06356bSdh142964 16954c06356bSdh142964 mutex_enter(&pwp->cq_lock); 16964c06356bSdh142964 } 16974c06356bSdh142964 16984c06356bSdh142964 mutex_exit(&pwp->cq_lock); 16994c06356bSdh142964 DTRACE_PROBE1(pmcs__scsa__cq__run__stop, pmcs_cq_thr_info_t *, cqti); 17004c06356bSdh142964 thread_exit(); 17014c06356bSdh142964 } 17024c06356bSdh142964 17034c06356bSdh142964 /* 17044c06356bSdh142964 * Run a SAS command. Called with pwrk->lock held, returns unlocked. 17054c06356bSdh142964 */ 17064c06356bSdh142964 static int 17074c06356bSdh142964 pmcs_SAS_run(pmcs_cmd_t *sp, pmcwork_t *pwrk) 17084c06356bSdh142964 { 17094c06356bSdh142964 pmcs_hw_t *pwp = CMD2PMC(sp); 17104c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 17114c06356bSdh142964 pmcs_xscsi_t *xp = pwrk->xp; 17124c06356bSdh142964 uint32_t iq, *ptr; 17134c06356bSdh142964 sas_ssp_cmd_iu_t sc; 17144c06356bSdh142964 17154c06356bSdh142964 mutex_enter(&xp->statlock); 1716b18a19c2SJesse Butler if (!xp->assigned) { 17174c06356bSdh142964 mutex_exit(&xp->statlock); 17184c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 17194c06356bSdh142964 } 17204c06356bSdh142964 if ((xp->actv_cnt >= xp->qdepth) || xp->recover_wait) { 17214c06356bSdh142964 mutex_exit(&xp->statlock); 17224c06356bSdh142964 mutex_enter(&xp->wqlock); 17234c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 17244c06356bSdh142964 mutex_exit(&xp->wqlock); 17254c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 17264c06356bSdh142964 } 17274c06356bSdh142964 GET_IO_IQ_ENTRY(pwp, ptr, pwrk->phy->device_id, iq); 17284c06356bSdh142964 if (ptr == NULL) { 17294c06356bSdh142964 mutex_exit(&xp->statlock); 17304c06356bSdh142964 /* 17314c06356bSdh142964 * This is a temporary failure not likely to unblocked by 17324c06356bSdh142964 * commands completing as the test for scheduling the 17334c06356bSdh142964 * restart of work is a per-device test. 17344c06356bSdh142964 */ 17354c06356bSdh142964 mutex_enter(&xp->wqlock); 17364c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 17374c06356bSdh142964 mutex_exit(&xp->wqlock); 1738c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 17394c06356bSdh142964 "%s: Failed to get IO IQ entry for tgt %d", 17404c06356bSdh142964 __func__, xp->target_num); 17414c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES); 17424c06356bSdh142964 17434c06356bSdh142964 } 17444c06356bSdh142964 17454c06356bSdh142964 ptr[0] = 17464c06356bSdh142964 LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SSP_INI_IO_START)); 17474c06356bSdh142964 ptr[1] = LE_32(pwrk->htag); 17484c06356bSdh142964 ptr[2] = LE_32(pwrk->phy->device_id); 17494c06356bSdh142964 ptr[3] = LE_32(pkt->pkt_dma_len); 17504c06356bSdh142964 if (ptr[3]) { 17514c06356bSdh142964 ASSERT(pkt->pkt_numcookies); 17524c06356bSdh142964 if (pkt->pkt_dma_flags & DDI_DMA_READ) { 17534c06356bSdh142964 ptr[4] = LE_32(PMCIN_DATADIR_2_INI); 17544c06356bSdh142964 } else { 17554c06356bSdh142964 ptr[4] = LE_32(PMCIN_DATADIR_2_DEV); 17564c06356bSdh142964 } 17574c06356bSdh142964 if (pmcs_dma_load(pwp, sp, ptr)) { 17584c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]); 17594c06356bSdh142964 mutex_exit(&xp->statlock); 17604c06356bSdh142964 mutex_enter(&xp->wqlock); 17614c06356bSdh142964 if (STAILQ_EMPTY(&xp->wq)) { 17624c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 17634c06356bSdh142964 mutex_exit(&xp->wqlock); 17644c06356bSdh142964 } else { 17654c06356bSdh142964 mutex_exit(&xp->wqlock); 17664c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = STATUS_QFULL; 17674c06356bSdh142964 CMD2PKT(sp)->pkt_reason = CMD_CMPLT; 17684c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | 17694c06356bSdh142964 STATE_GOT_TARGET | STATE_SENT_CMD | 17704c06356bSdh142964 STATE_GOT_STATUS; 17714c06356bSdh142964 mutex_enter(&pwp->cq_lock); 17724c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 17734c06356bSdh142964 mutex_exit(&pwp->cq_lock); 1774c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 17754c06356bSdh142964 "%s: Failed to dma_load for tgt %d (QF)", 17764c06356bSdh142964 __func__, xp->target_num); 17774c06356bSdh142964 } 17784c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES); 17794c06356bSdh142964 } 17804c06356bSdh142964 } else { 17814c06356bSdh142964 ptr[4] = LE_32(PMCIN_DATADIR_NONE); 17824c06356bSdh142964 CLEAN_MESSAGE(ptr, 12); 17834c06356bSdh142964 } 17844c06356bSdh142964 xp->actv_cnt++; 17854c06356bSdh142964 if (xp->actv_cnt > xp->maxdepth) { 17864c06356bSdh142964 xp->maxdepth = xp->actv_cnt; 1787c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp, "%s: max depth " 1788c3bc407cSdh142964 "now %u", pwrk->phy->path, xp->maxdepth); 17894c06356bSdh142964 } 17904c06356bSdh142964 mutex_exit(&xp->statlock); 17914c06356bSdh142964 17924c06356bSdh142964 17934c06356bSdh142964 #ifdef DEBUG 17944c06356bSdh142964 /* 17954c06356bSdh142964 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED 17964c06356bSdh142964 * event when this goes out on the wire. 17974c06356bSdh142964 */ 17984c06356bSdh142964 ptr[4] |= PMCIN_MESSAGE_REPORT; 17994c06356bSdh142964 #endif 18004c06356bSdh142964 /* 18014c06356bSdh142964 * Fill in the SSP IU 18024c06356bSdh142964 */ 18034c06356bSdh142964 18044c06356bSdh142964 bzero(&sc, sizeof (sas_ssp_cmd_iu_t)); 18054c06356bSdh142964 bcopy((uint8_t *)&sp->cmd_lun->scsi_lun, sc.lun, sizeof (scsi_lun_t)); 18064c06356bSdh142964 18074c06356bSdh142964 switch (pkt->pkt_flags & FLAG_TAGMASK) { 18084c06356bSdh142964 case FLAG_HTAG: 18094c06356bSdh142964 sc.task_attribute = SAS_CMD_TASK_ATTR_HEAD; 18104c06356bSdh142964 break; 18114c06356bSdh142964 case FLAG_OTAG: 18124c06356bSdh142964 sc.task_attribute = SAS_CMD_TASK_ATTR_ORDERED; 18134c06356bSdh142964 break; 18144c06356bSdh142964 case FLAG_STAG: 18154c06356bSdh142964 default: 18164c06356bSdh142964 sc.task_attribute = SAS_CMD_TASK_ATTR_SIMPLE; 18174c06356bSdh142964 break; 18184c06356bSdh142964 } 18194c06356bSdh142964 (void) memcpy(sc.cdb, pkt->pkt_cdbp, 18204c06356bSdh142964 min(SCSA_CDBLEN(sp), sizeof (sc.cdb))); 18214c06356bSdh142964 (void) memcpy(&ptr[5], &sc, sizeof (sas_ssp_cmd_iu_t)); 18224c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ONCHIP; 18234c06356bSdh142964 mutex_exit(&pwrk->lock); 1824c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 18254c06356bSdh142964 "%s: giving pkt %p (tag %x) to the hardware", __func__, 18264c06356bSdh142964 (void *)pkt, pwrk->htag); 18274c06356bSdh142964 #ifdef DEBUG 18284c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SAS INI Message", ptr); 18294c06356bSdh142964 #endif 18304c06356bSdh142964 mutex_enter(&xp->aqlock); 18314c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next); 18324c06356bSdh142964 mutex_exit(&xp->aqlock); 18334c06356bSdh142964 INC_IQ_ENTRY(pwp, iq); 18344c06356bSdh142964 18354c06356bSdh142964 /* 18364c06356bSdh142964 * If we just submitted the last command queued from device state 18374c06356bSdh142964 * recovery, clear the wq_recovery_tail pointer. 18384c06356bSdh142964 */ 18394c06356bSdh142964 mutex_enter(&xp->wqlock); 18404c06356bSdh142964 if (xp->wq_recovery_tail == sp) { 18414c06356bSdh142964 xp->wq_recovery_tail = NULL; 18424c06356bSdh142964 } 18434c06356bSdh142964 mutex_exit(&xp->wqlock); 18444c06356bSdh142964 18454c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 18464c06356bSdh142964 } 18474c06356bSdh142964 18484c06356bSdh142964 /* 18494c06356bSdh142964 * Complete a SAS command 18504c06356bSdh142964 * 18514c06356bSdh142964 * Called with pwrk lock held. 18524c06356bSdh142964 * The free of pwrk releases the lock. 18534c06356bSdh142964 */ 18544c06356bSdh142964 18554c06356bSdh142964 static void 18564c06356bSdh142964 pmcs_SAS_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg) 18574c06356bSdh142964 { 18584c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg; 18594c06356bSdh142964 pmcs_phy_t *pptr = pwrk->phy; 18604c06356bSdh142964 pmcs_xscsi_t *xp = pwrk->xp; 18614c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 18624c06356bSdh142964 int dead; 18634c06356bSdh142964 uint32_t sts; 18644c06356bSdh142964 boolean_t aborted = B_FALSE; 18654c06356bSdh142964 boolean_t do_ds_recovery = B_FALSE; 18664c06356bSdh142964 18674c06356bSdh142964 ASSERT(xp != NULL); 18684c06356bSdh142964 ASSERT(sp != NULL); 18694c06356bSdh142964 ASSERT(pptr != NULL); 18704c06356bSdh142964 18714c06356bSdh142964 DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int, 18724c06356bSdh142964 (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start, 18734c06356bSdh142964 hrtime_t, gethrtime()); 18744c06356bSdh142964 18754c06356bSdh142964 dead = pwrk->dead; 18764c06356bSdh142964 18774c06356bSdh142964 if (msg) { 18784c06356bSdh142964 sts = LE_32(msg[2]); 18794c06356bSdh142964 } else { 18804c06356bSdh142964 sts = 0; 18814c06356bSdh142964 } 18824c06356bSdh142964 18834c06356bSdh142964 if (dead != 0) { 1884c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag " 1885c3bc407cSdh142964 "0x%x for %s", __func__, pwrk->htag, pptr->path); 18864c06356bSdh142964 goto out; 18874c06356bSdh142964 } 18884c06356bSdh142964 18894c06356bSdh142964 if (sts == PMCOUT_STATUS_ABORTED) { 18904c06356bSdh142964 aborted = B_TRUE; 18914c06356bSdh142964 } 18924c06356bSdh142964 18934c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) { 1894c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 18954c06356bSdh142964 "%s: cmd 0x%p (tag 0x%x) timed out for %s", 18964c06356bSdh142964 __func__, (void *)sp, pwrk->htag, pptr->path); 18974c06356bSdh142964 goto out; 18984c06356bSdh142964 } 18994c06356bSdh142964 19004c06356bSdh142964 /* 19014c06356bSdh142964 * If the status isn't okay but not underflow, 19024c06356bSdh142964 * step to the side and parse the (possible) error. 19034c06356bSdh142964 */ 19044c06356bSdh142964 #ifdef DEBUG 19054c06356bSdh142964 if (msg) { 19064c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg); 19074c06356bSdh142964 } 19084c06356bSdh142964 #endif 19094c06356bSdh142964 if (!msg) { 19104c06356bSdh142964 goto out; 19114c06356bSdh142964 } 19124c06356bSdh142964 19134c06356bSdh142964 switch (sts) { 19144c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS: 19154c06356bSdh142964 case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL: 19164c06356bSdh142964 case PMCOUT_STATUS_IO_DS_IN_RECOVERY: 1917c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 19184c06356bSdh142964 "%s: PHY %s requires device state recovery (status=%d)", 19194c06356bSdh142964 __func__, pptr->path, sts); 19204c06356bSdh142964 do_ds_recovery = B_TRUE; 19214c06356bSdh142964 break; 19224c06356bSdh142964 case PMCOUT_STATUS_UNDERFLOW: 19234c06356bSdh142964 (void) pmcs_set_resid(pkt, pkt->pkt_dma_len, LE_32(msg[3])); 1924c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, NULL, NULL, 19254c06356bSdh142964 "%s: underflow %u for cdb 0x%x", 19264c06356bSdh142964 __func__, LE_32(msg[3]), pkt->pkt_cdbp[0] & 0xff); 19274c06356bSdh142964 sts = PMCOUT_STATUS_OK; 19284c06356bSdh142964 msg[3] = 0; 19294c06356bSdh142964 break; 19304c06356bSdh142964 case PMCOUT_STATUS_OK: 19314c06356bSdh142964 pkt->pkt_resid = 0; 19324c06356bSdh142964 break; 19334c06356bSdh142964 } 19344c06356bSdh142964 19354c06356bSdh142964 if (sts != PMCOUT_STATUS_OK) { 19364c06356bSdh142964 pmcs_ioerror(pwp, SAS, pwrk, msg); 19374c06356bSdh142964 } else { 19384c06356bSdh142964 if (msg[3]) { 19394c06356bSdh142964 uint8_t local[PMCS_QENTRY_SIZE << 1], *xd; 19404c06356bSdh142964 sas_ssp_rsp_iu_t *rptr = (void *)local; 19414c06356bSdh142964 const int lim = 19424c06356bSdh142964 (PMCS_QENTRY_SIZE << 1) - SAS_RSP_HDR_SIZE; 19434c06356bSdh142964 static const uint8_t ssp_rsp_evec[] = { 19444c06356bSdh142964 0x58, 0x61, 0x56, 0x72, 0x00 19454c06356bSdh142964 }; 19464c06356bSdh142964 19474c06356bSdh142964 /* 19484c06356bSdh142964 * Transform the the first part of the response 19494c06356bSdh142964 * to host canonical form. This gives us enough 19504c06356bSdh142964 * information to figure out what to do with the 19514c06356bSdh142964 * rest (which remains unchanged in the incoming 19524c06356bSdh142964 * message which can be up to two queue entries 19534c06356bSdh142964 * in length). 19544c06356bSdh142964 */ 19554c06356bSdh142964 pmcs_endian_transform(pwp, local, &msg[5], 19564c06356bSdh142964 ssp_rsp_evec); 19574c06356bSdh142964 xd = (uint8_t *)(&msg[5]); 19584c06356bSdh142964 xd += SAS_RSP_HDR_SIZE; 19594c06356bSdh142964 19604c06356bSdh142964 if (rptr->datapres == SAS_RSP_DATAPRES_RESPONSE_DATA) { 19614c06356bSdh142964 if (rptr->response_data_length != 4) { 19624c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 19634c06356bSdh142964 "Bad SAS RESPONSE DATA LENGTH", 19644c06356bSdh142964 msg); 19654c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 19664c06356bSdh142964 goto out; 19674c06356bSdh142964 } 19684c06356bSdh142964 (void) memcpy(&sts, xd, sizeof (uint32_t)); 19694c06356bSdh142964 sts = BE_32(sts); 19704c06356bSdh142964 /* 19714c06356bSdh142964 * The only response code we should legally get 19724c06356bSdh142964 * here is an INVALID FRAME response code. 19734c06356bSdh142964 */ 19744c06356bSdh142964 if (sts == SAS_RSP_INVALID_FRAME) { 1975c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 19764c06356bSdh142964 "%s: pkt %p tgt %u path %s " 19774c06356bSdh142964 "completed: INVALID FRAME response", 19784c06356bSdh142964 __func__, (void *)pkt, 19794c06356bSdh142964 xp->target_num, pptr->path); 19804c06356bSdh142964 } else { 1981c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 19824c06356bSdh142964 "%s: pkt %p tgt %u path %s " 19834c06356bSdh142964 "completed: illegal response 0x%x", 19844c06356bSdh142964 __func__, (void *)pkt, 19854c06356bSdh142964 xp->target_num, pptr->path, sts); 19864c06356bSdh142964 } 19874c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 19884c06356bSdh142964 goto out; 19894c06356bSdh142964 } 19904c06356bSdh142964 if (rptr->datapres == SAS_RSP_DATAPRES_SENSE_DATA) { 19914c06356bSdh142964 uint32_t slen; 19924c06356bSdh142964 slen = rptr->sense_data_length; 19934c06356bSdh142964 if (slen > lim) { 19944c06356bSdh142964 slen = lim; 19954c06356bSdh142964 } 19964c06356bSdh142964 pmcs_latch_status(pwp, sp, rptr->status, xd, 19974c06356bSdh142964 slen, pptr->path); 19984c06356bSdh142964 } else if (rptr->datapres == SAS_RSP_DATAPRES_NO_DATA) { 19994b456463SDavid Hollister pmcout_ssp_comp_t *sspcp; 20004b456463SDavid Hollister sspcp = (pmcout_ssp_comp_t *)msg; 20014b456463SDavid Hollister uint32_t *residp; 20024c06356bSdh142964 /* 20034c06356bSdh142964 * This is the case for a plain SCSI status. 20044b456463SDavid Hollister * Note: If RESC_V is set and we're here, there 20054b456463SDavid Hollister * is a residual. We need to find it and update 20064b456463SDavid Hollister * the packet accordingly. 20074c06356bSdh142964 */ 20084c06356bSdh142964 pmcs_latch_status(pwp, sp, rptr->status, NULL, 20094c06356bSdh142964 0, pptr->path); 20104b456463SDavid Hollister 20114b456463SDavid Hollister if (sspcp->resc_v) { 20124b456463SDavid Hollister /* 20134b456463SDavid Hollister * Point residual to the SSP_RESP_IU 20144b456463SDavid Hollister */ 20154b456463SDavid Hollister residp = (uint32_t *)(sspcp + 1); 20164b456463SDavid Hollister /* 20174b456463SDavid Hollister * param contains the number of bytes 20184b456463SDavid Hollister * between where the SSP_RESP_IU may 20194b456463SDavid Hollister * or may not be and the residual. 20204b456463SDavid Hollister * Increment residp by the appropriate 20214b456463SDavid Hollister * number of words: (param+resc_pad)/4). 20224b456463SDavid Hollister */ 20234b456463SDavid Hollister residp += (LE_32(sspcp->param) + 20244b456463SDavid Hollister sspcp->resc_pad) / 20254b456463SDavid Hollister sizeof (uint32_t); 20264b456463SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, 20274b456463SDavid Hollister pptr, xp, "%s: tgt 0x%p " 20284b456463SDavid Hollister "residual %d for pkt 0x%p", 20294b456463SDavid Hollister __func__, (void *) xp, *residp, 20304b456463SDavid Hollister (void *) pkt); 20314b456463SDavid Hollister ASSERT(LE_32(*residp) <= 20324b456463SDavid Hollister pkt->pkt_dma_len); 20334b456463SDavid Hollister (void) pmcs_set_resid(pkt, 20344b456463SDavid Hollister pkt->pkt_dma_len, LE_32(*residp)); 20354b456463SDavid Hollister } 20364c06356bSdh142964 } else { 20374c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG, 20384c06356bSdh142964 "illegal SAS response", msg); 20394c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 20404c06356bSdh142964 goto out; 20414c06356bSdh142964 } 20424c06356bSdh142964 } else { 20434c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, 20444c06356bSdh142964 pptr->path); 20454c06356bSdh142964 } 20464c06356bSdh142964 if (pkt->pkt_dma_len) { 20474c06356bSdh142964 pkt->pkt_state |= STATE_XFERRED_DATA; 20484c06356bSdh142964 } 20494c06356bSdh142964 } 2050c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, 20514c06356bSdh142964 "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x", 20524c06356bSdh142964 __func__, (void *)pkt, xp->target_num, pkt->pkt_reason, 20534c06356bSdh142964 pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]); 20544c06356bSdh142964 20554c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_ABORTED) { 2056c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 20574c06356bSdh142964 "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p", 20584c06356bSdh142964 __func__, (void *)pkt, pptr->path, (void *)pwrk); 20594c06356bSdh142964 aborted = B_TRUE; 20604c06356bSdh142964 } 20614c06356bSdh142964 20624c06356bSdh142964 out: 20634c06356bSdh142964 pmcs_pwork(pwp, pwrk); 20644c06356bSdh142964 pmcs_dma_unload(pwp, sp); 20654c06356bSdh142964 20664c06356bSdh142964 mutex_enter(&xp->statlock); 20674c06356bSdh142964 if (xp->dev_gone) { 20684c06356bSdh142964 mutex_exit(&xp->statlock); 2069c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, 20704c06356bSdh142964 "%s: Completing command for dead target 0x%p", __func__, 20714c06356bSdh142964 (void *)xp); 20724c06356bSdh142964 return; 20734c06356bSdh142964 } 20744c06356bSdh142964 20754c06356bSdh142964 ASSERT(xp->actv_cnt > 0); 20764c06356bSdh142964 if (--(xp->actv_cnt) == 0) { 20774c06356bSdh142964 if (xp->draining) { 2078c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, 20794c06356bSdh142964 "%s: waking up drain waiters", __func__); 20804c06356bSdh142964 cv_signal(&pwp->drain_cv); 20814c06356bSdh142964 } 20824c06356bSdh142964 } 20834c06356bSdh142964 mutex_exit(&xp->statlock); 20844c06356bSdh142964 if (dead == 0) { 20854c06356bSdh142964 #ifdef DEBUG 20864c06356bSdh142964 pmcs_cmd_t *wp; 20874c06356bSdh142964 mutex_enter(&xp->aqlock); 20884c06356bSdh142964 STAILQ_FOREACH(wp, &xp->aq, cmd_next) { 20894c06356bSdh142964 if (wp == sp) { 20904c06356bSdh142964 break; 20914c06356bSdh142964 } 20924c06356bSdh142964 } 20934c06356bSdh142964 ASSERT(wp != NULL); 20944c06356bSdh142964 #else 20954c06356bSdh142964 mutex_enter(&xp->aqlock); 20964c06356bSdh142964 #endif 20974c06356bSdh142964 STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next); 20984c06356bSdh142964 if (aborted) { 2099c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 21004c06356bSdh142964 "%s: Aborted cmd for tgt 0x%p, signaling waiters", 21014c06356bSdh142964 __func__, (void *)xp); 21024c06356bSdh142964 cv_signal(&xp->abort_cv); 21034c06356bSdh142964 } 21044c06356bSdh142964 mutex_exit(&xp->aqlock); 21054c06356bSdh142964 } 21064c06356bSdh142964 21074c06356bSdh142964 /* 21084c06356bSdh142964 * If do_ds_recovery is set, we need to initiate device state 21094c06356bSdh142964 * recovery. In this case, we put this I/O back on the head of 21104c06356bSdh142964 * the wait queue to run again after recovery is complete 21114c06356bSdh142964 */ 21124c06356bSdh142964 if (do_ds_recovery) { 21134c06356bSdh142964 mutex_enter(&xp->statlock); 21144c06356bSdh142964 pmcs_start_dev_state_recovery(xp, pptr); 21154c06356bSdh142964 mutex_exit(&xp->statlock); 2116c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, "%s: Putting cmd 0x%p " 2117c3bc407cSdh142964 "back on wq during recovery for tgt 0x%p", __func__, 2118c3bc407cSdh142964 (void *)sp, (void *)xp); 21194c06356bSdh142964 mutex_enter(&xp->wqlock); 21204c06356bSdh142964 if (xp->wq_recovery_tail == NULL) { 21214c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 21224c06356bSdh142964 } else { 21234c06356bSdh142964 /* 21244c06356bSdh142964 * If there are other I/Os waiting at the head due to 21254c06356bSdh142964 * device state recovery, add this one in the right spot 21264c06356bSdh142964 * to maintain proper order. 21274c06356bSdh142964 */ 21284c06356bSdh142964 STAILQ_INSERT_AFTER(&xp->wq, xp->wq_recovery_tail, sp, 21294c06356bSdh142964 cmd_next); 21304c06356bSdh142964 } 21314c06356bSdh142964 xp->wq_recovery_tail = sp; 21324c06356bSdh142964 mutex_exit(&xp->wqlock); 21334c06356bSdh142964 } else { 21344c06356bSdh142964 /* 21354c06356bSdh142964 * If we're not initiating device state recovery and this 21364c06356bSdh142964 * command was not "dead", put it on the completion queue 21374c06356bSdh142964 */ 21384c06356bSdh142964 if (!dead) { 21394c06356bSdh142964 mutex_enter(&pwp->cq_lock); 21404c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 21414c06356bSdh142964 mutex_exit(&pwp->cq_lock); 21424c06356bSdh142964 } 21434c06356bSdh142964 } 21444c06356bSdh142964 } 21454c06356bSdh142964 21464c06356bSdh142964 /* 21474c06356bSdh142964 * Run a SATA command (normal reads and writes), 21484c06356bSdh142964 * or block and schedule a SATL interpretation 21494c06356bSdh142964 * of the command. 21504c06356bSdh142964 * 21514c06356bSdh142964 * Called with pwrk lock held, returns unlocked. 21524c06356bSdh142964 */ 21534c06356bSdh142964 21544c06356bSdh142964 static int 21554c06356bSdh142964 pmcs_SATA_run(pmcs_cmd_t *sp, pmcwork_t *pwrk) 21564c06356bSdh142964 { 21574c06356bSdh142964 pmcs_hw_t *pwp = CMD2PMC(sp); 21584c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 21594c06356bSdh142964 pmcs_xscsi_t *xp; 21604c06356bSdh142964 uint8_t cdb_base, asc, tag; 21614c06356bSdh142964 uint32_t *ptr, iq, nblk, i, mtype; 21624c06356bSdh142964 fis_t fis; 21634c06356bSdh142964 size_t amt; 21644c06356bSdh142964 uint64_t lba; 21654c06356bSdh142964 21664c06356bSdh142964 xp = pwrk->xp; 21674c06356bSdh142964 21684c06356bSdh142964 /* 21694c06356bSdh142964 * First, see if this is just a plain read/write command. 21704c06356bSdh142964 * If not, we have to queue it up for processing, block 21714c06356bSdh142964 * any additional commands from coming in, and wake up 21724c06356bSdh142964 * the thread that will process this command. 21734c06356bSdh142964 */ 21744c06356bSdh142964 cdb_base = pkt->pkt_cdbp[0] & 0x1f; 21754c06356bSdh142964 if (cdb_base != SCMD_READ && cdb_base != SCMD_WRITE) { 2176c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL, 2177c3bc407cSdh142964 "%s: special SATA cmd %p", __func__, (void *)sp); 21784c06356bSdh142964 21794c06356bSdh142964 ASSERT(xp->phy != NULL); 21804c06356bSdh142964 pmcs_pwork(pwp, pwrk); 21814c06356bSdh142964 pmcs_lock_phy(xp->phy); 21824c06356bSdh142964 mutex_enter(&xp->statlock); 21834c06356bSdh142964 xp->special_needed = 1; /* Set the special_needed flag */ 21844c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->sq, sp, cmd_next); 21854c06356bSdh142964 if (pmcs_run_sata_special(pwp, xp)) { 21864c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN); 21874c06356bSdh142964 } 21884c06356bSdh142964 mutex_exit(&xp->statlock); 21894c06356bSdh142964 pmcs_unlock_phy(xp->phy); 21904c06356bSdh142964 21914c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 21924c06356bSdh142964 } 21934c06356bSdh142964 2194c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: regular cmd", __func__); 21954c06356bSdh142964 21964c06356bSdh142964 mutex_enter(&xp->statlock); 2197b18a19c2SJesse Butler if (!xp->assigned) { 21984c06356bSdh142964 mutex_exit(&xp->statlock); 21994c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 22004c06356bSdh142964 } 22014c06356bSdh142964 if (xp->special_running || xp->special_needed || xp->recover_wait) { 22024c06356bSdh142964 mutex_exit(&xp->statlock); 22034c06356bSdh142964 mutex_enter(&xp->wqlock); 22044c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 22054c06356bSdh142964 mutex_exit(&xp->wqlock); 22064c06356bSdh142964 /* 22074c06356bSdh142964 * By the time we get here the special 22084c06356bSdh142964 * commands running or waiting to be run 22094c06356bSdh142964 * may have come and gone, so kick our 22104c06356bSdh142964 * worker to run the waiting queues 22114c06356bSdh142964 * just in case. 22124c06356bSdh142964 */ 22134c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 22144c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 22154c06356bSdh142964 } 22164c06356bSdh142964 lba = xp->capacity; 22174c06356bSdh142964 mutex_exit(&xp->statlock); 22184c06356bSdh142964 22194c06356bSdh142964 /* 22204c06356bSdh142964 * Extract data length and lba parameters out of the command. The 22214c06356bSdh142964 * function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB 22224c06356bSdh142964 * values are considered illegal. 22234c06356bSdh142964 */ 22244c06356bSdh142964 asc = pmcs_SATA_rwparm(pkt->pkt_cdbp, &nblk, &lba, lba); 22254c06356bSdh142964 if (asc) { 22264c06356bSdh142964 uint8_t sns[18]; 22274c06356bSdh142964 bzero(sns, sizeof (sns)); 22284c06356bSdh142964 sns[0] = 0xf0; 22294c06356bSdh142964 sns[2] = 0x5; 22304c06356bSdh142964 sns[12] = asc; 22314c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, sns, sizeof (sns), 22324c06356bSdh142964 pwrk->phy->path); 22334c06356bSdh142964 pmcs_pwork(pwp, pwrk); 22344c06356bSdh142964 mutex_enter(&pwp->cq_lock); 22354c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 22364c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp); 22374c06356bSdh142964 mutex_exit(&pwp->cq_lock); 22384c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 22394c06356bSdh142964 } 22404c06356bSdh142964 22414c06356bSdh142964 /* 22424c06356bSdh142964 * If the command decodes as not moving any data, complete it here. 22434c06356bSdh142964 */ 22444c06356bSdh142964 amt = nblk; 22454c06356bSdh142964 amt <<= 9; 22464c06356bSdh142964 amt = pmcs_set_resid(pkt, amt, nblk << 9); 22474c06356bSdh142964 if (amt == 0) { 22484c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, 22494c06356bSdh142964 pwrk->phy->path); 22504c06356bSdh142964 pmcs_pwork(pwp, pwrk); 22514c06356bSdh142964 mutex_enter(&pwp->cq_lock); 22524c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 22534c06356bSdh142964 PMCS_CQ_RUN_LOCKED(pwp); 22544c06356bSdh142964 mutex_exit(&pwp->cq_lock); 22554c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 22564c06356bSdh142964 } 22574c06356bSdh142964 22584c06356bSdh142964 /* 22594c06356bSdh142964 * Get an inbound queue entry for this I/O 22604c06356bSdh142964 */ 22614c06356bSdh142964 GET_IO_IQ_ENTRY(pwp, ptr, xp->phy->device_id, iq); 22624c06356bSdh142964 if (ptr == NULL) { 22634c06356bSdh142964 /* 22644c06356bSdh142964 * This is a temporary failure not likely to unblocked by 22654c06356bSdh142964 * commands completing as the test for scheduling the 22664c06356bSdh142964 * restart of work is a per-device test. 22674c06356bSdh142964 */ 22684c06356bSdh142964 mutex_enter(&xp->wqlock); 22694c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 22704c06356bSdh142964 mutex_exit(&xp->wqlock); 22714c06356bSdh142964 pmcs_dma_unload(pwp, sp); 22724c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 2273c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 22744c06356bSdh142964 "%s: Failed to get IO IQ entry for tgt %d", 22754c06356bSdh142964 __func__, xp->target_num); 22764c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES); 22774c06356bSdh142964 } 22784c06356bSdh142964 22794c06356bSdh142964 /* 22804c06356bSdh142964 * Get a tag. At this point, hold statlock until the tagmap is 22814c06356bSdh142964 * updated (just prior to sending the cmd to the hardware). 22824c06356bSdh142964 */ 22834c06356bSdh142964 mutex_enter(&xp->statlock); 22844c06356bSdh142964 for (tag = 0; tag < xp->qdepth; tag++) { 22854c06356bSdh142964 if ((xp->tagmap & (1 << tag)) == 0) { 22864c06356bSdh142964 break; 22874c06356bSdh142964 } 22884c06356bSdh142964 } 22894c06356bSdh142964 22904c06356bSdh142964 if (tag == xp->qdepth) { 22914c06356bSdh142964 mutex_exit(&xp->statlock); 22924c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]); 22934c06356bSdh142964 mutex_enter(&xp->wqlock); 22944c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 22954c06356bSdh142964 mutex_exit(&xp->wqlock); 22964c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_OTHER); 22974c06356bSdh142964 } 22984c06356bSdh142964 22994c06356bSdh142964 sp->cmd_satltag = (uint8_t)tag; 23004c06356bSdh142964 23014c06356bSdh142964 /* 23024c06356bSdh142964 * Set up the command 23034c06356bSdh142964 */ 23044c06356bSdh142964 bzero(fis, sizeof (fis)); 23054c06356bSdh142964 ptr[0] = 23064c06356bSdh142964 LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SATA_HOST_IO_START)); 23074c06356bSdh142964 ptr[1] = LE_32(pwrk->htag); 23084c06356bSdh142964 ptr[2] = LE_32(pwrk->phy->device_id); 23094c06356bSdh142964 ptr[3] = LE_32(amt); 23104c06356bSdh142964 23114c06356bSdh142964 if (xp->ncq) { 23124c06356bSdh142964 mtype = SATA_PROTOCOL_FPDMA | (tag << 16); 23134c06356bSdh142964 fis[0] = ((nblk & 0xff) << 24) | (C_BIT << 8) | FIS_REG_H2DEV; 23144c06356bSdh142964 if (cdb_base == SCMD_READ) { 23154c06356bSdh142964 fis[0] |= (READ_FPDMA_QUEUED << 16); 23164c06356bSdh142964 } else { 23174c06356bSdh142964 fis[0] |= (WRITE_FPDMA_QUEUED << 16); 23184c06356bSdh142964 } 23194c06356bSdh142964 fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff); 23204c06356bSdh142964 fis[2] = ((nblk & 0xff00) << 16) | ((lba >> 24) & 0xffffff); 23214c06356bSdh142964 fis[3] = tag << 3; 23224c06356bSdh142964 } else { 23234c06356bSdh142964 int op; 23244c06356bSdh142964 fis[0] = (C_BIT << 8) | FIS_REG_H2DEV; 23254c06356bSdh142964 if (xp->pio) { 23264c06356bSdh142964 mtype = SATA_PROTOCOL_PIO; 23274c06356bSdh142964 if (cdb_base == SCMD_READ) { 23284c06356bSdh142964 op = READ_SECTORS_EXT; 23294c06356bSdh142964 } else { 23304c06356bSdh142964 op = WRITE_SECTORS_EXT; 23314c06356bSdh142964 } 23324c06356bSdh142964 } else { 23334c06356bSdh142964 mtype = SATA_PROTOCOL_DMA; 23344c06356bSdh142964 if (cdb_base == SCMD_READ) { 23354c06356bSdh142964 op = READ_DMA_EXT; 23364c06356bSdh142964 } else { 23374c06356bSdh142964 op = WRITE_DMA_EXT; 23384c06356bSdh142964 } 23394c06356bSdh142964 } 23404c06356bSdh142964 fis[0] |= (op << 16); 23414c06356bSdh142964 fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff); 23424c06356bSdh142964 fis[2] = (lba >> 24) & 0xffffff; 23434c06356bSdh142964 fis[3] = nblk; 23444c06356bSdh142964 } 23454c06356bSdh142964 23464c06356bSdh142964 if (cdb_base == SCMD_READ) { 23474c06356bSdh142964 ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_INI); 23484c06356bSdh142964 } else { 23494c06356bSdh142964 ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_DEV); 23504c06356bSdh142964 } 23514c06356bSdh142964 #ifdef DEBUG 23524c06356bSdh142964 /* 23534c06356bSdh142964 * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED 23544c06356bSdh142964 * event when this goes out on the wire. 23554c06356bSdh142964 */ 23564c06356bSdh142964 ptr[4] |= PMCIN_MESSAGE_REPORT; 23574c06356bSdh142964 #endif 23584c06356bSdh142964 for (i = 0; i < (sizeof (fis_t))/(sizeof (uint32_t)); i++) { 23594c06356bSdh142964 ptr[i+5] = LE_32(fis[i]); 23604c06356bSdh142964 } 23614c06356bSdh142964 if (pmcs_dma_load(pwp, sp, ptr)) { 23624c06356bSdh142964 mutex_exit(&xp->statlock); 23634c06356bSdh142964 mutex_exit(&pwp->iqp_lock[iq]); 23644c06356bSdh142964 mutex_enter(&xp->wqlock); 23654c06356bSdh142964 STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next); 23664c06356bSdh142964 mutex_exit(&xp->wqlock); 2367c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, 23684c06356bSdh142964 "%s: Failed to dma_load for tgt %d", 23694c06356bSdh142964 __func__, xp->target_num); 23704c06356bSdh142964 return (PMCS_WQ_RUN_FAIL_RES); 23714c06356bSdh142964 23724c06356bSdh142964 } 23734c06356bSdh142964 23744c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ONCHIP; 23754c06356bSdh142964 mutex_exit(&pwrk->lock); 23764c06356bSdh142964 xp->tagmap |= (1 << tag); 23774c06356bSdh142964 xp->actv_cnt++; 23784c06356bSdh142964 if (xp->actv_cnt > xp->maxdepth) { 23794c06356bSdh142964 xp->maxdepth = xp->actv_cnt; 2380c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp, 2381c3bc407cSdh142964 "%s: max depth now %u", pwrk->phy->path, xp->maxdepth); 23824c06356bSdh142964 } 23834c06356bSdh142964 mutex_exit(&xp->statlock); 23844c06356bSdh142964 mutex_enter(&xp->aqlock); 23854c06356bSdh142964 STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next); 23864c06356bSdh142964 mutex_exit(&xp->aqlock); 2387c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, 2388c3bc407cSdh142964 "%s: giving pkt %p to hardware", __func__, (void *)pkt); 23894c06356bSdh142964 #ifdef DEBUG 23904c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SATA INI Message", ptr); 23914c06356bSdh142964 #endif 23924c06356bSdh142964 INC_IQ_ENTRY(pwp, iq); 23934c06356bSdh142964 23944c06356bSdh142964 return (PMCS_WQ_RUN_SUCCESS); 23954c06356bSdh142964 } 23964c06356bSdh142964 23974c06356bSdh142964 /* 23984c06356bSdh142964 * Complete a SATA command. Called with pwrk lock held. 23994c06356bSdh142964 */ 24004c06356bSdh142964 void 24014c06356bSdh142964 pmcs_SATA_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg) 24024c06356bSdh142964 { 24034c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg; 24044c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 24054c06356bSdh142964 pmcs_phy_t *pptr = pwrk->phy; 24064c06356bSdh142964 int dead; 24074c06356bSdh142964 uint32_t sts; 24084c06356bSdh142964 pmcs_xscsi_t *xp; 24094c06356bSdh142964 boolean_t aborted = B_FALSE; 24104c06356bSdh142964 24114c06356bSdh142964 xp = pwrk->xp; 24124c06356bSdh142964 ASSERT(xp != NULL); 24134c06356bSdh142964 24144c06356bSdh142964 DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int, 24154c06356bSdh142964 (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start, 24164c06356bSdh142964 hrtime_t, gethrtime()); 24174c06356bSdh142964 24184c06356bSdh142964 dead = pwrk->dead; 24194c06356bSdh142964 24204c06356bSdh142964 if (msg) { 24214c06356bSdh142964 sts = LE_32(msg[2]); 24224c06356bSdh142964 } else { 24234c06356bSdh142964 sts = 0; 24244c06356bSdh142964 } 24254c06356bSdh142964 24264c06356bSdh142964 if (dead != 0) { 2427c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag " 2428c3bc407cSdh142964 "0x%x for %s", __func__, pwrk->htag, pptr->path); 24294c06356bSdh142964 goto out; 24304c06356bSdh142964 } 24314c06356bSdh142964 if ((pwrk->state == PMCS_WORK_STATE_TIMED_OUT) && 24324c06356bSdh142964 (sts != PMCOUT_STATUS_ABORTED)) { 2433c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 24344c06356bSdh142964 "%s: cmd 0x%p (tag 0x%x) timed out for %s", 24354c06356bSdh142964 __func__, (void *)sp, pwrk->htag, pptr->path); 24364c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD; 24374c06356bSdh142964 /* pkt_reason already set to CMD_TIMEOUT */ 24384c06356bSdh142964 ASSERT(CMD2PKT(sp)->pkt_reason == CMD_TIMEOUT); 24394c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | 24404c06356bSdh142964 STATE_SENT_CMD; 24414c06356bSdh142964 CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT; 24424c06356bSdh142964 goto out; 24434c06356bSdh142964 } 24444c06356bSdh142964 2445c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, "%s: pkt %p tgt %u done", 24464c06356bSdh142964 __func__, (void *)pkt, xp->target_num); 24474c06356bSdh142964 24484c06356bSdh142964 /* 24494c06356bSdh142964 * If the status isn't okay but not underflow, 24504c06356bSdh142964 * step to the side and parse the (possible) error. 24514c06356bSdh142964 */ 24524c06356bSdh142964 #ifdef DEBUG 24534c06356bSdh142964 if (msg) { 24544c06356bSdh142964 pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg); 24554c06356bSdh142964 } 24564c06356bSdh142964 #endif 24574c06356bSdh142964 if (!msg) { 24584c06356bSdh142964 goto out; 24594c06356bSdh142964 } 24604c06356bSdh142964 24614c06356bSdh142964 /* 24624c06356bSdh142964 * If the status isn't okay or we got a FIS response of some kind, 24634c06356bSdh142964 * step to the side and parse the (possible) error. 24644c06356bSdh142964 */ 24654c06356bSdh142964 if ((sts != PMCOUT_STATUS_OK) || (LE_32(msg[3]) != 0)) { 24664c06356bSdh142964 if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) { 24674c06356bSdh142964 mutex_exit(&pwrk->lock); 24684c06356bSdh142964 pmcs_lock_phy(pptr); 24694c06356bSdh142964 mutex_enter(&xp->statlock); 24704c06356bSdh142964 if ((xp->resetting == 0) && (xp->reset_success != 0) && 24714c06356bSdh142964 (xp->reset_wait == 0)) { 24724c06356bSdh142964 mutex_exit(&xp->statlock); 24734c06356bSdh142964 if (pmcs_reset_phy(pwp, pptr, 24744c06356bSdh142964 PMCS_PHYOP_LINK_RESET) != 0) { 2475c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 2476c3bc407cSdh142964 "%s: PHY (%s) Local Control/Link " 2477c3bc407cSdh142964 "Reset FAILED as part of error " 2478c3bc407cSdh142964 "recovery", __func__, pptr->path); 24794c06356bSdh142964 } 24804c06356bSdh142964 mutex_enter(&xp->statlock); 24814c06356bSdh142964 } 24824c06356bSdh142964 mutex_exit(&xp->statlock); 24834c06356bSdh142964 pmcs_unlock_phy(pptr); 24844c06356bSdh142964 mutex_enter(&pwrk->lock); 24854c06356bSdh142964 } 24864c06356bSdh142964 pmcs_ioerror(pwp, SATA, pwrk, msg); 24874c06356bSdh142964 } else { 24884c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, 24894c06356bSdh142964 pwrk->phy->path); 24904c06356bSdh142964 pkt->pkt_state |= STATE_XFERRED_DATA; 24914c06356bSdh142964 pkt->pkt_resid = 0; 24924c06356bSdh142964 } 24934c06356bSdh142964 2494c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, 24954c06356bSdh142964 "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x", 24964c06356bSdh142964 __func__, (void *)pkt, xp->target_num, pkt->pkt_reason, 24974c06356bSdh142964 pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]); 24984c06356bSdh142964 24994c06356bSdh142964 if (pwrk->state == PMCS_WORK_STATE_ABORTED) { 2500c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 25014c06356bSdh142964 "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p", 25024c06356bSdh142964 __func__, (void *)pkt, pptr->path, (void *)pwrk); 25034c06356bSdh142964 aborted = B_TRUE; 25044c06356bSdh142964 } 25054c06356bSdh142964 25064c06356bSdh142964 out: 25074c06356bSdh142964 pmcs_pwork(pwp, pwrk); 25084c06356bSdh142964 pmcs_dma_unload(pwp, sp); 25094c06356bSdh142964 25104c06356bSdh142964 mutex_enter(&xp->statlock); 25114c06356bSdh142964 xp->tagmap &= ~(1 << sp->cmd_satltag); 25124c06356bSdh142964 25134c06356bSdh142964 if (xp->dev_gone) { 25144c06356bSdh142964 mutex_exit(&xp->statlock); 2515c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, 25164c06356bSdh142964 "%s: Completing command for dead target 0x%p", __func__, 25174c06356bSdh142964 (void *)xp); 25184c06356bSdh142964 return; 25194c06356bSdh142964 } 25204c06356bSdh142964 25214c06356bSdh142964 ASSERT(xp->actv_cnt > 0); 25224c06356bSdh142964 if (--(xp->actv_cnt) == 0) { 25234c06356bSdh142964 if (xp->draining) { 2524c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, 25254c06356bSdh142964 "%s: waking up drain waiters", __func__); 25264c06356bSdh142964 cv_signal(&pwp->drain_cv); 25274c06356bSdh142964 } else if (xp->special_needed) { 25284c06356bSdh142964 SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN); 25294c06356bSdh142964 } 25304c06356bSdh142964 } 25314c06356bSdh142964 mutex_exit(&xp->statlock); 25324c06356bSdh142964 25334c06356bSdh142964 if (dead == 0) { 25344c06356bSdh142964 #ifdef DEBUG 25354c06356bSdh142964 pmcs_cmd_t *wp; 25364c06356bSdh142964 mutex_enter(&xp->aqlock); 25374c06356bSdh142964 STAILQ_FOREACH(wp, &xp->aq, cmd_next) { 25384c06356bSdh142964 if (wp == sp) { 25394c06356bSdh142964 break; 25404c06356bSdh142964 } 25414c06356bSdh142964 } 25424c06356bSdh142964 ASSERT(wp != NULL); 25434c06356bSdh142964 #else 25444c06356bSdh142964 mutex_enter(&xp->aqlock); 25454c06356bSdh142964 #endif 25464c06356bSdh142964 STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next); 25474c06356bSdh142964 if (aborted) { 2548c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 25494c06356bSdh142964 "%s: Aborted cmd for tgt 0x%p, signaling waiters", 25504c06356bSdh142964 __func__, (void *)xp); 25514c06356bSdh142964 cv_signal(&xp->abort_cv); 25524c06356bSdh142964 } 25534c06356bSdh142964 mutex_exit(&xp->aqlock); 25544c06356bSdh142964 mutex_enter(&pwp->cq_lock); 25554c06356bSdh142964 STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next); 25564c06356bSdh142964 mutex_exit(&pwp->cq_lock); 25574c06356bSdh142964 } 25584c06356bSdh142964 } 25594c06356bSdh142964 25604c06356bSdh142964 static uint8_t 25614c06356bSdh142964 pmcs_SATA_rwparm(uint8_t *cdb, uint32_t *xfr, uint64_t *lba, uint64_t lbamax) 25624c06356bSdh142964 { 25634c06356bSdh142964 uint8_t asc = 0; 25644c06356bSdh142964 switch (cdb[0]) { 25654c06356bSdh142964 case SCMD_READ_G5: 25664c06356bSdh142964 case SCMD_WRITE_G5: 25674c06356bSdh142964 *xfr = 25684c06356bSdh142964 (((uint32_t)cdb[10]) << 24) | 25694c06356bSdh142964 (((uint32_t)cdb[11]) << 16) | 25704c06356bSdh142964 (((uint32_t)cdb[12]) << 8) | 25714c06356bSdh142964 ((uint32_t)cdb[13]); 25724c06356bSdh142964 *lba = 25734c06356bSdh142964 (((uint64_t)cdb[2]) << 56) | 25744c06356bSdh142964 (((uint64_t)cdb[3]) << 48) | 25754c06356bSdh142964 (((uint64_t)cdb[4]) << 40) | 25764c06356bSdh142964 (((uint64_t)cdb[5]) << 32) | 25774c06356bSdh142964 (((uint64_t)cdb[6]) << 24) | 25784c06356bSdh142964 (((uint64_t)cdb[7]) << 16) | 25794c06356bSdh142964 (((uint64_t)cdb[8]) << 8) | 25804c06356bSdh142964 ((uint64_t)cdb[9]); 25814c06356bSdh142964 /* Check for illegal bits */ 25824c06356bSdh142964 if (cdb[15]) { 25834c06356bSdh142964 asc = 0x24; /* invalid field in cdb */ 25844c06356bSdh142964 } 25854c06356bSdh142964 break; 25864c06356bSdh142964 case SCMD_READ_G4: 25874c06356bSdh142964 case SCMD_WRITE_G4: 25884c06356bSdh142964 *xfr = 25894c06356bSdh142964 (((uint32_t)cdb[6]) << 16) | 25904c06356bSdh142964 (((uint32_t)cdb[7]) << 8) | 25914c06356bSdh142964 ((uint32_t)cdb[8]); 25924c06356bSdh142964 *lba = 25934c06356bSdh142964 (((uint32_t)cdb[2]) << 24) | 25944c06356bSdh142964 (((uint32_t)cdb[3]) << 16) | 25954c06356bSdh142964 (((uint32_t)cdb[4]) << 8) | 25964c06356bSdh142964 ((uint32_t)cdb[5]); 25974c06356bSdh142964 /* Check for illegal bits */ 25984c06356bSdh142964 if (cdb[11]) { 25994c06356bSdh142964 asc = 0x24; /* invalid field in cdb */ 26004c06356bSdh142964 } 26014c06356bSdh142964 break; 26024c06356bSdh142964 case SCMD_READ_G1: 26034c06356bSdh142964 case SCMD_WRITE_G1: 26044c06356bSdh142964 *xfr = (((uint32_t)cdb[7]) << 8) | ((uint32_t)cdb[8]); 26054c06356bSdh142964 *lba = 26064c06356bSdh142964 (((uint32_t)cdb[2]) << 24) | 26074c06356bSdh142964 (((uint32_t)cdb[3]) << 16) | 26084c06356bSdh142964 (((uint32_t)cdb[4]) << 8) | 26094c06356bSdh142964 ((uint32_t)cdb[5]); 26104c06356bSdh142964 /* Check for illegal bits */ 26114c06356bSdh142964 if (cdb[9]) { 26124c06356bSdh142964 asc = 0x24; /* invalid field in cdb */ 26134c06356bSdh142964 } 26144c06356bSdh142964 break; 26154c06356bSdh142964 case SCMD_READ: 26164c06356bSdh142964 case SCMD_WRITE: 26174c06356bSdh142964 *xfr = cdb[4]; 26184c06356bSdh142964 if (*xfr == 0) { 26194c06356bSdh142964 *xfr = 256; 26204c06356bSdh142964 } 26214c06356bSdh142964 *lba = 26224c06356bSdh142964 (((uint32_t)cdb[1] & 0x1f) << 16) | 26234c06356bSdh142964 (((uint32_t)cdb[2]) << 8) | 26244c06356bSdh142964 ((uint32_t)cdb[3]); 26254c06356bSdh142964 /* Check for illegal bits */ 26264c06356bSdh142964 if (cdb[5]) { 26274c06356bSdh142964 asc = 0x24; /* invalid field in cdb */ 26284c06356bSdh142964 } 26294c06356bSdh142964 break; 26304c06356bSdh142964 } 26314c06356bSdh142964 26324c06356bSdh142964 if (asc == 0) { 26334c06356bSdh142964 if ((*lba + *xfr) > lbamax) { 26344c06356bSdh142964 asc = 0x21; /* logical block out of range */ 26354c06356bSdh142964 } 26364c06356bSdh142964 } 26374c06356bSdh142964 return (asc); 26384c06356bSdh142964 } 26394c06356bSdh142964 26404c06356bSdh142964 /* 26414c06356bSdh142964 * Called with pwrk lock held. 26424c06356bSdh142964 */ 26434c06356bSdh142964 static void 26444c06356bSdh142964 pmcs_ioerror(pmcs_hw_t *pwp, pmcs_dtype_t t, pmcwork_t *pwrk, uint32_t *w) 26454c06356bSdh142964 { 26464c06356bSdh142964 static uint8_t por[] = { 26474c06356bSdh142964 0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28 26484c06356bSdh142964 }; 26494c06356bSdh142964 static uint8_t parity[] = { 26504c06356bSdh142964 0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5 26514c06356bSdh142964 }; 26524c06356bSdh142964 const char *msg; 26534c06356bSdh142964 char buf[20]; 26544c06356bSdh142964 pmcs_cmd_t *sp = pwrk->arg; 26554c06356bSdh142964 pmcs_phy_t *phyp = pwrk->phy; 26564c06356bSdh142964 struct scsi_pkt *pkt = CMD2PKT(sp); 26574c06356bSdh142964 uint32_t status; 26584c06356bSdh142964 uint32_t resid; 26594c06356bSdh142964 26604c06356bSdh142964 ASSERT(w != NULL); 26614c06356bSdh142964 status = LE_32(w[2]); 26624c06356bSdh142964 resid = LE_32(w[3]); 26634c06356bSdh142964 26644c06356bSdh142964 msg = pmcs_status_str(status); 26654c06356bSdh142964 if (msg == NULL) { 26664c06356bSdh142964 (void) snprintf(buf, sizeof (buf), "Error 0x%x", status); 26674c06356bSdh142964 msg = buf; 26684c06356bSdh142964 } 26694c06356bSdh142964 26704c06356bSdh142964 if (status != PMCOUT_STATUS_OK) { 2671c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, NULL, 26724c06356bSdh142964 "%s: device %s tag 0x%x status %s @ %llu", __func__, 26734c06356bSdh142964 phyp->path, pwrk->htag, msg, 26744c06356bSdh142964 (unsigned long long)gethrtime()); 26754c06356bSdh142964 } 26764c06356bSdh142964 26774c06356bSdh142964 pkt->pkt_reason = CMD_CMPLT; /* default reason */ 26784c06356bSdh142964 26794c06356bSdh142964 switch (status) { 26804c06356bSdh142964 case PMCOUT_STATUS_OK: 26814c06356bSdh142964 if (t == SATA) { 26824c06356bSdh142964 int i; 26834c06356bSdh142964 fis_t fis; 26844c06356bSdh142964 for (i = 0; i < sizeof (fis) / sizeof (fis[0]); i++) { 26854c06356bSdh142964 fis[i] = LE_32(w[4+i]); 26864c06356bSdh142964 } 26874c06356bSdh142964 if ((fis[0] & 0xff) != FIS_REG_D2H) { 2688c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 26894c06356bSdh142964 "unexpected fis code 0x%x", fis[0] & 0xff); 26904c06356bSdh142964 } else { 2691c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 2692c3bc407cSdh142964 "FIS ERROR"); 26934c06356bSdh142964 pmcs_fis_dump(pwp, fis); 26944c06356bSdh142964 } 26954c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 26964c06356bSdh142964 break; 26974c06356bSdh142964 } 26984c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path); 26994c06356bSdh142964 break; 27004c06356bSdh142964 27014c06356bSdh142964 case PMCOUT_STATUS_ABORTED: 27024c06356bSdh142964 /* 27034c06356bSdh142964 * Command successfully aborted. 27044c06356bSdh142964 */ 27054c06356bSdh142964 if (phyp->dead) { 27064c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE; 27074c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 27084c06356bSdh142964 } else if (pwrk->ssp_event != 0) { 27094c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 27104c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 27114c06356bSdh142964 } else if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) { 27124c06356bSdh142964 pkt->pkt_reason = CMD_TIMEOUT; 27134c06356bSdh142964 pkt->pkt_statistics |= STAT_TIMEOUT; 27144c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | 27154c06356bSdh142964 STATE_SENT_CMD; 27164c06356bSdh142964 } else { 27174c06356bSdh142964 pkt->pkt_reason = CMD_ABORTED; 27184c06356bSdh142964 pkt->pkt_statistics |= STAT_ABORTED; 27194c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET | 27204c06356bSdh142964 STATE_SENT_CMD; 27214c06356bSdh142964 } 27224c06356bSdh142964 27234c06356bSdh142964 /* 27244c06356bSdh142964 * PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past 27254c06356bSdh142964 * this point, so go ahead and mark it as aborted. 27264c06356bSdh142964 */ 27274c06356bSdh142964 pwrk->state = PMCS_WORK_STATE_ABORTED; 27284c06356bSdh142964 break; 27294c06356bSdh142964 27304c06356bSdh142964 case PMCOUT_STATUS_UNDERFLOW: 27314c06356bSdh142964 /* 27324c06356bSdh142964 * This will only get called for SATA 27334c06356bSdh142964 */ 27344c06356bSdh142964 pkt->pkt_resid = resid; 27354c06356bSdh142964 if (pkt->pkt_dma_len < pkt->pkt_resid) { 27364c06356bSdh142964 (void) pmcs_set_resid(pkt, pkt->pkt_dma_len, resid); 27374c06356bSdh142964 } 27384c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path); 27394c06356bSdh142964 break; 27404c06356bSdh142964 27414c06356bSdh142964 case PMCOUT_STATUS_NO_DEVICE: 27424c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT: 27434c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE; 27444c06356bSdh142964 break; 27454c06356bSdh142964 27464c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION: 27474c06356bSdh142964 /* 27484c06356bSdh142964 * Need to do rediscovery. We probably have 27494c06356bSdh142964 * the wrong device (disk swap), so kill 27504c06356bSdh142964 * this one. 27514c06356bSdh142964 */ 27524c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED: 27534c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION: 27544c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED: 27554c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_EROOR: 27564c06356bSdh142964 /* 27574c06356bSdh142964 * Need to do rediscovery. 27584c06356bSdh142964 */ 27594c06356bSdh142964 if (!phyp->dead) { 27604c06356bSdh142964 mutex_exit(&pwrk->lock); 27614c06356bSdh142964 pmcs_lock_phy(pwrk->phy); 27624c06356bSdh142964 pmcs_kill_changed(pwp, pwrk->phy, 0); 27634c06356bSdh142964 pmcs_unlock_phy(pwrk->phy); 27644c06356bSdh142964 mutex_enter(&pwrk->lock); 27654c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE; 27664c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 27674c06356bSdh142964 } else { 27684c06356bSdh142964 pkt->pkt_reason = CMD_DEV_GONE; 27694c06356bSdh142964 } 27704c06356bSdh142964 break; 27714c06356bSdh142964 27724c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK: 27734c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS: 27744c06356bSdh142964 case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION: 27754c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED: 27764c06356bSdh142964 /* cmd is pending on the target */ 27774c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH: 27784c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE: 27794c06356bSdh142964 /* transitory - commands sent while in NCQ failure mode */ 27804c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE: 27814c06356bSdh142964 /* NCQ failure */ 27824c06356bSdh142964 case PMCOUT_STATUS_IO_PORT_IN_RESET: 27834c06356bSdh142964 case PMCOUT_STATUS_XFER_ERR_BREAK: 27844c06356bSdh142964 case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY: 27854c06356bSdh142964 pkt->pkt_reason = CMD_INCOMPLETE; 27864c06356bSdh142964 pkt->pkt_state = STATE_GOT_BUS; 27874c06356bSdh142964 break; 27884c06356bSdh142964 27894c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT: 27904c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_BUSY, NULL, 0, phyp->path); 27914c06356bSdh142964 break; 27924c06356bSdh142964 27934c06356bSdh142964 case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY: 27944c06356bSdh142964 /* synthesize a RESERVATION CONFLICT */ 27954c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_RESERVATION_CONFLICT, NULL, 27964c06356bSdh142964 0, phyp->path); 27974c06356bSdh142964 break; 27984c06356bSdh142964 27994c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST: 28004c06356bSdh142964 /* synthesize a power-on/reset */ 28014c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, por, sizeof (por), 28024c06356bSdh142964 phyp->path); 28034c06356bSdh142964 break; 28044c06356bSdh142964 28054c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE: 28064c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN: 28074c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED: 28084c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT: 28094c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK: 28104c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK: 28114c06356bSdh142964 /* synthesize a PARITY ERROR */ 28124c06356bSdh142964 pmcs_latch_status(pwp, sp, STATUS_CHECK, parity, 28134c06356bSdh142964 sizeof (parity), phyp->path); 28144c06356bSdh142964 break; 28154c06356bSdh142964 28164c06356bSdh142964 case PMCOUT_STATUS_IO_XFER_ERROR_DMA: 28174c06356bSdh142964 case PMCOUT_STATUS_IO_NOT_VALID: 28184c06356bSdh142964 case PMCOUT_STATUS_PROG_ERROR: 28194c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED: 28204c06356bSdh142964 case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */ 28214c06356bSdh142964 default: 28224c06356bSdh142964 pkt->pkt_reason = CMD_TRAN_ERR; 28234c06356bSdh142964 break; 28244c06356bSdh142964 } 28254c06356bSdh142964 } 28264c06356bSdh142964 28274c06356bSdh142964 /* 28284c06356bSdh142964 * Latch up SCSI status 28294c06356bSdh142964 */ 28304c06356bSdh142964 28314c06356bSdh142964 void 28324c06356bSdh142964 pmcs_latch_status(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint8_t status, 28334c06356bSdh142964 uint8_t *snsp, size_t snslen, char *path) 28344c06356bSdh142964 { 28354c06356bSdh142964 static const char c1[] = 28364c06356bSdh142964 "%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) " 28374c06356bSdh142964 "HTAG 0x%x @ %llu"; 28384c06356bSdh142964 static const char c2[] = 28394c06356bSdh142964 "%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu"; 28404c06356bSdh142964 28414c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET | 28424c06356bSdh142964 STATE_SENT_CMD | STATE_GOT_STATUS; 28434c06356bSdh142964 CMD2PKT(sp)->pkt_scbp[0] = status; 28444c06356bSdh142964 28454c06356bSdh142964 if (status == STATUS_CHECK && snsp && 28464c06356bSdh142964 (size_t)SCSA_STSLEN(sp) >= sizeof (struct scsi_arq_status)) { 28474c06356bSdh142964 struct scsi_arq_status *aqp = 28484c06356bSdh142964 (void *) CMD2PKT(sp)->pkt_scbp; 28494c06356bSdh142964 size_t amt = sizeof (struct scsi_extended_sense); 28504c06356bSdh142964 uint8_t key = scsi_sense_key(snsp); 28514c06356bSdh142964 uint8_t asc = scsi_sense_asc(snsp); 28524c06356bSdh142964 uint8_t ascq = scsi_sense_ascq(snsp); 28534c06356bSdh142964 if (amt > snslen) { 28544c06356bSdh142964 amt = snslen; 28554c06356bSdh142964 } 2856c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c1, path, 2857c3bc407cSdh142964 status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, key, asc, ascq, 28584c06356bSdh142964 sp->cmd_tag, (unsigned long long)gethrtime()); 28594c06356bSdh142964 CMD2PKT(sp)->pkt_state |= STATE_ARQ_DONE; 28604c06356bSdh142964 (*(uint8_t *)&aqp->sts_rqpkt_status) = STATUS_GOOD; 28614c06356bSdh142964 aqp->sts_rqpkt_statistics = 0; 28624c06356bSdh142964 aqp->sts_rqpkt_reason = CMD_CMPLT; 28634c06356bSdh142964 aqp->sts_rqpkt_state = STATE_GOT_BUS | 28644c06356bSdh142964 STATE_GOT_TARGET | STATE_SENT_CMD | 28654c06356bSdh142964 STATE_XFERRED_DATA | STATE_GOT_STATUS; 28664c06356bSdh142964 (void) memcpy(&aqp->sts_sensedata, snsp, amt); 28674c06356bSdh142964 if (aqp->sts_sensedata.es_class != CLASS_EXTENDED_SENSE) { 28684c06356bSdh142964 aqp->sts_rqpkt_reason = CMD_TRAN_ERR; 28694c06356bSdh142964 aqp->sts_rqpkt_state = 0; 28704c06356bSdh142964 aqp->sts_rqpkt_resid = 28714c06356bSdh142964 sizeof (struct scsi_extended_sense); 28724c06356bSdh142964 } else { 28734c06356bSdh142964 aqp->sts_rqpkt_resid = 28744c06356bSdh142964 sizeof (struct scsi_extended_sense) - amt; 28754c06356bSdh142964 } 28764c06356bSdh142964 } else if (status) { 2877c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c2, 28784c06356bSdh142964 path, status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, 28794c06356bSdh142964 sp->cmd_tag, (unsigned long long)gethrtime()); 28804c06356bSdh142964 } 28814c06356bSdh142964 28824c06356bSdh142964 CMD2PKT(sp)->pkt_reason = CMD_CMPLT; 28834c06356bSdh142964 } 28844c06356bSdh142964 28854c06356bSdh142964 /* 28864c06356bSdh142964 * Calculate and set packet residual and return the amount 28874c06356bSdh142964 * left over after applying various filters. 28884c06356bSdh142964 */ 28894c06356bSdh142964 size_t 28904c06356bSdh142964 pmcs_set_resid(struct scsi_pkt *pkt, size_t amt, uint32_t cdbamt) 28914c06356bSdh142964 { 28924c06356bSdh142964 pkt->pkt_resid = cdbamt; 28934c06356bSdh142964 if (amt > pkt->pkt_resid) { 28944c06356bSdh142964 amt = pkt->pkt_resid; 28954c06356bSdh142964 } 28964c06356bSdh142964 if (amt > pkt->pkt_dma_len) { 28974c06356bSdh142964 amt = pkt->pkt_dma_len; 28984c06356bSdh142964 } 28994c06356bSdh142964 return (amt); 29004c06356bSdh142964 } 29014c06356bSdh142964 29024c06356bSdh142964 /* 29034c06356bSdh142964 * Return the existing target softstate if there is one. If there is, 29044c06356bSdh142964 * the PHY is locked as well and that lock must be freed by the caller 29054c06356bSdh142964 * after the target/PHY linkage is established. 29064c06356bSdh142964 */ 29074c06356bSdh142964 pmcs_xscsi_t * 29084c06356bSdh142964 pmcs_get_target(pmcs_iport_t *iport, char *tgt_port) 29094c06356bSdh142964 { 29104c06356bSdh142964 pmcs_hw_t *pwp = iport->pwp; 29114c06356bSdh142964 pmcs_phy_t *phyp; 29124c06356bSdh142964 pmcs_xscsi_t *tgt; 29134c06356bSdh142964 uint64_t wwn; 29144c06356bSdh142964 char unit_address[PMCS_MAX_UA_SIZE]; 29154c06356bSdh142964 int ua_form = 1; 29164c06356bSdh142964 29174c06356bSdh142964 /* 29184c06356bSdh142964 * Find the PHY for this target 29194c06356bSdh142964 */ 29204c06356bSdh142964 phyp = pmcs_find_phy_by_sas_address(pwp, iport, NULL, tgt_port); 29214c06356bSdh142964 if (phyp == NULL) { 2922c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL, 2923c3bc407cSdh142964 "%s: No PHY for target @ %s", __func__, tgt_port); 29244c06356bSdh142964 return (NULL); 29254c06356bSdh142964 } 29264c06356bSdh142964 29274c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port); 29284c06356bSdh142964 29294c06356bSdh142964 if (tgt) { 29304c06356bSdh142964 /* 29314c06356bSdh142964 * There's already a target. Check its PHY pointer to see 29324c06356bSdh142964 * if we need to clear the old linkages 29334c06356bSdh142964 */ 29344c06356bSdh142964 if (tgt->phy && (tgt->phy != phyp)) { 2935c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 29364c06356bSdh142964 "%s: Target PHY updated from %p to %p", __func__, 29374c06356bSdh142964 (void *)tgt->phy, (void *)phyp); 29384c06356bSdh142964 if (!IS_ROOT_PHY(tgt->phy)) { 29394c06356bSdh142964 pmcs_dec_phy_ref_count(tgt->phy); 29404c06356bSdh142964 pmcs_inc_phy_ref_count(phyp); 29414c06356bSdh142964 } 29424c06356bSdh142964 tgt->phy->target = NULL; 29434c06356bSdh142964 } 29444c06356bSdh142964 29454c06356bSdh142964 tgt->phy = phyp; 29464c06356bSdh142964 phyp->target = tgt; 29474c06356bSdh142964 return (tgt); 29484c06356bSdh142964 } 29494c06356bSdh142964 29504c06356bSdh142964 /* 29514c06356bSdh142964 * Make sure the PHY we found is on the correct iport 29524c06356bSdh142964 */ 29534c06356bSdh142964 if (phyp->iport != iport) { 2954c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 29554c06356bSdh142964 "%s: No target at %s on this iport", __func__, tgt_port); 29564c06356bSdh142964 pmcs_unlock_phy(phyp); 29574c06356bSdh142964 return (NULL); 29584c06356bSdh142964 } 29594c06356bSdh142964 29604c06356bSdh142964 /* 29614c06356bSdh142964 * Allocate the new softstate 29624c06356bSdh142964 */ 29634c06356bSdh142964 wwn = pmcs_barray2wwn(phyp->sas_address); 29644c06356bSdh142964 (void) scsi_wwn_to_wwnstr(wwn, ua_form, unit_address); 29654c06356bSdh142964 29664c06356bSdh142964 if (ddi_soft_state_bystr_zalloc(iport->tgt_sstate, unit_address) != 29674c06356bSdh142964 DDI_SUCCESS) { 2968c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 29694c06356bSdh142964 "%s: Couldn't alloc softstate for device at %s", 29704c06356bSdh142964 __func__, unit_address); 29714c06356bSdh142964 pmcs_unlock_phy(phyp); 29724c06356bSdh142964 return (NULL); 29734c06356bSdh142964 } 29744c06356bSdh142964 29754c06356bSdh142964 tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, unit_address); 29764c06356bSdh142964 STAILQ_INIT(&tgt->wq); 29774c06356bSdh142964 STAILQ_INIT(&tgt->aq); 29784c06356bSdh142964 STAILQ_INIT(&tgt->sq); 29794c06356bSdh142964 mutex_init(&tgt->statlock, NULL, MUTEX_DRIVER, 29804c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri)); 29814c06356bSdh142964 mutex_init(&tgt->wqlock, NULL, MUTEX_DRIVER, 29824c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri)); 29834c06356bSdh142964 mutex_init(&tgt->aqlock, NULL, MUTEX_DRIVER, 29844c06356bSdh142964 DDI_INTR_PRI(pwp->intr_pri)); 29854c06356bSdh142964 cv_init(&tgt->reset_cv, NULL, CV_DRIVER, NULL); 29864c06356bSdh142964 cv_init(&tgt->abort_cv, NULL, CV_DRIVER, NULL); 29874c06356bSdh142964 tgt->qdepth = 1; 29884c06356bSdh142964 tgt->target_num = PMCS_INVALID_TARGET_NUM; 29894c06356bSdh142964 bcopy(unit_address, tgt->unit_address, PMCS_MAX_UA_SIZE); 29904c06356bSdh142964 tgt->pwp = pwp; 29914c06356bSdh142964 tgt->ua = strdup(iport->ua); 29924c06356bSdh142964 tgt->phy = phyp; 29934c06356bSdh142964 ASSERT((phyp->target == NULL) || (phyp->target == tgt)); 29944c06356bSdh142964 if (phyp->target == NULL) { 29954c06356bSdh142964 phyp->target = tgt; 29964c06356bSdh142964 } 29974c06356bSdh142964 29984c06356bSdh142964 /* 29994c06356bSdh142964 * Don't allocate LUN softstate for SMP targets 30004c06356bSdh142964 */ 30014c06356bSdh142964 if (phyp->dtype == EXPANDER) { 30024c06356bSdh142964 return (tgt); 30034c06356bSdh142964 } 30044c06356bSdh142964 30054c06356bSdh142964 if (ddi_soft_state_bystr_init(&tgt->lun_sstate, 30064c06356bSdh142964 sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) { 3007c3bc407cSdh142964 pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, 30084c06356bSdh142964 "%s: LUN soft_state_bystr_init failed", __func__); 30094c06356bSdh142964 ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port); 30104c06356bSdh142964 pmcs_unlock_phy(phyp); 30114c06356bSdh142964 return (NULL); 30124c06356bSdh142964 } 30134c06356bSdh142964 30144c06356bSdh142964 return (tgt); 30154c06356bSdh142964 } 3016