110696SDavid.Hollister@Sun.COM /*
210696SDavid.Hollister@Sun.COM * CDDL HEADER START
310696SDavid.Hollister@Sun.COM *
410696SDavid.Hollister@Sun.COM * The contents of this file are subject to the terms of the
510696SDavid.Hollister@Sun.COM * Common Development and Distribution License (the "License").
610696SDavid.Hollister@Sun.COM * You may not use this file except in compliance with the License.
710696SDavid.Hollister@Sun.COM *
810696SDavid.Hollister@Sun.COM * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
910696SDavid.Hollister@Sun.COM * or http://www.opensolaris.org/os/licensing.
1010696SDavid.Hollister@Sun.COM * See the License for the specific language governing permissions
1110696SDavid.Hollister@Sun.COM * and limitations under the License.
1210696SDavid.Hollister@Sun.COM *
1310696SDavid.Hollister@Sun.COM * When distributing Covered Code, include this CDDL HEADER in each
1410696SDavid.Hollister@Sun.COM * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1510696SDavid.Hollister@Sun.COM * If applicable, add the following below this CDDL HEADER, with the
1610696SDavid.Hollister@Sun.COM * fields enclosed by brackets "[]" replaced with your own identifying
1710696SDavid.Hollister@Sun.COM * information: Portions Copyright [yyyy] [name of copyright owner]
1810696SDavid.Hollister@Sun.COM *
1910696SDavid.Hollister@Sun.COM * CDDL HEADER END
2012060SDavid.Hollister@Sun.COM */
2112060SDavid.Hollister@Sun.COM /*
2212060SDavid.Hollister@Sun.COM * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
2310696SDavid.Hollister@Sun.COM */
2410696SDavid.Hollister@Sun.COM /*
2510696SDavid.Hollister@Sun.COM * SCSI (SCSA) midlayer interface for PMC drier.
2610696SDavid.Hollister@Sun.COM */
2710696SDavid.Hollister@Sun.COM
2810696SDavid.Hollister@Sun.COM #include <sys/scsi/adapters/pmcs/pmcs.h>
2910696SDavid.Hollister@Sun.COM
3010696SDavid.Hollister@Sun.COM extern scsi_lun_t scsi_lun64_to_lun(scsi_lun64_t lun64);
3110696SDavid.Hollister@Sun.COM
3210696SDavid.Hollister@Sun.COM static int pmcs_scsa_tran_tgt_init(dev_info_t *, dev_info_t *,
3310696SDavid.Hollister@Sun.COM scsi_hba_tran_t *, struct scsi_device *);
3410696SDavid.Hollister@Sun.COM static void pmcs_scsa_tran_tgt_free(dev_info_t *, dev_info_t *,
3510696SDavid.Hollister@Sun.COM scsi_hba_tran_t *, struct scsi_device *);
3610696SDavid.Hollister@Sun.COM static int pmcs_scsa_start(struct scsi_address *, struct scsi_pkt *);
3710696SDavid.Hollister@Sun.COM static int pmcs_scsa_abort(struct scsi_address *, struct scsi_pkt *);
3810696SDavid.Hollister@Sun.COM static int pmcs_scsa_reset(struct scsi_address *, int);
3910696SDavid.Hollister@Sun.COM static int pmcs_scsi_reset_notify(struct scsi_address *, int,
4010696SDavid.Hollister@Sun.COM void (*)(caddr_t), caddr_t);
4110696SDavid.Hollister@Sun.COM static int pmcs_scsa_getcap(struct scsi_address *, char *, int);
4210696SDavid.Hollister@Sun.COM static int pmcs_scsa_setcap(struct scsi_address *, char *, int, int);
4310696SDavid.Hollister@Sun.COM static int pmcs_scsa_setup_pkt(struct scsi_pkt *, int (*)(caddr_t), caddr_t);
4410696SDavid.Hollister@Sun.COM static void pmcs_scsa_teardown_pkt(struct scsi_pkt *);
4511052SChris.Horne@Sun.COM
4611052SChris.Horne@Sun.COM static int pmcs_smp_init(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
4711052SChris.Horne@Sun.COM smp_device_t *);
4811052SChris.Horne@Sun.COM static void pmcs_smp_free(dev_info_t *, dev_info_t *, smp_hba_tran_t *,
4911052SChris.Horne@Sun.COM smp_device_t *);
5010696SDavid.Hollister@Sun.COM static int pmcs_smp_start(struct smp_pkt *);
5110696SDavid.Hollister@Sun.COM
5210696SDavid.Hollister@Sun.COM static int pmcs_scsi_quiesce(dev_info_t *);
5310696SDavid.Hollister@Sun.COM static int pmcs_scsi_unquiesce(dev_info_t *);
5410696SDavid.Hollister@Sun.COM
5510696SDavid.Hollister@Sun.COM static int pmcs_cap(struct scsi_address *, char *, int, int, int);
5610696SDavid.Hollister@Sun.COM static pmcs_xscsi_t *
5710696SDavid.Hollister@Sun.COM pmcs_addr2xp(struct scsi_address *, uint64_t *, pmcs_cmd_t *);
5810696SDavid.Hollister@Sun.COM static int pmcs_SAS_run(pmcs_cmd_t *, pmcwork_t *);
5910696SDavid.Hollister@Sun.COM static void pmcs_SAS_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
6010696SDavid.Hollister@Sun.COM
6110696SDavid.Hollister@Sun.COM static int pmcs_SATA_run(pmcs_cmd_t *, pmcwork_t *);
6210696SDavid.Hollister@Sun.COM static void pmcs_SATA_done(pmcs_hw_t *, pmcwork_t *, uint32_t *);
6310696SDavid.Hollister@Sun.COM static uint8_t pmcs_SATA_rwparm(uint8_t *, uint32_t *, uint64_t *, uint64_t);
6410696SDavid.Hollister@Sun.COM
6510696SDavid.Hollister@Sun.COM static void pmcs_ioerror(pmcs_hw_t *, pmcs_dtype_t pmcs_dtype,
6612060SDavid.Hollister@Sun.COM pmcwork_t *, uint32_t *, uint32_t);
6710696SDavid.Hollister@Sun.COM
6810696SDavid.Hollister@Sun.COM
6910696SDavid.Hollister@Sun.COM int
pmcs_scsa_init(pmcs_hw_t * pwp,const ddi_dma_attr_t * ap)7010696SDavid.Hollister@Sun.COM pmcs_scsa_init(pmcs_hw_t *pwp, const ddi_dma_attr_t *ap)
7110696SDavid.Hollister@Sun.COM {
7210696SDavid.Hollister@Sun.COM scsi_hba_tran_t *tran;
7310696SDavid.Hollister@Sun.COM ddi_dma_attr_t pmcs_scsa_dattr;
7410696SDavid.Hollister@Sun.COM int flags;
7510696SDavid.Hollister@Sun.COM
7610696SDavid.Hollister@Sun.COM (void) memcpy(&pmcs_scsa_dattr, ap, sizeof (ddi_dma_attr_t));
7710696SDavid.Hollister@Sun.COM pmcs_scsa_dattr.dma_attr_sgllen =
7810696SDavid.Hollister@Sun.COM ((PMCS_SGL_NCHUNKS - 1) * (PMCS_MAX_CHUNKS - 1)) + PMCS_SGL_NCHUNKS;
7910696SDavid.Hollister@Sun.COM pmcs_scsa_dattr.dma_attr_flags = DDI_DMA_RELAXED_ORDERING;
8010696SDavid.Hollister@Sun.COM pmcs_scsa_dattr.dma_attr_flags |= DDI_DMA_FLAGERR;
8110696SDavid.Hollister@Sun.COM
8210696SDavid.Hollister@Sun.COM /*
8310696SDavid.Hollister@Sun.COM * Allocate a transport structure
8410696SDavid.Hollister@Sun.COM */
8510696SDavid.Hollister@Sun.COM tran = scsi_hba_tran_alloc(pwp->dip, SCSI_HBA_CANSLEEP);
8610696SDavid.Hollister@Sun.COM if (tran == NULL) {
8711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
8811048SDavid.Hollister@Sun.COM "scsi_hba_tran_alloc failed");
8910696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
9010696SDavid.Hollister@Sun.COM }
9110696SDavid.Hollister@Sun.COM
9210696SDavid.Hollister@Sun.COM tran->tran_hba_private = pwp;
9310696SDavid.Hollister@Sun.COM tran->tran_tgt_init = pmcs_scsa_tran_tgt_init;
9410696SDavid.Hollister@Sun.COM tran->tran_tgt_free = pmcs_scsa_tran_tgt_free;
9510696SDavid.Hollister@Sun.COM tran->tran_start = pmcs_scsa_start;
9610696SDavid.Hollister@Sun.COM tran->tran_abort = pmcs_scsa_abort;
9710696SDavid.Hollister@Sun.COM tran->tran_reset = pmcs_scsa_reset;
9810696SDavid.Hollister@Sun.COM tran->tran_reset_notify = pmcs_scsi_reset_notify;
9910696SDavid.Hollister@Sun.COM tran->tran_getcap = pmcs_scsa_getcap;
10010696SDavid.Hollister@Sun.COM tran->tran_setcap = pmcs_scsa_setcap;
10110696SDavid.Hollister@Sun.COM tran->tran_setup_pkt = pmcs_scsa_setup_pkt;
10210696SDavid.Hollister@Sun.COM tran->tran_teardown_pkt = pmcs_scsa_teardown_pkt;
10310696SDavid.Hollister@Sun.COM tran->tran_quiesce = pmcs_scsi_quiesce;
10410696SDavid.Hollister@Sun.COM tran->tran_unquiesce = pmcs_scsi_unquiesce;
10510696SDavid.Hollister@Sun.COM tran->tran_interconnect_type = INTERCONNECT_SAS;
10610696SDavid.Hollister@Sun.COM tran->tran_hba_len = sizeof (pmcs_cmd_t);
10710696SDavid.Hollister@Sun.COM
10810696SDavid.Hollister@Sun.COM /*
10910696SDavid.Hollister@Sun.COM * Attach this instance of the hba
11010696SDavid.Hollister@Sun.COM */
11110696SDavid.Hollister@Sun.COM
11210696SDavid.Hollister@Sun.COM flags = SCSI_HBA_TRAN_SCB | SCSI_HBA_TRAN_CDB | SCSI_HBA_ADDR_COMPLEX |
11310696SDavid.Hollister@Sun.COM SCSI_HBA_TRAN_PHCI | SCSI_HBA_HBA;
11410696SDavid.Hollister@Sun.COM
11510696SDavid.Hollister@Sun.COM if (scsi_hba_attach_setup(pwp->dip, &pmcs_scsa_dattr, tran, flags)) {
11610696SDavid.Hollister@Sun.COM scsi_hba_tran_free(tran);
11711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
11811048SDavid.Hollister@Sun.COM "scsi_hba_attach failed");
11910696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
12010696SDavid.Hollister@Sun.COM }
12110696SDavid.Hollister@Sun.COM pwp->tran = tran;
12210696SDavid.Hollister@Sun.COM
12310696SDavid.Hollister@Sun.COM /*
12410696SDavid.Hollister@Sun.COM * Attach the SMP part of this hba
12510696SDavid.Hollister@Sun.COM */
12611052SChris.Horne@Sun.COM pwp->smp_tran = smp_hba_tran_alloc(pwp->dip);
12710696SDavid.Hollister@Sun.COM ASSERT(pwp->smp_tran != NULL);
12811052SChris.Horne@Sun.COM pwp->smp_tran->smp_tran_hba_private = pwp;
12911052SChris.Horne@Sun.COM pwp->smp_tran->smp_tran_init = pmcs_smp_init;
13011052SChris.Horne@Sun.COM pwp->smp_tran->smp_tran_free = pmcs_smp_free;
13111052SChris.Horne@Sun.COM pwp->smp_tran->smp_tran_start = pmcs_smp_start;
13210696SDavid.Hollister@Sun.COM
13311052SChris.Horne@Sun.COM if (smp_hba_attach_setup(pwp->dip, pwp->smp_tran) != DDI_SUCCESS) {
13411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
13511052SChris.Horne@Sun.COM "smp_hba_attach failed");
13611052SChris.Horne@Sun.COM smp_hba_tran_free(pwp->smp_tran);
13710696SDavid.Hollister@Sun.COM pwp->smp_tran = NULL;
13810696SDavid.Hollister@Sun.COM scsi_hba_tran_free(tran);
13910696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
14010696SDavid.Hollister@Sun.COM }
14110696SDavid.Hollister@Sun.COM
14210696SDavid.Hollister@Sun.COM return (DDI_SUCCESS);
14310696SDavid.Hollister@Sun.COM }
14410696SDavid.Hollister@Sun.COM
14510696SDavid.Hollister@Sun.COM /*
14610696SDavid.Hollister@Sun.COM * SCSA entry points
14710696SDavid.Hollister@Sun.COM */
14810696SDavid.Hollister@Sun.COM
14910696SDavid.Hollister@Sun.COM static int
pmcs_scsa_tran_tgt_init(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * tran,struct scsi_device * sd)15010696SDavid.Hollister@Sun.COM pmcs_scsa_tran_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
15110696SDavid.Hollister@Sun.COM scsi_hba_tran_t *tran, struct scsi_device *sd)
15210696SDavid.Hollister@Sun.COM {
15310696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = NULL;
15410696SDavid.Hollister@Sun.COM int rval;
15510696SDavid.Hollister@Sun.COM char *variant_prop = "sata";
15610696SDavid.Hollister@Sun.COM char *tgt_port = NULL, *ua = NULL;
15710696SDavid.Hollister@Sun.COM pmcs_xscsi_t *tgt = NULL;
15810696SDavid.Hollister@Sun.COM pmcs_iport_t *iport;
15910696SDavid.Hollister@Sun.COM pmcs_lun_t *lun = NULL;
16010696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp = NULL;
16110696SDavid.Hollister@Sun.COM uint64_t lun_num;
16210696SDavid.Hollister@Sun.COM boolean_t got_scratch = B_FALSE;
16310696SDavid.Hollister@Sun.COM
16410696SDavid.Hollister@Sun.COM /*
16510696SDavid.Hollister@Sun.COM * First, make sure we're an iport and get the pointer to the HBA
16610696SDavid.Hollister@Sun.COM * node's softstate
16710696SDavid.Hollister@Sun.COM */
16810696SDavid.Hollister@Sun.COM if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
16911048SDavid.Hollister@Sun.COM pmcs_prt(TRAN2PMC(tran), PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
17010696SDavid.Hollister@Sun.COM "%s: We don't enumerate devices on the HBA node", __func__);
17110696SDavid.Hollister@Sun.COM goto tgt_init_fail;
17210696SDavid.Hollister@Sun.COM }
17310696SDavid.Hollister@Sun.COM
17410696SDavid.Hollister@Sun.COM pwp = ITRAN2PMC(tran);
17510696SDavid.Hollister@Sun.COM iport = ITRAN2IPORT(tran);
17610696SDavid.Hollister@Sun.COM
17710696SDavid.Hollister@Sun.COM /*
17811556SReed.Liu@Sun.COM * Get the unit-address
17911556SReed.Liu@Sun.COM */
18011556SReed.Liu@Sun.COM ua = scsi_device_unit_address(sd);
18111556SReed.Liu@Sun.COM if (ua == NULL) {
18211556SReed.Liu@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
18311556SReed.Liu@Sun.COM "%s: Couldn't get UA", __func__);
18411556SReed.Liu@Sun.COM pwp = NULL;
18511556SReed.Liu@Sun.COM goto tgt_init_fail;
18611556SReed.Liu@Sun.COM }
18711556SReed.Liu@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
18811556SReed.Liu@Sun.COM "got ua '%s'", ua);
18911556SReed.Liu@Sun.COM
19011556SReed.Liu@Sun.COM /*
19110696SDavid.Hollister@Sun.COM * Get the target address
19210696SDavid.Hollister@Sun.COM */
19310696SDavid.Hollister@Sun.COM rval = scsi_device_prop_lookup_string(sd, SCSI_DEVICE_PROP_PATH,
19410696SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_TARGET_PORT, &tgt_port);
19510696SDavid.Hollister@Sun.COM if (rval != DDI_PROP_SUCCESS) {
19611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
19711048SDavid.Hollister@Sun.COM "Couldn't get target UA");
19810696SDavid.Hollister@Sun.COM pwp = NULL;
19910696SDavid.Hollister@Sun.COM goto tgt_init_fail;
20010696SDavid.Hollister@Sun.COM }
20111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
20211048SDavid.Hollister@Sun.COM "got tgt_port '%s'", tgt_port);
20310696SDavid.Hollister@Sun.COM
20410696SDavid.Hollister@Sun.COM /*
20510696SDavid.Hollister@Sun.COM * Validate that this tran_tgt_init is for an active iport.
20610696SDavid.Hollister@Sun.COM */
20710696SDavid.Hollister@Sun.COM if (iport->ua_state == UA_INACTIVE) {
20811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
20910696SDavid.Hollister@Sun.COM "%s: Got tran_tgt_init on inactive iport for '%s'",
21010696SDavid.Hollister@Sun.COM __func__, tgt_port);
21110696SDavid.Hollister@Sun.COM pwp = NULL;
21210696SDavid.Hollister@Sun.COM goto tgt_init_fail;
21310696SDavid.Hollister@Sun.COM }
21410696SDavid.Hollister@Sun.COM
21510696SDavid.Hollister@Sun.COM /*
21610696SDavid.Hollister@Sun.COM * Since we're going to wait for scratch, be sure to acquire it while
21710696SDavid.Hollister@Sun.COM * we're not holding any other locks
21810696SDavid.Hollister@Sun.COM */
21910696SDavid.Hollister@Sun.COM (void) pmcs_acquire_scratch(pwp, B_TRUE);
22010696SDavid.Hollister@Sun.COM got_scratch = B_TRUE;
22110696SDavid.Hollister@Sun.COM
22210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
22310696SDavid.Hollister@Sun.COM
22410696SDavid.Hollister@Sun.COM /*
22510696SDavid.Hollister@Sun.COM * See if there's already a target softstate. If not, allocate one.
22610696SDavid.Hollister@Sun.COM */
22711692SJesse.Butler@Sun.COM tgt = pmcs_get_target(iport, tgt_port, B_TRUE);
22810696SDavid.Hollister@Sun.COM
22910696SDavid.Hollister@Sun.COM if (tgt == NULL) {
23012668Ssrikanth.suravajhala@oracle.com pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: "
23112668Ssrikanth.suravajhala@oracle.com "No tgt for tgt_port (%s)", __func__, tgt_port);
23210696SDavid.Hollister@Sun.COM goto tgt_init_fail;
23310696SDavid.Hollister@Sun.COM }
23410696SDavid.Hollister@Sun.COM
23510696SDavid.Hollister@Sun.COM phyp = tgt->phy;
23610696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(phyp)) {
23710696SDavid.Hollister@Sun.COM pmcs_inc_phy_ref_count(phyp);
23810696SDavid.Hollister@Sun.COM }
23910696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&phyp->phy_lock));
24010696SDavid.Hollister@Sun.COM
24111556SReed.Liu@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt, "@%s tgt = 0x%p, dip = 0x%p",
24211556SReed.Liu@Sun.COM ua, (void *)tgt, (void *)tgt_dip);
24310696SDavid.Hollister@Sun.COM
24411556SReed.Liu@Sun.COM /* Now get the lun */
24510696SDavid.Hollister@Sun.COM lun_num = scsi_device_prop_get_int64(sd, SCSI_DEVICE_PROP_PATH,
24610696SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_LUN64, SCSI_LUN64_ILLEGAL);
24710696SDavid.Hollister@Sun.COM if (lun_num == SCSI_LUN64_ILLEGAL) {
24811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
24911048SDavid.Hollister@Sun.COM "No LUN for tgt %p", (void *)tgt);
25010696SDavid.Hollister@Sun.COM goto tgt_init_fail;
25110696SDavid.Hollister@Sun.COM }
25210696SDavid.Hollister@Sun.COM
25311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt, "%s: @%s tgt 0x%p phy "
25411048SDavid.Hollister@Sun.COM "0x%p (%s)", __func__, ua, (void *)tgt, (void *)phyp, phyp->path);
25510696SDavid.Hollister@Sun.COM
25610696SDavid.Hollister@Sun.COM mutex_enter(&tgt->statlock);
25710696SDavid.Hollister@Sun.COM tgt->dtype = phyp->dtype;
25810696SDavid.Hollister@Sun.COM if (tgt->dtype != SAS && tgt->dtype != SATA) {
25911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
26011048SDavid.Hollister@Sun.COM "PHY 0x%p went away?", (void *)phyp);
26110696SDavid.Hollister@Sun.COM goto tgt_init_fail;
26210696SDavid.Hollister@Sun.COM }
26310696SDavid.Hollister@Sun.COM
26410696SDavid.Hollister@Sun.COM /* We don't support SATA devices at LUN > 0. */
26510696SDavid.Hollister@Sun.COM if ((tgt->dtype == SATA) && (lun_num > 0)) {
26611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
26710696SDavid.Hollister@Sun.COM "%s: No support for SATA devices at LUN > 0 "
26810696SDavid.Hollister@Sun.COM "(target = 0x%p)", __func__, (void *)tgt);
26910696SDavid.Hollister@Sun.COM goto tgt_init_fail;
27010696SDavid.Hollister@Sun.COM }
27110696SDavid.Hollister@Sun.COM
27210696SDavid.Hollister@Sun.COM /*
27310696SDavid.Hollister@Sun.COM * Allocate LU soft state. We use ddi_soft_state_bystr_zalloc instead
27410696SDavid.Hollister@Sun.COM * of kmem_alloc because ddi_soft_state_bystr_zalloc allows us to
27510696SDavid.Hollister@Sun.COM * verify that the framework never tries to initialize two scsi_device
27610696SDavid.Hollister@Sun.COM * structures with the same unit-address at the same time.
27710696SDavid.Hollister@Sun.COM */
27810696SDavid.Hollister@Sun.COM if (ddi_soft_state_bystr_zalloc(tgt->lun_sstate, ua) != DDI_SUCCESS) {
27911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
28010696SDavid.Hollister@Sun.COM "Couldn't allocate LU soft state");
28110696SDavid.Hollister@Sun.COM goto tgt_init_fail;
28210696SDavid.Hollister@Sun.COM }
28310696SDavid.Hollister@Sun.COM
28410696SDavid.Hollister@Sun.COM lun = ddi_soft_state_bystr_get(tgt->lun_sstate, ua);
28510696SDavid.Hollister@Sun.COM if (lun == NULL) {
28611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, phyp, tgt,
28711048SDavid.Hollister@Sun.COM "Couldn't get LU soft state");
28810696SDavid.Hollister@Sun.COM goto tgt_init_fail;
28910696SDavid.Hollister@Sun.COM }
29010696SDavid.Hollister@Sun.COM scsi_device_hba_private_set(sd, lun);
29110696SDavid.Hollister@Sun.COM lun->lun_num = lun_num;
29210696SDavid.Hollister@Sun.COM
29310696SDavid.Hollister@Sun.COM /* convert the scsi_lun64_t value to SCSI standard form */
29410696SDavid.Hollister@Sun.COM lun->scsi_lun = scsi_lun64_to_lun(lun_num);
29510696SDavid.Hollister@Sun.COM
29610696SDavid.Hollister@Sun.COM ASSERT(strlen(ua) < (PMCS_MAX_UA_SIZE - 1));
29710696SDavid.Hollister@Sun.COM bcopy(ua, lun->unit_address, strnlen(ua, PMCS_MAX_UA_SIZE - 1));
29810696SDavid.Hollister@Sun.COM
29910696SDavid.Hollister@Sun.COM lun->target = tgt;
30010696SDavid.Hollister@Sun.COM
30110696SDavid.Hollister@Sun.COM /*
30210696SDavid.Hollister@Sun.COM * If this is the first tran_tgt_init, add this target to our list
30310696SDavid.Hollister@Sun.COM */
30410696SDavid.Hollister@Sun.COM if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
30510696SDavid.Hollister@Sun.COM int target;
30610696SDavid.Hollister@Sun.COM for (target = 0; target < pwp->max_dev; target++) {
30710696SDavid.Hollister@Sun.COM if (pwp->targets[target] != NULL) {
30810696SDavid.Hollister@Sun.COM continue;
30910696SDavid.Hollister@Sun.COM }
31010696SDavid.Hollister@Sun.COM
31110696SDavid.Hollister@Sun.COM pwp->targets[target] = tgt;
31210696SDavid.Hollister@Sun.COM tgt->target_num = (uint16_t)target;
31310696SDavid.Hollister@Sun.COM break;
31410696SDavid.Hollister@Sun.COM }
31510696SDavid.Hollister@Sun.COM
31610696SDavid.Hollister@Sun.COM if (target == pwp->max_dev) {
31711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
31810696SDavid.Hollister@Sun.COM "Target list full.");
31910696SDavid.Hollister@Sun.COM goto tgt_init_fail;
32010696SDavid.Hollister@Sun.COM }
32110696SDavid.Hollister@Sun.COM }
32210696SDavid.Hollister@Sun.COM
32310696SDavid.Hollister@Sun.COM tgt->dip = sd->sd_dev;
32411501SDavid.Hollister@Sun.COM lun->sd = sd;
32511501SDavid.Hollister@Sun.COM list_insert_tail(&tgt->lun_list, lun);
32610696SDavid.Hollister@Sun.COM
32710696SDavid.Hollister@Sun.COM if (!pmcs_assign_device(pwp, tgt)) {
32810696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
32910696SDavid.Hollister@Sun.COM pwp->targets[tgt->target_num] = NULL;
33010696SDavid.Hollister@Sun.COM tgt->target_num = PMCS_INVALID_TARGET_NUM;
33110696SDavid.Hollister@Sun.COM tgt->phy = NULL;
33211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
33310696SDavid.Hollister@Sun.COM "%s: pmcs_assign_device failed for target 0x%p",
33410696SDavid.Hollister@Sun.COM __func__, (void *)tgt);
33510696SDavid.Hollister@Sun.COM goto tgt_init_fail;
33610696SDavid.Hollister@Sun.COM }
33710696SDavid.Hollister@Sun.COM
33810696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
33910696SDavid.Hollister@Sun.COM tgt->ref_count++;
34010696SDavid.Hollister@Sun.COM
34110696SDavid.Hollister@Sun.COM (void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
34210696SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_TARGET, (uint32_t)(tgt->target_num));
34310696SDavid.Hollister@Sun.COM
34410696SDavid.Hollister@Sun.COM /* SM-HBA */
34510696SDavid.Hollister@Sun.COM if (tgt->dtype == SATA) {
34610696SDavid.Hollister@Sun.COM /* TCR in PSARC/1997/281 opinion */
34710696SDavid.Hollister@Sun.COM (void) scsi_device_prop_update_string(sd,
34810696SDavid.Hollister@Sun.COM SCSI_DEVICE_PROP_PATH, "variant", variant_prop);
34910696SDavid.Hollister@Sun.COM }
35010696SDavid.Hollister@Sun.COM
35110696SDavid.Hollister@Sun.COM tgt->phy_addressable = PMCS_PHY_ADDRESSABLE(phyp);
35210696SDavid.Hollister@Sun.COM
35310696SDavid.Hollister@Sun.COM if (tgt->phy_addressable) {
35410696SDavid.Hollister@Sun.COM (void) scsi_device_prop_update_int(sd, SCSI_DEVICE_PROP_PATH,
35510696SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_SATA_PHY, phyp->phynum);
35610696SDavid.Hollister@Sun.COM }
35710696SDavid.Hollister@Sun.COM
35810696SDavid.Hollister@Sun.COM /* SM-HBA */
35910696SDavid.Hollister@Sun.COM (void) pmcs_smhba_set_scsi_device_props(pwp, phyp, sd);
36011307SDavid.Hollister@Sun.COM /*
36111307SDavid.Hollister@Sun.COM * Make sure attached port and target port pm props are updated
36211307SDavid.Hollister@Sun.COM * By passing in 0s, we're not actually updating any values, but
36311307SDavid.Hollister@Sun.COM * the properties should now get updated on the node.
36411307SDavid.Hollister@Sun.COM */
36510696SDavid.Hollister@Sun.COM
36610696SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
36711501SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(phyp, 0, 0, B_TRUE);
36810696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
36910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
37010696SDavid.Hollister@Sun.COM scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
37110696SDavid.Hollister@Sun.COM return (DDI_SUCCESS);
37210696SDavid.Hollister@Sun.COM
37310696SDavid.Hollister@Sun.COM tgt_init_fail:
37411442SDavid.Hollister@Sun.COM scsi_device_hba_private_set(sd, NULL);
37510696SDavid.Hollister@Sun.COM if (got_scratch) {
37610696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
37710696SDavid.Hollister@Sun.COM }
37810696SDavid.Hollister@Sun.COM if (lun) {
37911501SDavid.Hollister@Sun.COM list_remove(&tgt->lun_list, lun);
38010696SDavid.Hollister@Sun.COM ddi_soft_state_bystr_free(tgt->lun_sstate, ua);
38110696SDavid.Hollister@Sun.COM }
38210696SDavid.Hollister@Sun.COM if (phyp) {
38310696SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
38410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
38510696SDavid.Hollister@Sun.COM /*
38610696SDavid.Hollister@Sun.COM * phyp's ref count was incremented in pmcs_new_tport.
38710696SDavid.Hollister@Sun.COM * We're failing configuration, we now need to decrement it.
38810696SDavid.Hollister@Sun.COM */
38910696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(phyp)) {
39010696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(phyp);
39110696SDavid.Hollister@Sun.COM }
39210696SDavid.Hollister@Sun.COM phyp->target = NULL;
39310696SDavid.Hollister@Sun.COM }
39410696SDavid.Hollister@Sun.COM if (tgt && tgt->ref_count == 0) {
39510696SDavid.Hollister@Sun.COM ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
39610696SDavid.Hollister@Sun.COM }
39710696SDavid.Hollister@Sun.COM if (pwp) {
39810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
39911556SReed.Liu@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
40011556SReed.Liu@Sun.COM "%s: failed for @%s tgt 0x%p phy 0x%p", __func__, ua,
40111556SReed.Liu@Sun.COM (void *)tgt, (void *)phyp);
40210696SDavid.Hollister@Sun.COM }
40310696SDavid.Hollister@Sun.COM if (tgt_port) {
40410696SDavid.Hollister@Sun.COM scsi_device_prop_free(sd, SCSI_DEVICE_PROP_PATH, tgt_port);
40510696SDavid.Hollister@Sun.COM }
40610696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
40710696SDavid.Hollister@Sun.COM }
40810696SDavid.Hollister@Sun.COM
40910696SDavid.Hollister@Sun.COM static void
pmcs_scsa_tran_tgt_free(dev_info_t * hba_dip,dev_info_t * tgt_dip,scsi_hba_tran_t * tran,struct scsi_device * sd)41010696SDavid.Hollister@Sun.COM pmcs_scsa_tran_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
41110696SDavid.Hollister@Sun.COM scsi_hba_tran_t *tran, struct scsi_device *sd)
41210696SDavid.Hollister@Sun.COM {
41310696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(hba_dip, tgt_dip));
41410696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp;
41510696SDavid.Hollister@Sun.COM pmcs_lun_t *lun;
41610696SDavid.Hollister@Sun.COM pmcs_xscsi_t *target;
41710696SDavid.Hollister@Sun.COM char *unit_address;
41810696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp;
41910696SDavid.Hollister@Sun.COM
42010696SDavid.Hollister@Sun.COM if (scsi_hba_iport_unit_address(hba_dip) == NULL) {
42110696SDavid.Hollister@Sun.COM pwp = TRAN2PMC(tran);
42211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
42310696SDavid.Hollister@Sun.COM "%s: We don't enumerate devices on the HBA node", __func__);
42410696SDavid.Hollister@Sun.COM return;
42510696SDavid.Hollister@Sun.COM }
42610696SDavid.Hollister@Sun.COM
42710696SDavid.Hollister@Sun.COM lun = (pmcs_lun_t *)scsi_device_hba_private_get(sd);
42810696SDavid.Hollister@Sun.COM
42910696SDavid.Hollister@Sun.COM ASSERT((lun != NULL) && (lun->target != NULL));
43010696SDavid.Hollister@Sun.COM ASSERT(lun->target->ref_count > 0);
43110696SDavid.Hollister@Sun.COM
43210696SDavid.Hollister@Sun.COM target = lun->target;
43310696SDavid.Hollister@Sun.COM unit_address = lun->unit_address;
43411501SDavid.Hollister@Sun.COM list_remove(&target->lun_list, lun);
43510696SDavid.Hollister@Sun.COM
43610696SDavid.Hollister@Sun.COM pwp = ITRAN2PMC(tran);
43710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
43812385Ssrikanth.suravajhala@oracle.com phyp = target->phy;
43912385Ssrikanth.suravajhala@oracle.com if (phyp) {
44012385Ssrikanth.suravajhala@oracle.com mutex_enter(&phyp->phy_lock);
44112385Ssrikanth.suravajhala@oracle.com }
44210696SDavid.Hollister@Sun.COM mutex_enter(&target->statlock);
44310696SDavid.Hollister@Sun.COM
44411501SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target,
44511501SDavid.Hollister@Sun.COM "%s: for @%s tgt 0x%p phy 0x%p", __func__, unit_address,
44611501SDavid.Hollister@Sun.COM (void *)target, (void *)phyp);
44711501SDavid.Hollister@Sun.COM ddi_soft_state_bystr_free(lun->target->lun_sstate, unit_address);
44811501SDavid.Hollister@Sun.COM
44911087SRamana.Srikanth@Sun.COM if (target->recover_wait) {
45011087SRamana.Srikanth@Sun.COM mutex_exit(&target->statlock);
45112385Ssrikanth.suravajhala@oracle.com if (phyp) {
45212385Ssrikanth.suravajhala@oracle.com mutex_exit(&phyp->phy_lock);
45312385Ssrikanth.suravajhala@oracle.com }
45411087SRamana.Srikanth@Sun.COM mutex_exit(&pwp->lock);
45511087SRamana.Srikanth@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target, "%s: "
45611087SRamana.Srikanth@Sun.COM "Target 0x%p in device state recovery, fail tran_tgt_free",
45711087SRamana.Srikanth@Sun.COM __func__, (void *)target);
45811087SRamana.Srikanth@Sun.COM return;
45911087SRamana.Srikanth@Sun.COM }
46011087SRamana.Srikanth@Sun.COM
46110696SDavid.Hollister@Sun.COM /*
46210696SDavid.Hollister@Sun.COM * If this target still has a PHY pointer and that PHY's target pointer
46310696SDavid.Hollister@Sun.COM * has been cleared, then that PHY has been reaped. In that case, there
46410696SDavid.Hollister@Sun.COM * would be no need to decrement the reference count
46510696SDavid.Hollister@Sun.COM */
46610696SDavid.Hollister@Sun.COM if (phyp && !IS_ROOT_PHY(phyp) && phyp->target) {
46710696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(phyp);
46810696SDavid.Hollister@Sun.COM }
46910696SDavid.Hollister@Sun.COM
47010696SDavid.Hollister@Sun.COM if (--target->ref_count == 0) {
47110696SDavid.Hollister@Sun.COM /*
47210696SDavid.Hollister@Sun.COM * Remove this target from our list. The target soft
47310696SDavid.Hollister@Sun.COM * state will remain, and the device will remain registered
47410696SDavid.Hollister@Sun.COM * with the hardware unless/until we're told the device
47510696SDavid.Hollister@Sun.COM * physically went away.
47610696SDavid.Hollister@Sun.COM */
47711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, target,
47810696SDavid.Hollister@Sun.COM "%s: Free target 0x%p (vtgt %d)", __func__, (void *)target,
47910696SDavid.Hollister@Sun.COM target->target_num);
48010696SDavid.Hollister@Sun.COM pwp->targets[target->target_num] = NULL;
48110696SDavid.Hollister@Sun.COM target->target_num = PMCS_INVALID_TARGET_NUM;
48212937Sjesse.butler@oracle.com /* If the PHY has a pointer to this target, clear it */
48312937Sjesse.butler@oracle.com if (phyp && (phyp->target == target)) {
48410696SDavid.Hollister@Sun.COM phyp->target = NULL;
48510696SDavid.Hollister@Sun.COM }
48610696SDavid.Hollister@Sun.COM target->phy = NULL;
48712849Ssrikanth.suravajhala@oracle.com if (phyp) {
48812849Ssrikanth.suravajhala@oracle.com mutex_exit(&phyp->phy_lock);
48912849Ssrikanth.suravajhala@oracle.com }
49010696SDavid.Hollister@Sun.COM pmcs_destroy_target(target);
49110696SDavid.Hollister@Sun.COM } else {
49210696SDavid.Hollister@Sun.COM mutex_exit(&target->statlock);
49312849Ssrikanth.suravajhala@oracle.com if (phyp) {
49412849Ssrikanth.suravajhala@oracle.com mutex_exit(&phyp->phy_lock);
49512849Ssrikanth.suravajhala@oracle.com }
49610696SDavid.Hollister@Sun.COM }
49710696SDavid.Hollister@Sun.COM
49810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
49910696SDavid.Hollister@Sun.COM }
50010696SDavid.Hollister@Sun.COM
50110696SDavid.Hollister@Sun.COM static int
pmcs_scsa_start(struct scsi_address * ap,struct scsi_pkt * pkt)50210696SDavid.Hollister@Sun.COM pmcs_scsa_start(struct scsi_address *ap, struct scsi_pkt *pkt)
50310696SDavid.Hollister@Sun.COM {
50410696SDavid.Hollister@Sun.COM pmcs_cmd_t *sp = PKT2CMD(pkt);
50510696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = ADDR2PMC(ap);
50610696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
50710696SDavid.Hollister@Sun.COM boolean_t blocked;
50810696SDavid.Hollister@Sun.COM uint32_t hba_state;
50910696SDavid.Hollister@Sun.COM
51011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
51111048SDavid.Hollister@Sun.COM "%s: pkt %p sd %p cdb0=0x%02x dl=%lu", __func__, (void *)pkt,
51210696SDavid.Hollister@Sun.COM (void *)scsi_address_device(&pkt->pkt_address),
51310696SDavid.Hollister@Sun.COM pkt->pkt_cdbp[0] & 0xff, pkt->pkt_dma_len);
51410696SDavid.Hollister@Sun.COM
51510696SDavid.Hollister@Sun.COM if (pkt->pkt_flags & FLAG_NOINTR) {
51611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
51711048SDavid.Hollister@Sun.COM "%s: nointr pkt", __func__);
51810696SDavid.Hollister@Sun.COM return (TRAN_BADPKT);
51910696SDavid.Hollister@Sun.COM }
52010696SDavid.Hollister@Sun.COM
52110696SDavid.Hollister@Sun.COM sp->cmd_tag = 0;
52210696SDavid.Hollister@Sun.COM pkt->pkt_state = pkt->pkt_statistics = 0;
52310696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_INCOMPLETE;
52410696SDavid.Hollister@Sun.COM
52510696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
52610696SDavid.Hollister@Sun.COM hba_state = pwp->state;
52710696SDavid.Hollister@Sun.COM blocked = pwp->blocked;
52810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
52910696SDavid.Hollister@Sun.COM
53010696SDavid.Hollister@Sun.COM if (hba_state != STATE_RUNNING) {
53111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
53211048SDavid.Hollister@Sun.COM "%s: hba dead", __func__);
53310696SDavid.Hollister@Sun.COM return (TRAN_FATAL_ERROR);
53410696SDavid.Hollister@Sun.COM }
53510696SDavid.Hollister@Sun.COM
53610696SDavid.Hollister@Sun.COM xp = pmcs_addr2xp(ap, NULL, sp);
53710696SDavid.Hollister@Sun.COM if (xp == NULL) {
53811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
53910696SDavid.Hollister@Sun.COM "%s: dropping due to null target", __func__);
54010755SJesse.Butler@Sun.COM goto dead_target;
54110696SDavid.Hollister@Sun.COM }
54210696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&xp->statlock));
54310696SDavid.Hollister@Sun.COM
54410696SDavid.Hollister@Sun.COM /*
54510755SJesse.Butler@Sun.COM * First, check to see if the device is gone.
54610696SDavid.Hollister@Sun.COM */
54710755SJesse.Butler@Sun.COM if (xp->dev_gone) {
54811347SRamana.Srikanth@Sun.COM xp->actv_pkts++;
54910696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
55011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, xp,
55110755SJesse.Butler@Sun.COM "%s: dropping due to dead target 0x%p",
55210696SDavid.Hollister@Sun.COM __func__, (void *)xp);
55310755SJesse.Butler@Sun.COM goto dead_target;
55410696SDavid.Hollister@Sun.COM }
55510696SDavid.Hollister@Sun.COM
55610696SDavid.Hollister@Sun.COM /*
55710696SDavid.Hollister@Sun.COM * If we're blocked (quiesced) just return.
55810696SDavid.Hollister@Sun.COM */
55910696SDavid.Hollister@Sun.COM if (blocked) {
56011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
56111048SDavid.Hollister@Sun.COM "%s: hba blocked", __func__);
56211347SRamana.Srikanth@Sun.COM xp->actv_pkts++;
56310696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
56410696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
56510696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
56610696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
56710696SDavid.Hollister@Sun.COM return (TRAN_ACCEPT);
56810696SDavid.Hollister@Sun.COM }
56910696SDavid.Hollister@Sun.COM
57010696SDavid.Hollister@Sun.COM /*
57110696SDavid.Hollister@Sun.COM * If we're draining or resetting, queue and return.
57210696SDavid.Hollister@Sun.COM */
57310696SDavid.Hollister@Sun.COM if (xp->draining || xp->resetting || xp->recover_wait) {
57411347SRamana.Srikanth@Sun.COM xp->actv_pkts++;
57510696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
57610696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
57710696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
57810696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
57911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, xp,
58010696SDavid.Hollister@Sun.COM "%s: draining/resetting/recovering (cnt %u)",
58110696SDavid.Hollister@Sun.COM __func__, xp->actv_cnt);
58210696SDavid.Hollister@Sun.COM /*
58310696SDavid.Hollister@Sun.COM * By the time we get here, draining or
58410696SDavid.Hollister@Sun.COM * resetting may have come and gone, not
58510696SDavid.Hollister@Sun.COM * yet noticing that we had put something
58610696SDavid.Hollister@Sun.COM * on the wait queue, so schedule a worker
58710696SDavid.Hollister@Sun.COM * to look at this later.
58810696SDavid.Hollister@Sun.COM */
58910696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
59010696SDavid.Hollister@Sun.COM return (TRAN_ACCEPT);
59110696SDavid.Hollister@Sun.COM }
59211347SRamana.Srikanth@Sun.COM
59311347SRamana.Srikanth@Sun.COM xp->actv_pkts++;
59410696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
59510696SDavid.Hollister@Sun.COM
59610696SDavid.Hollister@Sun.COM /*
59710696SDavid.Hollister@Sun.COM * Queue this command to the tail of the wait queue.
59810696SDavid.Hollister@Sun.COM * This keeps us getting commands out of order.
59910696SDavid.Hollister@Sun.COM */
60010696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
60110696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&xp->wq, sp, cmd_next);
60210696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
60310696SDavid.Hollister@Sun.COM
60410696SDavid.Hollister@Sun.COM /*
60510696SDavid.Hollister@Sun.COM * Now run the queue for this device.
60610696SDavid.Hollister@Sun.COM */
60710696SDavid.Hollister@Sun.COM (void) pmcs_scsa_wq_run_one(pwp, xp);
60810696SDavid.Hollister@Sun.COM
60910696SDavid.Hollister@Sun.COM return (TRAN_ACCEPT);
61010696SDavid.Hollister@Sun.COM
61110755SJesse.Butler@Sun.COM dead_target:
61210696SDavid.Hollister@Sun.COM pkt->pkt_state = STATE_GOT_BUS;
61310696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_DEV_GONE;
61410696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
61510696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
61610696SDavid.Hollister@Sun.COM PMCS_CQ_RUN_LOCKED(pwp);
61710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
61810696SDavid.Hollister@Sun.COM return (TRAN_ACCEPT);
61910696SDavid.Hollister@Sun.COM }
62010696SDavid.Hollister@Sun.COM
62111545SRamana.Srikanth@Sun.COM /* Return code 1 = Success */
62210696SDavid.Hollister@Sun.COM static int
pmcs_scsa_abort(struct scsi_address * ap,struct scsi_pkt * pkt)62310696SDavid.Hollister@Sun.COM pmcs_scsa_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
62410696SDavid.Hollister@Sun.COM {
62510696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = ADDR2PMC(ap);
62611545SRamana.Srikanth@Sun.COM pmcs_cmd_t *sp = NULL;
62711545SRamana.Srikanth@Sun.COM pmcs_xscsi_t *xp = NULL;
62811545SRamana.Srikanth@Sun.COM pmcs_phy_t *pptr = NULL;
62911545SRamana.Srikanth@Sun.COM pmcs_lun_t *pmcs_lun = (pmcs_lun_t *)
63011545SRamana.Srikanth@Sun.COM scsi_device_hba_private_get(scsi_address_device(ap));
63110696SDavid.Hollister@Sun.COM uint32_t tag;
63210696SDavid.Hollister@Sun.COM uint64_t lun;
63310696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
63410696SDavid.Hollister@Sun.COM
63510696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
63610696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
63710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
63811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
63911048SDavid.Hollister@Sun.COM "%s: hba dead", __func__);
64010696SDavid.Hollister@Sun.COM return (0);
64110696SDavid.Hollister@Sun.COM }
64210696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
64310696SDavid.Hollister@Sun.COM
64411545SRamana.Srikanth@Sun.COM if (pkt == NULL) {
64511545SRamana.Srikanth@Sun.COM if (pmcs_lun == NULL) {
64611545SRamana.Srikanth@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: "
64711545SRamana.Srikanth@Sun.COM "No pmcs_lun_t struct to do ABORT_ALL", __func__);
64811545SRamana.Srikanth@Sun.COM return (0);
64911545SRamana.Srikanth@Sun.COM }
65011545SRamana.Srikanth@Sun.COM xp = pmcs_lun->target;
65111545SRamana.Srikanth@Sun.COM if (xp != NULL) {
65211545SRamana.Srikanth@Sun.COM pptr = xp->phy;
65311545SRamana.Srikanth@Sun.COM }
65411545SRamana.Srikanth@Sun.COM if (pptr == NULL) {
65511545SRamana.Srikanth@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp, "%s: pkt is "
65611545SRamana.Srikanth@Sun.COM "NULL. No tgt/phy to do ABORT_ALL", __func__);
65711545SRamana.Srikanth@Sun.COM return (0);
65811545SRamana.Srikanth@Sun.COM }
65911545SRamana.Srikanth@Sun.COM pmcs_lock_phy(pptr);
66011545SRamana.Srikanth@Sun.COM if (pmcs_abort(pwp, pptr, 0, 1, 0)) {
66111545SRamana.Srikanth@Sun.COM pptr->abort_pending = 1;
66211545SRamana.Srikanth@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
66311545SRamana.Srikanth@Sun.COM }
66411545SRamana.Srikanth@Sun.COM pmcs_unlock_phy(pptr);
66511545SRamana.Srikanth@Sun.COM return (1);
66611545SRamana.Srikanth@Sun.COM }
66711545SRamana.Srikanth@Sun.COM
66811545SRamana.Srikanth@Sun.COM sp = PKT2CMD(pkt);
66911545SRamana.Srikanth@Sun.COM xp = sp->cmd_target;
67011545SRamana.Srikanth@Sun.COM
67110696SDavid.Hollister@Sun.COM if (sp->cmd_lun) {
67210696SDavid.Hollister@Sun.COM lun = sp->cmd_lun->lun_num;
67310696SDavid.Hollister@Sun.COM } else {
67410696SDavid.Hollister@Sun.COM lun = 0;
67510696SDavid.Hollister@Sun.COM }
67610696SDavid.Hollister@Sun.COM if (xp == NULL) {
67710696SDavid.Hollister@Sun.COM return (0);
67810696SDavid.Hollister@Sun.COM }
67910696SDavid.Hollister@Sun.COM
68010696SDavid.Hollister@Sun.COM /*
68110696SDavid.Hollister@Sun.COM * See if we have a real work structure associated with this cmd.
68210696SDavid.Hollister@Sun.COM */
68312258Ssrikanth.suravajhala@oracle.com pwrk = pmcs_tag2wp(pwp, sp->cmd_tag, B_FALSE);
68410696SDavid.Hollister@Sun.COM if (pwrk && pwrk->arg == sp) {
68510696SDavid.Hollister@Sun.COM tag = pwrk->htag;
68610696SDavid.Hollister@Sun.COM pptr = pwrk->phy;
68710696SDavid.Hollister@Sun.COM pwrk->timer = 0; /* we don't time this here */
68810696SDavid.Hollister@Sun.COM ASSERT(pwrk->state == PMCS_WORK_STATE_ONCHIP);
68910696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
69010696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
69110696SDavid.Hollister@Sun.COM if (pptr->dtype == SAS) {
69210696SDavid.Hollister@Sun.COM if (pmcs_ssp_tmf(pwp, pptr, SAS_ABORT_TASK, tag, lun,
69310696SDavid.Hollister@Sun.COM NULL)) {
69410696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
69510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
69610696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
69710696SDavid.Hollister@Sun.COM return (0);
69810696SDavid.Hollister@Sun.COM }
69910696SDavid.Hollister@Sun.COM } else {
70010696SDavid.Hollister@Sun.COM /*
70110696SDavid.Hollister@Sun.COM * XXX: Was the command that was active an
70210696SDavid.Hollister@Sun.COM * NCQ I/O command?
70310696SDavid.Hollister@Sun.COM */
70410696SDavid.Hollister@Sun.COM pptr->need_rl_ext = 1;
70510696SDavid.Hollister@Sun.COM if (pmcs_sata_abort_ncq(pwp, pptr)) {
70610696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
70710696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
70810696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
70910696SDavid.Hollister@Sun.COM return (0);
71010696SDavid.Hollister@Sun.COM }
71110696SDavid.Hollister@Sun.COM }
71210696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
71310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
71410696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE);
71510696SDavid.Hollister@Sun.COM return (1);
71610696SDavid.Hollister@Sun.COM }
71710696SDavid.Hollister@Sun.COM if (pwrk) {
71810696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
71910696SDavid.Hollister@Sun.COM }
72010696SDavid.Hollister@Sun.COM /*
72110696SDavid.Hollister@Sun.COM * Okay, those weren't the droids we were looking for.
72210696SDavid.Hollister@Sun.COM * See if the command is on any of the wait queues.
72310696SDavid.Hollister@Sun.COM */
72410696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
72510696SDavid.Hollister@Sun.COM sp = NULL;
72610696SDavid.Hollister@Sun.COM STAILQ_FOREACH(sp, &xp->wq, cmd_next) {
72710696SDavid.Hollister@Sun.COM if (sp == PKT2CMD(pkt)) {
72810696SDavid.Hollister@Sun.COM STAILQ_REMOVE(&xp->wq, sp, pmcs_cmd, cmd_next);
72910696SDavid.Hollister@Sun.COM break;
73010696SDavid.Hollister@Sun.COM }
73110696SDavid.Hollister@Sun.COM }
73210696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
73310696SDavid.Hollister@Sun.COM if (sp) {
73410696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_ABORTED;
73510696SDavid.Hollister@Sun.COM pkt->pkt_statistics |= STAT_ABORTED;
73610696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
73710696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
73810696SDavid.Hollister@Sun.COM PMCS_CQ_RUN_LOCKED(pwp);
73910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
74010696SDavid.Hollister@Sun.COM return (1);
74110696SDavid.Hollister@Sun.COM }
74210696SDavid.Hollister@Sun.COM return (0);
74310696SDavid.Hollister@Sun.COM }
74410696SDavid.Hollister@Sun.COM
74510696SDavid.Hollister@Sun.COM /*
74610696SDavid.Hollister@Sun.COM * SCSA reset functions
74710696SDavid.Hollister@Sun.COM */
74810696SDavid.Hollister@Sun.COM static int
pmcs_scsa_reset(struct scsi_address * ap,int level)74910696SDavid.Hollister@Sun.COM pmcs_scsa_reset(struct scsi_address *ap, int level)
75010696SDavid.Hollister@Sun.COM {
75110696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = ADDR2PMC(ap);
75210696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
75310696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
75410696SDavid.Hollister@Sun.COM uint64_t lun = (uint64_t)-1, *lp = NULL;
75510696SDavid.Hollister@Sun.COM int rval;
75610696SDavid.Hollister@Sun.COM
75710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
75810696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
75910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
76011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
76111048SDavid.Hollister@Sun.COM "%s: hba dead", __func__);
76210696SDavid.Hollister@Sun.COM return (0);
76310696SDavid.Hollister@Sun.COM }
76410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
76510696SDavid.Hollister@Sun.COM
76610696SDavid.Hollister@Sun.COM switch (level) {
76710696SDavid.Hollister@Sun.COM case RESET_ALL:
76810696SDavid.Hollister@Sun.COM rval = 0;
76910696SDavid.Hollister@Sun.COM break;
77010696SDavid.Hollister@Sun.COM case RESET_LUN:
77110696SDavid.Hollister@Sun.COM /*
77210696SDavid.Hollister@Sun.COM * Point lp at lun so that pmcs_addr2xp
77310696SDavid.Hollister@Sun.COM * will fill out the 64 bit lun number.
77410696SDavid.Hollister@Sun.COM */
77510696SDavid.Hollister@Sun.COM lp = &lun;
77610696SDavid.Hollister@Sun.COM /* FALLTHROUGH */
77710696SDavid.Hollister@Sun.COM case RESET_TARGET:
77810696SDavid.Hollister@Sun.COM xp = pmcs_addr2xp(ap, lp, NULL);
77910696SDavid.Hollister@Sun.COM if (xp == NULL) {
78011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
78110696SDavid.Hollister@Sun.COM "%s: no xp found for this scsi address", __func__);
78210696SDavid.Hollister@Sun.COM return (0);
78310696SDavid.Hollister@Sun.COM }
78410696SDavid.Hollister@Sun.COM
78510755SJesse.Butler@Sun.COM if (xp->dev_gone) {
78610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
78711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
78810696SDavid.Hollister@Sun.COM "%s: Target 0x%p has gone away", __func__,
78910696SDavid.Hollister@Sun.COM (void *)xp);
79010696SDavid.Hollister@Sun.COM return (0);
79110696SDavid.Hollister@Sun.COM }
79210696SDavid.Hollister@Sun.COM
79310696SDavid.Hollister@Sun.COM /*
79410696SDavid.Hollister@Sun.COM * If we're already performing this action, or if device
79510696SDavid.Hollister@Sun.COM * state recovery is already running, just return failure.
79610696SDavid.Hollister@Sun.COM */
79710696SDavid.Hollister@Sun.COM if (xp->resetting || xp->recover_wait) {
79810696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
79910696SDavid.Hollister@Sun.COM return (0);
80010696SDavid.Hollister@Sun.COM }
80110696SDavid.Hollister@Sun.COM xp->reset_wait = 0;
80210696SDavid.Hollister@Sun.COM xp->reset_success = 0;
80310696SDavid.Hollister@Sun.COM xp->resetting = 1;
80410696SDavid.Hollister@Sun.COM pptr = xp->phy;
80510696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
80610696SDavid.Hollister@Sun.COM
80710696SDavid.Hollister@Sun.COM if (pmcs_reset_dev(pwp, pptr, lun)) {
80810696SDavid.Hollister@Sun.COM rval = 0;
80910696SDavid.Hollister@Sun.COM } else {
81010696SDavid.Hollister@Sun.COM rval = 1;
81110696SDavid.Hollister@Sun.COM }
81210696SDavid.Hollister@Sun.COM
81310696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
81410696SDavid.Hollister@Sun.COM if (rval == 1) {
81510696SDavid.Hollister@Sun.COM xp->reset_success = 1;
81610696SDavid.Hollister@Sun.COM }
81710696SDavid.Hollister@Sun.COM if (xp->reset_wait) {
81810696SDavid.Hollister@Sun.COM xp->reset_wait = 0;
81910696SDavid.Hollister@Sun.COM cv_signal(&xp->reset_cv);
82010696SDavid.Hollister@Sun.COM }
82110696SDavid.Hollister@Sun.COM xp->resetting = 0;
82210696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
82310696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
82410696SDavid.Hollister@Sun.COM break;
82510696SDavid.Hollister@Sun.COM default:
82610696SDavid.Hollister@Sun.COM rval = 0;
82710696SDavid.Hollister@Sun.COM break;
82810696SDavid.Hollister@Sun.COM }
82910696SDavid.Hollister@Sun.COM
83010696SDavid.Hollister@Sun.COM return (rval);
83110696SDavid.Hollister@Sun.COM }
83210696SDavid.Hollister@Sun.COM
83310696SDavid.Hollister@Sun.COM static int
pmcs_scsi_reset_notify(struct scsi_address * ap,int flag,void (* callback)(caddr_t),caddr_t arg)83410696SDavid.Hollister@Sun.COM pmcs_scsi_reset_notify(struct scsi_address *ap, int flag,
83510696SDavid.Hollister@Sun.COM void (*callback)(caddr_t), caddr_t arg)
83610696SDavid.Hollister@Sun.COM {
83710696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = ADDR2PMC(ap);
83810696SDavid.Hollister@Sun.COM return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
83910696SDavid.Hollister@Sun.COM &pwp->lock, &pwp->reset_notify_listf));
84010696SDavid.Hollister@Sun.COM }
84110696SDavid.Hollister@Sun.COM
84210696SDavid.Hollister@Sun.COM
84310696SDavid.Hollister@Sun.COM static int
pmcs_cap(struct scsi_address * ap,char * cap,int val,int tonly,int set)84410696SDavid.Hollister@Sun.COM pmcs_cap(struct scsi_address *ap, char *cap, int val, int tonly, int set)
84510696SDavid.Hollister@Sun.COM {
84610696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(val, tonly));
84710696SDavid.Hollister@Sun.COM int cidx, rval = 0;
84810696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
84910696SDavid.Hollister@Sun.COM
85010696SDavid.Hollister@Sun.COM cidx = scsi_hba_lookup_capstr(cap);
85110696SDavid.Hollister@Sun.COM if (cidx == -1) {
85210696SDavid.Hollister@Sun.COM return (-1);
85310696SDavid.Hollister@Sun.COM }
85410696SDavid.Hollister@Sun.COM
85510696SDavid.Hollister@Sun.COM xp = pmcs_addr2xp(ap, NULL, NULL);
85610696SDavid.Hollister@Sun.COM if (xp == NULL) {
85710696SDavid.Hollister@Sun.COM return (-1);
85810696SDavid.Hollister@Sun.COM }
85910696SDavid.Hollister@Sun.COM
86010696SDavid.Hollister@Sun.COM switch (cidx) {
86110696SDavid.Hollister@Sun.COM case SCSI_CAP_DMA_MAX:
86210696SDavid.Hollister@Sun.COM case SCSI_CAP_INITIATOR_ID:
86310696SDavid.Hollister@Sun.COM if (set == 0) {
86410696SDavid.Hollister@Sun.COM rval = INT_MAX; /* argh */
86510696SDavid.Hollister@Sun.COM }
86610696SDavid.Hollister@Sun.COM break;
86710696SDavid.Hollister@Sun.COM case SCSI_CAP_DISCONNECT:
86810696SDavid.Hollister@Sun.COM case SCSI_CAP_SYNCHRONOUS:
86910696SDavid.Hollister@Sun.COM case SCSI_CAP_WIDE_XFER:
87010696SDavid.Hollister@Sun.COM case SCSI_CAP_PARITY:
87110696SDavid.Hollister@Sun.COM case SCSI_CAP_ARQ:
87210696SDavid.Hollister@Sun.COM case SCSI_CAP_UNTAGGED_QING:
87310696SDavid.Hollister@Sun.COM if (set == 0) {
87410696SDavid.Hollister@Sun.COM rval = 1;
87510696SDavid.Hollister@Sun.COM }
87610696SDavid.Hollister@Sun.COM break;
87710696SDavid.Hollister@Sun.COM
87810696SDavid.Hollister@Sun.COM case SCSI_CAP_TAGGED_QING:
87910696SDavid.Hollister@Sun.COM rval = 1;
88010696SDavid.Hollister@Sun.COM break;
88110696SDavid.Hollister@Sun.COM
88210696SDavid.Hollister@Sun.COM case SCSI_CAP_MSG_OUT:
88310696SDavid.Hollister@Sun.COM case SCSI_CAP_RESET_NOTIFICATION:
88410696SDavid.Hollister@Sun.COM case SCSI_CAP_QFULL_RETRIES:
88510696SDavid.Hollister@Sun.COM case SCSI_CAP_QFULL_RETRY_INTERVAL:
88610696SDavid.Hollister@Sun.COM break;
88710696SDavid.Hollister@Sun.COM case SCSI_CAP_SCSI_VERSION:
88810696SDavid.Hollister@Sun.COM if (set == 0) {
88910696SDavid.Hollister@Sun.COM rval = SCSI_VERSION_3;
89010696SDavid.Hollister@Sun.COM }
89110696SDavid.Hollister@Sun.COM break;
89210696SDavid.Hollister@Sun.COM case SCSI_CAP_INTERCONNECT_TYPE:
89310696SDavid.Hollister@Sun.COM if (set) {
89410696SDavid.Hollister@Sun.COM break;
89510696SDavid.Hollister@Sun.COM }
89610696SDavid.Hollister@Sun.COM if (xp->phy_addressable) {
89710696SDavid.Hollister@Sun.COM rval = INTERCONNECT_SATA;
89810696SDavid.Hollister@Sun.COM } else {
89910696SDavid.Hollister@Sun.COM rval = INTERCONNECT_SAS;
90010696SDavid.Hollister@Sun.COM }
90110696SDavid.Hollister@Sun.COM break;
90210696SDavid.Hollister@Sun.COM case SCSI_CAP_CDB_LEN:
90310696SDavid.Hollister@Sun.COM if (set == 0) {
90410696SDavid.Hollister@Sun.COM rval = 16;
90510696SDavid.Hollister@Sun.COM }
90610696SDavid.Hollister@Sun.COM break;
90710696SDavid.Hollister@Sun.COM case SCSI_CAP_LUN_RESET:
90810696SDavid.Hollister@Sun.COM if (set) {
90910696SDavid.Hollister@Sun.COM break;
91010696SDavid.Hollister@Sun.COM }
91110696SDavid.Hollister@Sun.COM if (xp->dtype == SATA) {
91210696SDavid.Hollister@Sun.COM rval = 0;
91310696SDavid.Hollister@Sun.COM } else {
91410696SDavid.Hollister@Sun.COM rval = 1;
91510696SDavid.Hollister@Sun.COM }
91610696SDavid.Hollister@Sun.COM break;
91710696SDavid.Hollister@Sun.COM default:
91810696SDavid.Hollister@Sun.COM rval = -1;
91910696SDavid.Hollister@Sun.COM break;
92010696SDavid.Hollister@Sun.COM }
92110696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
92211048SDavid.Hollister@Sun.COM pmcs_prt(ADDR2PMC(ap), PMCS_PRT_DEBUG3, NULL, NULL,
92310696SDavid.Hollister@Sun.COM "%s: cap %s val %d set %d rval %d",
92410696SDavid.Hollister@Sun.COM __func__, cap, val, set, rval);
92510696SDavid.Hollister@Sun.COM return (rval);
92610696SDavid.Hollister@Sun.COM }
92710696SDavid.Hollister@Sun.COM
92810696SDavid.Hollister@Sun.COM /*
92910696SDavid.Hollister@Sun.COM * Returns with statlock held if the xp is found.
93010696SDavid.Hollister@Sun.COM * Fills in pmcs_cmd_t with values if pmcs_cmd_t pointer non-NULL.
93110696SDavid.Hollister@Sun.COM */
93210696SDavid.Hollister@Sun.COM static pmcs_xscsi_t *
pmcs_addr2xp(struct scsi_address * ap,uint64_t * lp,pmcs_cmd_t * sp)93310696SDavid.Hollister@Sun.COM pmcs_addr2xp(struct scsi_address *ap, uint64_t *lp, pmcs_cmd_t *sp)
93410696SDavid.Hollister@Sun.COM {
93510696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
93610696SDavid.Hollister@Sun.COM pmcs_lun_t *lun = (pmcs_lun_t *)
93710696SDavid.Hollister@Sun.COM scsi_device_hba_private_get(scsi_address_device(ap));
93810696SDavid.Hollister@Sun.COM
93910696SDavid.Hollister@Sun.COM if ((lun == NULL) || (lun->target == NULL)) {
94010696SDavid.Hollister@Sun.COM return (NULL);
94110696SDavid.Hollister@Sun.COM }
94210696SDavid.Hollister@Sun.COM xp = lun->target;
94310696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
94410696SDavid.Hollister@Sun.COM
94510755SJesse.Butler@Sun.COM if (xp->dev_gone || (xp->phy == NULL)) {
94611347SRamana.Srikanth@Sun.COM /*
94711347SRamana.Srikanth@Sun.COM * This may be a retried packet, so it's possible cmd_target
94811347SRamana.Srikanth@Sun.COM * and cmd_lun may still be populated. Clear them.
94911347SRamana.Srikanth@Sun.COM */
95011347SRamana.Srikanth@Sun.COM if (sp != NULL) {
95111347SRamana.Srikanth@Sun.COM sp->cmd_target = NULL;
95211347SRamana.Srikanth@Sun.COM sp->cmd_lun = NULL;
95311347SRamana.Srikanth@Sun.COM }
95410696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
95510696SDavid.Hollister@Sun.COM return (NULL);
95610696SDavid.Hollister@Sun.COM }
95710696SDavid.Hollister@Sun.COM
95810696SDavid.Hollister@Sun.COM if (sp != NULL) {
95910696SDavid.Hollister@Sun.COM sp->cmd_target = xp;
96010696SDavid.Hollister@Sun.COM sp->cmd_lun = lun;
96110696SDavid.Hollister@Sun.COM }
96210696SDavid.Hollister@Sun.COM if (lp) {
96310696SDavid.Hollister@Sun.COM *lp = lun->lun_num;
96410696SDavid.Hollister@Sun.COM }
96510696SDavid.Hollister@Sun.COM return (xp);
96610696SDavid.Hollister@Sun.COM }
96710696SDavid.Hollister@Sun.COM
96810696SDavid.Hollister@Sun.COM static int
pmcs_scsa_getcap(struct scsi_address * ap,char * cap,int whom)96910696SDavid.Hollister@Sun.COM pmcs_scsa_getcap(struct scsi_address *ap, char *cap, int whom)
97010696SDavid.Hollister@Sun.COM {
97110696SDavid.Hollister@Sun.COM int r;
97210696SDavid.Hollister@Sun.COM if (cap == NULL) {
97310696SDavid.Hollister@Sun.COM return (-1);
97410696SDavid.Hollister@Sun.COM }
97510696SDavid.Hollister@Sun.COM r = pmcs_cap(ap, cap, 0, whom, 0);
97610696SDavid.Hollister@Sun.COM return (r);
97710696SDavid.Hollister@Sun.COM }
97810696SDavid.Hollister@Sun.COM
97910696SDavid.Hollister@Sun.COM static int
pmcs_scsa_setcap(struct scsi_address * ap,char * cap,int value,int whom)98010696SDavid.Hollister@Sun.COM pmcs_scsa_setcap(struct scsi_address *ap, char *cap, int value, int whom)
98110696SDavid.Hollister@Sun.COM {
98210696SDavid.Hollister@Sun.COM int r;
98310696SDavid.Hollister@Sun.COM if (cap == NULL) {
98410696SDavid.Hollister@Sun.COM return (-1);
98510696SDavid.Hollister@Sun.COM }
98610696SDavid.Hollister@Sun.COM r = pmcs_cap(ap, cap, value, whom, 1);
98710696SDavid.Hollister@Sun.COM return (r);
98810696SDavid.Hollister@Sun.COM }
98910696SDavid.Hollister@Sun.COM
99010696SDavid.Hollister@Sun.COM static int
pmcs_scsa_setup_pkt(struct scsi_pkt * pkt,int (* callback)(caddr_t),caddr_t cbarg)99110696SDavid.Hollister@Sun.COM pmcs_scsa_setup_pkt(struct scsi_pkt *pkt, int (*callback)(caddr_t),
99210696SDavid.Hollister@Sun.COM caddr_t cbarg)
99310696SDavid.Hollister@Sun.COM {
99410696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(callback, cbarg));
99510696SDavid.Hollister@Sun.COM pmcs_cmd_t *sp = pkt->pkt_ha_private;
99610696SDavid.Hollister@Sun.COM
99710696SDavid.Hollister@Sun.COM bzero(sp, sizeof (pmcs_cmd_t));
99810696SDavid.Hollister@Sun.COM sp->cmd_pkt = pkt;
99910696SDavid.Hollister@Sun.COM return (0);
100010696SDavid.Hollister@Sun.COM }
100110696SDavid.Hollister@Sun.COM
100210696SDavid.Hollister@Sun.COM static void
pmcs_scsa_teardown_pkt(struct scsi_pkt * pkt)100310696SDavid.Hollister@Sun.COM pmcs_scsa_teardown_pkt(struct scsi_pkt *pkt)
100410696SDavid.Hollister@Sun.COM {
100510696SDavid.Hollister@Sun.COM pmcs_cmd_t *sp = pkt->pkt_ha_private;
100610696SDavid.Hollister@Sun.COM sp->cmd_target = NULL;
100710696SDavid.Hollister@Sun.COM sp->cmd_lun = NULL;
100810696SDavid.Hollister@Sun.COM }
100910696SDavid.Hollister@Sun.COM
101010696SDavid.Hollister@Sun.COM static int
pmcs_smp_start(struct smp_pkt * smp_pkt)101111052SChris.Horne@Sun.COM pmcs_smp_start(struct smp_pkt *smp_pkt)
101210696SDavid.Hollister@Sun.COM {
101310696SDavid.Hollister@Sun.COM struct pmcwork *pwrk;
101412462Sjesse.butler@oracle.com pmcs_iport_t *iport;
101510696SDavid.Hollister@Sun.COM const uint_t rdoff = SAS_SMP_MAX_PAYLOAD;
101610696SDavid.Hollister@Sun.COM uint32_t msg[PMCS_MSG_SIZE], *ptr, htag, status;
101710696SDavid.Hollister@Sun.COM uint64_t wwn;
101811052SChris.Horne@Sun.COM pmcs_hw_t *pwp;
101910696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr;
102010696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
102110696SDavid.Hollister@Sun.COM uint_t reqsz, rspsz, will_retry;
102210696SDavid.Hollister@Sun.COM int result;
102310696SDavid.Hollister@Sun.COM
102411052SChris.Horne@Sun.COM pwp = smp_pkt->smp_pkt_address->smp_a_hba_tran->smp_tran_hba_private;
102511052SChris.Horne@Sun.COM bcopy(smp_pkt->smp_pkt_address->smp_a_wwn, &wwn, SAS_WWN_BYTE_SIZE);
102610696SDavid.Hollister@Sun.COM
102711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
102811048SDavid.Hollister@Sun.COM "%s: starting for wwn 0x%" PRIx64, __func__, wwn);
102910696SDavid.Hollister@Sun.COM
103011052SChris.Horne@Sun.COM will_retry = smp_pkt->smp_pkt_will_retry;
103110696SDavid.Hollister@Sun.COM
103210696SDavid.Hollister@Sun.COM (void) pmcs_acquire_scratch(pwp, B_TRUE);
103311052SChris.Horne@Sun.COM reqsz = smp_pkt->smp_pkt_reqsize;
103410696SDavid.Hollister@Sun.COM if (reqsz > SAS_SMP_MAX_PAYLOAD) {
103510696SDavid.Hollister@Sun.COM reqsz = SAS_SMP_MAX_PAYLOAD;
103610696SDavid.Hollister@Sun.COM }
103711052SChris.Horne@Sun.COM (void) memcpy(pwp->scratch, smp_pkt->smp_pkt_req, reqsz);
103810696SDavid.Hollister@Sun.COM
103911052SChris.Horne@Sun.COM rspsz = smp_pkt->smp_pkt_rspsize;
104010696SDavid.Hollister@Sun.COM if (rspsz > SAS_SMP_MAX_PAYLOAD) {
104110696SDavid.Hollister@Sun.COM rspsz = SAS_SMP_MAX_PAYLOAD;
104210696SDavid.Hollister@Sun.COM }
104310696SDavid.Hollister@Sun.COM
104410696SDavid.Hollister@Sun.COM /*
104510696SDavid.Hollister@Sun.COM * The request size from the SMP driver always includes 4 bytes
104610696SDavid.Hollister@Sun.COM * for the CRC. The PMCS chip, however, doesn't want to see those
104710696SDavid.Hollister@Sun.COM * counts as part of the transfer size.
104810696SDavid.Hollister@Sun.COM */
104910696SDavid.Hollister@Sun.COM reqsz -= 4;
105010696SDavid.Hollister@Sun.COM
105110696SDavid.Hollister@Sun.COM pptr = pmcs_find_phy_by_wwn(pwp, wwn);
105210696SDavid.Hollister@Sun.COM /* PHY is now locked */
105310696SDavid.Hollister@Sun.COM if (pptr == NULL || pptr->dtype != EXPANDER) {
105410696SDavid.Hollister@Sun.COM if (pptr) {
105510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
105610696SDavid.Hollister@Sun.COM }
105710696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
105811601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
105911048SDavid.Hollister@Sun.COM "%s: could not find phy", __func__);
106011052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason = ENXIO;
106110696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
106210696SDavid.Hollister@Sun.COM }
106310696SDavid.Hollister@Sun.COM
106411601SDavid.Hollister@Sun.COM if ((pptr->iport == NULL) || !pptr->valid_device_id) {
106511601SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
106611601SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
106711601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, pptr->target,
106811601SDavid.Hollister@Sun.COM "%s: Can't reach PHY %s", __func__, pptr->path);
106911601SDavid.Hollister@Sun.COM smp_pkt->smp_pkt_reason = ENXIO;
107011601SDavid.Hollister@Sun.COM return (DDI_FAILURE);
107111601SDavid.Hollister@Sun.COM }
107211601SDavid.Hollister@Sun.COM
107310696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_WAIT, pptr);
107410696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
107510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
107610696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
107711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, NULL,
107810696SDavid.Hollister@Sun.COM "%s: could not get work structure", __func__);
107911052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EBUSY;
108010696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
108110696SDavid.Hollister@Sun.COM }
108210696SDavid.Hollister@Sun.COM
108310696SDavid.Hollister@Sun.COM pwrk->arg = msg;
108410696SDavid.Hollister@Sun.COM pwrk->dtype = EXPANDER;
108510696SDavid.Hollister@Sun.COM mutex_enter(&pwp->iqp_lock[PMCS_IQ_OTHER]);
108610696SDavid.Hollister@Sun.COM ptr = GET_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
108710696SDavid.Hollister@Sun.COM if (ptr == NULL) {
108810696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
108910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[PMCS_IQ_OTHER]);
109010696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
109110696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
109211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
109311048SDavid.Hollister@Sun.COM "%s: could not get IQ entry", __func__);
109411052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason = will_retry ? EAGAIN :EBUSY;
109510696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
109610696SDavid.Hollister@Sun.COM }
109710696SDavid.Hollister@Sun.COM msg[0] = LE_32(PMCS_HIPRI(pwp, PMCS_OQ_GENERAL, PMCIN_SMP_REQUEST));
109810696SDavid.Hollister@Sun.COM msg[1] = LE_32(pwrk->htag);
109910696SDavid.Hollister@Sun.COM msg[2] = LE_32(pptr->device_id);
110010696SDavid.Hollister@Sun.COM msg[3] = LE_32(SMP_INDIRECT_RESPONSE | SMP_INDIRECT_REQUEST);
110110696SDavid.Hollister@Sun.COM msg[8] = LE_32(DWORD0(pwp->scratch_dma));
110210696SDavid.Hollister@Sun.COM msg[9] = LE_32(DWORD1(pwp->scratch_dma));
110310696SDavid.Hollister@Sun.COM msg[10] = LE_32(reqsz);
110410696SDavid.Hollister@Sun.COM msg[11] = 0;
110510696SDavid.Hollister@Sun.COM msg[12] = LE_32(DWORD0(pwp->scratch_dma+rdoff));
110610696SDavid.Hollister@Sun.COM msg[13] = LE_32(DWORD1(pwp->scratch_dma+rdoff));
110710696SDavid.Hollister@Sun.COM msg[14] = LE_32(rspsz);
110810696SDavid.Hollister@Sun.COM msg[15] = 0;
110910696SDavid.Hollister@Sun.COM
111010696SDavid.Hollister@Sun.COM COPY_MESSAGE(ptr, msg, PMCS_MSG_SIZE);
111112462Sjesse.butler@oracle.com
111212462Sjesse.butler@oracle.com pmcs_hold_iport(pptr->iport);
111312462Sjesse.butler@oracle.com iport = pptr->iport;
111412462Sjesse.butler@oracle.com pmcs_smp_acquire(iport);
111510696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
111610696SDavid.Hollister@Sun.COM htag = pwrk->htag;
111710696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, PMCS_IQ_OTHER);
111810696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
111911052SChris.Horne@Sun.COM WAIT_FOR(pwrk, smp_pkt->smp_pkt_timeout * 1000, result);
112010696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
1121*13090Sjesse.butler@oracle.com pmcs_smp_release(iport);
1122*13090Sjesse.butler@oracle.com pmcs_rele_iport(iport);
112310696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
112410696SDavid.Hollister@Sun.COM if (result) {
112510696SDavid.Hollister@Sun.COM pmcs_timed_out(pwp, htag, __func__);
112610696SDavid.Hollister@Sun.COM if (pmcs_abort(pwp, pptr, htag, 0, 0)) {
112711601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
112810696SDavid.Hollister@Sun.COM "%s: Unable to issue SMP ABORT for htag 0x%08x",
112910696SDavid.Hollister@Sun.COM __func__, htag);
113010696SDavid.Hollister@Sun.COM } else {
113111601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
113210696SDavid.Hollister@Sun.COM "%s: Issuing SMP ABORT for htag 0x%08x",
113310696SDavid.Hollister@Sun.COM __func__, htag);
113410696SDavid.Hollister@Sun.COM }
113510696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
113610696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
113711052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason = ETIMEDOUT;
113810696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
113910696SDavid.Hollister@Sun.COM }
114010696SDavid.Hollister@Sun.COM status = LE_32(msg[2]);
114110696SDavid.Hollister@Sun.COM if (status == PMCOUT_STATUS_OVERFLOW) {
114210696SDavid.Hollister@Sun.COM status = PMCOUT_STATUS_OK;
114311052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason = EOVERFLOW;
114410696SDavid.Hollister@Sun.COM }
114510696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK) {
114610696SDavid.Hollister@Sun.COM const char *emsg = pmcs_status_str(status);
114710696SDavid.Hollister@Sun.COM if (emsg == NULL) {
114811601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
114910696SDavid.Hollister@Sun.COM "SMP operation failed (0x%x)", status);
115010696SDavid.Hollister@Sun.COM } else {
115111601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, pptr->target,
115210696SDavid.Hollister@Sun.COM "SMP operation failed (%s)", emsg);
115310696SDavid.Hollister@Sun.COM }
115410696SDavid.Hollister@Sun.COM
115510696SDavid.Hollister@Sun.COM if ((status == PMCOUT_STATUS_ERROR_HW_TIMEOUT) ||
115610696SDavid.Hollister@Sun.COM (status == PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT)) {
115711052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason =
115811052SChris.Horne@Sun.COM will_retry ? EAGAIN : ETIMEDOUT;
115910696SDavid.Hollister@Sun.COM result = DDI_FAILURE;
116010696SDavid.Hollister@Sun.COM } else if (status ==
116110696SDavid.Hollister@Sun.COM PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS) {
116210696SDavid.Hollister@Sun.COM xp = pptr->target;
116310696SDavid.Hollister@Sun.COM if (xp == NULL) {
116411052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason = EIO;
116510696SDavid.Hollister@Sun.COM result = DDI_FAILURE;
116610696SDavid.Hollister@Sun.COM goto out;
116710696SDavid.Hollister@Sun.COM }
116810696SDavid.Hollister@Sun.COM if (xp->dev_state !=
116910696SDavid.Hollister@Sun.COM PMCS_DEVICE_STATE_NON_OPERATIONAL) {
117010696SDavid.Hollister@Sun.COM xp->dev_state =
117110696SDavid.Hollister@Sun.COM PMCS_DEVICE_STATE_NON_OPERATIONAL;
117211601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, xp->phy,
117311601SDavid.Hollister@Sun.COM xp, "%s: Got _IT_NEXUS_LOSS SMP status. "
117410696SDavid.Hollister@Sun.COM "Tgt(0x%p) dev_state set to "
117510696SDavid.Hollister@Sun.COM "_NON_OPERATIONAL", __func__,
117610696SDavid.Hollister@Sun.COM (void *)xp);
117710696SDavid.Hollister@Sun.COM }
117810696SDavid.Hollister@Sun.COM /* ABORT any pending commands related to this device */
117910696SDavid.Hollister@Sun.COM if (pmcs_abort(pwp, pptr, pptr->device_id, 1, 1) != 0) {
118010696SDavid.Hollister@Sun.COM pptr->abort_pending = 1;
118111052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason = EIO;
118210696SDavid.Hollister@Sun.COM result = DDI_FAILURE;
118310696SDavid.Hollister@Sun.COM }
118410696SDavid.Hollister@Sun.COM } else {
118511052SChris.Horne@Sun.COM smp_pkt->smp_pkt_reason = will_retry ? EAGAIN : EIO;
118610696SDavid.Hollister@Sun.COM result = DDI_FAILURE;
118710696SDavid.Hollister@Sun.COM }
118810696SDavid.Hollister@Sun.COM } else {
118911052SChris.Horne@Sun.COM (void) memcpy(smp_pkt->smp_pkt_rsp,
119010696SDavid.Hollister@Sun.COM &((uint8_t *)pwp->scratch)[rdoff], rspsz);
119111052SChris.Horne@Sun.COM if (smp_pkt->smp_pkt_reason == EOVERFLOW) {
119210696SDavid.Hollister@Sun.COM result = DDI_FAILURE;
119310696SDavid.Hollister@Sun.COM } else {
119410696SDavid.Hollister@Sun.COM result = DDI_SUCCESS;
119510696SDavid.Hollister@Sun.COM }
119610696SDavid.Hollister@Sun.COM }
119710696SDavid.Hollister@Sun.COM out:
119811601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, pptr->target,
119911601SDavid.Hollister@Sun.COM "%s: done for wwn 0x%" PRIx64, __func__, wwn);
120011601SDavid.Hollister@Sun.COM
120110696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
120210696SDavid.Hollister@Sun.COM pmcs_release_scratch(pwp);
120310696SDavid.Hollister@Sun.COM return (result);
120410696SDavid.Hollister@Sun.COM }
120510696SDavid.Hollister@Sun.COM
120610696SDavid.Hollister@Sun.COM static int
pmcs_smp_init(dev_info_t * self,dev_info_t * child,smp_hba_tran_t * tran,smp_device_t * smp_sd)120710696SDavid.Hollister@Sun.COM pmcs_smp_init(dev_info_t *self, dev_info_t *child,
120811052SChris.Horne@Sun.COM smp_hba_tran_t *tran, smp_device_t *smp_sd)
120910696SDavid.Hollister@Sun.COM {
121011052SChris.Horne@Sun.COM _NOTE(ARGUNUSED(tran, smp_sd));
121110696SDavid.Hollister@Sun.COM pmcs_iport_t *iport;
121210696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp;
121310696SDavid.Hollister@Sun.COM pmcs_xscsi_t *tgt;
121410696SDavid.Hollister@Sun.COM pmcs_phy_t *phy, *pphy;
121510696SDavid.Hollister@Sun.COM uint64_t wwn;
121610696SDavid.Hollister@Sun.COM char *addr, *tgt_port;
121710696SDavid.Hollister@Sun.COM int ua_form = 1;
121810696SDavid.Hollister@Sun.COM
121910696SDavid.Hollister@Sun.COM iport = ddi_get_soft_state(pmcs_iport_softstate,
122010696SDavid.Hollister@Sun.COM ddi_get_instance(self));
122110696SDavid.Hollister@Sun.COM ASSERT(iport);
122210696SDavid.Hollister@Sun.COM if (iport == NULL)
122310696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
122410696SDavid.Hollister@Sun.COM pwp = iport->pwp;
122510696SDavid.Hollister@Sun.COM ASSERT(pwp);
122610696SDavid.Hollister@Sun.COM if (pwp == NULL)
122710696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
122810696SDavid.Hollister@Sun.COM
122910696SDavid.Hollister@Sun.COM /* Get "target-port" prop from devinfo node */
123010696SDavid.Hollister@Sun.COM if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
123110696SDavid.Hollister@Sun.COM DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
123210696SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
123311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
123411048SDavid.Hollister@Sun.COM "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
123510696SDavid.Hollister@Sun.COM /* Dont fail _smp_init() because we couldnt get/set a prop */
123610696SDavid.Hollister@Sun.COM return (DDI_SUCCESS);
123710696SDavid.Hollister@Sun.COM }
123810696SDavid.Hollister@Sun.COM
123910696SDavid.Hollister@Sun.COM /*
124010696SDavid.Hollister@Sun.COM * Validate that this tran_tgt_init is for an active iport.
124110696SDavid.Hollister@Sun.COM */
124210696SDavid.Hollister@Sun.COM if (iport->ua_state == UA_INACTIVE) {
124311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
124411048SDavid.Hollister@Sun.COM "%s: Init on inactive iport for '%s'", __func__, tgt_port);
124510696SDavid.Hollister@Sun.COM ddi_prop_free(tgt_port);
124610696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
124710696SDavid.Hollister@Sun.COM }
124810696SDavid.Hollister@Sun.COM
124910696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
125010696SDavid.Hollister@Sun.COM
125110696SDavid.Hollister@Sun.COM /* Retrieve softstate using unit-address */
125211692SJesse.Butler@Sun.COM tgt = pmcs_get_target(iport, tgt_port, B_TRUE);
125310696SDavid.Hollister@Sun.COM if (tgt == NULL) {
125411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
125511048SDavid.Hollister@Sun.COM "%s: tgt softstate not found", __func__);
125610696SDavid.Hollister@Sun.COM ddi_prop_free(tgt_port);
125710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
125810696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
125910696SDavid.Hollister@Sun.COM }
126010696SDavid.Hollister@Sun.COM
126111501SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)",
126211501SDavid.Hollister@Sun.COM __func__, ddi_get_name(child), tgt_port);
126311501SDavid.Hollister@Sun.COM
126411501SDavid.Hollister@Sun.COM mutex_enter(&tgt->statlock);
126510696SDavid.Hollister@Sun.COM phy = tgt->phy;
126610696SDavid.Hollister@Sun.COM ASSERT(mutex_owned(&phy->phy_lock));
126710696SDavid.Hollister@Sun.COM
126810696SDavid.Hollister@Sun.COM if (IS_ROOT_PHY(phy)) {
126910696SDavid.Hollister@Sun.COM /* Expander attached to HBA - don't ref_count it */
127010696SDavid.Hollister@Sun.COM wwn = pwp->sas_wwns[0];
127110696SDavid.Hollister@Sun.COM } else {
127210696SDavid.Hollister@Sun.COM pmcs_inc_phy_ref_count(phy);
127310696SDavid.Hollister@Sun.COM
127410696SDavid.Hollister@Sun.COM /*
127510696SDavid.Hollister@Sun.COM * Parent (in topology) is also an expander
127610696SDavid.Hollister@Sun.COM * Now that we've increased the ref count on phy, it's OK
127710696SDavid.Hollister@Sun.COM * to drop the lock so we can acquire the parent's lock.
127810696SDavid.Hollister@Sun.COM */
127910696SDavid.Hollister@Sun.COM pphy = phy->parent;
128011501SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
128110696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phy);
128210696SDavid.Hollister@Sun.COM pmcs_lock_phy(pphy);
128310696SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(pphy->sas_address);
128410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pphy);
128510696SDavid.Hollister@Sun.COM pmcs_lock_phy(phy);
128611501SDavid.Hollister@Sun.COM mutex_enter(&tgt->statlock);
128710696SDavid.Hollister@Sun.COM }
128810696SDavid.Hollister@Sun.COM
128910696SDavid.Hollister@Sun.COM /*
129010696SDavid.Hollister@Sun.COM * If this is the 1st smp_init, add this to our list.
129110696SDavid.Hollister@Sun.COM */
129210696SDavid.Hollister@Sun.COM if (tgt->target_num == PMCS_INVALID_TARGET_NUM) {
129310696SDavid.Hollister@Sun.COM int target;
129410696SDavid.Hollister@Sun.COM for (target = 0; target < pwp->max_dev; target++) {
129510696SDavid.Hollister@Sun.COM if (pwp->targets[target] != NULL) {
129610696SDavid.Hollister@Sun.COM continue;
129710696SDavid.Hollister@Sun.COM }
129810696SDavid.Hollister@Sun.COM
129910696SDavid.Hollister@Sun.COM pwp->targets[target] = tgt;
130010696SDavid.Hollister@Sun.COM tgt->target_num = (uint16_t)target;
130110696SDavid.Hollister@Sun.COM tgt->assigned = 1;
130210696SDavid.Hollister@Sun.COM tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
130310696SDavid.Hollister@Sun.COM break;
130410696SDavid.Hollister@Sun.COM }
130510696SDavid.Hollister@Sun.COM
130610696SDavid.Hollister@Sun.COM if (target == pwp->max_dev) {
130711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL,
130810696SDavid.Hollister@Sun.COM "Target list full.");
130910696SDavid.Hollister@Sun.COM goto smp_init_fail;
131010696SDavid.Hollister@Sun.COM }
131110696SDavid.Hollister@Sun.COM }
131210696SDavid.Hollister@Sun.COM
131310696SDavid.Hollister@Sun.COM if (!pmcs_assign_device(pwp, tgt)) {
131410696SDavid.Hollister@Sun.COM pwp->targets[tgt->target_num] = NULL;
131511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
131610696SDavid.Hollister@Sun.COM "%s: pmcs_assign_device failed for target 0x%p",
131710696SDavid.Hollister@Sun.COM __func__, (void *)tgt);
131810696SDavid.Hollister@Sun.COM goto smp_init_fail;
131910696SDavid.Hollister@Sun.COM }
132010696SDavid.Hollister@Sun.COM
132111307SDavid.Hollister@Sun.COM /*
132211307SDavid.Hollister@Sun.COM * Update the attached port and target port pm properties
132311307SDavid.Hollister@Sun.COM */
132411307SDavid.Hollister@Sun.COM tgt->smpd = smp_sd;
132511307SDavid.Hollister@Sun.COM
132610696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phy);
132710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
132810696SDavid.Hollister@Sun.COM
132910696SDavid.Hollister@Sun.COM tgt->ref_count++;
133010696SDavid.Hollister@Sun.COM tgt->dtype = phy->dtype;
133111501SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
133211501SDavid.Hollister@Sun.COM
133311501SDavid.Hollister@Sun.COM pmcs_update_phy_pm_props(phy, 0, 0, B_TRUE);
133410696SDavid.Hollister@Sun.COM
133510696SDavid.Hollister@Sun.COM addr = scsi_wwn_to_wwnstr(wwn, ua_form, NULL);
133611307SDavid.Hollister@Sun.COM if (smp_device_prop_update_string(smp_sd, SCSI_ADDR_PROP_ATTACHED_PORT,
133711307SDavid.Hollister@Sun.COM addr) != DDI_SUCCESS) {
133811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to set "
133911048SDavid.Hollister@Sun.COM "prop ("SCSI_ADDR_PROP_ATTACHED_PORT")", __func__);
134010696SDavid.Hollister@Sun.COM }
134110696SDavid.Hollister@Sun.COM (void) scsi_free_wwnstr(addr);
134210696SDavid.Hollister@Sun.COM ddi_prop_free(tgt_port);
134310696SDavid.Hollister@Sun.COM return (DDI_SUCCESS);
134410696SDavid.Hollister@Sun.COM
134510696SDavid.Hollister@Sun.COM smp_init_fail:
134610696SDavid.Hollister@Sun.COM tgt->phy = NULL;
134710696SDavid.Hollister@Sun.COM tgt->target_num = PMCS_INVALID_TARGET_NUM;
134810696SDavid.Hollister@Sun.COM phy->target = NULL;
134910696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(phy)) {
135010696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(phy);
135110696SDavid.Hollister@Sun.COM }
135211501SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
135310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phy);
135410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
135510696SDavid.Hollister@Sun.COM ddi_soft_state_bystr_free(iport->tgt_sstate, tgt->unit_address);
135610696SDavid.Hollister@Sun.COM ddi_prop_free(tgt_port);
135710696SDavid.Hollister@Sun.COM return (DDI_FAILURE);
135810696SDavid.Hollister@Sun.COM }
135910696SDavid.Hollister@Sun.COM
136010696SDavid.Hollister@Sun.COM static void
pmcs_smp_free(dev_info_t * self,dev_info_t * child,smp_hba_tran_t * tran,smp_device_t * smp)136110696SDavid.Hollister@Sun.COM pmcs_smp_free(dev_info_t *self, dev_info_t *child,
136211052SChris.Horne@Sun.COM smp_hba_tran_t *tran, smp_device_t *smp)
136310696SDavid.Hollister@Sun.COM {
136410696SDavid.Hollister@Sun.COM _NOTE(ARGUNUSED(tran, smp));
136510696SDavid.Hollister@Sun.COM pmcs_iport_t *iport;
136610696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp;
136710696SDavid.Hollister@Sun.COM pmcs_xscsi_t *tgt;
136812385Ssrikanth.suravajhala@oracle.com pmcs_phy_t *phyp;
136910696SDavid.Hollister@Sun.COM char *tgt_port;
137010696SDavid.Hollister@Sun.COM
137110696SDavid.Hollister@Sun.COM iport = ddi_get_soft_state(pmcs_iport_softstate,
137210696SDavid.Hollister@Sun.COM ddi_get_instance(self));
137310696SDavid.Hollister@Sun.COM ASSERT(iport);
137410696SDavid.Hollister@Sun.COM if (iport == NULL)
137510696SDavid.Hollister@Sun.COM return;
137610696SDavid.Hollister@Sun.COM
137710696SDavid.Hollister@Sun.COM pwp = iport->pwp;
137810696SDavid.Hollister@Sun.COM if (pwp == NULL)
137910696SDavid.Hollister@Sun.COM return;
138010696SDavid.Hollister@Sun.COM ASSERT(pwp);
138110696SDavid.Hollister@Sun.COM
138210696SDavid.Hollister@Sun.COM /* Get "target-port" prop from devinfo node */
138310696SDavid.Hollister@Sun.COM if (ddi_prop_lookup_string(DDI_DEV_T_ANY, child,
138410696SDavid.Hollister@Sun.COM DDI_PROP_DONTPASS | DDI_PROP_NOTPROM,
138510696SDavid.Hollister@Sun.COM SCSI_ADDR_PROP_TARGET_PORT, &tgt_port) != DDI_SUCCESS) {
138611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s: Failed to "
138711048SDavid.Hollister@Sun.COM "lookup prop ("SCSI_ADDR_PROP_TARGET_PORT")", __func__);
138810696SDavid.Hollister@Sun.COM return;
138910696SDavid.Hollister@Sun.COM }
139011601SDavid.Hollister@Sun.COM
139110696SDavid.Hollister@Sun.COM /* Retrieve softstate using unit-address */
139211601SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
139310696SDavid.Hollister@Sun.COM tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
139411601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt, "%s: %s (%s)", __func__,
139511601SDavid.Hollister@Sun.COM ddi_get_name(child), tgt_port);
139610696SDavid.Hollister@Sun.COM ddi_prop_free(tgt_port);
139710696SDavid.Hollister@Sun.COM
139810696SDavid.Hollister@Sun.COM if (tgt == NULL) {
139911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
140011048SDavid.Hollister@Sun.COM "%s: tgt softstate not found", __func__);
140111601SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
140210696SDavid.Hollister@Sun.COM return;
140310696SDavid.Hollister@Sun.COM }
140410696SDavid.Hollister@Sun.COM
140512385Ssrikanth.suravajhala@oracle.com phyp = tgt->phy;
140612385Ssrikanth.suravajhala@oracle.com if (phyp) {
140712385Ssrikanth.suravajhala@oracle.com mutex_enter(&phyp->phy_lock);
140812385Ssrikanth.suravajhala@oracle.com if (!IS_ROOT_PHY(phyp)) {
140912385Ssrikanth.suravajhala@oracle.com pmcs_dec_phy_ref_count(phyp);
141010696SDavid.Hollister@Sun.COM }
141110696SDavid.Hollister@Sun.COM }
141212385Ssrikanth.suravajhala@oracle.com mutex_enter(&tgt->statlock);
141310696SDavid.Hollister@Sun.COM
141410696SDavid.Hollister@Sun.COM if (--tgt->ref_count == 0) {
141510696SDavid.Hollister@Sun.COM /*
141610696SDavid.Hollister@Sun.COM * Remove this target from our list. The softstate
141710696SDavid.Hollister@Sun.COM * will remain, and the device will remain registered
141810696SDavid.Hollister@Sun.COM * with the hardware unless/until we're told that the
141910696SDavid.Hollister@Sun.COM * device physically went away.
142010696SDavid.Hollister@Sun.COM */
142111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, tgt,
142210696SDavid.Hollister@Sun.COM "Removing target 0x%p (vtgt %d) from target list",
142310696SDavid.Hollister@Sun.COM (void *)tgt, tgt->target_num);
142410696SDavid.Hollister@Sun.COM pwp->targets[tgt->target_num] = NULL;
142510696SDavid.Hollister@Sun.COM tgt->target_num = PMCS_INVALID_TARGET_NUM;
142612937Sjesse.butler@oracle.com /* If the PHY has a pointer to this target, clear it */
142712937Sjesse.butler@oracle.com if (phyp && (phyp->target == tgt)) {
142812385Ssrikanth.suravajhala@oracle.com phyp->target = NULL;
142911692SJesse.Butler@Sun.COM }
143012385Ssrikanth.suravajhala@oracle.com tgt->phy = NULL;
143111601SDavid.Hollister@Sun.COM pmcs_destroy_target(tgt);
143211601SDavid.Hollister@Sun.COM } else {
143311601SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
143410696SDavid.Hollister@Sun.COM }
143510696SDavid.Hollister@Sun.COM
143612385Ssrikanth.suravajhala@oracle.com if (phyp) {
143712385Ssrikanth.suravajhala@oracle.com mutex_exit(&phyp->phy_lock);
143812385Ssrikanth.suravajhala@oracle.com }
143910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
144010696SDavid.Hollister@Sun.COM }
144110696SDavid.Hollister@Sun.COM
144210696SDavid.Hollister@Sun.COM static int
pmcs_scsi_quiesce(dev_info_t * dip)144310696SDavid.Hollister@Sun.COM pmcs_scsi_quiesce(dev_info_t *dip)
144410696SDavid.Hollister@Sun.COM {
144510696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp;
144610696SDavid.Hollister@Sun.COM int totactive = -1;
144710696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
144810696SDavid.Hollister@Sun.COM uint16_t target;
144910696SDavid.Hollister@Sun.COM
145010696SDavid.Hollister@Sun.COM if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
145110696SDavid.Hollister@Sun.COM return (0); /* iport */
145210696SDavid.Hollister@Sun.COM
145310696SDavid.Hollister@Sun.COM pwp = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
145410696SDavid.Hollister@Sun.COM if (pwp == NULL) {
145510696SDavid.Hollister@Sun.COM return (-1);
145610696SDavid.Hollister@Sun.COM }
145710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
145810696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
145910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
146010696SDavid.Hollister@Sun.COM return (-1);
146110696SDavid.Hollister@Sun.COM }
146210696SDavid.Hollister@Sun.COM
146311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
146411692SJesse.Butler@Sun.COM pwp->quiesced = pwp->blocked = 1;
146510696SDavid.Hollister@Sun.COM while (totactive) {
146610696SDavid.Hollister@Sun.COM totactive = 0;
146710696SDavid.Hollister@Sun.COM for (target = 0; target < pwp->max_dev; target++) {
146810696SDavid.Hollister@Sun.COM xp = pwp->targets[target];
146910696SDavid.Hollister@Sun.COM if (xp == NULL) {
147010696SDavid.Hollister@Sun.COM continue;
147110696SDavid.Hollister@Sun.COM }
147210696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
147310696SDavid.Hollister@Sun.COM if (xp->actv_cnt) {
147410696SDavid.Hollister@Sun.COM totactive += xp->actv_cnt;
147510696SDavid.Hollister@Sun.COM xp->draining = 1;
147610696SDavid.Hollister@Sun.COM }
147710696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
147810696SDavid.Hollister@Sun.COM }
147910696SDavid.Hollister@Sun.COM if (totactive) {
148010696SDavid.Hollister@Sun.COM cv_wait(&pwp->drain_cv, &pwp->lock);
148110696SDavid.Hollister@Sun.COM }
148210696SDavid.Hollister@Sun.COM /*
148310696SDavid.Hollister@Sun.COM * The pwp->blocked may have been reset. e.g a SCSI bus reset
148410696SDavid.Hollister@Sun.COM */
148510696SDavid.Hollister@Sun.COM pwp->blocked = 1;
148610696SDavid.Hollister@Sun.COM }
148710696SDavid.Hollister@Sun.COM
148810696SDavid.Hollister@Sun.COM for (target = 0; target < pwp->max_dev; target++) {
148910696SDavid.Hollister@Sun.COM xp = pwp->targets[target];
149010696SDavid.Hollister@Sun.COM if (xp == NULL) {
149110696SDavid.Hollister@Sun.COM continue;
149210696SDavid.Hollister@Sun.COM }
149310696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
149410696SDavid.Hollister@Sun.COM xp->draining = 0;
149510696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
149610696SDavid.Hollister@Sun.COM }
149710696SDavid.Hollister@Sun.COM
149810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
149910696SDavid.Hollister@Sun.COM if (totactive == 0) {
150011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
150111048SDavid.Hollister@Sun.COM "%s drain complete", __func__);
150210696SDavid.Hollister@Sun.COM }
150310696SDavid.Hollister@Sun.COM return (0);
150410696SDavid.Hollister@Sun.COM }
150510696SDavid.Hollister@Sun.COM
150610696SDavid.Hollister@Sun.COM static int
pmcs_scsi_unquiesce(dev_info_t * dip)150710696SDavid.Hollister@Sun.COM pmcs_scsi_unquiesce(dev_info_t *dip)
150810696SDavid.Hollister@Sun.COM {
150910696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp;
151010696SDavid.Hollister@Sun.COM
151110696SDavid.Hollister@Sun.COM if (ddi_get_soft_state(pmcs_iport_softstate, ddi_get_instance(dip)))
151210696SDavid.Hollister@Sun.COM return (0); /* iport */
151310696SDavid.Hollister@Sun.COM
151410696SDavid.Hollister@Sun.COM pwp = ddi_get_soft_state(pmcs_softc_state, ddi_get_instance(dip));
151510696SDavid.Hollister@Sun.COM if (pwp == NULL) {
151610696SDavid.Hollister@Sun.COM return (-1);
151710696SDavid.Hollister@Sun.COM }
151810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
151910696SDavid.Hollister@Sun.COM if (pwp->state != STATE_RUNNING) {
152010696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
152110696SDavid.Hollister@Sun.COM return (-1);
152210696SDavid.Hollister@Sun.COM }
152311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, "%s called", __func__);
152411692SJesse.Butler@Sun.COM pwp->blocked = pwp->quiesced = 0;
152510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
152610696SDavid.Hollister@Sun.COM
152710696SDavid.Hollister@Sun.COM /*
152810696SDavid.Hollister@Sun.COM * Run all pending commands.
152910696SDavid.Hollister@Sun.COM */
153010696SDavid.Hollister@Sun.COM pmcs_scsa_wq_run(pwp);
153110696SDavid.Hollister@Sun.COM
153210696SDavid.Hollister@Sun.COM /*
153310696SDavid.Hollister@Sun.COM * Complete all completed commands.
153410696SDavid.Hollister@Sun.COM * This also unlocks us.
153510696SDavid.Hollister@Sun.COM */
153610696SDavid.Hollister@Sun.COM PMCS_CQ_RUN(pwp);
153710696SDavid.Hollister@Sun.COM return (0);
153810696SDavid.Hollister@Sun.COM }
153910696SDavid.Hollister@Sun.COM
154010696SDavid.Hollister@Sun.COM /*
154110696SDavid.Hollister@Sun.COM * Start commands for a particular device
154210696SDavid.Hollister@Sun.COM * If the actual start of a command fails, return B_FALSE. Any other result
154310696SDavid.Hollister@Sun.COM * is a B_TRUE return.
154410696SDavid.Hollister@Sun.COM */
154510696SDavid.Hollister@Sun.COM boolean_t
pmcs_scsa_wq_run_one(pmcs_hw_t * pwp,pmcs_xscsi_t * xp)154610696SDavid.Hollister@Sun.COM pmcs_scsa_wq_run_one(pmcs_hw_t *pwp, pmcs_xscsi_t *xp)
154710696SDavid.Hollister@Sun.COM {
154810696SDavid.Hollister@Sun.COM pmcs_cmd_t *sp;
154910696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp;
155010696SDavid.Hollister@Sun.COM pmcwork_t *pwrk;
155110696SDavid.Hollister@Sun.COM boolean_t run_one, blocked;
155210696SDavid.Hollister@Sun.COM int rval;
155310696SDavid.Hollister@Sun.COM
155410696SDavid.Hollister@Sun.COM /*
155510696SDavid.Hollister@Sun.COM * First, check to see if we're blocked or resource limited
155610696SDavid.Hollister@Sun.COM */
155710696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
155810696SDavid.Hollister@Sun.COM blocked = pwp->blocked;
155910696SDavid.Hollister@Sun.COM /*
156010696SDavid.Hollister@Sun.COM * If resource_limited is set, we're resource constrained and
156110696SDavid.Hollister@Sun.COM * we will run only one work request for this target.
156210696SDavid.Hollister@Sun.COM */
156310696SDavid.Hollister@Sun.COM run_one = pwp->resource_limited;
156410696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
156510696SDavid.Hollister@Sun.COM
156610696SDavid.Hollister@Sun.COM if (blocked) {
156710696SDavid.Hollister@Sun.COM /* Queues will get restarted when we get unblocked */
156810696SDavid.Hollister@Sun.COM return (B_TRUE);
156910696SDavid.Hollister@Sun.COM }
157010696SDavid.Hollister@Sun.COM
157110696SDavid.Hollister@Sun.COM /*
157210696SDavid.Hollister@Sun.COM * Might as well verify the queue is not empty before moving on
157310696SDavid.Hollister@Sun.COM */
157410696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
157510696SDavid.Hollister@Sun.COM if (STAILQ_EMPTY(&xp->wq)) {
157610696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
157710696SDavid.Hollister@Sun.COM return (B_TRUE);
157810696SDavid.Hollister@Sun.COM }
157910696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
158010696SDavid.Hollister@Sun.COM
158110696SDavid.Hollister@Sun.COM /*
158210696SDavid.Hollister@Sun.COM * If we're draining or resetting, just reschedule work queue and bail.
158310696SDavid.Hollister@Sun.COM */
158410696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
158510696SDavid.Hollister@Sun.COM if (xp->draining || xp->resetting || xp->special_running ||
158610696SDavid.Hollister@Sun.COM xp->special_needed) {
158710696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
158810696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
158910696SDavid.Hollister@Sun.COM return (B_TRUE);
159010696SDavid.Hollister@Sun.COM }
159110696SDavid.Hollister@Sun.COM
159210696SDavid.Hollister@Sun.COM /*
159310755SJesse.Butler@Sun.COM * Next, check to see if the target is gone.
159410696SDavid.Hollister@Sun.COM */
159510755SJesse.Butler@Sun.COM if (xp->dev_gone) {
159611048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
159710755SJesse.Butler@Sun.COM "%s: Flushing wait queue for dead tgt 0x%p", __func__,
159810696SDavid.Hollister@Sun.COM (void *)xp);
159910696SDavid.Hollister@Sun.COM pmcs_flush_target_queues(pwp, xp, PMCS_TGT_WAIT_QUEUE);
160010696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
160110696SDavid.Hollister@Sun.COM return (B_TRUE);
160210696SDavid.Hollister@Sun.COM }
160310696SDavid.Hollister@Sun.COM
160410696SDavid.Hollister@Sun.COM /*
160510696SDavid.Hollister@Sun.COM * Increment the PHY's ref_count now so we know it won't go away
160610696SDavid.Hollister@Sun.COM * after we drop the target lock. Drop it before returning. If the
160710696SDavid.Hollister@Sun.COM * PHY dies, the commands we attempt to send will fail, but at least
160810696SDavid.Hollister@Sun.COM * we know we have a real PHY pointer.
160910696SDavid.Hollister@Sun.COM */
161010696SDavid.Hollister@Sun.COM phyp = xp->phy;
161110696SDavid.Hollister@Sun.COM pmcs_inc_phy_ref_count(phyp);
161210696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
161310696SDavid.Hollister@Sun.COM
161410696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
161510696SDavid.Hollister@Sun.COM while ((sp = STAILQ_FIRST(&xp->wq)) != NULL) {
161610696SDavid.Hollister@Sun.COM pwrk = pmcs_gwork(pwp, PMCS_TAG_TYPE_CBACK, phyp);
161710696SDavid.Hollister@Sun.COM if (pwrk == NULL) {
161811847SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
161911847SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
162011847SDavid.Hollister@Sun.COM if (pwp->resource_limited == 0) {
162111847SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL,
162211847SDavid.Hollister@Sun.COM "%s: out of work structures", __func__);
162311847SDavid.Hollister@Sun.COM }
162411847SDavid.Hollister@Sun.COM pwp->resource_limited = 1;
162510696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
162611847SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
162711847SDavid.Hollister@Sun.COM return (B_FALSE);
162810696SDavid.Hollister@Sun.COM }
162910696SDavid.Hollister@Sun.COM STAILQ_REMOVE_HEAD(&xp->wq, cmd_next);
163010696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
163110696SDavid.Hollister@Sun.COM
163210696SDavid.Hollister@Sun.COM pwrk->xp = xp;
163310696SDavid.Hollister@Sun.COM pwrk->arg = sp;
163412988Ssrikanth.suravajhala@oracle.com pwrk->timer = 0;
163510696SDavid.Hollister@Sun.COM sp->cmd_tag = pwrk->htag;
163610696SDavid.Hollister@Sun.COM
163710696SDavid.Hollister@Sun.COM pwrk->dtype = xp->dtype;
163810696SDavid.Hollister@Sun.COM
163910696SDavid.Hollister@Sun.COM if (xp->dtype == SAS) {
164010696SDavid.Hollister@Sun.COM pwrk->ptr = (void *) pmcs_SAS_done;
164110696SDavid.Hollister@Sun.COM if ((rval = pmcs_SAS_run(sp, pwrk)) != 0) {
164212977Sjesse.butler@oracle.com if (rval != PMCS_WQ_RUN_FAIL_RES_CMP) {
164312977Sjesse.butler@oracle.com sp->cmd_tag = NULL;
164412977Sjesse.butler@oracle.com }
164510696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(phyp);
164610696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
164710696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
164810696SDavid.Hollister@Sun.COM if (rval == PMCS_WQ_RUN_FAIL_RES) {
164910696SDavid.Hollister@Sun.COM return (B_FALSE);
165010696SDavid.Hollister@Sun.COM } else {
165110696SDavid.Hollister@Sun.COM return (B_TRUE);
165210696SDavid.Hollister@Sun.COM }
165310696SDavid.Hollister@Sun.COM }
165410696SDavid.Hollister@Sun.COM } else {
165510696SDavid.Hollister@Sun.COM ASSERT(xp->dtype == SATA);
165610696SDavid.Hollister@Sun.COM pwrk->ptr = (void *) pmcs_SATA_done;
165710696SDavid.Hollister@Sun.COM if ((rval = pmcs_SATA_run(sp, pwrk)) != 0) {
165810696SDavid.Hollister@Sun.COM sp->cmd_tag = NULL;
165910696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(phyp);
166010696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
166110696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
166210696SDavid.Hollister@Sun.COM if (rval == PMCS_WQ_RUN_FAIL_RES) {
166310696SDavid.Hollister@Sun.COM return (B_FALSE);
166410696SDavid.Hollister@Sun.COM } else {
166510696SDavid.Hollister@Sun.COM return (B_TRUE);
166610696SDavid.Hollister@Sun.COM }
166710696SDavid.Hollister@Sun.COM }
166810696SDavid.Hollister@Sun.COM }
166910696SDavid.Hollister@Sun.COM
167010696SDavid.Hollister@Sun.COM if (run_one) {
167110696SDavid.Hollister@Sun.COM goto wq_out;
167210696SDavid.Hollister@Sun.COM }
167310696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
167410696SDavid.Hollister@Sun.COM }
167510696SDavid.Hollister@Sun.COM
167610696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
167710696SDavid.Hollister@Sun.COM
167810696SDavid.Hollister@Sun.COM wq_out:
167910696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(phyp);
168010696SDavid.Hollister@Sun.COM return (B_TRUE);
168110696SDavid.Hollister@Sun.COM }
168210696SDavid.Hollister@Sun.COM
168310696SDavid.Hollister@Sun.COM /*
168410696SDavid.Hollister@Sun.COM * Start commands for all devices.
168510696SDavid.Hollister@Sun.COM */
168610696SDavid.Hollister@Sun.COM void
pmcs_scsa_wq_run(pmcs_hw_t * pwp)168710696SDavid.Hollister@Sun.COM pmcs_scsa_wq_run(pmcs_hw_t *pwp)
168810696SDavid.Hollister@Sun.COM {
168910696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
169010696SDavid.Hollister@Sun.COM uint16_t target_start, target;
169110696SDavid.Hollister@Sun.COM boolean_t rval = B_TRUE;
169210696SDavid.Hollister@Sun.COM
169310696SDavid.Hollister@Sun.COM mutex_enter(&pwp->lock);
169410696SDavid.Hollister@Sun.COM target_start = pwp->last_wq_dev;
169510696SDavid.Hollister@Sun.COM target = target_start;
169610696SDavid.Hollister@Sun.COM
169710696SDavid.Hollister@Sun.COM do {
169810696SDavid.Hollister@Sun.COM xp = pwp->targets[target];
169911635SRamana.Srikanth@Sun.COM if ((xp == NULL) || (STAILQ_EMPTY(&xp->wq))) {
170010696SDavid.Hollister@Sun.COM if (++target == pwp->max_dev) {
170110696SDavid.Hollister@Sun.COM target = 0;
170210696SDavid.Hollister@Sun.COM }
170310696SDavid.Hollister@Sun.COM continue;
170410696SDavid.Hollister@Sun.COM }
170510696SDavid.Hollister@Sun.COM
170610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
170710696SDavid.Hollister@Sun.COM rval = pmcs_scsa_wq_run_one(pwp, xp);
170811635SRamana.Srikanth@Sun.COM mutex_enter(&pwp->lock);
170911635SRamana.Srikanth@Sun.COM
171010696SDavid.Hollister@Sun.COM if (rval == B_FALSE) {
171110696SDavid.Hollister@Sun.COM break;
171210696SDavid.Hollister@Sun.COM }
171311635SRamana.Srikanth@Sun.COM
171410696SDavid.Hollister@Sun.COM if (++target == pwp->max_dev) {
171510696SDavid.Hollister@Sun.COM target = 0;
171610696SDavid.Hollister@Sun.COM }
171710696SDavid.Hollister@Sun.COM } while (target != target_start);
171810696SDavid.Hollister@Sun.COM
171910696SDavid.Hollister@Sun.COM if (rval) {
172011847SDavid.Hollister@Sun.COM /*
172111847SDavid.Hollister@Sun.COM * If we were resource limited, but apparently are not now,
172211847SDavid.Hollister@Sun.COM * reschedule the work queues anyway.
172311847SDavid.Hollister@Sun.COM */
172411847SDavid.Hollister@Sun.COM if (pwp->resource_limited) {
172511847SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
172611847SDavid.Hollister@Sun.COM }
172710696SDavid.Hollister@Sun.COM pwp->resource_limited = 0; /* Not resource-constrained */
172810696SDavid.Hollister@Sun.COM } else {
172911847SDavid.Hollister@Sun.COM /*
173011847SDavid.Hollister@Sun.COM * Give everybody a chance, and reschedule to run the queues
173111847SDavid.Hollister@Sun.COM * again as long as we're limited.
173211847SDavid.Hollister@Sun.COM */
173311847SDavid.Hollister@Sun.COM pwp->resource_limited = 1;
173411847SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
173510696SDavid.Hollister@Sun.COM }
173610696SDavid.Hollister@Sun.COM
173710696SDavid.Hollister@Sun.COM pwp->last_wq_dev = target;
173810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->lock);
173910696SDavid.Hollister@Sun.COM }
174010696SDavid.Hollister@Sun.COM
174110696SDavid.Hollister@Sun.COM /*
174210696SDavid.Hollister@Sun.COM * Pull the completion queue, drop the lock and complete all elements.
174310696SDavid.Hollister@Sun.COM */
174410696SDavid.Hollister@Sun.COM
174510696SDavid.Hollister@Sun.COM void
pmcs_scsa_cq_run(void * arg)174610696SDavid.Hollister@Sun.COM pmcs_scsa_cq_run(void *arg)
174710696SDavid.Hollister@Sun.COM {
174810696SDavid.Hollister@Sun.COM pmcs_cq_thr_info_t *cqti = (pmcs_cq_thr_info_t *)arg;
174910696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = cqti->cq_pwp;
175010696SDavid.Hollister@Sun.COM pmcs_cmd_t *sp, *nxt;
175110696SDavid.Hollister@Sun.COM struct scsi_pkt *pkt;
175211347SRamana.Srikanth@Sun.COM pmcs_xscsi_t *tgt;
175310696SDavid.Hollister@Sun.COM pmcs_iocomp_cb_t *ioccb, *ioccb_next;
175410696SDavid.Hollister@Sun.COM pmcs_cb_t callback;
175510696SDavid.Hollister@Sun.COM
175610696SDavid.Hollister@Sun.COM DTRACE_PROBE1(pmcs__scsa__cq__run__start, pmcs_cq_thr_info_t *, cqti);
175710696SDavid.Hollister@Sun.COM
175810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
175910696SDavid.Hollister@Sun.COM
176010696SDavid.Hollister@Sun.COM while (!pwp->cq_info.cq_stop) {
176110696SDavid.Hollister@Sun.COM /*
176210696SDavid.Hollister@Sun.COM * First, check the I/O completion callback queue.
176310696SDavid.Hollister@Sun.COM */
176410696SDavid.Hollister@Sun.COM ioccb = pwp->iocomp_cb_head;
176510696SDavid.Hollister@Sun.COM pwp->iocomp_cb_head = NULL;
176610696SDavid.Hollister@Sun.COM pwp->iocomp_cb_tail = NULL;
176710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
176810696SDavid.Hollister@Sun.COM
176910696SDavid.Hollister@Sun.COM while (ioccb) {
177010696SDavid.Hollister@Sun.COM /*
177110696SDavid.Hollister@Sun.COM * Grab the lock on the work structure. The callback
177210696SDavid.Hollister@Sun.COM * routine is responsible for clearing it.
177310696SDavid.Hollister@Sun.COM */
177410696SDavid.Hollister@Sun.COM mutex_enter(&ioccb->pwrk->lock);
177510696SDavid.Hollister@Sun.COM ioccb_next = ioccb->next;
177610696SDavid.Hollister@Sun.COM callback = (pmcs_cb_t)ioccb->pwrk->ptr;
177710696SDavid.Hollister@Sun.COM (*callback)(pwp, ioccb->pwrk,
177810696SDavid.Hollister@Sun.COM (uint32_t *)((void *)ioccb->iomb));
177910696SDavid.Hollister@Sun.COM kmem_cache_free(pwp->iocomp_cb_cache, ioccb);
178010696SDavid.Hollister@Sun.COM ioccb = ioccb_next;
178110696SDavid.Hollister@Sun.COM }
178210696SDavid.Hollister@Sun.COM
178310696SDavid.Hollister@Sun.COM /*
178410696SDavid.Hollister@Sun.COM * Next, run the completion queue
178510696SDavid.Hollister@Sun.COM */
178610696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
178710696SDavid.Hollister@Sun.COM sp = STAILQ_FIRST(&pwp->cq);
178810696SDavid.Hollister@Sun.COM STAILQ_INIT(&pwp->cq);
178910696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
179010696SDavid.Hollister@Sun.COM
179110696SDavid.Hollister@Sun.COM DTRACE_PROBE1(pmcs__scsa__cq__run__start__loop,
179210696SDavid.Hollister@Sun.COM pmcs_cq_thr_info_t *, cqti);
179310696SDavid.Hollister@Sun.COM
179410696SDavid.Hollister@Sun.COM if (sp && pmcs_check_acc_dma_handle(pwp)) {
179510696SDavid.Hollister@Sun.COM ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED);
179610696SDavid.Hollister@Sun.COM }
179710696SDavid.Hollister@Sun.COM
179810696SDavid.Hollister@Sun.COM while (sp) {
179910696SDavid.Hollister@Sun.COM nxt = STAILQ_NEXT(sp, cmd_next);
180010696SDavid.Hollister@Sun.COM pkt = CMD2PKT(sp);
180111347SRamana.Srikanth@Sun.COM tgt = sp->cmd_target;
180211347SRamana.Srikanth@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, tgt,
180310696SDavid.Hollister@Sun.COM "%s: calling completion on %p for tgt %p", __func__,
180411347SRamana.Srikanth@Sun.COM (void *)sp, (void *)tgt);
180511347SRamana.Srikanth@Sun.COM if (tgt) {
180611347SRamana.Srikanth@Sun.COM mutex_enter(&tgt->statlock);
180711347SRamana.Srikanth@Sun.COM ASSERT(tgt->actv_pkts != 0);
180811347SRamana.Srikanth@Sun.COM tgt->actv_pkts--;
180911347SRamana.Srikanth@Sun.COM mutex_exit(&tgt->statlock);
181011347SRamana.Srikanth@Sun.COM }
181110696SDavid.Hollister@Sun.COM scsi_hba_pkt_comp(pkt);
181210696SDavid.Hollister@Sun.COM sp = nxt;
181310696SDavid.Hollister@Sun.COM }
181410696SDavid.Hollister@Sun.COM
181510696SDavid.Hollister@Sun.COM DTRACE_PROBE1(pmcs__scsa__cq__run__end__loop,
181610696SDavid.Hollister@Sun.COM pmcs_cq_thr_info_t *, cqti);
181710696SDavid.Hollister@Sun.COM
181812343Sdavid.hollister@oracle.com /*
181912343Sdavid.hollister@oracle.com * Check if there are more completions to do. If so, and we've
182012343Sdavid.hollister@oracle.com * not been told to stop, skip the wait and cycle through again.
182112343Sdavid.hollister@oracle.com */
182210696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
182312343Sdavid.hollister@oracle.com if ((pwp->iocomp_cb_head == NULL) && STAILQ_EMPTY(&pwp->cq) &&
182412343Sdavid.hollister@oracle.com !pwp->cq_info.cq_stop) {
182512343Sdavid.hollister@oracle.com mutex_exit(&pwp->cq_lock);
182612343Sdavid.hollister@oracle.com mutex_enter(&cqti->cq_thr_lock);
182712343Sdavid.hollister@oracle.com cv_wait(&cqti->cq_cv, &cqti->cq_thr_lock);
182812343Sdavid.hollister@oracle.com mutex_exit(&cqti->cq_thr_lock);
182912343Sdavid.hollister@oracle.com mutex_enter(&pwp->cq_lock);
183012343Sdavid.hollister@oracle.com }
183110696SDavid.Hollister@Sun.COM }
183210696SDavid.Hollister@Sun.COM
183310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
183410696SDavid.Hollister@Sun.COM DTRACE_PROBE1(pmcs__scsa__cq__run__stop, pmcs_cq_thr_info_t *, cqti);
183510696SDavid.Hollister@Sun.COM thread_exit();
183610696SDavid.Hollister@Sun.COM }
183710696SDavid.Hollister@Sun.COM
183810696SDavid.Hollister@Sun.COM /*
183910696SDavid.Hollister@Sun.COM * Run a SAS command. Called with pwrk->lock held, returns unlocked.
184010696SDavid.Hollister@Sun.COM */
184110696SDavid.Hollister@Sun.COM static int
pmcs_SAS_run(pmcs_cmd_t * sp,pmcwork_t * pwrk)184210696SDavid.Hollister@Sun.COM pmcs_SAS_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
184310696SDavid.Hollister@Sun.COM {
184410696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = CMD2PMC(sp);
184510696SDavid.Hollister@Sun.COM struct scsi_pkt *pkt = CMD2PKT(sp);
184610696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp = pwrk->xp;
184713005Ssrikanth.suravajhala@oracle.com uint32_t iq, lhtag, *ptr;
184810696SDavid.Hollister@Sun.COM sas_ssp_cmd_iu_t sc;
184913005Ssrikanth.suravajhala@oracle.com int sp_pkt_time = 0;
185010696SDavid.Hollister@Sun.COM
185112462Sjesse.butler@oracle.com ASSERT(xp != NULL);
185210696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
185310755SJesse.Butler@Sun.COM if (!xp->assigned) {
185410696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
185510696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_FAIL_OTHER);
185610696SDavid.Hollister@Sun.COM }
185710696SDavid.Hollister@Sun.COM if ((xp->actv_cnt >= xp->qdepth) || xp->recover_wait) {
185810696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
185910696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
186010696SDavid.Hollister@Sun.COM STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
186110696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
186210696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_FAIL_OTHER);
186310696SDavid.Hollister@Sun.COM }
186410696SDavid.Hollister@Sun.COM GET_IO_IQ_ENTRY(pwp, ptr, pwrk->phy->device_id, iq);
186510696SDavid.Hollister@Sun.COM if (ptr == NULL) {
186610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
186710696SDavid.Hollister@Sun.COM /*
186810696SDavid.Hollister@Sun.COM * This is a temporary failure not likely to unblocked by
186910696SDavid.Hollister@Sun.COM * commands completing as the test for scheduling the
187010696SDavid.Hollister@Sun.COM * restart of work is a per-device test.
187110696SDavid.Hollister@Sun.COM */
187210696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
187310696SDavid.Hollister@Sun.COM STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
187410696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
187511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
187610696SDavid.Hollister@Sun.COM "%s: Failed to get IO IQ entry for tgt %d",
187710696SDavid.Hollister@Sun.COM __func__, xp->target_num);
187810696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_FAIL_RES);
187910696SDavid.Hollister@Sun.COM
188010696SDavid.Hollister@Sun.COM }
188110696SDavid.Hollister@Sun.COM
188210696SDavid.Hollister@Sun.COM ptr[0] =
188310696SDavid.Hollister@Sun.COM LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SSP_INI_IO_START));
188410696SDavid.Hollister@Sun.COM ptr[1] = LE_32(pwrk->htag);
188510696SDavid.Hollister@Sun.COM ptr[2] = LE_32(pwrk->phy->device_id);
188610696SDavid.Hollister@Sun.COM ptr[3] = LE_32(pkt->pkt_dma_len);
188710696SDavid.Hollister@Sun.COM if (ptr[3]) {
188810696SDavid.Hollister@Sun.COM ASSERT(pkt->pkt_numcookies);
188910696SDavid.Hollister@Sun.COM if (pkt->pkt_dma_flags & DDI_DMA_READ) {
189010696SDavid.Hollister@Sun.COM ptr[4] = LE_32(PMCIN_DATADIR_2_INI);
189110696SDavid.Hollister@Sun.COM } else {
189210696SDavid.Hollister@Sun.COM ptr[4] = LE_32(PMCIN_DATADIR_2_DEV);
189310696SDavid.Hollister@Sun.COM }
189410696SDavid.Hollister@Sun.COM if (pmcs_dma_load(pwp, sp, ptr)) {
189510696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[iq]);
189610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
189710696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
189810696SDavid.Hollister@Sun.COM if (STAILQ_EMPTY(&xp->wq)) {
189910696SDavid.Hollister@Sun.COM STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
190010696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
190112977Sjesse.butler@oracle.com return (PMCS_WQ_RUN_FAIL_RES);
190210696SDavid.Hollister@Sun.COM } else {
190310696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
190410696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_scbp[0] = STATUS_QFULL;
190510696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
190610696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS |
190710696SDavid.Hollister@Sun.COM STATE_GOT_TARGET | STATE_SENT_CMD |
190810696SDavid.Hollister@Sun.COM STATE_GOT_STATUS;
190912977Sjesse.butler@oracle.com sp->cmd_tag = NULL;
191010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
191110696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
191212343Sdavid.hollister@oracle.com PMCS_CQ_RUN_LOCKED(pwp);
191310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
191411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
191510696SDavid.Hollister@Sun.COM "%s: Failed to dma_load for tgt %d (QF)",
191610696SDavid.Hollister@Sun.COM __func__, xp->target_num);
191712977Sjesse.butler@oracle.com return (PMCS_WQ_RUN_FAIL_RES_CMP);
191810696SDavid.Hollister@Sun.COM }
191910696SDavid.Hollister@Sun.COM }
192010696SDavid.Hollister@Sun.COM } else {
192110696SDavid.Hollister@Sun.COM ptr[4] = LE_32(PMCIN_DATADIR_NONE);
192210696SDavid.Hollister@Sun.COM CLEAN_MESSAGE(ptr, 12);
192310696SDavid.Hollister@Sun.COM }
192410696SDavid.Hollister@Sun.COM xp->actv_cnt++;
192510696SDavid.Hollister@Sun.COM if (xp->actv_cnt > xp->maxdepth) {
192610696SDavid.Hollister@Sun.COM xp->maxdepth = xp->actv_cnt;
192711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp, "%s: max depth "
192811048SDavid.Hollister@Sun.COM "now %u", pwrk->phy->path, xp->maxdepth);
192910696SDavid.Hollister@Sun.COM }
193010696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
193110696SDavid.Hollister@Sun.COM
193210696SDavid.Hollister@Sun.COM
193310696SDavid.Hollister@Sun.COM #ifdef DEBUG
193410696SDavid.Hollister@Sun.COM /*
193510696SDavid.Hollister@Sun.COM * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
193610696SDavid.Hollister@Sun.COM * event when this goes out on the wire.
193710696SDavid.Hollister@Sun.COM */
193810696SDavid.Hollister@Sun.COM ptr[4] |= PMCIN_MESSAGE_REPORT;
193910696SDavid.Hollister@Sun.COM #endif
194010696SDavid.Hollister@Sun.COM /*
194110696SDavid.Hollister@Sun.COM * Fill in the SSP IU
194210696SDavid.Hollister@Sun.COM */
194310696SDavid.Hollister@Sun.COM
194410696SDavid.Hollister@Sun.COM bzero(&sc, sizeof (sas_ssp_cmd_iu_t));
194510696SDavid.Hollister@Sun.COM bcopy((uint8_t *)&sp->cmd_lun->scsi_lun, sc.lun, sizeof (scsi_lun_t));
194610696SDavid.Hollister@Sun.COM
194710696SDavid.Hollister@Sun.COM switch (pkt->pkt_flags & FLAG_TAGMASK) {
194810696SDavid.Hollister@Sun.COM case FLAG_HTAG:
194910696SDavid.Hollister@Sun.COM sc.task_attribute = SAS_CMD_TASK_ATTR_HEAD;
195010696SDavid.Hollister@Sun.COM break;
195110696SDavid.Hollister@Sun.COM case FLAG_OTAG:
195210696SDavid.Hollister@Sun.COM sc.task_attribute = SAS_CMD_TASK_ATTR_ORDERED;
195310696SDavid.Hollister@Sun.COM break;
195410696SDavid.Hollister@Sun.COM case FLAG_STAG:
195510696SDavid.Hollister@Sun.COM default:
195610696SDavid.Hollister@Sun.COM sc.task_attribute = SAS_CMD_TASK_ATTR_SIMPLE;
195710696SDavid.Hollister@Sun.COM break;
195810696SDavid.Hollister@Sun.COM }
195910696SDavid.Hollister@Sun.COM (void) memcpy(sc.cdb, pkt->pkt_cdbp,
196010696SDavid.Hollister@Sun.COM min(SCSA_CDBLEN(sp), sizeof (sc.cdb)));
196110696SDavid.Hollister@Sun.COM (void) memcpy(&ptr[5], &sc, sizeof (sas_ssp_cmd_iu_t));
196210696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
196313005Ssrikanth.suravajhala@oracle.com lhtag = pwrk->htag;
196410696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
196511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
196610696SDavid.Hollister@Sun.COM "%s: giving pkt %p (tag %x) to the hardware", __func__,
196710696SDavid.Hollister@Sun.COM (void *)pkt, pwrk->htag);
196810696SDavid.Hollister@Sun.COM #ifdef DEBUG
196910696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SAS INI Message", ptr);
197010696SDavid.Hollister@Sun.COM #endif
197110696SDavid.Hollister@Sun.COM mutex_enter(&xp->aqlock);
197210696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
197310696SDavid.Hollister@Sun.COM mutex_exit(&xp->aqlock);
197413005Ssrikanth.suravajhala@oracle.com sp_pkt_time = CMD2PKT(sp)->pkt_time;
197510696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, iq);
197612988Ssrikanth.suravajhala@oracle.com mutex_enter(&pwrk->lock);
197713005Ssrikanth.suravajhala@oracle.com if (lhtag == pwrk->htag) {
197813005Ssrikanth.suravajhala@oracle.com pwrk->timer = US2WT(sp_pkt_time * 1000000);
197913005Ssrikanth.suravajhala@oracle.com if (pwrk->timer == 0) {
198013005Ssrikanth.suravajhala@oracle.com pwrk->timer = US2WT(1000000);
198113005Ssrikanth.suravajhala@oracle.com }
198212988Ssrikanth.suravajhala@oracle.com }
198312988Ssrikanth.suravajhala@oracle.com mutex_exit(&pwrk->lock);
198410696SDavid.Hollister@Sun.COM
198510696SDavid.Hollister@Sun.COM /*
198610696SDavid.Hollister@Sun.COM * If we just submitted the last command queued from device state
198710696SDavid.Hollister@Sun.COM * recovery, clear the wq_recovery_tail pointer.
198810696SDavid.Hollister@Sun.COM */
198910696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
199010696SDavid.Hollister@Sun.COM if (xp->wq_recovery_tail == sp) {
199110696SDavid.Hollister@Sun.COM xp->wq_recovery_tail = NULL;
199210696SDavid.Hollister@Sun.COM }
199310696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
199410696SDavid.Hollister@Sun.COM
199510696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_SUCCESS);
199610696SDavid.Hollister@Sun.COM }
199710696SDavid.Hollister@Sun.COM
199810696SDavid.Hollister@Sun.COM /*
199910696SDavid.Hollister@Sun.COM * Complete a SAS command
200010696SDavid.Hollister@Sun.COM *
200110696SDavid.Hollister@Sun.COM * Called with pwrk lock held.
200210696SDavid.Hollister@Sun.COM * The free of pwrk releases the lock.
200310696SDavid.Hollister@Sun.COM */
200410696SDavid.Hollister@Sun.COM
200510696SDavid.Hollister@Sun.COM static void
pmcs_SAS_done(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * msg)200610696SDavid.Hollister@Sun.COM pmcs_SAS_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
200710696SDavid.Hollister@Sun.COM {
200810696SDavid.Hollister@Sun.COM pmcs_cmd_t *sp = pwrk->arg;
200910696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr = pwrk->phy;
201010696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp = pwrk->xp;
201110696SDavid.Hollister@Sun.COM struct scsi_pkt *pkt = CMD2PKT(sp);
201210696SDavid.Hollister@Sun.COM int dead;
201310696SDavid.Hollister@Sun.COM uint32_t sts;
201410696SDavid.Hollister@Sun.COM boolean_t aborted = B_FALSE;
201510696SDavid.Hollister@Sun.COM boolean_t do_ds_recovery = B_FALSE;
201610696SDavid.Hollister@Sun.COM
201710696SDavid.Hollister@Sun.COM ASSERT(xp != NULL);
201810696SDavid.Hollister@Sun.COM ASSERT(sp != NULL);
201910696SDavid.Hollister@Sun.COM ASSERT(pptr != NULL);
202010696SDavid.Hollister@Sun.COM
202110696SDavid.Hollister@Sun.COM DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
202210696SDavid.Hollister@Sun.COM (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
202310696SDavid.Hollister@Sun.COM hrtime_t, gethrtime());
202410696SDavid.Hollister@Sun.COM
202510696SDavid.Hollister@Sun.COM dead = pwrk->dead;
202610696SDavid.Hollister@Sun.COM
202710696SDavid.Hollister@Sun.COM if (msg) {
202810696SDavid.Hollister@Sun.COM sts = LE_32(msg[2]);
202910696SDavid.Hollister@Sun.COM } else {
203010696SDavid.Hollister@Sun.COM sts = 0;
203110696SDavid.Hollister@Sun.COM }
203210696SDavid.Hollister@Sun.COM
203310696SDavid.Hollister@Sun.COM if (dead != 0) {
203411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
203511048SDavid.Hollister@Sun.COM "0x%x for %s", __func__, pwrk->htag, pptr->path);
203610696SDavid.Hollister@Sun.COM goto out;
203710696SDavid.Hollister@Sun.COM }
203810696SDavid.Hollister@Sun.COM
203910696SDavid.Hollister@Sun.COM if (sts == PMCOUT_STATUS_ABORTED) {
204010696SDavid.Hollister@Sun.COM aborted = B_TRUE;
204110696SDavid.Hollister@Sun.COM }
204210696SDavid.Hollister@Sun.COM
204310696SDavid.Hollister@Sun.COM if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
204411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
204510696SDavid.Hollister@Sun.COM "%s: cmd 0x%p (tag 0x%x) timed out for %s",
204610696SDavid.Hollister@Sun.COM __func__, (void *)sp, pwrk->htag, pptr->path);
204711347SRamana.Srikanth@Sun.COM CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
204811347SRamana.Srikanth@Sun.COM CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
204911347SRamana.Srikanth@Sun.COM STATE_SENT_CMD;
205011347SRamana.Srikanth@Sun.COM CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
205110696SDavid.Hollister@Sun.COM goto out;
205210696SDavid.Hollister@Sun.COM }
205310696SDavid.Hollister@Sun.COM
205410696SDavid.Hollister@Sun.COM /*
205510696SDavid.Hollister@Sun.COM * If the status isn't okay but not underflow,
205610696SDavid.Hollister@Sun.COM * step to the side and parse the (possible) error.
205710696SDavid.Hollister@Sun.COM */
205810696SDavid.Hollister@Sun.COM #ifdef DEBUG
205910696SDavid.Hollister@Sun.COM if (msg) {
206010696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
206110696SDavid.Hollister@Sun.COM }
206210696SDavid.Hollister@Sun.COM #endif
206310696SDavid.Hollister@Sun.COM if (!msg) {
206410696SDavid.Hollister@Sun.COM goto out;
206510696SDavid.Hollister@Sun.COM }
206610696SDavid.Hollister@Sun.COM
206710696SDavid.Hollister@Sun.COM switch (sts) {
206810696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
206910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_DS_NON_OPERATIONAL:
207010696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_DS_IN_RECOVERY:
207111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
207211347SRamana.Srikanth@Sun.COM "%s: PHY %s requires DS recovery (status=%d)",
207310696SDavid.Hollister@Sun.COM __func__, pptr->path, sts);
207410696SDavid.Hollister@Sun.COM do_ds_recovery = B_TRUE;
207510696SDavid.Hollister@Sun.COM break;
207610696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_UNDERFLOW:
207710696SDavid.Hollister@Sun.COM (void) pmcs_set_resid(pkt, pkt->pkt_dma_len, LE_32(msg[3]));
207811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW, NULL, NULL,
207910696SDavid.Hollister@Sun.COM "%s: underflow %u for cdb 0x%x",
208010696SDavid.Hollister@Sun.COM __func__, LE_32(msg[3]), pkt->pkt_cdbp[0] & 0xff);
208110696SDavid.Hollister@Sun.COM sts = PMCOUT_STATUS_OK;
208210696SDavid.Hollister@Sun.COM msg[3] = 0;
208310696SDavid.Hollister@Sun.COM break;
208410696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OK:
208510696SDavid.Hollister@Sun.COM pkt->pkt_resid = 0;
208610696SDavid.Hollister@Sun.COM break;
208710696SDavid.Hollister@Sun.COM }
208810696SDavid.Hollister@Sun.COM
208910696SDavid.Hollister@Sun.COM if (sts != PMCOUT_STATUS_OK) {
209012060SDavid.Hollister@Sun.COM pmcs_ioerror(pwp, SAS, pwrk, msg, sts);
209110696SDavid.Hollister@Sun.COM } else {
209210696SDavid.Hollister@Sun.COM if (msg[3]) {
209310696SDavid.Hollister@Sun.COM uint8_t local[PMCS_QENTRY_SIZE << 1], *xd;
209410696SDavid.Hollister@Sun.COM sas_ssp_rsp_iu_t *rptr = (void *)local;
209510696SDavid.Hollister@Sun.COM const int lim =
209610696SDavid.Hollister@Sun.COM (PMCS_QENTRY_SIZE << 1) - SAS_RSP_HDR_SIZE;
209710696SDavid.Hollister@Sun.COM static const uint8_t ssp_rsp_evec[] = {
209810696SDavid.Hollister@Sun.COM 0x58, 0x61, 0x56, 0x72, 0x00
209910696SDavid.Hollister@Sun.COM };
210010696SDavid.Hollister@Sun.COM
210110696SDavid.Hollister@Sun.COM /*
210210696SDavid.Hollister@Sun.COM * Transform the the first part of the response
210310696SDavid.Hollister@Sun.COM * to host canonical form. This gives us enough
210410696SDavid.Hollister@Sun.COM * information to figure out what to do with the
210510696SDavid.Hollister@Sun.COM * rest (which remains unchanged in the incoming
210610696SDavid.Hollister@Sun.COM * message which can be up to two queue entries
210710696SDavid.Hollister@Sun.COM * in length).
210810696SDavid.Hollister@Sun.COM */
210910696SDavid.Hollister@Sun.COM pmcs_endian_transform(pwp, local, &msg[5],
211010696SDavid.Hollister@Sun.COM ssp_rsp_evec);
211110696SDavid.Hollister@Sun.COM xd = (uint8_t *)(&msg[5]);
211210696SDavid.Hollister@Sun.COM xd += SAS_RSP_HDR_SIZE;
211310696SDavid.Hollister@Sun.COM
211410696SDavid.Hollister@Sun.COM if (rptr->datapres == SAS_RSP_DATAPRES_RESPONSE_DATA) {
211510696SDavid.Hollister@Sun.COM if (rptr->response_data_length != 4) {
211610696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
211710696SDavid.Hollister@Sun.COM "Bad SAS RESPONSE DATA LENGTH",
211810696SDavid.Hollister@Sun.COM msg);
211910696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_TRAN_ERR;
212010696SDavid.Hollister@Sun.COM goto out;
212110696SDavid.Hollister@Sun.COM }
212210696SDavid.Hollister@Sun.COM (void) memcpy(&sts, xd, sizeof (uint32_t));
212310696SDavid.Hollister@Sun.COM sts = BE_32(sts);
212410696SDavid.Hollister@Sun.COM /*
212510696SDavid.Hollister@Sun.COM * The only response code we should legally get
212610696SDavid.Hollister@Sun.COM * here is an INVALID FRAME response code.
212710696SDavid.Hollister@Sun.COM */
212810696SDavid.Hollister@Sun.COM if (sts == SAS_RSP_INVALID_FRAME) {
212911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
213010696SDavid.Hollister@Sun.COM "%s: pkt %p tgt %u path %s "
213110696SDavid.Hollister@Sun.COM "completed: INVALID FRAME response",
213210696SDavid.Hollister@Sun.COM __func__, (void *)pkt,
213310696SDavid.Hollister@Sun.COM xp->target_num, pptr->path);
213410696SDavid.Hollister@Sun.COM } else {
213511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
213610696SDavid.Hollister@Sun.COM "%s: pkt %p tgt %u path %s "
213710696SDavid.Hollister@Sun.COM "completed: illegal response 0x%x",
213810696SDavid.Hollister@Sun.COM __func__, (void *)pkt,
213910696SDavid.Hollister@Sun.COM xp->target_num, pptr->path, sts);
214010696SDavid.Hollister@Sun.COM }
214110696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_TRAN_ERR;
214210696SDavid.Hollister@Sun.COM goto out;
214310696SDavid.Hollister@Sun.COM }
214410696SDavid.Hollister@Sun.COM if (rptr->datapres == SAS_RSP_DATAPRES_SENSE_DATA) {
214510696SDavid.Hollister@Sun.COM uint32_t slen;
214610696SDavid.Hollister@Sun.COM slen = rptr->sense_data_length;
214710696SDavid.Hollister@Sun.COM if (slen > lim) {
214810696SDavid.Hollister@Sun.COM slen = lim;
214910696SDavid.Hollister@Sun.COM }
215010696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, rptr->status, xd,
215110696SDavid.Hollister@Sun.COM slen, pptr->path);
215210696SDavid.Hollister@Sun.COM } else if (rptr->datapres == SAS_RSP_DATAPRES_NO_DATA) {
215311241SDavid.Hollister@Sun.COM pmcout_ssp_comp_t *sspcp;
215411241SDavid.Hollister@Sun.COM sspcp = (pmcout_ssp_comp_t *)msg;
215511241SDavid.Hollister@Sun.COM uint32_t *residp;
215610696SDavid.Hollister@Sun.COM /*
215710696SDavid.Hollister@Sun.COM * This is the case for a plain SCSI status.
215811241SDavid.Hollister@Sun.COM * Note: If RESC_V is set and we're here, there
215911241SDavid.Hollister@Sun.COM * is a residual. We need to find it and update
216011241SDavid.Hollister@Sun.COM * the packet accordingly.
216110696SDavid.Hollister@Sun.COM */
216210696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, rptr->status, NULL,
216310696SDavid.Hollister@Sun.COM 0, pptr->path);
216411241SDavid.Hollister@Sun.COM
216511241SDavid.Hollister@Sun.COM if (sspcp->resc_v) {
216611241SDavid.Hollister@Sun.COM /*
216711241SDavid.Hollister@Sun.COM * Point residual to the SSP_RESP_IU
216811241SDavid.Hollister@Sun.COM */
216911241SDavid.Hollister@Sun.COM residp = (uint32_t *)(sspcp + 1);
217011241SDavid.Hollister@Sun.COM /*
217111241SDavid.Hollister@Sun.COM * param contains the number of bytes
217211241SDavid.Hollister@Sun.COM * between where the SSP_RESP_IU may
217311241SDavid.Hollister@Sun.COM * or may not be and the residual.
217411241SDavid.Hollister@Sun.COM * Increment residp by the appropriate
217511241SDavid.Hollister@Sun.COM * number of words: (param+resc_pad)/4).
217611241SDavid.Hollister@Sun.COM */
217711241SDavid.Hollister@Sun.COM residp += (LE_32(sspcp->param) +
217811241SDavid.Hollister@Sun.COM sspcp->resc_pad) /
217911241SDavid.Hollister@Sun.COM sizeof (uint32_t);
218011241SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_UNDERFLOW,
218111241SDavid.Hollister@Sun.COM pptr, xp, "%s: tgt 0x%p "
218211241SDavid.Hollister@Sun.COM "residual %d for pkt 0x%p",
218311241SDavid.Hollister@Sun.COM __func__, (void *) xp, *residp,
218411241SDavid.Hollister@Sun.COM (void *) pkt);
218511241SDavid.Hollister@Sun.COM ASSERT(LE_32(*residp) <=
218611241SDavid.Hollister@Sun.COM pkt->pkt_dma_len);
218711241SDavid.Hollister@Sun.COM (void) pmcs_set_resid(pkt,
218811241SDavid.Hollister@Sun.COM pkt->pkt_dma_len, LE_32(*residp));
218911241SDavid.Hollister@Sun.COM }
219010696SDavid.Hollister@Sun.COM } else {
219110696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG,
219210696SDavid.Hollister@Sun.COM "illegal SAS response", msg);
219310696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_TRAN_ERR;
219410696SDavid.Hollister@Sun.COM goto out;
219510696SDavid.Hollister@Sun.COM }
219610696SDavid.Hollister@Sun.COM } else {
219710696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
219810696SDavid.Hollister@Sun.COM pptr->path);
219910696SDavid.Hollister@Sun.COM }
220010696SDavid.Hollister@Sun.COM if (pkt->pkt_dma_len) {
220110696SDavid.Hollister@Sun.COM pkt->pkt_state |= STATE_XFERRED_DATA;
220210696SDavid.Hollister@Sun.COM }
220310696SDavid.Hollister@Sun.COM }
220411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
220510696SDavid.Hollister@Sun.COM "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
220610696SDavid.Hollister@Sun.COM __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
220710696SDavid.Hollister@Sun.COM pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
220810696SDavid.Hollister@Sun.COM
220910696SDavid.Hollister@Sun.COM if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
221011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
221110696SDavid.Hollister@Sun.COM "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
221210696SDavid.Hollister@Sun.COM __func__, (void *)pkt, pptr->path, (void *)pwrk);
221310696SDavid.Hollister@Sun.COM aborted = B_TRUE;
221410696SDavid.Hollister@Sun.COM }
221510696SDavid.Hollister@Sun.COM
221610696SDavid.Hollister@Sun.COM out:
221710696SDavid.Hollister@Sun.COM pmcs_dma_unload(pwp, sp);
221810696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
221911601SDavid.Hollister@Sun.COM
222011601SDavid.Hollister@Sun.COM /*
222111692SJesse.Butler@Sun.COM * If the device no longer has a PHY pointer, clear the PHY pointer
222211692SJesse.Butler@Sun.COM * from the work structure before we free it. Otherwise, pmcs_pwork
222311692SJesse.Butler@Sun.COM * may decrement the ref_count on a PHY that's been freed.
222411692SJesse.Butler@Sun.COM */
222511692SJesse.Butler@Sun.COM if (xp->phy == NULL) {
222611692SJesse.Butler@Sun.COM pwrk->phy = NULL;
222711692SJesse.Butler@Sun.COM }
222811692SJesse.Butler@Sun.COM
222912506Sjesse.butler@oracle.com /*
223012506Sjesse.butler@oracle.com * We may arrive here due to a command timing out, which in turn
223112506Sjesse.butler@oracle.com * could be addressed in a different context. So, free the work
223212506Sjesse.butler@oracle.com * back, but only after confirming it's not already been freed
223312506Sjesse.butler@oracle.com * elsewhere.
223412506Sjesse.butler@oracle.com */
223512539Sjesse.butler@oracle.com if (pwrk->htag != PMCS_TAG_FREE) {
223612506Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
223712506Sjesse.butler@oracle.com }
223811692SJesse.Butler@Sun.COM
223911692SJesse.Butler@Sun.COM /*
224011601SDavid.Hollister@Sun.COM * If the device is gone, we only put this command on the completion
224111601SDavid.Hollister@Sun.COM * queue if the work structure is not marked dead. If it's marked
224211601SDavid.Hollister@Sun.COM * dead, it will already have been put there.
224311601SDavid.Hollister@Sun.COM */
224410696SDavid.Hollister@Sun.COM if (xp->dev_gone) {
224510696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
224611601SDavid.Hollister@Sun.COM if (!dead) {
224711635SRamana.Srikanth@Sun.COM mutex_enter(&xp->aqlock);
224811635SRamana.Srikanth@Sun.COM STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
224911635SRamana.Srikanth@Sun.COM mutex_exit(&xp->aqlock);
225011692SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
225111635SRamana.Srikanth@Sun.COM "%s: Removing cmd 0x%p (htag 0x%x) from aq",
225211635SRamana.Srikanth@Sun.COM __func__, (void *)sp, sp->cmd_tag);
225311601SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
225411601SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
225512343Sdavid.hollister@oracle.com PMCS_CQ_RUN_LOCKED(pwp);
225611601SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
225711601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
225811601SDavid.Hollister@Sun.COM "%s: Completing command for dead target 0x%p",
225911601SDavid.Hollister@Sun.COM __func__, (void *)xp);
226011601SDavid.Hollister@Sun.COM }
226110696SDavid.Hollister@Sun.COM return;
226210696SDavid.Hollister@Sun.COM }
226310696SDavid.Hollister@Sun.COM
226410696SDavid.Hollister@Sun.COM ASSERT(xp->actv_cnt > 0);
226510696SDavid.Hollister@Sun.COM if (--(xp->actv_cnt) == 0) {
226610696SDavid.Hollister@Sun.COM if (xp->draining) {
226711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
226810696SDavid.Hollister@Sun.COM "%s: waking up drain waiters", __func__);
226910696SDavid.Hollister@Sun.COM cv_signal(&pwp->drain_cv);
227010696SDavid.Hollister@Sun.COM }
227110696SDavid.Hollister@Sun.COM }
227210696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
227311847SDavid.Hollister@Sun.COM
227411847SDavid.Hollister@Sun.COM /*
227511847SDavid.Hollister@Sun.COM * If the status is other than OK, determine if it's something that
227611847SDavid.Hollister@Sun.COM * is worth re-attempting enumeration. If so, mark the PHY.
227711847SDavid.Hollister@Sun.COM */
227811847SDavid.Hollister@Sun.COM if (sts != PMCOUT_STATUS_OK) {
227911847SDavid.Hollister@Sun.COM pmcs_status_disposition(pptr, sts);
228011847SDavid.Hollister@Sun.COM }
228111847SDavid.Hollister@Sun.COM
228210696SDavid.Hollister@Sun.COM if (dead == 0) {
228310696SDavid.Hollister@Sun.COM #ifdef DEBUG
228410696SDavid.Hollister@Sun.COM pmcs_cmd_t *wp;
228510696SDavid.Hollister@Sun.COM mutex_enter(&xp->aqlock);
228610696SDavid.Hollister@Sun.COM STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
228710696SDavid.Hollister@Sun.COM if (wp == sp) {
228810696SDavid.Hollister@Sun.COM break;
228910696SDavid.Hollister@Sun.COM }
229010696SDavid.Hollister@Sun.COM }
229110696SDavid.Hollister@Sun.COM ASSERT(wp != NULL);
229210696SDavid.Hollister@Sun.COM #else
229310696SDavid.Hollister@Sun.COM mutex_enter(&xp->aqlock);
229410696SDavid.Hollister@Sun.COM #endif
229511692SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
229611601SDavid.Hollister@Sun.COM "%s: Removing cmd 0x%p (htag 0x%x) from aq", __func__,
229711601SDavid.Hollister@Sun.COM (void *)sp, sp->cmd_tag);
229810696SDavid.Hollister@Sun.COM STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
229910696SDavid.Hollister@Sun.COM if (aborted) {
230011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
230110696SDavid.Hollister@Sun.COM "%s: Aborted cmd for tgt 0x%p, signaling waiters",
230210696SDavid.Hollister@Sun.COM __func__, (void *)xp);
230310696SDavid.Hollister@Sun.COM cv_signal(&xp->abort_cv);
230410696SDavid.Hollister@Sun.COM }
230510696SDavid.Hollister@Sun.COM mutex_exit(&xp->aqlock);
230610696SDavid.Hollister@Sun.COM }
230710696SDavid.Hollister@Sun.COM
230810696SDavid.Hollister@Sun.COM /*
230910696SDavid.Hollister@Sun.COM * If do_ds_recovery is set, we need to initiate device state
231010696SDavid.Hollister@Sun.COM * recovery. In this case, we put this I/O back on the head of
231110696SDavid.Hollister@Sun.COM * the wait queue to run again after recovery is complete
231210696SDavid.Hollister@Sun.COM */
231310696SDavid.Hollister@Sun.COM if (do_ds_recovery) {
231410696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
231510696SDavid.Hollister@Sun.COM pmcs_start_dev_state_recovery(xp, pptr);
231610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
231711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp, "%s: Putting cmd 0x%p "
231811048SDavid.Hollister@Sun.COM "back on wq during recovery for tgt 0x%p", __func__,
231911048SDavid.Hollister@Sun.COM (void *)sp, (void *)xp);
232010696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
232110696SDavid.Hollister@Sun.COM if (xp->wq_recovery_tail == NULL) {
232210696SDavid.Hollister@Sun.COM STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
232310696SDavid.Hollister@Sun.COM } else {
232410696SDavid.Hollister@Sun.COM /*
232510696SDavid.Hollister@Sun.COM * If there are other I/Os waiting at the head due to
232610696SDavid.Hollister@Sun.COM * device state recovery, add this one in the right spot
232710696SDavid.Hollister@Sun.COM * to maintain proper order.
232810696SDavid.Hollister@Sun.COM */
232910696SDavid.Hollister@Sun.COM STAILQ_INSERT_AFTER(&xp->wq, xp->wq_recovery_tail, sp,
233010696SDavid.Hollister@Sun.COM cmd_next);
233110696SDavid.Hollister@Sun.COM }
233210696SDavid.Hollister@Sun.COM xp->wq_recovery_tail = sp;
233310696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
233410696SDavid.Hollister@Sun.COM } else {
233510696SDavid.Hollister@Sun.COM /*
233610696SDavid.Hollister@Sun.COM * If we're not initiating device state recovery and this
233710696SDavid.Hollister@Sun.COM * command was not "dead", put it on the completion queue
233810696SDavid.Hollister@Sun.COM */
233910696SDavid.Hollister@Sun.COM if (!dead) {
234010696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
234110696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
234212343Sdavid.hollister@oracle.com PMCS_CQ_RUN_LOCKED(pwp);
234310696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
234410696SDavid.Hollister@Sun.COM }
234510696SDavid.Hollister@Sun.COM }
234610696SDavid.Hollister@Sun.COM }
234710696SDavid.Hollister@Sun.COM
234810696SDavid.Hollister@Sun.COM /*
234910696SDavid.Hollister@Sun.COM * Run a SATA command (normal reads and writes),
235010696SDavid.Hollister@Sun.COM * or block and schedule a SATL interpretation
235110696SDavid.Hollister@Sun.COM * of the command.
235210696SDavid.Hollister@Sun.COM *
235310696SDavid.Hollister@Sun.COM * Called with pwrk lock held, returns unlocked.
235410696SDavid.Hollister@Sun.COM */
235510696SDavid.Hollister@Sun.COM
235610696SDavid.Hollister@Sun.COM static int
pmcs_SATA_run(pmcs_cmd_t * sp,pmcwork_t * pwrk)235710696SDavid.Hollister@Sun.COM pmcs_SATA_run(pmcs_cmd_t *sp, pmcwork_t *pwrk)
235810696SDavid.Hollister@Sun.COM {
235910696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = CMD2PMC(sp);
236010696SDavid.Hollister@Sun.COM struct scsi_pkt *pkt = CMD2PKT(sp);
236110696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
236210696SDavid.Hollister@Sun.COM uint8_t cdb_base, asc, tag;
236313005Ssrikanth.suravajhala@oracle.com uint32_t *ptr, lhtag, iq, nblk, i, mtype;
236410696SDavid.Hollister@Sun.COM fis_t fis;
236510696SDavid.Hollister@Sun.COM size_t amt;
236610696SDavid.Hollister@Sun.COM uint64_t lba;
236713005Ssrikanth.suravajhala@oracle.com int sp_pkt_time = 0;
236810696SDavid.Hollister@Sun.COM
236910696SDavid.Hollister@Sun.COM xp = pwrk->xp;
237012462Sjesse.butler@oracle.com ASSERT(xp != NULL);
237110696SDavid.Hollister@Sun.COM
237210696SDavid.Hollister@Sun.COM /*
237310696SDavid.Hollister@Sun.COM * First, see if this is just a plain read/write command.
237410696SDavid.Hollister@Sun.COM * If not, we have to queue it up for processing, block
237510696SDavid.Hollister@Sun.COM * any additional commands from coming in, and wake up
237610696SDavid.Hollister@Sun.COM * the thread that will process this command.
237710696SDavid.Hollister@Sun.COM */
237810696SDavid.Hollister@Sun.COM cdb_base = pkt->pkt_cdbp[0] & 0x1f;
237910696SDavid.Hollister@Sun.COM if (cdb_base != SCMD_READ && cdb_base != SCMD_WRITE) {
238011048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, NULL, NULL,
238111048SDavid.Hollister@Sun.COM "%s: special SATA cmd %p", __func__, (void *)sp);
238210696SDavid.Hollister@Sun.COM
238310696SDavid.Hollister@Sun.COM ASSERT(xp->phy != NULL);
238410696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
238510696SDavid.Hollister@Sun.COM pmcs_lock_phy(xp->phy);
238610696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
238710696SDavid.Hollister@Sun.COM xp->special_needed = 1; /* Set the special_needed flag */
238810696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&xp->sq, sp, cmd_next);
238910696SDavid.Hollister@Sun.COM if (pmcs_run_sata_special(pwp, xp)) {
239010696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
239110696SDavid.Hollister@Sun.COM }
239210696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
239310696SDavid.Hollister@Sun.COM pmcs_unlock_phy(xp->phy);
239410696SDavid.Hollister@Sun.COM
239510696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_SUCCESS);
239610696SDavid.Hollister@Sun.COM }
239710696SDavid.Hollister@Sun.COM
239811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL, "%s: regular cmd", __func__);
239910696SDavid.Hollister@Sun.COM
240010696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
240110755SJesse.Butler@Sun.COM if (!xp->assigned) {
240210696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
240310696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_FAIL_OTHER);
240410696SDavid.Hollister@Sun.COM }
240510696SDavid.Hollister@Sun.COM if (xp->special_running || xp->special_needed || xp->recover_wait) {
240610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
240710696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
240810696SDavid.Hollister@Sun.COM STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
240910696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
241010696SDavid.Hollister@Sun.COM /*
241110696SDavid.Hollister@Sun.COM * By the time we get here the special
241210696SDavid.Hollister@Sun.COM * commands running or waiting to be run
241310696SDavid.Hollister@Sun.COM * may have come and gone, so kick our
241410696SDavid.Hollister@Sun.COM * worker to run the waiting queues
241510696SDavid.Hollister@Sun.COM * just in case.
241610696SDavid.Hollister@Sun.COM */
241710696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
241810696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_FAIL_OTHER);
241910696SDavid.Hollister@Sun.COM }
242010696SDavid.Hollister@Sun.COM lba = xp->capacity;
242110696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
242210696SDavid.Hollister@Sun.COM
242310696SDavid.Hollister@Sun.COM /*
242410696SDavid.Hollister@Sun.COM * Extract data length and lba parameters out of the command. The
242510696SDavid.Hollister@Sun.COM * function pmcs_SATA_rwparm returns a non-zero ASC value if the CDB
242610696SDavid.Hollister@Sun.COM * values are considered illegal.
242710696SDavid.Hollister@Sun.COM */
242810696SDavid.Hollister@Sun.COM asc = pmcs_SATA_rwparm(pkt->pkt_cdbp, &nblk, &lba, lba);
242910696SDavid.Hollister@Sun.COM if (asc) {
243010696SDavid.Hollister@Sun.COM uint8_t sns[18];
243110696SDavid.Hollister@Sun.COM bzero(sns, sizeof (sns));
243210696SDavid.Hollister@Sun.COM sns[0] = 0xf0;
243310696SDavid.Hollister@Sun.COM sns[2] = 0x5;
243410696SDavid.Hollister@Sun.COM sns[12] = asc;
243510696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_CHECK, sns, sizeof (sns),
243610696SDavid.Hollister@Sun.COM pwrk->phy->path);
243710696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
243810696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
243910696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
244010696SDavid.Hollister@Sun.COM PMCS_CQ_RUN_LOCKED(pwp);
244110696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
244210696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_SUCCESS);
244310696SDavid.Hollister@Sun.COM }
244410696SDavid.Hollister@Sun.COM
244510696SDavid.Hollister@Sun.COM /*
244610696SDavid.Hollister@Sun.COM * If the command decodes as not moving any data, complete it here.
244710696SDavid.Hollister@Sun.COM */
244810696SDavid.Hollister@Sun.COM amt = nblk;
244910696SDavid.Hollister@Sun.COM amt <<= 9;
245010696SDavid.Hollister@Sun.COM amt = pmcs_set_resid(pkt, amt, nblk << 9);
245110696SDavid.Hollister@Sun.COM if (amt == 0) {
245210696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
245310696SDavid.Hollister@Sun.COM pwrk->phy->path);
245410696SDavid.Hollister@Sun.COM pmcs_pwork(pwp, pwrk);
245510696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
245610696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
245710696SDavid.Hollister@Sun.COM PMCS_CQ_RUN_LOCKED(pwp);
245810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
245910696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_SUCCESS);
246010696SDavid.Hollister@Sun.COM }
246110696SDavid.Hollister@Sun.COM
246210696SDavid.Hollister@Sun.COM /*
246310696SDavid.Hollister@Sun.COM * Get an inbound queue entry for this I/O
246410696SDavid.Hollister@Sun.COM */
246510696SDavid.Hollister@Sun.COM GET_IO_IQ_ENTRY(pwp, ptr, xp->phy->device_id, iq);
246610696SDavid.Hollister@Sun.COM if (ptr == NULL) {
246710696SDavid.Hollister@Sun.COM /*
246810696SDavid.Hollister@Sun.COM * This is a temporary failure not likely to unblocked by
246910696SDavid.Hollister@Sun.COM * commands completing as the test for scheduling the
247010696SDavid.Hollister@Sun.COM * restart of work is a per-device test.
247110696SDavid.Hollister@Sun.COM */
247210696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
247310696SDavid.Hollister@Sun.COM STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
247410696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
247510696SDavid.Hollister@Sun.COM pmcs_dma_unload(pwp, sp);
247610696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES);
247711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
247810696SDavid.Hollister@Sun.COM "%s: Failed to get IO IQ entry for tgt %d",
247910696SDavid.Hollister@Sun.COM __func__, xp->target_num);
248010696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_FAIL_RES);
248110696SDavid.Hollister@Sun.COM }
248210696SDavid.Hollister@Sun.COM
248310696SDavid.Hollister@Sun.COM /*
248410696SDavid.Hollister@Sun.COM * Get a tag. At this point, hold statlock until the tagmap is
248510696SDavid.Hollister@Sun.COM * updated (just prior to sending the cmd to the hardware).
248610696SDavid.Hollister@Sun.COM */
248710696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
248810696SDavid.Hollister@Sun.COM for (tag = 0; tag < xp->qdepth; tag++) {
248910696SDavid.Hollister@Sun.COM if ((xp->tagmap & (1 << tag)) == 0) {
249010696SDavid.Hollister@Sun.COM break;
249110696SDavid.Hollister@Sun.COM }
249210696SDavid.Hollister@Sun.COM }
249310696SDavid.Hollister@Sun.COM
249410696SDavid.Hollister@Sun.COM if (tag == xp->qdepth) {
249510696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
249610696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[iq]);
249710696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
249810696SDavid.Hollister@Sun.COM STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
249910696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
250010696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_FAIL_OTHER);
250110696SDavid.Hollister@Sun.COM }
250210696SDavid.Hollister@Sun.COM
250310696SDavid.Hollister@Sun.COM sp->cmd_satltag = (uint8_t)tag;
250410696SDavid.Hollister@Sun.COM
250510696SDavid.Hollister@Sun.COM /*
250610696SDavid.Hollister@Sun.COM * Set up the command
250710696SDavid.Hollister@Sun.COM */
250810696SDavid.Hollister@Sun.COM bzero(fis, sizeof (fis));
250910696SDavid.Hollister@Sun.COM ptr[0] =
251010696SDavid.Hollister@Sun.COM LE_32(PMCS_IOMB_IN_SAS(PMCS_OQ_IODONE, PMCIN_SATA_HOST_IO_START));
251110696SDavid.Hollister@Sun.COM ptr[1] = LE_32(pwrk->htag);
251210696SDavid.Hollister@Sun.COM ptr[2] = LE_32(pwrk->phy->device_id);
251310696SDavid.Hollister@Sun.COM ptr[3] = LE_32(amt);
251410696SDavid.Hollister@Sun.COM
251510696SDavid.Hollister@Sun.COM if (xp->ncq) {
251610696SDavid.Hollister@Sun.COM mtype = SATA_PROTOCOL_FPDMA | (tag << 16);
251710696SDavid.Hollister@Sun.COM fis[0] = ((nblk & 0xff) << 24) | (C_BIT << 8) | FIS_REG_H2DEV;
251810696SDavid.Hollister@Sun.COM if (cdb_base == SCMD_READ) {
251910696SDavid.Hollister@Sun.COM fis[0] |= (READ_FPDMA_QUEUED << 16);
252010696SDavid.Hollister@Sun.COM } else {
252110696SDavid.Hollister@Sun.COM fis[0] |= (WRITE_FPDMA_QUEUED << 16);
252210696SDavid.Hollister@Sun.COM }
252310696SDavid.Hollister@Sun.COM fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
252410696SDavid.Hollister@Sun.COM fis[2] = ((nblk & 0xff00) << 16) | ((lba >> 24) & 0xffffff);
252510696SDavid.Hollister@Sun.COM fis[3] = tag << 3;
252610696SDavid.Hollister@Sun.COM } else {
252710696SDavid.Hollister@Sun.COM int op;
252810696SDavid.Hollister@Sun.COM fis[0] = (C_BIT << 8) | FIS_REG_H2DEV;
252910696SDavid.Hollister@Sun.COM if (xp->pio) {
253010696SDavid.Hollister@Sun.COM mtype = SATA_PROTOCOL_PIO;
253110696SDavid.Hollister@Sun.COM if (cdb_base == SCMD_READ) {
253210696SDavid.Hollister@Sun.COM op = READ_SECTORS_EXT;
253310696SDavid.Hollister@Sun.COM } else {
253410696SDavid.Hollister@Sun.COM op = WRITE_SECTORS_EXT;
253510696SDavid.Hollister@Sun.COM }
253610696SDavid.Hollister@Sun.COM } else {
253710696SDavid.Hollister@Sun.COM mtype = SATA_PROTOCOL_DMA;
253810696SDavid.Hollister@Sun.COM if (cdb_base == SCMD_READ) {
253910696SDavid.Hollister@Sun.COM op = READ_DMA_EXT;
254010696SDavid.Hollister@Sun.COM } else {
254110696SDavid.Hollister@Sun.COM op = WRITE_DMA_EXT;
254210696SDavid.Hollister@Sun.COM }
254310696SDavid.Hollister@Sun.COM }
254410696SDavid.Hollister@Sun.COM fis[0] |= (op << 16);
254510696SDavid.Hollister@Sun.COM fis[1] = (FEATURE_LBA << 24) | (lba & 0xffffff);
254610696SDavid.Hollister@Sun.COM fis[2] = (lba >> 24) & 0xffffff;
254710696SDavid.Hollister@Sun.COM fis[3] = nblk;
254810696SDavid.Hollister@Sun.COM }
254910696SDavid.Hollister@Sun.COM
255010696SDavid.Hollister@Sun.COM if (cdb_base == SCMD_READ) {
255110696SDavid.Hollister@Sun.COM ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_INI);
255210696SDavid.Hollister@Sun.COM } else {
255310696SDavid.Hollister@Sun.COM ptr[4] = LE_32(mtype | PMCIN_DATADIR_2_DEV);
255410696SDavid.Hollister@Sun.COM }
255510696SDavid.Hollister@Sun.COM #ifdef DEBUG
255610696SDavid.Hollister@Sun.COM /*
255710696SDavid.Hollister@Sun.COM * Generate a PMCOUT_STATUS_XFER_CMD_FRAME_ISSUED
255810696SDavid.Hollister@Sun.COM * event when this goes out on the wire.
255910696SDavid.Hollister@Sun.COM */
256010696SDavid.Hollister@Sun.COM ptr[4] |= PMCIN_MESSAGE_REPORT;
256110696SDavid.Hollister@Sun.COM #endif
256210696SDavid.Hollister@Sun.COM for (i = 0; i < (sizeof (fis_t))/(sizeof (uint32_t)); i++) {
256310696SDavid.Hollister@Sun.COM ptr[i+5] = LE_32(fis[i]);
256410696SDavid.Hollister@Sun.COM }
256510696SDavid.Hollister@Sun.COM if (pmcs_dma_load(pwp, sp, ptr)) {
256610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
256710696SDavid.Hollister@Sun.COM mutex_exit(&pwp->iqp_lock[iq]);
256810696SDavid.Hollister@Sun.COM mutex_enter(&xp->wqlock);
256910696SDavid.Hollister@Sun.COM STAILQ_INSERT_HEAD(&xp->wq, sp, cmd_next);
257010696SDavid.Hollister@Sun.COM mutex_exit(&xp->wqlock);
257111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, xp,
257210696SDavid.Hollister@Sun.COM "%s: Failed to dma_load for tgt %d",
257310696SDavid.Hollister@Sun.COM __func__, xp->target_num);
257410696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_FAIL_RES);
257510696SDavid.Hollister@Sun.COM
257610696SDavid.Hollister@Sun.COM }
257710696SDavid.Hollister@Sun.COM
257810696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ONCHIP;
257913005Ssrikanth.suravajhala@oracle.com lhtag = pwrk->htag;
258010696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
258110696SDavid.Hollister@Sun.COM xp->tagmap |= (1 << tag);
258210696SDavid.Hollister@Sun.COM xp->actv_cnt++;
258310696SDavid.Hollister@Sun.COM if (xp->actv_cnt > xp->maxdepth) {
258410696SDavid.Hollister@Sun.COM xp->maxdepth = xp->actv_cnt;
258511048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pwrk->phy, xp,
258611048SDavid.Hollister@Sun.COM "%s: max depth now %u", pwrk->phy->path, xp->maxdepth);
258710696SDavid.Hollister@Sun.COM }
258810696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
258910696SDavid.Hollister@Sun.COM mutex_enter(&xp->aqlock);
259010696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&xp->aq, sp, cmd_next);
259110696SDavid.Hollister@Sun.COM mutex_exit(&xp->aqlock);
259211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, NULL, NULL,
259311048SDavid.Hollister@Sun.COM "%s: giving pkt %p to hardware", __func__, (void *)pkt);
259410696SDavid.Hollister@Sun.COM #ifdef DEBUG
259510696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "SATA INI Message", ptr);
259610696SDavid.Hollister@Sun.COM #endif
259713005Ssrikanth.suravajhala@oracle.com sp_pkt_time = CMD2PKT(sp)->pkt_time;
259810696SDavid.Hollister@Sun.COM INC_IQ_ENTRY(pwp, iq);
259912988Ssrikanth.suravajhala@oracle.com mutex_enter(&pwrk->lock);
260013005Ssrikanth.suravajhala@oracle.com if (lhtag == pwrk->htag) {
260113005Ssrikanth.suravajhala@oracle.com pwrk->timer = US2WT(sp_pkt_time * 1000000);
260213005Ssrikanth.suravajhala@oracle.com if (pwrk->timer == 0) {
260313005Ssrikanth.suravajhala@oracle.com pwrk->timer = US2WT(1000000);
260413005Ssrikanth.suravajhala@oracle.com }
260512988Ssrikanth.suravajhala@oracle.com }
260612988Ssrikanth.suravajhala@oracle.com mutex_exit(&pwrk->lock);
260710696SDavid.Hollister@Sun.COM
260810696SDavid.Hollister@Sun.COM return (PMCS_WQ_RUN_SUCCESS);
260910696SDavid.Hollister@Sun.COM }
261010696SDavid.Hollister@Sun.COM
261110696SDavid.Hollister@Sun.COM /*
261210696SDavid.Hollister@Sun.COM * Complete a SATA command. Called with pwrk lock held.
261310696SDavid.Hollister@Sun.COM */
261410696SDavid.Hollister@Sun.COM void
pmcs_SATA_done(pmcs_hw_t * pwp,pmcwork_t * pwrk,uint32_t * msg)261510696SDavid.Hollister@Sun.COM pmcs_SATA_done(pmcs_hw_t *pwp, pmcwork_t *pwrk, uint32_t *msg)
261610696SDavid.Hollister@Sun.COM {
261710696SDavid.Hollister@Sun.COM pmcs_cmd_t *sp = pwrk->arg;
261810696SDavid.Hollister@Sun.COM struct scsi_pkt *pkt = CMD2PKT(sp);
261910696SDavid.Hollister@Sun.COM pmcs_phy_t *pptr = pwrk->phy;
262010696SDavid.Hollister@Sun.COM int dead;
262110696SDavid.Hollister@Sun.COM uint32_t sts;
262210696SDavid.Hollister@Sun.COM pmcs_xscsi_t *xp;
262310696SDavid.Hollister@Sun.COM boolean_t aborted = B_FALSE;
262410696SDavid.Hollister@Sun.COM
262510696SDavid.Hollister@Sun.COM xp = pwrk->xp;
262610696SDavid.Hollister@Sun.COM ASSERT(xp != NULL);
262710696SDavid.Hollister@Sun.COM
262810696SDavid.Hollister@Sun.COM DTRACE_PROBE4(pmcs__io__done, uint64_t, pkt->pkt_dma_len, int,
262910696SDavid.Hollister@Sun.COM (pkt->pkt_dma_flags & DDI_DMA_READ) != 0, hrtime_t, pwrk->start,
263010696SDavid.Hollister@Sun.COM hrtime_t, gethrtime());
263110696SDavid.Hollister@Sun.COM
263210696SDavid.Hollister@Sun.COM dead = pwrk->dead;
263310696SDavid.Hollister@Sun.COM
263410696SDavid.Hollister@Sun.COM if (msg) {
263510696SDavid.Hollister@Sun.COM sts = LE_32(msg[2]);
263610696SDavid.Hollister@Sun.COM } else {
263710696SDavid.Hollister@Sun.COM sts = 0;
263810696SDavid.Hollister@Sun.COM }
263910696SDavid.Hollister@Sun.COM
264010696SDavid.Hollister@Sun.COM if (dead != 0) {
264111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, "%s: dead cmd tag "
264211048SDavid.Hollister@Sun.COM "0x%x for %s", __func__, pwrk->htag, pptr->path);
264310696SDavid.Hollister@Sun.COM goto out;
264410696SDavid.Hollister@Sun.COM }
264510696SDavid.Hollister@Sun.COM if ((pwrk->state == PMCS_WORK_STATE_TIMED_OUT) &&
264610696SDavid.Hollister@Sun.COM (sts != PMCOUT_STATUS_ABORTED)) {
264711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
264810696SDavid.Hollister@Sun.COM "%s: cmd 0x%p (tag 0x%x) timed out for %s",
264910696SDavid.Hollister@Sun.COM __func__, (void *)sp, pwrk->htag, pptr->path);
265010696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_scbp[0] = STATUS_GOOD;
265110696SDavid.Hollister@Sun.COM /* pkt_reason already set to CMD_TIMEOUT */
265210696SDavid.Hollister@Sun.COM ASSERT(CMD2PKT(sp)->pkt_reason == CMD_TIMEOUT);
265310696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
265410696SDavid.Hollister@Sun.COM STATE_SENT_CMD;
265510696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT;
265610696SDavid.Hollister@Sun.COM goto out;
265710696SDavid.Hollister@Sun.COM }
265810696SDavid.Hollister@Sun.COM
265911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp, "%s: pkt %p tgt %u done",
266010696SDavid.Hollister@Sun.COM __func__, (void *)pkt, xp->target_num);
266110696SDavid.Hollister@Sun.COM
266210696SDavid.Hollister@Sun.COM /*
266310696SDavid.Hollister@Sun.COM * If the status isn't okay but not underflow,
266410696SDavid.Hollister@Sun.COM * step to the side and parse the (possible) error.
266510696SDavid.Hollister@Sun.COM */
266610696SDavid.Hollister@Sun.COM #ifdef DEBUG
266710696SDavid.Hollister@Sun.COM if (msg) {
266810696SDavid.Hollister@Sun.COM pmcs_print_entry(pwp, PMCS_PRT_DEBUG3, "Outbound Message", msg);
266910696SDavid.Hollister@Sun.COM }
267010696SDavid.Hollister@Sun.COM #endif
267110696SDavid.Hollister@Sun.COM if (!msg) {
267210696SDavid.Hollister@Sun.COM goto out;
267310696SDavid.Hollister@Sun.COM }
267410696SDavid.Hollister@Sun.COM
267510696SDavid.Hollister@Sun.COM /*
267610696SDavid.Hollister@Sun.COM * If the status isn't okay or we got a FIS response of some kind,
267710696SDavid.Hollister@Sun.COM * step to the side and parse the (possible) error.
267810696SDavid.Hollister@Sun.COM */
267910696SDavid.Hollister@Sun.COM if ((sts != PMCOUT_STATUS_OK) || (LE_32(msg[3]) != 0)) {
268010696SDavid.Hollister@Sun.COM if (sts == PMCOUT_STATUS_IO_DS_NON_OPERATIONAL) {
268110696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
268210696SDavid.Hollister@Sun.COM pmcs_lock_phy(pptr);
268310696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
268410696SDavid.Hollister@Sun.COM if ((xp->resetting == 0) && (xp->reset_success != 0) &&
268510696SDavid.Hollister@Sun.COM (xp->reset_wait == 0)) {
268610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
268710696SDavid.Hollister@Sun.COM if (pmcs_reset_phy(pwp, pptr,
268810696SDavid.Hollister@Sun.COM PMCS_PHYOP_LINK_RESET) != 0) {
268911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
269011048SDavid.Hollister@Sun.COM "%s: PHY (%s) Local Control/Link "
269111048SDavid.Hollister@Sun.COM "Reset FAILED as part of error "
269211048SDavid.Hollister@Sun.COM "recovery", __func__, pptr->path);
269310696SDavid.Hollister@Sun.COM }
269410696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
269510696SDavid.Hollister@Sun.COM }
269610696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
269710696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pptr);
269810696SDavid.Hollister@Sun.COM mutex_enter(&pwrk->lock);
269910696SDavid.Hollister@Sun.COM }
270012060SDavid.Hollister@Sun.COM pmcs_ioerror(pwp, SATA, pwrk, msg, sts);
270110696SDavid.Hollister@Sun.COM } else {
270210696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0,
270310696SDavid.Hollister@Sun.COM pwrk->phy->path);
270410696SDavid.Hollister@Sun.COM pkt->pkt_state |= STATE_XFERRED_DATA;
270510696SDavid.Hollister@Sun.COM pkt->pkt_resid = 0;
270610696SDavid.Hollister@Sun.COM }
270710696SDavid.Hollister@Sun.COM
270811048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
270910696SDavid.Hollister@Sun.COM "%s: pkt %p tgt %u done reason=%x state=%x resid=%ld status=%x",
271010696SDavid.Hollister@Sun.COM __func__, (void *)pkt, xp->target_num, pkt->pkt_reason,
271110696SDavid.Hollister@Sun.COM pkt->pkt_state, pkt->pkt_resid, pkt->pkt_scbp[0]);
271210696SDavid.Hollister@Sun.COM
271310696SDavid.Hollister@Sun.COM if (pwrk->state == PMCS_WORK_STATE_ABORTED) {
271411048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
271510696SDavid.Hollister@Sun.COM "%s: scsi_pkt 0x%p aborted for PHY %s; work = 0x%p",
271610696SDavid.Hollister@Sun.COM __func__, (void *)pkt, pptr->path, (void *)pwrk);
271710696SDavid.Hollister@Sun.COM aborted = B_TRUE;
271810696SDavid.Hollister@Sun.COM }
271910696SDavid.Hollister@Sun.COM
272010696SDavid.Hollister@Sun.COM out:
272110696SDavid.Hollister@Sun.COM pmcs_dma_unload(pwp, sp);
272210696SDavid.Hollister@Sun.COM mutex_enter(&xp->statlock);
272310696SDavid.Hollister@Sun.COM xp->tagmap &= ~(1 << sp->cmd_satltag);
272410696SDavid.Hollister@Sun.COM
272511692SJesse.Butler@Sun.COM /*
272611692SJesse.Butler@Sun.COM * If the device no longer has a PHY pointer, clear the PHY pointer
272711692SJesse.Butler@Sun.COM * from the work structure before we free it. Otherwise, pmcs_pwork
272811692SJesse.Butler@Sun.COM * may decrement the ref_count on a PHY that's been freed.
272911692SJesse.Butler@Sun.COM */
273011692SJesse.Butler@Sun.COM if (xp->phy == NULL) {
273111692SJesse.Butler@Sun.COM pwrk->phy = NULL;
273211692SJesse.Butler@Sun.COM }
273311692SJesse.Butler@Sun.COM
273412506Sjesse.butler@oracle.com /*
273512506Sjesse.butler@oracle.com * We may arrive here due to a command timing out, which in turn
273612506Sjesse.butler@oracle.com * could be addressed in a different context. So, free the work
273712506Sjesse.butler@oracle.com * back, but only after confirming it's not already been freed
273812506Sjesse.butler@oracle.com * elsewhere.
273912506Sjesse.butler@oracle.com */
274012539Sjesse.butler@oracle.com if (pwrk->htag != PMCS_TAG_FREE) {
274112506Sjesse.butler@oracle.com pmcs_pwork(pwp, pwrk);
274212506Sjesse.butler@oracle.com }
274311692SJesse.Butler@Sun.COM
274410696SDavid.Hollister@Sun.COM if (xp->dev_gone) {
274510696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
274611601SDavid.Hollister@Sun.COM if (!dead) {
274711635SRamana.Srikanth@Sun.COM mutex_enter(&xp->aqlock);
274811635SRamana.Srikanth@Sun.COM STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
274911635SRamana.Srikanth@Sun.COM mutex_exit(&xp->aqlock);
275011692SJesse.Butler@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, pptr, xp,
275111635SRamana.Srikanth@Sun.COM "%s: Removing cmd 0x%p (htag 0x%x) from aq",
275211635SRamana.Srikanth@Sun.COM __func__, (void *)sp, sp->cmd_tag);
275311601SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
275411601SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
275512343Sdavid.hollister@oracle.com PMCS_CQ_RUN_LOCKED(pwp);
275611601SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
275711601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG2, pptr, xp,
275811601SDavid.Hollister@Sun.COM "%s: Completing command for dead target 0x%p",
275911601SDavid.Hollister@Sun.COM __func__, (void *)xp);
276011601SDavid.Hollister@Sun.COM }
276110696SDavid.Hollister@Sun.COM return;
276210696SDavid.Hollister@Sun.COM }
276310696SDavid.Hollister@Sun.COM
276410696SDavid.Hollister@Sun.COM ASSERT(xp->actv_cnt > 0);
276510696SDavid.Hollister@Sun.COM if (--(xp->actv_cnt) == 0) {
276610696SDavid.Hollister@Sun.COM if (xp->draining) {
276711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, pptr, xp,
276810696SDavid.Hollister@Sun.COM "%s: waking up drain waiters", __func__);
276910696SDavid.Hollister@Sun.COM cv_signal(&pwp->drain_cv);
277010696SDavid.Hollister@Sun.COM } else if (xp->special_needed) {
277110696SDavid.Hollister@Sun.COM SCHEDULE_WORK(pwp, PMCS_WORK_SATA_RUN);
277210696SDavid.Hollister@Sun.COM }
277310696SDavid.Hollister@Sun.COM }
277410696SDavid.Hollister@Sun.COM mutex_exit(&xp->statlock);
277510696SDavid.Hollister@Sun.COM
277611847SDavid.Hollister@Sun.COM /*
277711847SDavid.Hollister@Sun.COM * If the status is other than OK, determine if it's something that
277811847SDavid.Hollister@Sun.COM * is worth re-attempting enumeration. If so, mark the PHY.
277911847SDavid.Hollister@Sun.COM */
278011847SDavid.Hollister@Sun.COM if (sts != PMCOUT_STATUS_OK) {
278111847SDavid.Hollister@Sun.COM pmcs_status_disposition(pptr, sts);
278211847SDavid.Hollister@Sun.COM }
278311847SDavid.Hollister@Sun.COM
278410696SDavid.Hollister@Sun.COM if (dead == 0) {
278510696SDavid.Hollister@Sun.COM #ifdef DEBUG
278610696SDavid.Hollister@Sun.COM pmcs_cmd_t *wp;
278710696SDavid.Hollister@Sun.COM mutex_enter(&xp->aqlock);
278810696SDavid.Hollister@Sun.COM STAILQ_FOREACH(wp, &xp->aq, cmd_next) {
278910696SDavid.Hollister@Sun.COM if (wp == sp) {
279010696SDavid.Hollister@Sun.COM break;
279110696SDavid.Hollister@Sun.COM }
279210696SDavid.Hollister@Sun.COM }
279310696SDavid.Hollister@Sun.COM ASSERT(wp != NULL);
279410696SDavid.Hollister@Sun.COM #else
279510696SDavid.Hollister@Sun.COM mutex_enter(&xp->aqlock);
279610696SDavid.Hollister@Sun.COM #endif
279710696SDavid.Hollister@Sun.COM STAILQ_REMOVE(&xp->aq, sp, pmcs_cmd, cmd_next);
279810696SDavid.Hollister@Sun.COM if (aborted) {
279911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp,
280010696SDavid.Hollister@Sun.COM "%s: Aborted cmd for tgt 0x%p, signaling waiters",
280110696SDavid.Hollister@Sun.COM __func__, (void *)xp);
280210696SDavid.Hollister@Sun.COM cv_signal(&xp->abort_cv);
280310696SDavid.Hollister@Sun.COM }
280410696SDavid.Hollister@Sun.COM mutex_exit(&xp->aqlock);
280510696SDavid.Hollister@Sun.COM mutex_enter(&pwp->cq_lock);
280610696SDavid.Hollister@Sun.COM STAILQ_INSERT_TAIL(&pwp->cq, sp, cmd_next);
280712343Sdavid.hollister@oracle.com PMCS_CQ_RUN_LOCKED(pwp);
280810696SDavid.Hollister@Sun.COM mutex_exit(&pwp->cq_lock);
280910696SDavid.Hollister@Sun.COM }
281010696SDavid.Hollister@Sun.COM }
281110696SDavid.Hollister@Sun.COM
281210696SDavid.Hollister@Sun.COM static uint8_t
pmcs_SATA_rwparm(uint8_t * cdb,uint32_t * xfr,uint64_t * lba,uint64_t lbamax)281310696SDavid.Hollister@Sun.COM pmcs_SATA_rwparm(uint8_t *cdb, uint32_t *xfr, uint64_t *lba, uint64_t lbamax)
281410696SDavid.Hollister@Sun.COM {
281510696SDavid.Hollister@Sun.COM uint8_t asc = 0;
281610696SDavid.Hollister@Sun.COM switch (cdb[0]) {
281710696SDavid.Hollister@Sun.COM case SCMD_READ_G5:
281810696SDavid.Hollister@Sun.COM case SCMD_WRITE_G5:
281910696SDavid.Hollister@Sun.COM *xfr =
282010696SDavid.Hollister@Sun.COM (((uint32_t)cdb[10]) << 24) |
282110696SDavid.Hollister@Sun.COM (((uint32_t)cdb[11]) << 16) |
282210696SDavid.Hollister@Sun.COM (((uint32_t)cdb[12]) << 8) |
282310696SDavid.Hollister@Sun.COM ((uint32_t)cdb[13]);
282410696SDavid.Hollister@Sun.COM *lba =
282510696SDavid.Hollister@Sun.COM (((uint64_t)cdb[2]) << 56) |
282610696SDavid.Hollister@Sun.COM (((uint64_t)cdb[3]) << 48) |
282710696SDavid.Hollister@Sun.COM (((uint64_t)cdb[4]) << 40) |
282810696SDavid.Hollister@Sun.COM (((uint64_t)cdb[5]) << 32) |
282910696SDavid.Hollister@Sun.COM (((uint64_t)cdb[6]) << 24) |
283010696SDavid.Hollister@Sun.COM (((uint64_t)cdb[7]) << 16) |
283110696SDavid.Hollister@Sun.COM (((uint64_t)cdb[8]) << 8) |
283210696SDavid.Hollister@Sun.COM ((uint64_t)cdb[9]);
283310696SDavid.Hollister@Sun.COM /* Check for illegal bits */
283410696SDavid.Hollister@Sun.COM if (cdb[15]) {
283510696SDavid.Hollister@Sun.COM asc = 0x24; /* invalid field in cdb */
283610696SDavid.Hollister@Sun.COM }
283710696SDavid.Hollister@Sun.COM break;
283810696SDavid.Hollister@Sun.COM case SCMD_READ_G4:
283910696SDavid.Hollister@Sun.COM case SCMD_WRITE_G4:
284010696SDavid.Hollister@Sun.COM *xfr =
284110696SDavid.Hollister@Sun.COM (((uint32_t)cdb[6]) << 16) |
284210696SDavid.Hollister@Sun.COM (((uint32_t)cdb[7]) << 8) |
284310696SDavid.Hollister@Sun.COM ((uint32_t)cdb[8]);
284410696SDavid.Hollister@Sun.COM *lba =
284510696SDavid.Hollister@Sun.COM (((uint32_t)cdb[2]) << 24) |
284610696SDavid.Hollister@Sun.COM (((uint32_t)cdb[3]) << 16) |
284710696SDavid.Hollister@Sun.COM (((uint32_t)cdb[4]) << 8) |
284810696SDavid.Hollister@Sun.COM ((uint32_t)cdb[5]);
284910696SDavid.Hollister@Sun.COM /* Check for illegal bits */
285010696SDavid.Hollister@Sun.COM if (cdb[11]) {
285110696SDavid.Hollister@Sun.COM asc = 0x24; /* invalid field in cdb */
285210696SDavid.Hollister@Sun.COM }
285310696SDavid.Hollister@Sun.COM break;
285410696SDavid.Hollister@Sun.COM case SCMD_READ_G1:
285510696SDavid.Hollister@Sun.COM case SCMD_WRITE_G1:
285610696SDavid.Hollister@Sun.COM *xfr = (((uint32_t)cdb[7]) << 8) | ((uint32_t)cdb[8]);
285710696SDavid.Hollister@Sun.COM *lba =
285810696SDavid.Hollister@Sun.COM (((uint32_t)cdb[2]) << 24) |
285910696SDavid.Hollister@Sun.COM (((uint32_t)cdb[3]) << 16) |
286010696SDavid.Hollister@Sun.COM (((uint32_t)cdb[4]) << 8) |
286110696SDavid.Hollister@Sun.COM ((uint32_t)cdb[5]);
286210696SDavid.Hollister@Sun.COM /* Check for illegal bits */
286310696SDavid.Hollister@Sun.COM if (cdb[9]) {
286410696SDavid.Hollister@Sun.COM asc = 0x24; /* invalid field in cdb */
286510696SDavid.Hollister@Sun.COM }
286610696SDavid.Hollister@Sun.COM break;
286710696SDavid.Hollister@Sun.COM case SCMD_READ:
286810696SDavid.Hollister@Sun.COM case SCMD_WRITE:
286910696SDavid.Hollister@Sun.COM *xfr = cdb[4];
287010696SDavid.Hollister@Sun.COM if (*xfr == 0) {
287110696SDavid.Hollister@Sun.COM *xfr = 256;
287210696SDavid.Hollister@Sun.COM }
287310696SDavid.Hollister@Sun.COM *lba =
287410696SDavid.Hollister@Sun.COM (((uint32_t)cdb[1] & 0x1f) << 16) |
287510696SDavid.Hollister@Sun.COM (((uint32_t)cdb[2]) << 8) |
287610696SDavid.Hollister@Sun.COM ((uint32_t)cdb[3]);
287710696SDavid.Hollister@Sun.COM /* Check for illegal bits */
287810696SDavid.Hollister@Sun.COM if (cdb[5]) {
287910696SDavid.Hollister@Sun.COM asc = 0x24; /* invalid field in cdb */
288010696SDavid.Hollister@Sun.COM }
288110696SDavid.Hollister@Sun.COM break;
288210696SDavid.Hollister@Sun.COM }
288310696SDavid.Hollister@Sun.COM
288410696SDavid.Hollister@Sun.COM if (asc == 0) {
288510696SDavid.Hollister@Sun.COM if ((*lba + *xfr) > lbamax) {
288610696SDavid.Hollister@Sun.COM asc = 0x21; /* logical block out of range */
288710696SDavid.Hollister@Sun.COM }
288810696SDavid.Hollister@Sun.COM }
288910696SDavid.Hollister@Sun.COM return (asc);
289010696SDavid.Hollister@Sun.COM }
289110696SDavid.Hollister@Sun.COM
289210696SDavid.Hollister@Sun.COM /*
289310696SDavid.Hollister@Sun.COM * Called with pwrk lock held.
289410696SDavid.Hollister@Sun.COM */
289510696SDavid.Hollister@Sun.COM static void
pmcs_ioerror(pmcs_hw_t * pwp,pmcs_dtype_t t,pmcwork_t * pwrk,uint32_t * w,uint32_t status)289612060SDavid.Hollister@Sun.COM pmcs_ioerror(pmcs_hw_t *pwp, pmcs_dtype_t t, pmcwork_t *pwrk, uint32_t *w,
289712060SDavid.Hollister@Sun.COM uint32_t status)
289810696SDavid.Hollister@Sun.COM {
289910696SDavid.Hollister@Sun.COM static uint8_t por[] = {
290010696SDavid.Hollister@Sun.COM 0xf0, 0x0, 0x6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28
290110696SDavid.Hollister@Sun.COM };
290210696SDavid.Hollister@Sun.COM static uint8_t parity[] = {
290310696SDavid.Hollister@Sun.COM 0xf0, 0x0, 0xb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x47, 5
290410696SDavid.Hollister@Sun.COM };
290510696SDavid.Hollister@Sun.COM const char *msg;
290610696SDavid.Hollister@Sun.COM char buf[20];
290710696SDavid.Hollister@Sun.COM pmcs_cmd_t *sp = pwrk->arg;
290810696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp = pwrk->phy;
290910696SDavid.Hollister@Sun.COM struct scsi_pkt *pkt = CMD2PKT(sp);
291010696SDavid.Hollister@Sun.COM uint32_t resid;
291110696SDavid.Hollister@Sun.COM
291210696SDavid.Hollister@Sun.COM ASSERT(w != NULL);
291310696SDavid.Hollister@Sun.COM resid = LE_32(w[3]);
291410696SDavid.Hollister@Sun.COM
291510696SDavid.Hollister@Sun.COM msg = pmcs_status_str(status);
291610696SDavid.Hollister@Sun.COM if (msg == NULL) {
291710696SDavid.Hollister@Sun.COM (void) snprintf(buf, sizeof (buf), "Error 0x%x", status);
291810696SDavid.Hollister@Sun.COM msg = buf;
291910696SDavid.Hollister@Sun.COM }
292010696SDavid.Hollister@Sun.COM
292110696SDavid.Hollister@Sun.COM if (status != PMCOUT_STATUS_OK) {
292211601SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG1, phyp, NULL,
292310696SDavid.Hollister@Sun.COM "%s: device %s tag 0x%x status %s @ %llu", __func__,
292410696SDavid.Hollister@Sun.COM phyp->path, pwrk->htag, msg,
292510696SDavid.Hollister@Sun.COM (unsigned long long)gethrtime());
292610696SDavid.Hollister@Sun.COM }
292710696SDavid.Hollister@Sun.COM
292810696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_CMPLT; /* default reason */
292910696SDavid.Hollister@Sun.COM
293010696SDavid.Hollister@Sun.COM switch (status) {
293110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OK:
293210696SDavid.Hollister@Sun.COM if (t == SATA) {
293310696SDavid.Hollister@Sun.COM int i;
293410696SDavid.Hollister@Sun.COM fis_t fis;
293510696SDavid.Hollister@Sun.COM for (i = 0; i < sizeof (fis) / sizeof (fis[0]); i++) {
293610696SDavid.Hollister@Sun.COM fis[i] = LE_32(w[4+i]);
293710696SDavid.Hollister@Sun.COM }
293810696SDavid.Hollister@Sun.COM if ((fis[0] & 0xff) != FIS_REG_D2H) {
293911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
294010696SDavid.Hollister@Sun.COM "unexpected fis code 0x%x", fis[0] & 0xff);
294110696SDavid.Hollister@Sun.COM } else {
294211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
294311048SDavid.Hollister@Sun.COM "FIS ERROR");
294410696SDavid.Hollister@Sun.COM pmcs_fis_dump(pwp, fis);
294510696SDavid.Hollister@Sun.COM }
294610696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_TRAN_ERR;
294710696SDavid.Hollister@Sun.COM break;
294810696SDavid.Hollister@Sun.COM }
294910696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
295010696SDavid.Hollister@Sun.COM break;
295110696SDavid.Hollister@Sun.COM
295210696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_ABORTED:
295310696SDavid.Hollister@Sun.COM /*
295410696SDavid.Hollister@Sun.COM * Command successfully aborted.
295510696SDavid.Hollister@Sun.COM */
295610696SDavid.Hollister@Sun.COM if (phyp->dead) {
295710696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_DEV_GONE;
295810696SDavid.Hollister@Sun.COM pkt->pkt_state = STATE_GOT_BUS;
295910696SDavid.Hollister@Sun.COM } else if (pwrk->ssp_event != 0) {
296010696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_TRAN_ERR;
296110696SDavid.Hollister@Sun.COM pkt->pkt_state = STATE_GOT_BUS;
296210696SDavid.Hollister@Sun.COM } else if (pwrk->state == PMCS_WORK_STATE_TIMED_OUT) {
296310696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_TIMEOUT;
296410696SDavid.Hollister@Sun.COM pkt->pkt_statistics |= STAT_TIMEOUT;
296510696SDavid.Hollister@Sun.COM pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
296610696SDavid.Hollister@Sun.COM STATE_SENT_CMD;
296710696SDavid.Hollister@Sun.COM } else {
296810696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_ABORTED;
296910696SDavid.Hollister@Sun.COM pkt->pkt_statistics |= STAT_ABORTED;
297010696SDavid.Hollister@Sun.COM pkt->pkt_state = STATE_GOT_BUS | STATE_GOT_TARGET |
297110696SDavid.Hollister@Sun.COM STATE_SENT_CMD;
297210696SDavid.Hollister@Sun.COM }
297310696SDavid.Hollister@Sun.COM
297410696SDavid.Hollister@Sun.COM /*
297510696SDavid.Hollister@Sun.COM * PMCS_WORK_STATE_TIMED_OUT doesn't need to be preserved past
297610696SDavid.Hollister@Sun.COM * this point, so go ahead and mark it as aborted.
297710696SDavid.Hollister@Sun.COM */
297810696SDavid.Hollister@Sun.COM pwrk->state = PMCS_WORK_STATE_ABORTED;
297910696SDavid.Hollister@Sun.COM break;
298010696SDavid.Hollister@Sun.COM
298110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_UNDERFLOW:
298210696SDavid.Hollister@Sun.COM /*
298310696SDavid.Hollister@Sun.COM * This will only get called for SATA
298410696SDavid.Hollister@Sun.COM */
298510696SDavid.Hollister@Sun.COM pkt->pkt_resid = resid;
298610696SDavid.Hollister@Sun.COM if (pkt->pkt_dma_len < pkt->pkt_resid) {
298710696SDavid.Hollister@Sun.COM (void) pmcs_set_resid(pkt, pkt->pkt_dma_len, resid);
298810696SDavid.Hollister@Sun.COM }
298910696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_GOOD, NULL, 0, phyp->path);
299010696SDavid.Hollister@Sun.COM break;
299110696SDavid.Hollister@Sun.COM
299210696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_NO_DEVICE:
299310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_SATA_LINK_TIMEOUT:
299410696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_DEV_GONE;
299510696SDavid.Hollister@Sun.COM break;
299610696SDavid.Hollister@Sun.COM
299710696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_WRONG_DESTINATION:
299810696SDavid.Hollister@Sun.COM /*
299910696SDavid.Hollister@Sun.COM * Need to do rediscovery. We probably have
300010696SDavid.Hollister@Sun.COM * the wrong device (disk swap), so kill
300110696SDavid.Hollister@Sun.COM * this one.
300210696SDavid.Hollister@Sun.COM */
300310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_PROTOCOL_NOT_SUPPORTED:
300410696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_ZONE_VIOLATION:
300510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_CONNECTION_RATE_NOT_SUPPORTED:
300611601SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_UNKNOWN_ERROR:
300710696SDavid.Hollister@Sun.COM /*
300810696SDavid.Hollister@Sun.COM * Need to do rediscovery.
300910696SDavid.Hollister@Sun.COM */
301010696SDavid.Hollister@Sun.COM if (!phyp->dead) {
301110696SDavid.Hollister@Sun.COM mutex_exit(&pwrk->lock);
301210696SDavid.Hollister@Sun.COM pmcs_lock_phy(pwrk->phy);
301310696SDavid.Hollister@Sun.COM pmcs_kill_changed(pwp, pwrk->phy, 0);
301410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(pwrk->phy);
301510696SDavid.Hollister@Sun.COM mutex_enter(&pwrk->lock);
301610696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_INCOMPLETE;
301710696SDavid.Hollister@Sun.COM pkt->pkt_state = STATE_GOT_BUS;
301810696SDavid.Hollister@Sun.COM } else {
301910696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_DEV_GONE;
302010696SDavid.Hollister@Sun.COM }
302110696SDavid.Hollister@Sun.COM break;
302210696SDavid.Hollister@Sun.COM
302310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_BREAK:
302410696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_IT_NEXUS_LOSS:
302510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPENCNX_ERROR_BAD_DESTINATION:
302610696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_ERROR_NAK_RECEIVED:
302710696SDavid.Hollister@Sun.COM /* cmd is pending on the target */
302810696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_OFFSET_MISMATCH:
302910696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_REJECTED_NCQ_MODE:
303010696SDavid.Hollister@Sun.COM /* transitory - commands sent while in NCQ failure mode */
303110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_ABORTED_NCQ_MODE:
303210696SDavid.Hollister@Sun.COM /* NCQ failure */
303310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_PORT_IN_RESET:
303410696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERR_BREAK:
303510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERR_PHY_NOT_READY:
303610696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_INCOMPLETE;
303710696SDavid.Hollister@Sun.COM pkt->pkt_state = STATE_GOT_BUS;
303810696SDavid.Hollister@Sun.COM break;
303910696SDavid.Hollister@Sun.COM
304010696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_OPEN_RETRY_TIMEOUT:
304112060SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
304212060SDavid.Hollister@Sun.COM "STATUS_BUSY for htag 0x%08x", sp->cmd_tag);
304310696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_BUSY, NULL, 0, phyp->path);
304410696SDavid.Hollister@Sun.COM break;
304510696SDavid.Hollister@Sun.COM
304610696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_OPEN_CNX_ERROR_STP_RESOURCES_BUSY:
304710696SDavid.Hollister@Sun.COM /* synthesize a RESERVATION CONFLICT */
304811307SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, phyp->target,
304911307SDavid.Hollister@Sun.COM "%s: Potential affiliation active on 0x%" PRIx64, __func__,
305011307SDavid.Hollister@Sun.COM pmcs_barray2wwn(phyp->sas_address));
305110696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_RESERVATION_CONFLICT, NULL,
305210696SDavid.Hollister@Sun.COM 0, phyp->path);
305310696SDavid.Hollister@Sun.COM break;
305410696SDavid.Hollister@Sun.COM
305510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_ABORTED_DUE_TO_SRST:
305610696SDavid.Hollister@Sun.COM /* synthesize a power-on/reset */
305710696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_CHECK, por, sizeof (por),
305810696SDavid.Hollister@Sun.COM phyp->path);
305910696SDavid.Hollister@Sun.COM break;
306010696SDavid.Hollister@Sun.COM
306110696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_UNEXPECTED_PHASE:
306210696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_RDY_OVERRUN:
306310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_RDY_NOT_EXPECTED:
306410696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_ACK_NAK_TIMEOUT:
306510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_BREAK_BEFORE_ACK_NACK:
306610696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_CMD_ISSUE_PHY_DOWN_BEFORE_ACK_NAK:
306710696SDavid.Hollister@Sun.COM /* synthesize a PARITY ERROR */
306810696SDavid.Hollister@Sun.COM pmcs_latch_status(pwp, sp, STATUS_CHECK, parity,
306910696SDavid.Hollister@Sun.COM sizeof (parity), phyp->path);
307010696SDavid.Hollister@Sun.COM break;
307110696SDavid.Hollister@Sun.COM
307210696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_XFER_ERROR_DMA:
307310696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_IO_NOT_VALID:
307410696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_PROG_ERROR:
307510696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_PEER_ABORTED:
307610696SDavid.Hollister@Sun.COM case PMCOUT_STATUS_XFER_ERROR_SATA: /* non-NCQ failure */
307710696SDavid.Hollister@Sun.COM default:
307810696SDavid.Hollister@Sun.COM pkt->pkt_reason = CMD_TRAN_ERR;
307910696SDavid.Hollister@Sun.COM break;
308010696SDavid.Hollister@Sun.COM }
308110696SDavid.Hollister@Sun.COM }
308210696SDavid.Hollister@Sun.COM
308310696SDavid.Hollister@Sun.COM /*
308410696SDavid.Hollister@Sun.COM * Latch up SCSI status
308510696SDavid.Hollister@Sun.COM */
308610696SDavid.Hollister@Sun.COM
308710696SDavid.Hollister@Sun.COM void
pmcs_latch_status(pmcs_hw_t * pwp,pmcs_cmd_t * sp,uint8_t status,uint8_t * snsp,size_t snslen,char * path)308810696SDavid.Hollister@Sun.COM pmcs_latch_status(pmcs_hw_t *pwp, pmcs_cmd_t *sp, uint8_t status,
308910696SDavid.Hollister@Sun.COM uint8_t *snsp, size_t snslen, char *path)
309010696SDavid.Hollister@Sun.COM {
309110696SDavid.Hollister@Sun.COM static const char c1[] =
309210696SDavid.Hollister@Sun.COM "%s: Status Byte 0x%02x for CDB0=0x%02x (%02x %02x %02x) "
309310696SDavid.Hollister@Sun.COM "HTAG 0x%x @ %llu";
309410696SDavid.Hollister@Sun.COM static const char c2[] =
309510696SDavid.Hollister@Sun.COM "%s: Status Byte 0x%02x for CDB0=0x%02x HTAG 0x%x @ %llu";
309610696SDavid.Hollister@Sun.COM
309710696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_state |= STATE_GOT_BUS | STATE_GOT_TARGET |
309810696SDavid.Hollister@Sun.COM STATE_SENT_CMD | STATE_GOT_STATUS;
309910696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_scbp[0] = status;
310010696SDavid.Hollister@Sun.COM
310110696SDavid.Hollister@Sun.COM if (status == STATUS_CHECK && snsp &&
310210696SDavid.Hollister@Sun.COM (size_t)SCSA_STSLEN(sp) >= sizeof (struct scsi_arq_status)) {
310310696SDavid.Hollister@Sun.COM struct scsi_arq_status *aqp =
310410696SDavid.Hollister@Sun.COM (void *) CMD2PKT(sp)->pkt_scbp;
310510696SDavid.Hollister@Sun.COM size_t amt = sizeof (struct scsi_extended_sense);
310610696SDavid.Hollister@Sun.COM uint8_t key = scsi_sense_key(snsp);
310710696SDavid.Hollister@Sun.COM uint8_t asc = scsi_sense_asc(snsp);
310810696SDavid.Hollister@Sun.COM uint8_t ascq = scsi_sense_ascq(snsp);
310910696SDavid.Hollister@Sun.COM if (amt > snslen) {
311010696SDavid.Hollister@Sun.COM amt = snslen;
311110696SDavid.Hollister@Sun.COM }
311211048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c1, path,
311311048SDavid.Hollister@Sun.COM status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff, key, asc, ascq,
311410696SDavid.Hollister@Sun.COM sp->cmd_tag, (unsigned long long)gethrtime());
311510696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_state |= STATE_ARQ_DONE;
311610696SDavid.Hollister@Sun.COM (*(uint8_t *)&aqp->sts_rqpkt_status) = STATUS_GOOD;
311710696SDavid.Hollister@Sun.COM aqp->sts_rqpkt_statistics = 0;
311810696SDavid.Hollister@Sun.COM aqp->sts_rqpkt_reason = CMD_CMPLT;
311910696SDavid.Hollister@Sun.COM aqp->sts_rqpkt_state = STATE_GOT_BUS |
312010696SDavid.Hollister@Sun.COM STATE_GOT_TARGET | STATE_SENT_CMD |
312110696SDavid.Hollister@Sun.COM STATE_XFERRED_DATA | STATE_GOT_STATUS;
312210696SDavid.Hollister@Sun.COM (void) memcpy(&aqp->sts_sensedata, snsp, amt);
312310696SDavid.Hollister@Sun.COM if (aqp->sts_sensedata.es_class != CLASS_EXTENDED_SENSE) {
312410696SDavid.Hollister@Sun.COM aqp->sts_rqpkt_reason = CMD_TRAN_ERR;
312510696SDavid.Hollister@Sun.COM aqp->sts_rqpkt_state = 0;
312610696SDavid.Hollister@Sun.COM aqp->sts_rqpkt_resid =
312710696SDavid.Hollister@Sun.COM sizeof (struct scsi_extended_sense);
312810696SDavid.Hollister@Sun.COM } else {
312910696SDavid.Hollister@Sun.COM aqp->sts_rqpkt_resid =
313010696SDavid.Hollister@Sun.COM sizeof (struct scsi_extended_sense) - amt;
313110696SDavid.Hollister@Sun.COM }
313210696SDavid.Hollister@Sun.COM } else if (status) {
313311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_SCSI_STATUS, NULL, NULL, c2,
313410696SDavid.Hollister@Sun.COM path, status, CMD2PKT(sp)->pkt_cdbp[0] & 0xff,
313510696SDavid.Hollister@Sun.COM sp->cmd_tag, (unsigned long long)gethrtime());
313610696SDavid.Hollister@Sun.COM }
313710696SDavid.Hollister@Sun.COM
313810696SDavid.Hollister@Sun.COM CMD2PKT(sp)->pkt_reason = CMD_CMPLT;
313910696SDavid.Hollister@Sun.COM }
314010696SDavid.Hollister@Sun.COM
314110696SDavid.Hollister@Sun.COM /*
314210696SDavid.Hollister@Sun.COM * Calculate and set packet residual and return the amount
314310696SDavid.Hollister@Sun.COM * left over after applying various filters.
314410696SDavid.Hollister@Sun.COM */
314510696SDavid.Hollister@Sun.COM size_t
pmcs_set_resid(struct scsi_pkt * pkt,size_t amt,uint32_t cdbamt)314610696SDavid.Hollister@Sun.COM pmcs_set_resid(struct scsi_pkt *pkt, size_t amt, uint32_t cdbamt)
314710696SDavid.Hollister@Sun.COM {
314810696SDavid.Hollister@Sun.COM pkt->pkt_resid = cdbamt;
314910696SDavid.Hollister@Sun.COM if (amt > pkt->pkt_resid) {
315010696SDavid.Hollister@Sun.COM amt = pkt->pkt_resid;
315110696SDavid.Hollister@Sun.COM }
315210696SDavid.Hollister@Sun.COM if (amt > pkt->pkt_dma_len) {
315310696SDavid.Hollister@Sun.COM amt = pkt->pkt_dma_len;
315410696SDavid.Hollister@Sun.COM }
315510696SDavid.Hollister@Sun.COM return (amt);
315610696SDavid.Hollister@Sun.COM }
315710696SDavid.Hollister@Sun.COM
315810696SDavid.Hollister@Sun.COM /*
315911847SDavid.Hollister@Sun.COM * Return the existing target softstate (unlocked) if there is one. If so,
316011847SDavid.Hollister@Sun.COM * the PHY is locked and that lock must be freed by the caller after the
316111847SDavid.Hollister@Sun.COM * target/PHY linkage is established. If there isn't one, and alloc_tgt is
316211847SDavid.Hollister@Sun.COM * TRUE, then allocate one.
316310696SDavid.Hollister@Sun.COM */
316410696SDavid.Hollister@Sun.COM pmcs_xscsi_t *
pmcs_get_target(pmcs_iport_t * iport,char * tgt_port,boolean_t alloc_tgt)316511692SJesse.Butler@Sun.COM pmcs_get_target(pmcs_iport_t *iport, char *tgt_port, boolean_t alloc_tgt)
316610696SDavid.Hollister@Sun.COM {
316710696SDavid.Hollister@Sun.COM pmcs_hw_t *pwp = iport->pwp;
316810696SDavid.Hollister@Sun.COM pmcs_phy_t *phyp;
316910696SDavid.Hollister@Sun.COM pmcs_xscsi_t *tgt;
317010696SDavid.Hollister@Sun.COM uint64_t wwn;
317110696SDavid.Hollister@Sun.COM char unit_address[PMCS_MAX_UA_SIZE];
317210696SDavid.Hollister@Sun.COM int ua_form = 1;
317310696SDavid.Hollister@Sun.COM
317410696SDavid.Hollister@Sun.COM /*
317510696SDavid.Hollister@Sun.COM * Find the PHY for this target
317610696SDavid.Hollister@Sun.COM */
317710696SDavid.Hollister@Sun.COM phyp = pmcs_find_phy_by_sas_address(pwp, iport, NULL, tgt_port);
317810696SDavid.Hollister@Sun.COM if (phyp == NULL) {
317911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG3, NULL, NULL,
318011048SDavid.Hollister@Sun.COM "%s: No PHY for target @ %s", __func__, tgt_port);
318110696SDavid.Hollister@Sun.COM return (NULL);
318210696SDavid.Hollister@Sun.COM }
318310696SDavid.Hollister@Sun.COM
318410696SDavid.Hollister@Sun.COM tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, tgt_port);
318510696SDavid.Hollister@Sun.COM
318610696SDavid.Hollister@Sun.COM if (tgt) {
318711847SDavid.Hollister@Sun.COM mutex_enter(&tgt->statlock);
318810696SDavid.Hollister@Sun.COM /*
318910696SDavid.Hollister@Sun.COM * There's already a target. Check its PHY pointer to see
319010696SDavid.Hollister@Sun.COM * if we need to clear the old linkages
319110696SDavid.Hollister@Sun.COM */
319210696SDavid.Hollister@Sun.COM if (tgt->phy && (tgt->phy != phyp)) {
319311048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
319410696SDavid.Hollister@Sun.COM "%s: Target PHY updated from %p to %p", __func__,
319510696SDavid.Hollister@Sun.COM (void *)tgt->phy, (void *)phyp);
319610696SDavid.Hollister@Sun.COM if (!IS_ROOT_PHY(tgt->phy)) {
319710696SDavid.Hollister@Sun.COM pmcs_dec_phy_ref_count(tgt->phy);
319810696SDavid.Hollister@Sun.COM pmcs_inc_phy_ref_count(phyp);
319910696SDavid.Hollister@Sun.COM }
320010696SDavid.Hollister@Sun.COM tgt->phy->target = NULL;
320110696SDavid.Hollister@Sun.COM }
320210696SDavid.Hollister@Sun.COM
320311847SDavid.Hollister@Sun.COM /*
320411847SDavid.Hollister@Sun.COM * If this target has no PHY pointer and alloc_tgt is FALSE,
320511847SDavid.Hollister@Sun.COM * that implies we expect the target to already exist. This
320611847SDavid.Hollister@Sun.COM * implies that there has already been a tran_tgt_init on at
320711847SDavid.Hollister@Sun.COM * least one LU.
320811847SDavid.Hollister@Sun.COM */
320911847SDavid.Hollister@Sun.COM if ((tgt->phy == NULL) && !alloc_tgt) {
321011847SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, tgt,
321111847SDavid.Hollister@Sun.COM "%s: Establish linkage from new PHY to old target @"
321211847SDavid.Hollister@Sun.COM "%s", __func__, tgt->unit_address);
321311847SDavid.Hollister@Sun.COM for (int idx = 0; idx < tgt->ref_count; idx++) {
321411847SDavid.Hollister@Sun.COM pmcs_inc_phy_ref_count(phyp);
321511847SDavid.Hollister@Sun.COM }
321611847SDavid.Hollister@Sun.COM }
321711847SDavid.Hollister@Sun.COM
321812937Sjesse.butler@oracle.com /*
321912937Sjesse.butler@oracle.com * Set this target pointer back up, since it's been
322012937Sjesse.butler@oracle.com * through pmcs_clear_xp().
322112937Sjesse.butler@oracle.com */
322212937Sjesse.butler@oracle.com tgt->dev_gone = 0;
322312937Sjesse.butler@oracle.com tgt->assigned = 1;
322412937Sjesse.butler@oracle.com tgt->dtype = phyp->dtype;
322512937Sjesse.butler@oracle.com tgt->dev_state = PMCS_DEVICE_STATE_OPERATIONAL;
322610696SDavid.Hollister@Sun.COM tgt->phy = phyp;
322710696SDavid.Hollister@Sun.COM phyp->target = tgt;
322811847SDavid.Hollister@Sun.COM
322911847SDavid.Hollister@Sun.COM mutex_exit(&tgt->statlock);
323010696SDavid.Hollister@Sun.COM return (tgt);
323110696SDavid.Hollister@Sun.COM }
323210696SDavid.Hollister@Sun.COM
323310696SDavid.Hollister@Sun.COM /*
323410696SDavid.Hollister@Sun.COM * Make sure the PHY we found is on the correct iport
323510696SDavid.Hollister@Sun.COM */
323610696SDavid.Hollister@Sun.COM if (phyp->iport != iport) {
323711048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL,
323810696SDavid.Hollister@Sun.COM "%s: No target at %s on this iport", __func__, tgt_port);
323910696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
324010696SDavid.Hollister@Sun.COM return (NULL);
324110696SDavid.Hollister@Sun.COM }
324210696SDavid.Hollister@Sun.COM
324310696SDavid.Hollister@Sun.COM /*
324411692SJesse.Butler@Sun.COM * If this was just a lookup (i.e. alloc_tgt is false), return now.
324511692SJesse.Butler@Sun.COM */
324611692SJesse.Butler@Sun.COM if (alloc_tgt == B_FALSE) {
324711692SJesse.Butler@Sun.COM pmcs_unlock_phy(phyp);
324811692SJesse.Butler@Sun.COM return (NULL);
324911692SJesse.Butler@Sun.COM }
325011692SJesse.Butler@Sun.COM
325111692SJesse.Butler@Sun.COM /*
325210696SDavid.Hollister@Sun.COM * Allocate the new softstate
325310696SDavid.Hollister@Sun.COM */
325410696SDavid.Hollister@Sun.COM wwn = pmcs_barray2wwn(phyp->sas_address);
325510696SDavid.Hollister@Sun.COM (void) scsi_wwn_to_wwnstr(wwn, ua_form, unit_address);
325610696SDavid.Hollister@Sun.COM
325710696SDavid.Hollister@Sun.COM if (ddi_soft_state_bystr_zalloc(iport->tgt_sstate, unit_address) !=
325810696SDavid.Hollister@Sun.COM DDI_SUCCESS) {
325911048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
326010696SDavid.Hollister@Sun.COM "%s: Couldn't alloc softstate for device at %s",
326110696SDavid.Hollister@Sun.COM __func__, unit_address);
326210696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
326310696SDavid.Hollister@Sun.COM return (NULL);
326410696SDavid.Hollister@Sun.COM }
326510696SDavid.Hollister@Sun.COM
326610696SDavid.Hollister@Sun.COM tgt = ddi_soft_state_bystr_get(iport->tgt_sstate, unit_address);
326711693SDavid.Hollister@Sun.COM ASSERT(tgt != NULL);
326810696SDavid.Hollister@Sun.COM STAILQ_INIT(&tgt->wq);
326910696SDavid.Hollister@Sun.COM STAILQ_INIT(&tgt->aq);
327010696SDavid.Hollister@Sun.COM STAILQ_INIT(&tgt->sq);
327110696SDavid.Hollister@Sun.COM mutex_init(&tgt->statlock, NULL, MUTEX_DRIVER,
327210696SDavid.Hollister@Sun.COM DDI_INTR_PRI(pwp->intr_pri));
327310696SDavid.Hollister@Sun.COM mutex_init(&tgt->wqlock, NULL, MUTEX_DRIVER,
327410696SDavid.Hollister@Sun.COM DDI_INTR_PRI(pwp->intr_pri));
327510696SDavid.Hollister@Sun.COM mutex_init(&tgt->aqlock, NULL, MUTEX_DRIVER,
327610696SDavid.Hollister@Sun.COM DDI_INTR_PRI(pwp->intr_pri));
327710696SDavid.Hollister@Sun.COM cv_init(&tgt->reset_cv, NULL, CV_DRIVER, NULL);
327810696SDavid.Hollister@Sun.COM cv_init(&tgt->abort_cv, NULL, CV_DRIVER, NULL);
327911501SDavid.Hollister@Sun.COM list_create(&tgt->lun_list, sizeof (pmcs_lun_t),
328011501SDavid.Hollister@Sun.COM offsetof(pmcs_lun_t, lun_list_next));
328110696SDavid.Hollister@Sun.COM tgt->qdepth = 1;
328210696SDavid.Hollister@Sun.COM tgt->target_num = PMCS_INVALID_TARGET_NUM;
328310696SDavid.Hollister@Sun.COM bcopy(unit_address, tgt->unit_address, PMCS_MAX_UA_SIZE);
328410696SDavid.Hollister@Sun.COM tgt->pwp = pwp;
328510696SDavid.Hollister@Sun.COM tgt->ua = strdup(iport->ua);
328610696SDavid.Hollister@Sun.COM tgt->phy = phyp;
328710696SDavid.Hollister@Sun.COM ASSERT((phyp->target == NULL) || (phyp->target == tgt));
328810696SDavid.Hollister@Sun.COM if (phyp->target == NULL) {
328910696SDavid.Hollister@Sun.COM phyp->target = tgt;
329010696SDavid.Hollister@Sun.COM }
329110696SDavid.Hollister@Sun.COM
329210696SDavid.Hollister@Sun.COM /*
329310696SDavid.Hollister@Sun.COM * Don't allocate LUN softstate for SMP targets
329410696SDavid.Hollister@Sun.COM */
329510696SDavid.Hollister@Sun.COM if (phyp->dtype == EXPANDER) {
329610696SDavid.Hollister@Sun.COM return (tgt);
329710696SDavid.Hollister@Sun.COM }
329810696SDavid.Hollister@Sun.COM
329910696SDavid.Hollister@Sun.COM if (ddi_soft_state_bystr_init(&tgt->lun_sstate,
330010696SDavid.Hollister@Sun.COM sizeof (pmcs_lun_t), PMCS_LUN_SSTATE_SZ) != 0) {
330111048SDavid.Hollister@Sun.COM pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, phyp, tgt,
330210696SDavid.Hollister@Sun.COM "%s: LUN soft_state_bystr_init failed", __func__);
330310696SDavid.Hollister@Sun.COM ddi_soft_state_bystr_free(iport->tgt_sstate, tgt_port);
330410696SDavid.Hollister@Sun.COM pmcs_unlock_phy(phyp);
330510696SDavid.Hollister@Sun.COM return (NULL);
330610696SDavid.Hollister@Sun.COM }
330710696SDavid.Hollister@Sun.COM
330810696SDavid.Hollister@Sun.COM return (tgt);
330910696SDavid.Hollister@Sun.COM }
3310