1*8226594fSRick McNeal /*
2*8226594fSRick McNeal  * This file and its contents are supplied under the terms of the
3*8226594fSRick McNeal  * Common Development and Distribution License ("CDDL"), version 1.0.
4*8226594fSRick McNeal  * You may only use this file in accordance with the terms of version
5*8226594fSRick McNeal  * 1.0 of the CDDL.
6*8226594fSRick McNeal  *
7*8226594fSRick McNeal  * A full copy of the text of the CDDL should have accompanied this
8*8226594fSRick McNeal  * source.  A copy of the CDDL is also available via the Internet at
9*8226594fSRick McNeal  * http://www.illumos.org/license/CDDL.
10*8226594fSRick McNeal  */
11*8226594fSRick McNeal 
12*8226594fSRick McNeal /*
13*8226594fSRick McNeal  * Copyright 2023 Tintri by DDN, Inc. All rights reserved.
14*8226594fSRick McNeal  * Copyright 2021 RackTop Systems, Inc.
15*8226594fSRick McNeal  */
16*8226594fSRick McNeal 
17*8226594fSRick McNeal /*
18*8226594fSRick McNeal  * This file contains all routines necessary to interface with SCSA trans.
19*8226594fSRick McNeal  */
20*8226594fSRick McNeal #include <smartpqi.h>
21*8226594fSRick McNeal 
22*8226594fSRick McNeal /*
23*8226594fSRick McNeal  * []------------------------------------------------------------------[]
24*8226594fSRick McNeal  * | Forward declarations for SCSA trans routines.			|
25*8226594fSRick McNeal  * []------------------------------------------------------------------[]
26*8226594fSRick McNeal  */
27*8226594fSRick McNeal static int pqi_scsi_tgt_init(dev_info_t *hba_dip, dev_info_t *tgt_dip,
28*8226594fSRick McNeal     scsi_hba_tran_t *hba_tran, struct scsi_device *sd);
29*8226594fSRick McNeal static void pqi_scsi_tgt_free(dev_info_t *hba_dip, dev_info_t *tgt_dip,
30*8226594fSRick McNeal     scsi_hba_tran_t *hba_tran, struct scsi_device *sd);
31*8226594fSRick McNeal static int pqi_start(struct scsi_address *ap, struct scsi_pkt *pkt);
32*8226594fSRick McNeal static int pqi_scsi_reset(struct scsi_address *ap, int level);
33*8226594fSRick McNeal static int pqi_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt);
34*8226594fSRick McNeal static int pqi_scsi_getcap(struct scsi_address *ap, char *cap, int tgtonly);
35*8226594fSRick McNeal static int pqi_scsi_setcap(struct scsi_address *ap, char *cap, int value,
36*8226594fSRick McNeal     int tgtonly);
37*8226594fSRick McNeal static struct scsi_pkt *pqi_init_pkt(struct scsi_address *ap,
38*8226594fSRick McNeal     struct scsi_pkt *pkt, struct buf *bp, int cmdlen, int statuslen, int tgtlen,
39*8226594fSRick McNeal     int flags,  int (*callback)(), caddr_t arg);
40*8226594fSRick McNeal static void pqi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt);
41*8226594fSRick McNeal static void pqi_dmafree(struct scsi_address *ap, struct scsi_pkt *pkt);
42*8226594fSRick McNeal static void pqi_sync_pkt(struct scsi_address *ap, struct scsi_pkt *pkt);
43*8226594fSRick McNeal static int pqi_reset_notify(struct scsi_address *ap, int flag,
44*8226594fSRick McNeal     void (*callback)(caddr_t), caddr_t arg);
45*8226594fSRick McNeal static int pqi_quiesce(dev_info_t *dip);
46*8226594fSRick McNeal static int pqi_unquiesce(dev_info_t *dip);
47*8226594fSRick McNeal static int pqi_bus_config(dev_info_t *pdip, uint_t flag,
48*8226594fSRick McNeal     ddi_bus_config_op_t op, void *arg, dev_info_t **childp);
49*8226594fSRick McNeal 
50*8226594fSRick McNeal /* ---- Support method declaration ---- */
51*8226594fSRick McNeal static int config_one(dev_info_t *pdip, pqi_state_t *s,  pqi_device_t *,
52*8226594fSRick McNeal     dev_info_t **childp);
53*8226594fSRick McNeal static void abort_all(struct scsi_address *ap, pqi_state_t *s);
54*8226594fSRick McNeal static int cmd_ext_alloc(pqi_cmd_t *cmd, int kf);
55*8226594fSRick McNeal static void cmd_ext_free(pqi_cmd_t *cmd);
56*8226594fSRick McNeal static boolean_t is_physical_dev(pqi_device_t *d);
57*8226594fSRick McNeal static void cmd_timeout_scan(void *);
58*8226594fSRick McNeal 
59*8226594fSRick McNeal boolean_t
smartpqi_register_hba(pqi_state_t * s)60*8226594fSRick McNeal smartpqi_register_hba(pqi_state_t *s)
61*8226594fSRick McNeal {
62*8226594fSRick McNeal 	scsi_hba_tran_t		*tran;
63*8226594fSRick McNeal 	int			flags;
64*8226594fSRick McNeal 	char			iport_str[16];
65*8226594fSRick McNeal 	int			instance = ddi_get_instance(s->s_dip);
66*8226594fSRick McNeal 
67*8226594fSRick McNeal 	tran = scsi_hba_tran_alloc(s->s_dip, SCSI_HBA_CANSLEEP);
68*8226594fSRick McNeal 	if (tran == NULL)
69*8226594fSRick McNeal 		return (B_FALSE);
70*8226594fSRick McNeal 	s->s_tran = tran;
71*8226594fSRick McNeal 
72*8226594fSRick McNeal 	tran->tran_hba_private		= s;
73*8226594fSRick McNeal 	tran->tran_tgt_private		= NULL;
74*8226594fSRick McNeal 
75*8226594fSRick McNeal 	tran->tran_tgt_init		= pqi_scsi_tgt_init;
76*8226594fSRick McNeal 	tran->tran_tgt_free		= pqi_scsi_tgt_free;
77*8226594fSRick McNeal 	tran->tran_tgt_probe		= scsi_hba_probe;
78*8226594fSRick McNeal 
79*8226594fSRick McNeal 	tran->tran_start		= pqi_start;
80*8226594fSRick McNeal 	tran->tran_reset		= pqi_scsi_reset;
81*8226594fSRick McNeal 	tran->tran_abort		= pqi_scsi_abort;
82*8226594fSRick McNeal 	tran->tran_getcap		= pqi_scsi_getcap;
83*8226594fSRick McNeal 	tran->tran_setcap		= pqi_scsi_setcap;
84*8226594fSRick McNeal 	tran->tran_bus_config		= pqi_bus_config;
85*8226594fSRick McNeal 
86*8226594fSRick McNeal 	tran->tran_init_pkt		= pqi_init_pkt;
87*8226594fSRick McNeal 	tran->tran_destroy_pkt		= pqi_destroy_pkt;
88*8226594fSRick McNeal 	tran->tran_dmafree		= pqi_dmafree;
89*8226594fSRick McNeal 	tran->tran_sync_pkt		= pqi_sync_pkt;
90*8226594fSRick McNeal 
91*8226594fSRick McNeal 	tran->tran_reset_notify		= pqi_reset_notify;
92*8226594fSRick McNeal 	tran->tran_quiesce		= pqi_quiesce;
93*8226594fSRick McNeal 	tran->tran_unquiesce		= pqi_unquiesce;
94*8226594fSRick McNeal 	tran->tran_bus_reset		= NULL;
95*8226594fSRick McNeal 
96*8226594fSRick McNeal 	tran->tran_add_eventcall	= NULL;
97*8226594fSRick McNeal 	tran->tran_get_eventcookie	= NULL;
98*8226594fSRick McNeal 	tran->tran_post_event		= NULL;
99*8226594fSRick McNeal 	tran->tran_remove_eventcall	= NULL;
100*8226594fSRick McNeal 	tran->tran_bus_config		= pqi_bus_config;
101*8226594fSRick McNeal 	tran->tran_interconnect_type	= INTERCONNECT_SAS;
102*8226594fSRick McNeal 
103*8226594fSRick McNeal 	/*
104*8226594fSRick McNeal 	 * scsi_vhci needs to have "initiator-port" set, but doesn't
105*8226594fSRick McNeal 	 * seem to care what it's set to. iSCSI uses the InitiatorName
106*8226594fSRick McNeal 	 * whereas mpt_sas uses the WWN port id, but this HBA doesn't
107*8226594fSRick McNeal 	 * have such a value. So, for now the instance number will be used.
108*8226594fSRick McNeal 	 */
109*8226594fSRick McNeal 	(void) snprintf(iport_str, sizeof (iport_str), "0x%x", instance);
110*8226594fSRick McNeal 	if (ddi_prop_update_string(DDI_DEV_T_NONE, s->s_dip,
111*8226594fSRick McNeal 	    SCSI_ADDR_PROP_INITIATOR_PORT, iport_str) != DDI_PROP_SUCCESS) {
112*8226594fSRick McNeal 		cmn_err(CE_WARN, "%s: Failed to create prop (%s) on %d\n",
113*8226594fSRick McNeal 		    __func__, SCSI_ADDR_PROP_INITIATOR_PORT, instance);
114*8226594fSRick McNeal 	}
115*8226594fSRick McNeal 
116*8226594fSRick McNeal 	flags = SCSI_HBA_ADDR_COMPLEX | SCSI_HBA_TRAN_SCB;
117*8226594fSRick McNeal 	if (scsi_hba_attach_setup(s->s_dip, &s->s_msg_dma_attr, tran,
118*8226594fSRick McNeal 	    flags) != DDI_SUCCESS) {
119*8226594fSRick McNeal 		dev_err(s->s_dip, CE_NOTE, "scsi_hba_attach_setup failed");
120*8226594fSRick McNeal 		scsi_hba_tran_free(s->s_tran);
121*8226594fSRick McNeal 		s->s_tran = NULL;
122*8226594fSRick McNeal 		return (B_FALSE);
123*8226594fSRick McNeal 	}
124*8226594fSRick McNeal 
125*8226594fSRick McNeal 	if (!s->s_disable_mpxio) {
126*8226594fSRick McNeal 		if (mdi_phci_register(MDI_HCI_CLASS_SCSI, s->s_dip, 0) !=
127*8226594fSRick McNeal 		    MDI_SUCCESS) {
128*8226594fSRick McNeal 			cmn_err(CE_WARN, "%s: Failed to register with mpxio",
129*8226594fSRick McNeal 			    __func__);
130*8226594fSRick McNeal 			s->s_disable_mpxio = B_TRUE;
131*8226594fSRick McNeal 		}
132*8226594fSRick McNeal 	}
133*8226594fSRick McNeal 
134*8226594fSRick McNeal 	s->s_cmd_timeout = timeout(cmd_timeout_scan, s,
135*8226594fSRick McNeal 	    CMD_TIMEOUT_SCAN_SECS * drv_usectohz(MICROSEC));
136*8226594fSRick McNeal 
137*8226594fSRick McNeal 	return (B_TRUE);
138*8226594fSRick McNeal }
139*8226594fSRick McNeal 
140*8226594fSRick McNeal void
smartpqi_unregister_hba(pqi_state_t * s)141*8226594fSRick McNeal smartpqi_unregister_hba(pqi_state_t *s)
142*8226594fSRick McNeal {
143*8226594fSRick McNeal 	if (!s->s_disable_mpxio)
144*8226594fSRick McNeal 		(void) mdi_phci_unregister(s->s_dip, 0);
145*8226594fSRick McNeal 
146*8226594fSRick McNeal 	if (s->s_cmd_timeout != NULL) {
147*8226594fSRick McNeal 		(void) untimeout(s->s_cmd_timeout);
148*8226594fSRick McNeal 		s->s_cmd_timeout = NULL;
149*8226594fSRick McNeal 	}
150*8226594fSRick McNeal 
151*8226594fSRick McNeal 	if (s->s_tran == NULL)
152*8226594fSRick McNeal 		return;
153*8226594fSRick McNeal 	scsi_hba_tran_free(s->s_tran);
154*8226594fSRick McNeal 	s->s_tran = NULL;
155*8226594fSRick McNeal }
156*8226594fSRick McNeal 
157*8226594fSRick McNeal static int
pqi_scsi_tgt_init(dev_info_t * hba_dip __unused,dev_info_t * tgt_dip,scsi_hba_tran_t * hba_tran,struct scsi_device * sd)158*8226594fSRick McNeal pqi_scsi_tgt_init(dev_info_t *hba_dip __unused, dev_info_t *tgt_dip,
159*8226594fSRick McNeal     scsi_hba_tran_t *hba_tran, struct scsi_device *sd)
160*8226594fSRick McNeal {
161*8226594fSRick McNeal 	pqi_device_t	*d;
162*8226594fSRick McNeal 	pqi_state_t	*s	= hba_tran->tran_hba_private;
163*8226594fSRick McNeal 	mdi_pathinfo_t	*pip;
164*8226594fSRick McNeal 	int		type;
165*8226594fSRick McNeal 	char		*ua;
166*8226594fSRick McNeal 
167*8226594fSRick McNeal 	if ((ua = scsi_device_unit_address(sd)) == NULL) {
168*8226594fSRick McNeal 		return (DDI_FAILURE);
169*8226594fSRick McNeal 	}
170*8226594fSRick McNeal 
171*8226594fSRick McNeal 	if ((d = pqi_find_target_ua(s, ua)) == NULL) {
172*8226594fSRick McNeal 		return (DDI_FAILURE);
173*8226594fSRick McNeal 	}
174*8226594fSRick McNeal 
175*8226594fSRick McNeal 	scsi_device_hba_private_set(sd, d);
176*8226594fSRick McNeal 
177*8226594fSRick McNeal 	type = mdi_get_component_type(tgt_dip);
178*8226594fSRick McNeal 	if (type == MDI_COMPONENT_CLIENT) {
179*8226594fSRick McNeal 		char	wwid_str[64];
180*8226594fSRick McNeal 
181*8226594fSRick McNeal 		if ((pip = (mdi_pathinfo_t *)sd->sd_private) == NULL)
182*8226594fSRick McNeal 			return (DDI_NOT_WELL_FORMED);
183*8226594fSRick McNeal 
184*8226594fSRick McNeal 		(void) snprintf(wwid_str, sizeof (wwid_str), "%" PRIx64,
185*8226594fSRick McNeal 		    d->pd_wwid);
186*8226594fSRick McNeal 		(void) mdi_prop_update_string(pip, SCSI_ADDR_PROP_TARGET_PORT,
187*8226594fSRick McNeal 		    wwid_str);
188*8226594fSRick McNeal 	}
189*8226594fSRick McNeal 
190*8226594fSRick McNeal 	return (DDI_SUCCESS);
191*8226594fSRick McNeal }
192*8226594fSRick McNeal 
193*8226594fSRick McNeal static void
pqi_scsi_tgt_free(dev_info_t * hba_dip __unused,dev_info_t * tgt_dip __unused,scsi_hba_tran_t * hba_tran __unused,struct scsi_device * sd __unused)194*8226594fSRick McNeal pqi_scsi_tgt_free(dev_info_t *hba_dip __unused, dev_info_t *tgt_dip __unused,
195*8226594fSRick McNeal     scsi_hba_tran_t *hba_tran __unused, struct scsi_device *sd __unused)
196*8226594fSRick McNeal {
197*8226594fSRick McNeal }
198*8226594fSRick McNeal 
199*8226594fSRick McNeal /*
200*8226594fSRick McNeal  * Notes:
201*8226594fSRick McNeal  *      - transport the command to the addressed SCSI target/lun device
202*8226594fSRick McNeal  *      - normal operation is to schedule the command to be transported,
203*8226594fSRick McNeal  *        and return TRAN_ACCEPT if this is successful.
204*8226594fSRick McNeal  *      - if NO_INTR, tran_start must poll device for command completion
205*8226594fSRick McNeal  */
206*8226594fSRick McNeal static int
pqi_start(struct scsi_address * ap,struct scsi_pkt * pkt)207*8226594fSRick McNeal pqi_start(struct scsi_address *ap, struct scsi_pkt *pkt)
208*8226594fSRick McNeal {
209*8226594fSRick McNeal 	boolean_t	poll	= ((pkt->pkt_flags & FLAG_NOINTR) != 0);
210*8226594fSRick McNeal 	int		rc;
211*8226594fSRick McNeal 	pqi_cmd_t	*cmd	= PKT2CMD(pkt);
212*8226594fSRick McNeal 	pqi_state_t	*s	= ap->a_hba_tran->tran_hba_private;
213*8226594fSRick McNeal 
214*8226594fSRick McNeal 	ASSERT3P(cmd->pc_pkt, ==, pkt);
215*8226594fSRick McNeal 	ASSERT3P(cmd->pc_softc, ==, s);
216*8226594fSRick McNeal 
217*8226594fSRick McNeal 	if (pqi_is_offline(s) || !cmd->pc_device->pd_online)
218*8226594fSRick McNeal 		return (TRAN_FATAL_ERROR);
219*8226594fSRick McNeal 
220*8226594fSRick McNeal 	/*
221*8226594fSRick McNeal 	 * Reinitialize some fields because the packet may have been
222*8226594fSRick McNeal 	 * resubmitted.
223*8226594fSRick McNeal 	 */
224*8226594fSRick McNeal 	pkt->pkt_reason = CMD_CMPLT;
225*8226594fSRick McNeal 	pkt->pkt_state = 0;
226*8226594fSRick McNeal 	pkt->pkt_statistics = 0;
227*8226594fSRick McNeal 
228*8226594fSRick McNeal 	/* ---- Zero status byte ---- */
229*8226594fSRick McNeal 	*(pkt->pkt_scbp) = 0;
230*8226594fSRick McNeal 
231*8226594fSRick McNeal 	if ((cmd->pc_flags & PQI_FLAG_DMA_VALID) != 0) {
232*8226594fSRick McNeal 		ASSERT(cmd->pc_dma_count);
233*8226594fSRick McNeal 		pkt->pkt_resid = cmd->pc_dma_count;
234*8226594fSRick McNeal 
235*8226594fSRick McNeal 		/* ---- Sync consistent packets first (only write data) ---- */
236*8226594fSRick McNeal 		if (((cmd->pc_flags & PQI_FLAG_IO_IOPB) != 0) ||
237*8226594fSRick McNeal 		    ((cmd->pc_flags & PQI_FLAG_IO_READ) == 0)) {
238*8226594fSRick McNeal 			(void) ddi_dma_sync(cmd->pc_dmahdl, 0, 0,
239*8226594fSRick McNeal 			    DDI_DMA_SYNC_FORDEV);
240*8226594fSRick McNeal 		}
241*8226594fSRick McNeal 	}
242*8226594fSRick McNeal 
243*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
244*8226594fSRick McNeal 	if (HBA_IS_QUIESCED(s) && !poll) {
245*8226594fSRick McNeal 		mutex_exit(&s->s_mutex);
246*8226594fSRick McNeal 		return (TRAN_BUSY);
247*8226594fSRick McNeal 	}
248*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
249*8226594fSRick McNeal 
250*8226594fSRick McNeal 	rc = pqi_transport_command(s, cmd);
251*8226594fSRick McNeal 
252*8226594fSRick McNeal 	if (poll) {
253*8226594fSRick McNeal 		boolean_t	qnotify;
254*8226594fSRick McNeal 
255*8226594fSRick McNeal 		if (rc == TRAN_ACCEPT) {
256*8226594fSRick McNeal 			uint32_t	old_state;
257*8226594fSRick McNeal 			int		timeo;
258*8226594fSRick McNeal 
259*8226594fSRick McNeal 			timeo = pkt->pkt_time ? pkt->pkt_time :
260*8226594fSRick McNeal 			    SCSI_POLL_TIMEOUT;
261*8226594fSRick McNeal 			timeo *= MILLISEC / 2;
262*8226594fSRick McNeal 			old_state = pqi_disable_intr(s);
263*8226594fSRick McNeal 			do {
264*8226594fSRick McNeal 				drv_usecwait(MILLISEC / 2);
265*8226594fSRick McNeal 				pqi_process_io_intr(s, &s->s_queue_groups[0]);
266*8226594fSRick McNeal 				if (--timeo == 0) {
267*8226594fSRick McNeal 					pkt->pkt_state |= STAT_TIMEOUT;
268*8226594fSRick McNeal 					pkt->pkt_reason = CMD_TIMEOUT;
269*8226594fSRick McNeal 					break;
270*8226594fSRick McNeal 				}
271*8226594fSRick McNeal 			} while (pkt->pkt_state == 0);
272*8226594fSRick McNeal 			pqi_enable_intr(s, old_state);
273*8226594fSRick McNeal 		}
274*8226594fSRick McNeal 
275*8226594fSRick McNeal 		scsi_hba_pkt_comp(pkt);
276*8226594fSRick McNeal 
277*8226594fSRick McNeal 		mutex_enter(&s->s_mutex);
278*8226594fSRick McNeal 		qnotify = HBA_QUIESCED_PENDING(s);
279*8226594fSRick McNeal 		mutex_exit(&s->s_mutex);
280*8226594fSRick McNeal 
281*8226594fSRick McNeal 		if (qnotify)
282*8226594fSRick McNeal 			pqi_quiesced_notify(s);
283*8226594fSRick McNeal 	}
284*8226594fSRick McNeal 
285*8226594fSRick McNeal 	return (rc);
286*8226594fSRick McNeal }
287*8226594fSRick McNeal 
288*8226594fSRick McNeal static int
pqi_scsi_reset(struct scsi_address * ap,int level)289*8226594fSRick McNeal pqi_scsi_reset(struct scsi_address *ap, int level)
290*8226594fSRick McNeal {
291*8226594fSRick McNeal 	pqi_device_t	*d;
292*8226594fSRick McNeal 	pqi_state_t	*s;
293*8226594fSRick McNeal 	int		rval = FALSE;
294*8226594fSRick McNeal 
295*8226594fSRick McNeal 	s = ap->a_hba_tran->tran_hba_private;
296*8226594fSRick McNeal 	switch (level) {
297*8226594fSRick McNeal 	case RESET_TARGET:
298*8226594fSRick McNeal 	case RESET_LUN:
299*8226594fSRick McNeal 		if ((d = scsi_device_hba_private_get(ap->a.a_sd)) == NULL)
300*8226594fSRick McNeal 			break;
301*8226594fSRick McNeal 
302*8226594fSRick McNeal 		pqi_lun_reset(s, d);
303*8226594fSRick McNeal 		rval = TRUE;
304*8226594fSRick McNeal 		break;
305*8226594fSRick McNeal 
306*8226594fSRick McNeal 	case RESET_BUS:
307*8226594fSRick McNeal 	case RESET_ALL:
308*8226594fSRick McNeal 		mutex_enter(&s->s_mutex);
309*8226594fSRick McNeal 		for (d = list_head(&s->s_devnodes); d != NULL;
310*8226594fSRick McNeal 		    d = list_next(&s->s_devnodes, d)) {
311*8226594fSRick McNeal 			pqi_lun_reset(s, d);
312*8226594fSRick McNeal 		}
313*8226594fSRick McNeal 		mutex_exit(&s->s_mutex);
314*8226594fSRick McNeal 		rval = TRUE;
315*8226594fSRick McNeal 		break;
316*8226594fSRick McNeal 	}
317*8226594fSRick McNeal 	return (rval);
318*8226594fSRick McNeal }
319*8226594fSRick McNeal 
320*8226594fSRick McNeal /*
321*8226594fSRick McNeal  * abort handling:
322*8226594fSRick McNeal  *
323*8226594fSRick McNeal  * Notes:
324*8226594fSRick McNeal  *      - if pkt is not NULL, abort just that command
325*8226594fSRick McNeal  *      - if pkt is NULL, abort all outstanding commands for target
326*8226594fSRick McNeal  */
327*8226594fSRick McNeal static int
pqi_scsi_abort(struct scsi_address * ap,struct scsi_pkt * pkt)328*8226594fSRick McNeal pqi_scsi_abort(struct scsi_address *ap, struct scsi_pkt *pkt)
329*8226594fSRick McNeal {
330*8226594fSRick McNeal 	boolean_t	qnotify	= B_FALSE;
331*8226594fSRick McNeal 	pqi_state_t	*s	= ADDR2PQI(ap);
332*8226594fSRick McNeal 
333*8226594fSRick McNeal 	if (pkt != NULL) {
334*8226594fSRick McNeal 		/* ---- Abort single command ---- */
335*8226594fSRick McNeal 		pqi_cmd_t	*cmd = PKT2CMD(pkt);
336*8226594fSRick McNeal 
337*8226594fSRick McNeal 		mutex_enter(&cmd->pc_device->pd_mutex);
338*8226594fSRick McNeal 		(void) pqi_fail_cmd(cmd, CMD_ABORTED, STAT_ABORTED);
339*8226594fSRick McNeal 		mutex_exit(&cmd->pc_device->pd_mutex);
340*8226594fSRick McNeal 	} else {
341*8226594fSRick McNeal 		abort_all(ap, s);
342*8226594fSRick McNeal 	}
343*8226594fSRick McNeal 	qnotify = HBA_QUIESCED_PENDING(s);
344*8226594fSRick McNeal 
345*8226594fSRick McNeal 	if (qnotify)
346*8226594fSRick McNeal 		pqi_quiesced_notify(s);
347*8226594fSRick McNeal 	return (1);
348*8226594fSRick McNeal }
349*8226594fSRick McNeal 
350*8226594fSRick McNeal /*
351*8226594fSRick McNeal  * capability handling:
352*8226594fSRick McNeal  * (*tran_getcap).  Get the capability named, and return its value.
353*8226594fSRick McNeal  */
354*8226594fSRick McNeal static int
pqi_scsi_getcap(struct scsi_address * ap,char * cap,int tgtonly __unused)355*8226594fSRick McNeal pqi_scsi_getcap(struct scsi_address *ap, char *cap, int tgtonly __unused)
356*8226594fSRick McNeal {
357*8226594fSRick McNeal 	pqi_state_t *s = ap->a_hba_tran->tran_hba_private;
358*8226594fSRick McNeal 
359*8226594fSRick McNeal 	if (cap == NULL)
360*8226594fSRick McNeal 		return (-1);
361*8226594fSRick McNeal 	switch (scsi_hba_lookup_capstr(cap)) {
362*8226594fSRick McNeal 	case SCSI_CAP_LUN_RESET:
363*8226594fSRick McNeal 		return ((s->s_flags & PQI_HBA_LUN_RESET_CAP) != 0);
364*8226594fSRick McNeal 	case SCSI_CAP_ARQ:
365*8226594fSRick McNeal 		return ((s->s_flags & PQI_HBA_AUTO_REQUEST_SENSE) != 0);
366*8226594fSRick McNeal 	case SCSI_CAP_UNTAGGED_QING:
367*8226594fSRick McNeal 		return (1);
368*8226594fSRick McNeal 	default:
369*8226594fSRick McNeal 		return (-1);
370*8226594fSRick McNeal 	}
371*8226594fSRick McNeal }
372*8226594fSRick McNeal 
373*8226594fSRick McNeal /*
374*8226594fSRick McNeal  * (*tran_setcap).  Set the capability named to the value given.
375*8226594fSRick McNeal  */
376*8226594fSRick McNeal static int
pqi_scsi_setcap(struct scsi_address * ap,char * cap,int value,int tgtonly __unused)377*8226594fSRick McNeal pqi_scsi_setcap(struct scsi_address *ap, char *cap, int value,
378*8226594fSRick McNeal     int tgtonly __unused)
379*8226594fSRick McNeal {
380*8226594fSRick McNeal 	pqi_state_t	*s	= ADDR2PQI(ap);
381*8226594fSRick McNeal 	int		rval	= FALSE;
382*8226594fSRick McNeal 
383*8226594fSRick McNeal 	if (cap == NULL)
384*8226594fSRick McNeal 		return (-1);
385*8226594fSRick McNeal 
386*8226594fSRick McNeal 	switch (scsi_hba_lookup_capstr(cap)) {
387*8226594fSRick McNeal 	case SCSI_CAP_ARQ:
388*8226594fSRick McNeal 		if (value)
389*8226594fSRick McNeal 			s->s_flags |= PQI_HBA_AUTO_REQUEST_SENSE;
390*8226594fSRick McNeal 		else
391*8226594fSRick McNeal 			s->s_flags &= ~PQI_HBA_AUTO_REQUEST_SENSE;
392*8226594fSRick McNeal 		rval = 1;
393*8226594fSRick McNeal 		break;
394*8226594fSRick McNeal 
395*8226594fSRick McNeal 	case SCSI_CAP_LUN_RESET:
396*8226594fSRick McNeal 		if (value)
397*8226594fSRick McNeal 			s->s_flags |= PQI_HBA_LUN_RESET_CAP;
398*8226594fSRick McNeal 		else
399*8226594fSRick McNeal 			s->s_flags &= ~PQI_HBA_LUN_RESET_CAP;
400*8226594fSRick McNeal 		break;
401*8226594fSRick McNeal 
402*8226594fSRick McNeal 	default:
403*8226594fSRick McNeal 		break;
404*8226594fSRick McNeal 	}
405*8226594fSRick McNeal 
406*8226594fSRick McNeal 	return (rval);
407*8226594fSRick McNeal }
408*8226594fSRick McNeal 
409*8226594fSRick McNeal int
pqi_cache_constructor(void * buf,void * un,int flags)410*8226594fSRick McNeal pqi_cache_constructor(void *buf, void *un, int flags)
411*8226594fSRick McNeal {
412*8226594fSRick McNeal 	pqi_cmd_t		*c	= (pqi_cmd_t *)buf;
413*8226594fSRick McNeal 	pqi_state_t		*s	= un;
414*8226594fSRick McNeal 	int			(*callback)(caddr_t);
415*8226594fSRick McNeal 
416*8226594fSRick McNeal 	bzero(c, sizeof (*c));
417*8226594fSRick McNeal 	c->pc_softc = s;
418*8226594fSRick McNeal 	callback = (flags == KM_SLEEP) ? DDI_DMA_SLEEP : DDI_DMA_DONTWAIT;
419*8226594fSRick McNeal 
420*8226594fSRick McNeal 	/* ---- Allocate a DMA handle for data transfers ---- */
421*8226594fSRick McNeal 	if (ddi_dma_alloc_handle(s->s_dip, &s->s_msg_dma_attr, callback,
422*8226594fSRick McNeal 	    NULL, &c->pc_dmahdl) != DDI_SUCCESS) {
423*8226594fSRick McNeal 		dev_err(s->s_dip, CE_WARN, "Failed to alloc dma handle");
424*8226594fSRick McNeal 		return (-1);
425*8226594fSRick McNeal 	}
426*8226594fSRick McNeal 
427*8226594fSRick McNeal 	return (0);
428*8226594fSRick McNeal }
429*8226594fSRick McNeal 
430*8226594fSRick McNeal void
pqi_cache_destructor(void * buf,void * un __unused)431*8226594fSRick McNeal pqi_cache_destructor(void *buf, void *un __unused)
432*8226594fSRick McNeal {
433*8226594fSRick McNeal 	pqi_cmd_t	*cmd = buf;
434*8226594fSRick McNeal 	if (cmd->pc_dmahdl != NULL) {
435*8226594fSRick McNeal 		(void) ddi_dma_unbind_handle(cmd->pc_dmahdl);
436*8226594fSRick McNeal 		ddi_dma_free_handle(&cmd->pc_dmahdl);
437*8226594fSRick McNeal 		cmd->pc_dmahdl = NULL;
438*8226594fSRick McNeal 	}
439*8226594fSRick McNeal }
440*8226594fSRick McNeal 
441*8226594fSRick McNeal /*
442*8226594fSRick McNeal  * tran_init_pkt(9E) - allocate scsi_pkt(9S) for command
443*8226594fSRick McNeal  *
444*8226594fSRick McNeal  * One of three possibilities:
445*8226594fSRick McNeal  *      - allocate scsi_pkt
446*8226594fSRick McNeal  *      - allocate scsi_pkt and DMA resources
447*8226594fSRick McNeal  *      - allocate DMA resources to an already-allocated pkt
448*8226594fSRick McNeal  */
449*8226594fSRick McNeal static struct scsi_pkt *
pqi_init_pkt(struct scsi_address * ap,struct scsi_pkt * pkt,struct buf * bp,int cmdlen,int statuslen,int tgtlen,int flags,int (* callback)(),caddr_t arg)450*8226594fSRick McNeal pqi_init_pkt(struct scsi_address *ap, struct scsi_pkt *pkt,
451*8226594fSRick McNeal     struct buf *bp, int cmdlen, int statuslen, int tgtlen, int flags,
452*8226594fSRick McNeal     int (*callback)(), caddr_t arg)
453*8226594fSRick McNeal {
454*8226594fSRick McNeal 	pqi_cmd_t	*cmd;
455*8226594fSRick McNeal 	pqi_state_t	*s;
456*8226594fSRick McNeal 	int		kf = (callback == SLEEP_FUNC) ? KM_SLEEP : KM_NOSLEEP;
457*8226594fSRick McNeal 	boolean_t	is_new = B_FALSE;
458*8226594fSRick McNeal 	int		rc;
459*8226594fSRick McNeal 	int		i;
460*8226594fSRick McNeal 	pqi_device_t	*devp;
461*8226594fSRick McNeal 
462*8226594fSRick McNeal 	s = ap->a_hba_tran->tran_hba_private;
463*8226594fSRick McNeal 
464*8226594fSRick McNeal 	if (pkt == NULL) {
465*8226594fSRick McNeal 		ddi_dma_handle_t	saved_dmahdl;
466*8226594fSRick McNeal 		pqi_cmd_action_t	saved_action;
467*8226594fSRick McNeal 
468*8226594fSRick McNeal 		if ((devp = scsi_device_hba_private_get(ap->a.a_sd)) == NULL)
469*8226594fSRick McNeal 			return (NULL);
470*8226594fSRick McNeal 		if ((cmd = kmem_cache_alloc(s->s_cmd_cache, kf)) == NULL)
471*8226594fSRick McNeal 			return (NULL);
472*8226594fSRick McNeal 
473*8226594fSRick McNeal 		is_new = B_TRUE;
474*8226594fSRick McNeal 		saved_dmahdl = cmd->pc_dmahdl;
475*8226594fSRick McNeal 		saved_action = cmd->pc_last_action;
476*8226594fSRick McNeal 
477*8226594fSRick McNeal 		(void) memset(cmd, 0, sizeof (*cmd));
478*8226594fSRick McNeal 		mutex_init(&cmd->pc_mutex, NULL, MUTEX_DRIVER, NULL);
479*8226594fSRick McNeal 
480*8226594fSRick McNeal 		cmd->pc_dmahdl = saved_dmahdl;
481*8226594fSRick McNeal 		cmd->pc_last_action = saved_action;
482*8226594fSRick McNeal 
483*8226594fSRick McNeal 		cmd->pc_device = devp;
484*8226594fSRick McNeal 		cmd->pc_pkt = &cmd->pc_cached_pkt;
485*8226594fSRick McNeal 		cmd->pc_softc = s;
486*8226594fSRick McNeal 		cmd->pc_tgtlen = tgtlen;
487*8226594fSRick McNeal 		cmd->pc_statuslen = statuslen;
488*8226594fSRick McNeal 		cmd->pc_cmdlen = cmdlen;
489*8226594fSRick McNeal 		cmd->pc_dma_count = 0;
490*8226594fSRick McNeal 
491*8226594fSRick McNeal 		pkt = cmd->pc_pkt;
492*8226594fSRick McNeal 		pkt->pkt_ha_private = cmd;
493*8226594fSRick McNeal 		pkt->pkt_address = *ap;
494*8226594fSRick McNeal 		pkt->pkt_scbp = (uint8_t *)&cmd->pc_cmd_scb;
495*8226594fSRick McNeal 		pkt->pkt_cdbp = cmd->pc_cdb;
496*8226594fSRick McNeal 		pkt->pkt_private = (opaque_t)cmd->pc_tgt_priv;
497*8226594fSRick McNeal 		if (pkt->pkt_time == 0)
498*8226594fSRick McNeal 			pkt->pkt_time = SCSI_POLL_TIMEOUT;
499*8226594fSRick McNeal 
500*8226594fSRick McNeal 		if (cmdlen > sizeof (cmd->pc_cdb) ||
501*8226594fSRick McNeal 		    statuslen > sizeof (cmd->pc_cmd_scb) ||
502*8226594fSRick McNeal 		    tgtlen > sizeof (cmd->pc_tgt_priv)) {
503*8226594fSRick McNeal 			if (cmd_ext_alloc(cmd, kf) != DDI_SUCCESS) {
504*8226594fSRick McNeal 				dev_err(s->s_dip, CE_WARN,
505*8226594fSRick McNeal 				    "extent allocation failed");
506*8226594fSRick McNeal 				goto out;
507*8226594fSRick McNeal 			}
508*8226594fSRick McNeal 		}
509*8226594fSRick McNeal 	} else {
510*8226594fSRick McNeal 		cmd = PKT2CMD(pkt);
511*8226594fSRick McNeal 		cmd->pc_flags &= PQI_FLAGS_PERSISTENT;
512*8226594fSRick McNeal 	}
513*8226594fSRick McNeal 
514*8226594fSRick McNeal 	/* ---- Handle partial DMA transfer ---- */
515*8226594fSRick McNeal 	if (cmd->pc_nwin > 0) {
516*8226594fSRick McNeal 		if (++cmd->pc_winidx >= cmd->pc_nwin)
517*8226594fSRick McNeal 			return (NULL);
518*8226594fSRick McNeal 		if (ddi_dma_getwin(cmd->pc_dmahdl, cmd->pc_winidx,
519*8226594fSRick McNeal 		    &cmd->pc_dma_offset, &cmd->pc_dma_len, &cmd->pc_dmac,
520*8226594fSRick McNeal 		    &cmd->pc_dmaccount) == DDI_FAILURE)
521*8226594fSRick McNeal 			return (NULL);
522*8226594fSRick McNeal 		goto handle_dma_cookies;
523*8226594fSRick McNeal 	}
524*8226594fSRick McNeal 
525*8226594fSRick McNeal 	/* ---- Setup data buffer ---- */
526*8226594fSRick McNeal 	if (bp != NULL && bp->b_bcount > 0 &&
527*8226594fSRick McNeal 	    (cmd->pc_flags & PQI_FLAG_DMA_VALID) == 0) {
528*8226594fSRick McNeal 		int	dma_flags;
529*8226594fSRick McNeal 
530*8226594fSRick McNeal 		ASSERT(cmd->pc_dmahdl != NULL);
531*8226594fSRick McNeal 
532*8226594fSRick McNeal 		if ((bp->b_flags & B_READ) != 0) {
533*8226594fSRick McNeal 			cmd->pc_flags |= PQI_FLAG_IO_READ;
534*8226594fSRick McNeal 			dma_flags = DDI_DMA_READ;
535*8226594fSRick McNeal 		} else {
536*8226594fSRick McNeal 			cmd->pc_flags &= ~PQI_FLAG_IO_READ;
537*8226594fSRick McNeal 			dma_flags = DDI_DMA_WRITE;
538*8226594fSRick McNeal 		}
539*8226594fSRick McNeal 		if ((flags & PKT_CONSISTENT) != 0) {
540*8226594fSRick McNeal 			cmd->pc_flags |= PQI_FLAG_IO_IOPB;
541*8226594fSRick McNeal 			dma_flags |= DDI_DMA_CONSISTENT;
542*8226594fSRick McNeal 		}
543*8226594fSRick McNeal 		if ((flags & PKT_DMA_PARTIAL) != 0) {
544*8226594fSRick McNeal 			dma_flags |= DDI_DMA_PARTIAL;
545*8226594fSRick McNeal 		}
546*8226594fSRick McNeal 		rc = ddi_dma_buf_bind_handle(cmd->pc_dmahdl, bp,
547*8226594fSRick McNeal 		    dma_flags, callback, arg, &cmd->pc_dmac,
548*8226594fSRick McNeal 		    &cmd->pc_dmaccount);
549*8226594fSRick McNeal 
550*8226594fSRick McNeal 		if (rc == DDI_DMA_PARTIAL_MAP) {
551*8226594fSRick McNeal 			(void) ddi_dma_numwin(cmd->pc_dmahdl, &cmd->pc_nwin);
552*8226594fSRick McNeal 			cmd->pc_winidx = 0;
553*8226594fSRick McNeal 			(void) ddi_dma_getwin(cmd->pc_dmahdl, cmd->pc_winidx,
554*8226594fSRick McNeal 			    &cmd->pc_dma_offset, &cmd->pc_dma_len,
555*8226594fSRick McNeal 			    &cmd->pc_dmac, &cmd->pc_dmaccount);
556*8226594fSRick McNeal 		} else if (rc != 0 && rc != DDI_DMA_MAPPED) {
557*8226594fSRick McNeal 			switch (rc) {
558*8226594fSRick McNeal 			case DDI_DMA_NORESOURCES:
559*8226594fSRick McNeal 				bioerror(bp, 0);
560*8226594fSRick McNeal 				break;
561*8226594fSRick McNeal 			case DDI_DMA_BADATTR:
562*8226594fSRick McNeal 			case DDI_DMA_NOMAPPING:
563*8226594fSRick McNeal 				bioerror(bp, EFAULT);
564*8226594fSRick McNeal 				break;
565*8226594fSRick McNeal 			case DDI_DMA_TOOBIG:
566*8226594fSRick McNeal 			default:
567*8226594fSRick McNeal 				bioerror(bp, EINVAL);
568*8226594fSRick McNeal 				break;
569*8226594fSRick McNeal 			}
570*8226594fSRick McNeal 			goto out;
571*8226594fSRick McNeal 		}
572*8226594fSRick McNeal 
573*8226594fSRick McNeal handle_dma_cookies:
574*8226594fSRick McNeal 		ASSERT(cmd->pc_dmaccount > 0);
575*8226594fSRick McNeal 		if (cmd->pc_dmaccount >
576*8226594fSRick McNeal 		    (sizeof (cmd->pc_cached_cookies) /
577*8226594fSRick McNeal 		    sizeof (ddi_dma_cookie_t))) {
578*8226594fSRick McNeal 			dev_err(s->s_dip, CE_WARN,
579*8226594fSRick McNeal 			    "invalid cookie count: %d", cmd->pc_dmaccount);
580*8226594fSRick McNeal 			goto out;
581*8226594fSRick McNeal 		}
582*8226594fSRick McNeal 		if (cmd->pc_dmaccount >
583*8226594fSRick McNeal 		    (s->s_sg_chain_buf_length / sizeof (pqi_sg_entry_t))) {
584*8226594fSRick McNeal 			dev_err(s->s_dip, CE_WARN,
585*8226594fSRick McNeal 			    "Cookie(0x%x) verses SG(0x%" PRIx64 ") mismatch",
586*8226594fSRick McNeal 			    cmd->pc_dmaccount,
587*8226594fSRick McNeal 			    s->s_sg_chain_buf_length / sizeof (pqi_sg_entry_t));
588*8226594fSRick McNeal 			goto out;
589*8226594fSRick McNeal 		}
590*8226594fSRick McNeal 
591*8226594fSRick McNeal 		cmd->pc_flags |= PQI_FLAG_DMA_VALID;
592*8226594fSRick McNeal 		cmd->pc_dma_count = cmd->pc_dmac.dmac_size;
593*8226594fSRick McNeal 		cmd->pc_cached_cookies[0] = cmd->pc_dmac;
594*8226594fSRick McNeal 
595*8226594fSRick McNeal 		for (i = 1; i < cmd->pc_dmaccount; i++) {
596*8226594fSRick McNeal 			ddi_dma_nextcookie(cmd->pc_dmahdl, &cmd->pc_dmac);
597*8226594fSRick McNeal 			cmd->pc_cached_cookies[i] = cmd->pc_dmac;
598*8226594fSRick McNeal 			cmd->pc_dma_count += cmd->pc_dmac.dmac_size;
599*8226594fSRick McNeal 		}
600*8226594fSRick McNeal 
601*8226594fSRick McNeal 		pkt->pkt_resid = bp->b_bcount - cmd->pc_dma_count;
602*8226594fSRick McNeal 	}
603*8226594fSRick McNeal 
604*8226594fSRick McNeal 	return (pkt);
605*8226594fSRick McNeal 
606*8226594fSRick McNeal out:
607*8226594fSRick McNeal 	if (is_new == B_TRUE)
608*8226594fSRick McNeal 		pqi_destroy_pkt(ap, pkt);
609*8226594fSRick McNeal 	return (NULL);
610*8226594fSRick McNeal }
611*8226594fSRick McNeal 
612*8226594fSRick McNeal /*
613*8226594fSRick McNeal  * tran_destroy_pkt(9E) - scsi_pkt(9s) deallocation
614*8226594fSRick McNeal  *
615*8226594fSRick McNeal  * Notes:
616*8226594fSRick McNeal  *      - also frees DMA resources if allocated
617*8226594fSRick McNeal  *      - implicit DMA synchonization
618*8226594fSRick McNeal  */
619*8226594fSRick McNeal static void
pqi_destroy_pkt(struct scsi_address * ap,struct scsi_pkt * pkt)620*8226594fSRick McNeal pqi_destroy_pkt(struct scsi_address *ap, struct scsi_pkt *pkt)
621*8226594fSRick McNeal {
622*8226594fSRick McNeal 	pqi_cmd_t	*c = PKT2CMD(pkt);
623*8226594fSRick McNeal 	pqi_state_t	*s = ADDR2PQI(ap);
624*8226594fSRick McNeal 
625*8226594fSRick McNeal 	if ((c->pc_flags & PQI_FLAG_DMA_VALID) != 0) {
626*8226594fSRick McNeal 		c->pc_flags &= ~PQI_FLAG_DMA_VALID;
627*8226594fSRick McNeal 		(void) ddi_dma_unbind_handle(c->pc_dmahdl);
628*8226594fSRick McNeal 	}
629*8226594fSRick McNeal 	cmd_ext_free(c);
630*8226594fSRick McNeal 
631*8226594fSRick McNeal 	kmem_cache_free(s->s_cmd_cache, c);
632*8226594fSRick McNeal }
633*8226594fSRick McNeal 
634*8226594fSRick McNeal /*
635*8226594fSRick McNeal  * tran_dmafree(9E) - deallocate DMA resources allocated for command
636*8226594fSRick McNeal  */
637*8226594fSRick McNeal static void
pqi_dmafree(struct scsi_address * ap __unused,struct scsi_pkt * pkt)638*8226594fSRick McNeal pqi_dmafree(struct scsi_address *ap __unused, struct scsi_pkt *pkt)
639*8226594fSRick McNeal {
640*8226594fSRick McNeal 	pqi_cmd_t	*cmd = PKT2CMD(pkt);
641*8226594fSRick McNeal 
642*8226594fSRick McNeal 	if (cmd->pc_flags & PQI_FLAG_DMA_VALID) {
643*8226594fSRick McNeal 		cmd->pc_flags &= ~PQI_FLAG_DMA_VALID;
644*8226594fSRick McNeal 		(void) ddi_dma_unbind_handle(cmd->pc_dmahdl);
645*8226594fSRick McNeal 	}
646*8226594fSRick McNeal }
647*8226594fSRick McNeal 
648*8226594fSRick McNeal /*
649*8226594fSRick McNeal  * tran_sync_pkt(9E) - explicit DMA synchronization
650*8226594fSRick McNeal  */
651*8226594fSRick McNeal static void
pqi_sync_pkt(struct scsi_address * ap __unused,struct scsi_pkt * pkt)652*8226594fSRick McNeal pqi_sync_pkt(struct scsi_address *ap __unused, struct scsi_pkt *pkt)
653*8226594fSRick McNeal {
654*8226594fSRick McNeal 	pqi_cmd_t	*cmd = PKT2CMD(pkt);
655*8226594fSRick McNeal 
656*8226594fSRick McNeal 	if (cmd->pc_dmahdl != NULL) {
657*8226594fSRick McNeal 		(void) ddi_dma_sync(cmd->pc_dmahdl, 0, 0,
658*8226594fSRick McNeal 		    (cmd->pc_flags & PQI_FLAG_IO_READ) ? DDI_DMA_SYNC_FORCPU :
659*8226594fSRick McNeal 		    DDI_DMA_SYNC_FORDEV);
660*8226594fSRick McNeal 	}
661*8226594fSRick McNeal }
662*8226594fSRick McNeal 
663*8226594fSRick McNeal static int
pqi_reset_notify(struct scsi_address * ap,int flag,void (* callback)(caddr_t),caddr_t arg)664*8226594fSRick McNeal pqi_reset_notify(struct scsi_address *ap, int flag,
665*8226594fSRick McNeal     void (*callback)(caddr_t), caddr_t arg)
666*8226594fSRick McNeal {
667*8226594fSRick McNeal 	pqi_state_t	*s = ADDR2PQI(ap);
668*8226594fSRick McNeal 
669*8226594fSRick McNeal 	return (scsi_hba_reset_notify_setup(ap, flag, callback, arg,
670*8226594fSRick McNeal 	    &s->s_mutex, &s->s_reset_notify_listf));
671*8226594fSRick McNeal }
672*8226594fSRick McNeal 
673*8226594fSRick McNeal /*
674*8226594fSRick McNeal  * Device / Hotplug control
675*8226594fSRick McNeal  */
676*8226594fSRick McNeal static int
pqi_quiesce(dev_info_t * dip)677*8226594fSRick McNeal pqi_quiesce(dev_info_t *dip)
678*8226594fSRick McNeal {
679*8226594fSRick McNeal 	pqi_state_t	*s;
680*8226594fSRick McNeal 	scsi_hba_tran_t	*tran;
681*8226594fSRick McNeal 
682*8226594fSRick McNeal 	if ((tran = ddi_get_driver_private(dip)) == NULL ||
683*8226594fSRick McNeal 	    (s = TRAN2PQI(tran)) == NULL)
684*8226594fSRick McNeal 		return (-1);
685*8226594fSRick McNeal 
686*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
687*8226594fSRick McNeal 	if (!HBA_IS_QUIESCED(s))
688*8226594fSRick McNeal 		s->s_flags |= PQI_HBA_QUIESCED;
689*8226594fSRick McNeal 
690*8226594fSRick McNeal 	if (s->s_cmd_queue_len != 0) {
691*8226594fSRick McNeal 		/* ---- Outstanding commands present, wait ---- */
692*8226594fSRick McNeal 		s->s_flags |= PQI_HBA_QUIESCED_PENDING;
693*8226594fSRick McNeal 		cv_wait(&s->s_quiescedvar, &s->s_mutex);
694*8226594fSRick McNeal 		ASSERT0(s->s_cmd_queue_len);
695*8226594fSRick McNeal 	}
696*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
697*8226594fSRick McNeal 
698*8226594fSRick McNeal 	return (0);
699*8226594fSRick McNeal }
700*8226594fSRick McNeal 
701*8226594fSRick McNeal static int
pqi_unquiesce(dev_info_t * dip)702*8226594fSRick McNeal pqi_unquiesce(dev_info_t *dip)
703*8226594fSRick McNeal {
704*8226594fSRick McNeal 	pqi_state_t	*s;
705*8226594fSRick McNeal 	scsi_hba_tran_t	*tran;
706*8226594fSRick McNeal 
707*8226594fSRick McNeal 	if ((tran = ddi_get_driver_private(dip)) == NULL ||
708*8226594fSRick McNeal 	    (s = TRAN2PQI(tran)) == NULL)
709*8226594fSRick McNeal 		return (-1);
710*8226594fSRick McNeal 
711*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
712*8226594fSRick McNeal 	if (!HBA_IS_QUIESCED(s)) {
713*8226594fSRick McNeal 		mutex_exit(&s->s_mutex);
714*8226594fSRick McNeal 		return (0);
715*8226594fSRick McNeal 	}
716*8226594fSRick McNeal 	ASSERT0(s->s_cmd_queue_len);
717*8226594fSRick McNeal 	s->s_flags &= ~PQI_HBA_QUIESCED;
718*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
719*8226594fSRick McNeal 
720*8226594fSRick McNeal 	return (0);
721*8226594fSRick McNeal }
722*8226594fSRick McNeal 
723*8226594fSRick McNeal static int
pqi_bus_config(dev_info_t * pdip,uint_t flag,ddi_bus_config_op_t op,void * arg,dev_info_t ** childp)724*8226594fSRick McNeal pqi_bus_config(dev_info_t *pdip, uint_t flag,
725*8226594fSRick McNeal     ddi_bus_config_op_t op, void *arg, dev_info_t **childp)
726*8226594fSRick McNeal {
727*8226594fSRick McNeal 	scsi_hba_tran_t	*tran;
728*8226594fSRick McNeal 	pqi_state_t	*s;
729*8226594fSRick McNeal 	int		ret	= NDI_FAILURE;
730*8226594fSRick McNeal 	pqi_device_t	*d;
731*8226594fSRick McNeal 	char		*ua;
732*8226594fSRick McNeal 
733*8226594fSRick McNeal 	tran = ddi_get_driver_private(pdip);
734*8226594fSRick McNeal 	s = tran->tran_hba_private;
735*8226594fSRick McNeal 	if (pqi_is_offline(s))
736*8226594fSRick McNeal 		return (NDI_FAILURE);
737*8226594fSRick McNeal 
738*8226594fSRick McNeal 	ndi_devi_enter(scsi_vhci_dip);
739*8226594fSRick McNeal 	ndi_devi_enter(pdip);
740*8226594fSRick McNeal 	switch (op) {
741*8226594fSRick McNeal 	case BUS_CONFIG_ONE:
742*8226594fSRick McNeal 		if ((ua = strrchr((char *)arg, '@')) != NULL) {
743*8226594fSRick McNeal 			ua++;
744*8226594fSRick McNeal 			d = pqi_find_target_ua(s, ua);
745*8226594fSRick McNeal 			if (d != NULL)
746*8226594fSRick McNeal 				ret = config_one(pdip, s, d, childp);
747*8226594fSRick McNeal 		} else {
748*8226594fSRick McNeal 			dev_err(s->s_dip, CE_WARN, "Couldn't decode %s",
749*8226594fSRick McNeal 			    (char *)arg);
750*8226594fSRick McNeal 		}
751*8226594fSRick McNeal 		flag |= NDI_MDI_FALLBACK;
752*8226594fSRick McNeal 		break;
753*8226594fSRick McNeal 
754*8226594fSRick McNeal 	case BUS_CONFIG_DRIVER:
755*8226594fSRick McNeal 	case BUS_CONFIG_ALL:
756*8226594fSRick McNeal 		ret = pqi_config_all(pdip, s);
757*8226594fSRick McNeal 		break;
758*8226594fSRick McNeal 	default:
759*8226594fSRick McNeal 		ret = NDI_FAILURE;
760*8226594fSRick McNeal 	}
761*8226594fSRick McNeal 	if (ret == NDI_SUCCESS)
762*8226594fSRick McNeal 		ret = ndi_busop_bus_config(pdip, flag, op, arg, childp, 0);
763*8226594fSRick McNeal 	ndi_devi_exit(pdip);
764*8226594fSRick McNeal 	ndi_devi_exit(scsi_vhci_dip);
765*8226594fSRick McNeal 
766*8226594fSRick McNeal 	return (ret);
767*8226594fSRick McNeal }
768*8226594fSRick McNeal 
769*8226594fSRick McNeal pqi_device_t *
pqi_find_target_ua(pqi_state_t * s,char * ua)770*8226594fSRick McNeal pqi_find_target_ua(pqi_state_t *s, char *ua)
771*8226594fSRick McNeal {
772*8226594fSRick McNeal 	pqi_device_t *d;
773*8226594fSRick McNeal 
774*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
775*8226594fSRick McNeal 	for (d = list_head(&s->s_devnodes); d != NULL;
776*8226594fSRick McNeal 	    d = list_next(&s->s_devnodes, d)) {
777*8226594fSRick McNeal 		if (d->pd_online && strcmp(ua, d->pd_unit_address) == 0)
778*8226594fSRick McNeal 			break;
779*8226594fSRick McNeal 	}
780*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
781*8226594fSRick McNeal 	return (d);
782*8226594fSRick McNeal }
783*8226594fSRick McNeal 
784*8226594fSRick McNeal int
pqi_config_all(dev_info_t * pdip,pqi_state_t * s)785*8226594fSRick McNeal pqi_config_all(dev_info_t *pdip, pqi_state_t *s)
786*8226594fSRick McNeal {
787*8226594fSRick McNeal 	pqi_device_t *d;
788*8226594fSRick McNeal 
789*8226594fSRick McNeal 	/*
790*8226594fSRick McNeal 	 * Make sure we bring the available devices into play first. These
791*8226594fSRick McNeal 	 * might be brand new devices just hotplugged into the system or
792*8226594fSRick McNeal 	 * they could be devices previously offlined because either they
793*8226594fSRick McNeal 	 * were pulled from an enclosure or a cable to the enclosure was
794*8226594fSRick McNeal 	 * pulled.
795*8226594fSRick McNeal 	 */
796*8226594fSRick McNeal 	/* ---- XXX Grab s_mutex ---- */
797*8226594fSRick McNeal 	for (d = list_head(&s->s_devnodes); d != NULL;
798*8226594fSRick McNeal 	    d = list_next(&s->s_devnodes, d)) {
799*8226594fSRick McNeal 		if (d->pd_online)
800*8226594fSRick McNeal 			(void) config_one(pdip, s, d, NULL);
801*8226594fSRick McNeal 	}
802*8226594fSRick McNeal 
803*8226594fSRick McNeal 	/*
804*8226594fSRick McNeal 	 * Now deal with devices that we had previously known about, but are
805*8226594fSRick McNeal 	 * no longer available.
806*8226594fSRick McNeal 	 */
807*8226594fSRick McNeal 	for (d = list_head(&s->s_devnodes); d != NULL;
808*8226594fSRick McNeal 	    d = list_next(&s->s_devnodes, d)) {
809*8226594fSRick McNeal 		if (!d->pd_online)
810*8226594fSRick McNeal 			(void) config_one(pdip, s, d, NULL);
811*8226594fSRick McNeal 	}
812*8226594fSRick McNeal 
813*8226594fSRick McNeal 	return (NDI_SUCCESS);
814*8226594fSRick McNeal }
815*8226594fSRick McNeal 
816*8226594fSRick McNeal void
pqi_quiesced_notify(pqi_state_t * s)817*8226594fSRick McNeal pqi_quiesced_notify(pqi_state_t *s)
818*8226594fSRick McNeal {
819*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
820*8226594fSRick McNeal 	if (s->s_cmd_queue_len == 0 &&
821*8226594fSRick McNeal 	    (s->s_flags & PQI_HBA_QUIESCED_PENDING) != 0) {
822*8226594fSRick McNeal 		s->s_flags &= ~PQI_HBA_QUIESCED_PENDING;
823*8226594fSRick McNeal 		cv_broadcast(&s->s_quiescedvar);
824*8226594fSRick McNeal 	}
825*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
826*8226594fSRick McNeal }
827*8226594fSRick McNeal 
828*8226594fSRick McNeal /*
829*8226594fSRick McNeal  * []------------------------------------------------------------------[]
830*8226594fSRick McNeal  * | Support routines used only by the trans_xxx routines		|
831*8226594fSRick McNeal  * []------------------------------------------------------------------[]
832*8226594fSRick McNeal  */
833*8226594fSRick McNeal #ifdef DEBUG
834*8226594fSRick McNeal int	pqi_force_timeout;
835*8226594fSRick McNeal #endif	/* DEBUG */
836*8226594fSRick McNeal 
837*8226594fSRick McNeal static void
cmd_timeout_drive(pqi_device_t * d)838*8226594fSRick McNeal cmd_timeout_drive(pqi_device_t *d)
839*8226594fSRick McNeal {
840*8226594fSRick McNeal 	uint32_t	timed_out_cnt = 0;
841*8226594fSRick McNeal 	pqi_cmd_t	*c, *next_c;
842*8226594fSRick McNeal 	hrtime_t	now = gethrtime();
843*8226594fSRick McNeal 
844*8226594fSRick McNeal 	mutex_enter(&d->pd_mutex);
845*8226594fSRick McNeal 
846*8226594fSRick McNeal rescan:
847*8226594fSRick McNeal 	c = list_head(&d->pd_cmd_list);
848*8226594fSRick McNeal 	while (c != NULL) {
849*8226594fSRick McNeal 		next_c = list_next(&d->pd_cmd_list, c);
850*8226594fSRick McNeal #ifdef DEBUG
851*8226594fSRick McNeal 		if (c->pc_expiration < now || pqi_force_timeout != 0) {
852*8226594fSRick McNeal 			pqi_force_timeout = 0;
853*8226594fSRick McNeal #else
854*8226594fSRick McNeal 		if (c->pc_expiration < now) {
855*8226594fSRick McNeal #endif	/* DEBUG */
856*8226594fSRick McNeal 			struct scsi_pkt	*pkt = CMD2PKT(c);
857*8226594fSRick McNeal 
858*8226594fSRick McNeal 			if (pkt != NULL) {
859*8226594fSRick McNeal 				pkt->pkt_reason = CMD_TIMEOUT;
860*8226594fSRick McNeal 				pkt->pkt_statistics = STAT_TIMEOUT;
861*8226594fSRick McNeal 			}
862*8226594fSRick McNeal 			ASSERT(c->pc_io_rqst != NULL);
863*8226594fSRick McNeal 			/*
864*8226594fSRick McNeal 			 * If the i/o has not been serviced yet,
865*8226594fSRick McNeal 			 * mark the i/o as timed out and clear it out
866*8226594fSRick McNeal 			 */
867*8226594fSRick McNeal 			if (pqi_timeout_io(c->pc_io_rqst)) {
868*8226594fSRick McNeal 				(void) pqi_cmd_action_nolock(c,
869*8226594fSRick McNeal 				    PQI_CMD_TIMEOUT);
870*8226594fSRick McNeal 				timed_out_cnt++;
871*8226594fSRick McNeal 				/*
872*8226594fSRick McNeal 				 * We dropped pd_mutex so the cmd
873*8226594fSRick McNeal 				 * list could have changed, restart the
874*8226594fSRick McNeal 				 * scan of the cmds.  This will terminate
875*8226594fSRick McNeal 				 * since timed out cmds are removed from
876*8226594fSRick McNeal 				 * the list.
877*8226594fSRick McNeal 				 */
878*8226594fSRick McNeal 				goto rescan;
879*8226594fSRick McNeal 			}
880*8226594fSRick McNeal 		}
881*8226594fSRick McNeal 		c = next_c;
882*8226594fSRick McNeal 	}
883*8226594fSRick McNeal 
884*8226594fSRick McNeal 	d->pd_timedout += timed_out_cnt;
885*8226594fSRick McNeal 	mutex_exit(&d->pd_mutex);
886*8226594fSRick McNeal }
887*8226594fSRick McNeal 
888*8226594fSRick McNeal static void
889*8226594fSRick McNeal cmd_timeout_scan(void *v)
890*8226594fSRick McNeal {
891*8226594fSRick McNeal 	pqi_state_t		*s = v;
892*8226594fSRick McNeal 	pqi_device_t		*d;
893*8226594fSRick McNeal 
894*8226594fSRick McNeal 	mutex_enter(&s->s_mutex);
895*8226594fSRick McNeal 
896*8226594fSRick McNeal 	for (d = list_head(&s->s_devnodes); d != NULL;
897*8226594fSRick McNeal 	    d = list_next(&s->s_devnodes, d)) {
898*8226594fSRick McNeal 		cmd_timeout_drive(d);
899*8226594fSRick McNeal 	}
900*8226594fSRick McNeal 	cmd_timeout_drive(&s->s_special_device);
901*8226594fSRick McNeal 
902*8226594fSRick McNeal 	mutex_exit(&s->s_mutex);
903*8226594fSRick McNeal 	s->s_cmd_timeout = timeout(cmd_timeout_scan, s,
904*8226594fSRick McNeal 	    CMD_TIMEOUT_SCAN_SECS * drv_usectohz(MICROSEC));
905*8226594fSRick McNeal }
906*8226594fSRick McNeal 
907*8226594fSRick McNeal static void
908*8226594fSRick McNeal abort_all(struct scsi_address *ap, pqi_state_t *s __unused)
909*8226594fSRick McNeal {
910*8226594fSRick McNeal 	pqi_device_t	*devp;
911*8226594fSRick McNeal 
912*8226594fSRick McNeal 	if ((devp = scsi_device_hba_private_get(ap->a.a_sd)) == NULL)
913*8226594fSRick McNeal 		return;
914*8226594fSRick McNeal 
915*8226594fSRick McNeal 	pqi_fail_drive_cmds(devp, CMD_ABORTED);
916*8226594fSRick McNeal }
917*8226594fSRick McNeal 
918*8226594fSRick McNeal static boolean_t
919*8226594fSRick McNeal create_phys_lun(pqi_state_t *s, pqi_device_t *d,
920*8226594fSRick McNeal     struct scsi_inquiry *inq, dev_info_t **childp)
921*8226594fSRick McNeal {
922*8226594fSRick McNeal 	char		**compatible	= NULL;
923*8226594fSRick McNeal 	char		*nodename	= NULL;
924*8226594fSRick McNeal 	int		ncompatible	= 0;
925*8226594fSRick McNeal 	dev_info_t	*dip;
926*8226594fSRick McNeal 
927*8226594fSRick McNeal 	/* ---- At this point we have a new device not in our list ---- */
928*8226594fSRick McNeal 	scsi_hba_nodename_compatible_get(inq, NULL,
929*8226594fSRick McNeal 	    inq->inq_dtype, NULL, &nodename, &compatible, &ncompatible);
930*8226594fSRick McNeal 	if (nodename == NULL)
931*8226594fSRick McNeal 		return (B_FALSE);
932*8226594fSRick McNeal 
933*8226594fSRick McNeal 	if (ndi_devi_alloc(s->s_dip, nodename, DEVI_SID_NODEID, &dip) !=
934*8226594fSRick McNeal 	    NDI_SUCCESS) {
935*8226594fSRick McNeal 		dev_err(s->s_dip, CE_WARN, "failed to alloc device instance");
936*8226594fSRick McNeal 		goto free_nodename;
937*8226594fSRick McNeal 	}
938*8226594fSRick McNeal 
939*8226594fSRick McNeal 	d->pd_dip = dip;
940*8226594fSRick McNeal 	d->pd_pip = NULL;
941*8226594fSRick McNeal 
942*8226594fSRick McNeal 	if (ndi_prop_update_int64(DDI_DEV_T_NONE, dip, LUN64_PROP,
943*8226594fSRick McNeal 	    d->pd_lun) != DDI_PROP_SUCCESS) {
944*8226594fSRick McNeal 		goto free_devi;
945*8226594fSRick McNeal 	}
946*8226594fSRick McNeal 
947*8226594fSRick McNeal 	if (ndi_prop_update_string_array(DDI_DEV_T_NONE, dip, COMPAT_PROP,
948*8226594fSRick McNeal 	    compatible, ncompatible) != DDI_PROP_SUCCESS) {
949*8226594fSRick McNeal 		goto free_devi;
950*8226594fSRick McNeal 	}
951*8226594fSRick McNeal 
952*8226594fSRick McNeal 	if (d->pd_wwid != 0) {
953*8226594fSRick McNeal 		char		wwn_str[20];
954*8226594fSRick McNeal 		(void) snprintf(wwn_str, 20, "w%016" PRIx64, d->pd_wwid);
955*8226594fSRick McNeal 		if (ndi_prop_update_string(DDI_DEV_T_NONE, dip,
956*8226594fSRick McNeal 		    SCSI_ADDR_PROP_TARGET_PORT, wwn_str) != DDI_PROP_SUCCESS) {
957*8226594fSRick McNeal 			goto free_devi;
958*8226594fSRick McNeal 		}
959*8226594fSRick McNeal 	} else {
960*8226594fSRick McNeal 		if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, TARGET_PROP,
961*8226594fSRick McNeal 		    d->pd_target) != DDI_PROP_SUCCESS) {
962*8226594fSRick McNeal 			goto free_devi;
963*8226594fSRick McNeal 		}
964*8226594fSRick McNeal 	}
965*8226594fSRick McNeal 
966*8226594fSRick McNeal 	if (d->pd_guid != NULL) {
967*8226594fSRick McNeal 		if (ddi_prop_update_string(DDI_DEV_T_NONE, dip, NDI_GUID,
968*8226594fSRick McNeal 		    d->pd_guid) != DDI_PROP_SUCCESS) {
969*8226594fSRick McNeal 			goto free_devi;
970*8226594fSRick McNeal 		}
971*8226594fSRick McNeal 	}
972*8226594fSRick McNeal 
973*8226594fSRick McNeal 	if (ndi_prop_update_int(DDI_DEV_T_NONE, dip, "pm-capable", 1) !=
974*8226594fSRick McNeal 	    DDI_PROP_SUCCESS) {
975*8226594fSRick McNeal 		goto free_devi;
976*8226594fSRick McNeal 	}
977*8226594fSRick McNeal 
978*8226594fSRick McNeal 	if (ndi_devi_online(dip, NDI_ONLINE_ATTACH) != NDI_SUCCESS)
979*8226594fSRick McNeal 		goto free_devi;
980*8226594fSRick McNeal 
981*8226594fSRick McNeal 	if (childp != NULL)
982*8226594fSRick McNeal 		*childp = dip;
983*8226594fSRick McNeal 
984*8226594fSRick McNeal 	scsi_hba_nodename_compatible_free(nodename, compatible);
985*8226594fSRick McNeal 
986*8226594fSRick McNeal 	return (B_TRUE);
987*8226594fSRick McNeal 
988*8226594fSRick McNeal free_devi:
989*8226594fSRick McNeal 	ndi_prop_remove_all(dip);
990*8226594fSRick McNeal 	(void) ndi_devi_free(dip);
991*8226594fSRick McNeal 	d->pd_dip = NULL;
992*8226594fSRick McNeal free_nodename:
993*8226594fSRick McNeal 	scsi_hba_nodename_compatible_free(nodename, compatible);
994*8226594fSRick McNeal 	return (B_FALSE);
995*8226594fSRick McNeal }
996*8226594fSRick McNeal 
997*8226594fSRick McNeal static boolean_t
998*8226594fSRick McNeal create_virt_lun(pqi_state_t *s, pqi_device_t *d, struct scsi_inquiry *inq,
999*8226594fSRick McNeal     dev_info_t **childp)
1000*8226594fSRick McNeal {
1001*8226594fSRick McNeal 	char		*nodename;
1002*8226594fSRick McNeal 	char		**compatible;
1003*8226594fSRick McNeal 	int		ncompatible;
1004*8226594fSRick McNeal 	int		rval;
1005*8226594fSRick McNeal 	mdi_pathinfo_t	*pip		= NULL;
1006*8226594fSRick McNeal 	char		*guid_ptr;
1007*8226594fSRick McNeal 	char		wwid_str[17];
1008*8226594fSRick McNeal 	dev_info_t	*lun_dip;
1009*8226594fSRick McNeal 	char		*old_guid;
1010*8226594fSRick McNeal 
1011*8226594fSRick McNeal 	if (d->pd_pip_offlined != NULL) {
1012*8226594fSRick McNeal 		lun_dip = mdi_pi_get_client(d->pd_pip_offlined);
1013*8226594fSRick McNeal 		ASSERT(lun_dip != NULL);
1014*8226594fSRick McNeal 
1015*8226594fSRick McNeal 		if (ddi_prop_lookup_string(DDI_DEV_T_ANY, lun_dip,
1016*8226594fSRick McNeal 		    (DDI_PROP_DONTPASS | DDI_PROP_NOTPROM),
1017*8226594fSRick McNeal 		    MDI_CLIENT_GUID_PROP, &old_guid) == DDI_SUCCESS) {
1018*8226594fSRick McNeal 			if (strncmp(d->pd_guid, old_guid,
1019*8226594fSRick McNeal 			    strlen(d->pd_guid)) == 0) {
1020*8226594fSRick McNeal 				/* ---- Same path came back online ---- */
1021*8226594fSRick McNeal 				(void) ddi_prop_free(old_guid);
1022*8226594fSRick McNeal 				if (mdi_pi_online(d->pd_pip_offlined, 0) ==
1023*8226594fSRick McNeal 				    DDI_SUCCESS) {
1024*8226594fSRick McNeal 					d->pd_pip = d->pd_pip_offlined;
1025*8226594fSRick McNeal 					d->pd_pip_offlined = NULL;
1026*8226594fSRick McNeal 					return (B_TRUE);
1027*8226594fSRick McNeal 				} else {
1028*8226594fSRick McNeal 					return (B_FALSE);
1029*8226594fSRick McNeal 				}
1030*8226594fSRick McNeal 			} else {
1031*8226594fSRick McNeal 				/* ---- Different device in slot ---- */
1032*8226594fSRick McNeal 				(void) ddi_prop_free(old_guid);
1033*8226594fSRick McNeal 				if (mdi_pi_offline(d->pd_pip_offlined, 0) !=
1034*8226594fSRick McNeal 				    DDI_SUCCESS) {
1035*8226594fSRick McNeal 					return (B_FALSE);
1036*8226594fSRick McNeal 				}
1037*8226594fSRick McNeal 				if (mdi_pi_free(d->pd_pip_offlined, 0) !=
1038*8226594fSRick McNeal 				    MDI_SUCCESS) {
1039*8226594fSRick McNeal 					return (B_FALSE);
1040*8226594fSRick McNeal 				}
1041*8226594fSRick McNeal 				d->pd_pip_offlined = NULL;
1042*8226594fSRick McNeal 			}
1043*8226594fSRick McNeal 		} else {
1044*8226594fSRick McNeal 			dev_err(s->s_dip, CE_WARN, "Can't get client-guid "
1045*8226594fSRick McNeal 			    "property for lun %lx", d->pd_wwid);
1046*8226594fSRick McNeal 			return (B_FALSE);
1047*8226594fSRick McNeal 		}
1048*8226594fSRick McNeal 	}
1049*8226594fSRick McNeal 
1050*8226594fSRick McNeal 	scsi_hba_nodename_compatible_get(inq, NULL, inq->inq_dtype, NULL,
1051*8226594fSRick McNeal 	    &nodename, &compatible, &ncompatible);
1052*8226594fSRick McNeal 	if (nodename == NULL)
1053*8226594fSRick McNeal 		return (B_FALSE);
1054*8226594fSRick McNeal 
1055*8226594fSRick McNeal 	if (d->pd_guid != NULL) {
1056*8226594fSRick McNeal 		guid_ptr = d->pd_guid;
1057*8226594fSRick McNeal 	} else {
1058*8226594fSRick McNeal 		(void) snprintf(wwid_str, sizeof (wwid_str), "%" PRIx64,
1059*8226594fSRick McNeal 		    d->pd_wwid);
1060*8226594fSRick McNeal 		guid_ptr = wwid_str;
1061*8226594fSRick McNeal 	}
1062*8226594fSRick McNeal 	rval = mdi_pi_alloc_compatible(s->s_dip, nodename, guid_ptr,
1063*8226594fSRick McNeal 	    d->pd_unit_address, compatible, ncompatible, 0, &pip);
1064*8226594fSRick McNeal 	if (rval == MDI_SUCCESS) {
1065*8226594fSRick McNeal 		mdi_pi_set_phci_private(pip, (caddr_t)d);
1066*8226594fSRick McNeal 
1067*8226594fSRick McNeal 		if (mdi_prop_update_string(pip, MDI_GUID, guid_ptr) !=
1068*8226594fSRick McNeal 		    DDI_SUCCESS) {
1069*8226594fSRick McNeal 			dev_err(s->s_dip, CE_WARN,
1070*8226594fSRick McNeal 			    "unable to create property (MDI_GUID) for %s",
1071*8226594fSRick McNeal 			    guid_ptr);
1072*8226594fSRick McNeal 			goto cleanup;
1073*8226594fSRick McNeal 		}
1074*8226594fSRick McNeal 
1075*8226594fSRick McNeal 		/*
1076*8226594fSRick McNeal 		 * For MPxIO, we actually don't really need to care
1077*8226594fSRick McNeal 		 * about the LUN or target property, because nothing
1078*8226594fSRick McNeal 		 * really uses them.
1079*8226594fSRick McNeal 		 */
1080*8226594fSRick McNeal 		if (mdi_prop_update_int64(pip, LUN64_PROP, d->pd_lun) !=
1081*8226594fSRick McNeal 		    DDI_SUCCESS) {
1082*8226594fSRick McNeal 			dev_err(s->s_dip, CE_WARN,
1083*8226594fSRick McNeal 			    "unable to create property (%s) for %s",
1084*8226594fSRick McNeal 			    LUN64_PROP, guid_ptr);
1085*8226594fSRick McNeal 			goto cleanup;
1086*8226594fSRick McNeal 		}
1087*8226594fSRick McNeal 
1088*8226594fSRick McNeal 		if (mdi_prop_update_string_array(pip, COMPAT_PROP,
1089*8226594fSRick McNeal 		    compatible, ncompatible) != DDI_SUCCESS) {
1090*8226594fSRick McNeal 			dev_err(s->s_dip, CE_WARN,
1091*8226594fSRick McNeal 			    "unable to create property (%s) for %s",
1092*8226594fSRick McNeal 			    COMPAT_PROP, guid_ptr);
1093*8226594fSRick McNeal 			goto cleanup;
1094*8226594fSRick McNeal 		}
1095*8226594fSRick McNeal 
1096*8226594fSRick McNeal 		if (mdi_pi_online(pip, 0) == MDI_NOT_SUPPORTED)
1097*8226594fSRick McNeal 			goto cleanup;
1098*8226594fSRick McNeal 
1099*8226594fSRick McNeal 		d->pd_dip = NULL;
1100*8226594fSRick McNeal 		d->pd_pip = pip;
1101*8226594fSRick McNeal 	}
1102*8226594fSRick McNeal 
1103*8226594fSRick McNeal 	scsi_hba_nodename_compatible_free(nodename, compatible);
1104*8226594fSRick McNeal 	if (childp != NULL)
1105*8226594fSRick McNeal 		*childp = mdi_pi_get_client(pip);
1106*8226594fSRick McNeal 	return (B_TRUE);
1107*8226594fSRick McNeal cleanup:
1108*8226594fSRick McNeal 	scsi_hba_nodename_compatible_free(nodename, compatible);
1109*8226594fSRick McNeal 	d->pd_pip = NULL;
1110*8226594fSRick McNeal 	d->pd_dip = NULL;
1111*8226594fSRick McNeal 	(void) mdi_prop_remove(pip, NULL);
1112*8226594fSRick McNeal 	(void) mdi_pi_free(pip, 0);
1113*8226594fSRick McNeal 	return (B_FALSE);
1114*8226594fSRick McNeal }
1115*8226594fSRick McNeal 
1116*8226594fSRick McNeal static int
1117*8226594fSRick McNeal config_one(dev_info_t *pdip, pqi_state_t *s, pqi_device_t *d,
1118*8226594fSRick McNeal     dev_info_t **childp)
1119*8226594fSRick McNeal {
1120*8226594fSRick McNeal 	struct scsi_inquiry	inq;
1121*8226594fSRick McNeal 	boolean_t		rval = B_FALSE;
1122*8226594fSRick McNeal 
1123*8226594fSRick McNeal 	/* ---- Inquiry target ---- */
1124*8226594fSRick McNeal 	if (!d->pd_online ||
1125*8226594fSRick McNeal 	    pqi_scsi_inquiry(s, d, 0, &inq, sizeof (inq)) == B_FALSE) {
1126*8226594fSRick McNeal 		pqi_fail_drive_cmds(d, CMD_DEV_GONE);
1127*8226594fSRick McNeal 
1128*8226594fSRick McNeal 		if (d->pd_dip != NULL) {
1129*8226594fSRick McNeal 			(void) ndi_devi_offline(d->pd_dip,
1130*8226594fSRick McNeal 			    NDI_DEVFS_CLEAN | NDI_DEVI_REMOVE);
1131*8226594fSRick McNeal 			d->pd_dip = NULL;
1132*8226594fSRick McNeal 		} else if (d->pd_pip != NULL) {
1133*8226594fSRick McNeal 			(void) mdi_pi_offline(d->pd_pip, 0);
1134*8226594fSRick McNeal 			d->pd_pip_offlined = d->pd_pip;
1135*8226594fSRick McNeal 			d->pd_pip = NULL;
1136*8226594fSRick McNeal 		}
1137*8226594fSRick McNeal 		return (NDI_FAILURE);
1138*8226594fSRick McNeal 	} else if (d->pd_dip != NULL) {
1139*8226594fSRick McNeal 		if (childp != NULL)
1140*8226594fSRick McNeal 			*childp = d->pd_dip;
1141*8226594fSRick McNeal 		return (NDI_SUCCESS);
1142*8226594fSRick McNeal 	} else if (d->pd_pip != NULL) {
1143*8226594fSRick McNeal 		if (childp != NULL)
1144*8226594fSRick McNeal 			*childp = mdi_pi_get_client(d->pd_pip);
1145*8226594fSRick McNeal 		return (NDI_SUCCESS);
1146*8226594fSRick McNeal 	}
1147*8226594fSRick McNeal 
1148*8226594fSRick McNeal 	d->pd_parent = pdip;
1149*8226594fSRick McNeal 	if ((!s->s_disable_mpxio) && is_physical_dev(d))
1150*8226594fSRick McNeal 		rval = create_virt_lun(s, d, &inq, childp);
1151*8226594fSRick McNeal 
1152*8226594fSRick McNeal 	if (rval == B_FALSE)
1153*8226594fSRick McNeal 		rval = create_phys_lun(s, d, &inq, childp);
1154*8226594fSRick McNeal 
1155*8226594fSRick McNeal 	return ((rval == B_TRUE) ? NDI_SUCCESS : NDI_FAILURE);
1156*8226594fSRick McNeal }
1157*8226594fSRick McNeal 
1158*8226594fSRick McNeal static void
1159*8226594fSRick McNeal cmd_ext_free(pqi_cmd_t *cmd)
1160*8226594fSRick McNeal {
1161*8226594fSRick McNeal 	struct scsi_pkt *pkt = CMD2PKT(cmd);
1162*8226594fSRick McNeal 
1163*8226594fSRick McNeal 	if ((cmd->pc_flags & PQI_FLAG_CDB_EXT) != 0) {
1164*8226594fSRick McNeal 		kmem_free(pkt->pkt_cdbp, cmd->pc_cmdlen);
1165*8226594fSRick McNeal 		cmd->pc_flags &= ~PQI_FLAG_CDB_EXT;
1166*8226594fSRick McNeal 	}
1167*8226594fSRick McNeal 	if ((cmd->pc_flags & PQI_FLAG_SCB_EXT) != 0) {
1168*8226594fSRick McNeal 		kmem_free(pkt->pkt_scbp, cmd->pc_statuslen);
1169*8226594fSRick McNeal 		cmd->pc_flags &= ~PQI_FLAG_SCB_EXT;
1170*8226594fSRick McNeal 	}
1171*8226594fSRick McNeal 	if ((cmd->pc_flags & PQI_FLAG_PRIV_EXT) != 0) {
1172*8226594fSRick McNeal 		kmem_free(pkt->pkt_private, cmd->pc_tgtlen);
1173*8226594fSRick McNeal 		cmd->pc_flags &= ~PQI_FLAG_PRIV_EXT;
1174*8226594fSRick McNeal 	}
1175*8226594fSRick McNeal }
1176*8226594fSRick McNeal 
1177*8226594fSRick McNeal static int
1178*8226594fSRick McNeal cmd_ext_alloc(pqi_cmd_t *cmd, int kf)
1179*8226594fSRick McNeal {
1180*8226594fSRick McNeal 	struct scsi_pkt		*pkt = CMD2PKT(cmd);
1181*8226594fSRick McNeal 	void			*buf;
1182*8226594fSRick McNeal 
1183*8226594fSRick McNeal 	if (cmd->pc_cmdlen > sizeof (cmd->pc_cdb)) {
1184*8226594fSRick McNeal 		if ((buf = kmem_zalloc(cmd->pc_cmdlen, kf)) == NULL)
1185*8226594fSRick McNeal 			return (DDI_FAILURE);
1186*8226594fSRick McNeal 		pkt->pkt_cdbp = buf;
1187*8226594fSRick McNeal 		cmd->pc_flags |= PQI_FLAG_CDB_EXT;
1188*8226594fSRick McNeal 	}
1189*8226594fSRick McNeal 
1190*8226594fSRick McNeal 	if (cmd->pc_statuslen > sizeof (cmd->pc_cmd_scb)) {
1191*8226594fSRick McNeal 		if ((buf = kmem_zalloc(cmd->pc_statuslen, kf)) == NULL)
1192*8226594fSRick McNeal 			goto out;
1193*8226594fSRick McNeal 		pkt->pkt_scbp = buf;
1194*8226594fSRick McNeal 		cmd->pc_flags |= PQI_FLAG_SCB_EXT;
1195*8226594fSRick McNeal 	}
1196*8226594fSRick McNeal 
1197*8226594fSRick McNeal 	if (cmd->pc_tgtlen > sizeof (cmd->pc_tgt_priv)) {
1198*8226594fSRick McNeal 		if ((buf = kmem_zalloc(cmd->pc_tgtlen, kf)) == NULL)
1199*8226594fSRick McNeal 			goto out;
1200*8226594fSRick McNeal 		pkt->pkt_private = buf;
1201*8226594fSRick McNeal 		cmd->pc_flags |= PQI_FLAG_PRIV_EXT;
1202*8226594fSRick McNeal 	}
1203*8226594fSRick McNeal 
1204*8226594fSRick McNeal 	return (DDI_SUCCESS);
1205*8226594fSRick McNeal 
1206*8226594fSRick McNeal out:
1207*8226594fSRick McNeal 	cmd_ext_free(cmd);
1208*8226594fSRick McNeal 
1209*8226594fSRick McNeal 	return (DDI_FAILURE);
1210*8226594fSRick McNeal }
1211*8226594fSRick McNeal 
1212*8226594fSRick McNeal static boolean_t
1213*8226594fSRick McNeal is_physical_dev(pqi_device_t *d)
1214*8226594fSRick McNeal {
1215*8226594fSRick McNeal 	return (d->pd_phys_dev ? B_TRUE : B_FALSE);
1216*8226594fSRick McNeal }
1217