xref: /freebsd/sys/dev/mpr/mpr_sas.c (revision 89d1c21f458d83a882c92a8a2b2b5be92cbdf2ec)
1991554f2SKenneth D. Merry /*-
2991554f2SKenneth D. Merry  * Copyright (c) 2009 Yahoo! Inc.
3a2c14879SStephen McConnell  * Copyright (c) 2011-2015 LSI Corp.
47a2a6a1aSStephen McConnell  * Copyright (c) 2013-2016 Avago Technologies
5991554f2SKenneth D. Merry  * All rights reserved.
6991554f2SKenneth D. Merry  *
7991554f2SKenneth D. Merry  * Redistribution and use in source and binary forms, with or without
8991554f2SKenneth D. Merry  * modification, are permitted provided that the following conditions
9991554f2SKenneth D. Merry  * are met:
10991554f2SKenneth D. Merry  * 1. Redistributions of source code must retain the above copyright
11991554f2SKenneth D. Merry  *    notice, this list of conditions and the following disclaimer.
12991554f2SKenneth D. Merry  * 2. Redistributions in binary form must reproduce the above copyright
13991554f2SKenneth D. Merry  *    notice, this list of conditions and the following disclaimer in the
14991554f2SKenneth D. Merry  *    documentation and/or other materials provided with the distribution.
15991554f2SKenneth D. Merry  *
16991554f2SKenneth D. Merry  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17991554f2SKenneth D. Merry  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18991554f2SKenneth D. Merry  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19991554f2SKenneth D. Merry  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20991554f2SKenneth D. Merry  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21991554f2SKenneth D. Merry  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22991554f2SKenneth D. Merry  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23991554f2SKenneth D. Merry  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24991554f2SKenneth D. Merry  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25991554f2SKenneth D. Merry  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26991554f2SKenneth D. Merry  * SUCH DAMAGE.
27a2c14879SStephen McConnell  *
28a2c14879SStephen McConnell  * Avago Technologies (LSI) MPT-Fusion Host Adapter FreeBSD
29a2c14879SStephen McConnell  *
30991554f2SKenneth D. Merry  */
31991554f2SKenneth D. Merry 
32991554f2SKenneth D. Merry #include <sys/cdefs.h>
33991554f2SKenneth D. Merry __FBSDID("$FreeBSD$");
34991554f2SKenneth D. Merry 
35a2c14879SStephen McConnell /* Communications core for Avago Technologies (LSI) MPT3 */
36991554f2SKenneth D. Merry 
37991554f2SKenneth D. Merry /* TODO Move headers to mprvar */
38991554f2SKenneth D. Merry #include <sys/types.h>
39991554f2SKenneth D. Merry #include <sys/param.h>
40991554f2SKenneth D. Merry #include <sys/systm.h>
41991554f2SKenneth D. Merry #include <sys/kernel.h>
42991554f2SKenneth D. Merry #include <sys/selinfo.h>
43991554f2SKenneth D. Merry #include <sys/module.h>
44991554f2SKenneth D. Merry #include <sys/bus.h>
45991554f2SKenneth D. Merry #include <sys/conf.h>
46991554f2SKenneth D. Merry #include <sys/bio.h>
47991554f2SKenneth D. Merry #include <sys/malloc.h>
48991554f2SKenneth D. Merry #include <sys/uio.h>
49991554f2SKenneth D. Merry #include <sys/sysctl.h>
50991554f2SKenneth D. Merry #include <sys/endian.h>
51991554f2SKenneth D. Merry #include <sys/queue.h>
52991554f2SKenneth D. Merry #include <sys/kthread.h>
53991554f2SKenneth D. Merry #include <sys/taskqueue.h>
54991554f2SKenneth D. Merry #include <sys/sbuf.h>
55991554f2SKenneth D. Merry 
56991554f2SKenneth D. Merry #include <machine/bus.h>
57991554f2SKenneth D. Merry #include <machine/resource.h>
58991554f2SKenneth D. Merry #include <sys/rman.h>
59991554f2SKenneth D. Merry 
60991554f2SKenneth D. Merry #include <machine/stdarg.h>
61991554f2SKenneth D. Merry 
62991554f2SKenneth D. Merry #include <cam/cam.h>
63991554f2SKenneth D. Merry #include <cam/cam_ccb.h>
64991554f2SKenneth D. Merry #include <cam/cam_debug.h>
65991554f2SKenneth D. Merry #include <cam/cam_sim.h>
66991554f2SKenneth D. Merry #include <cam/cam_xpt_sim.h>
67991554f2SKenneth D. Merry #include <cam/cam_xpt_periph.h>
68991554f2SKenneth D. Merry #include <cam/cam_periph.h>
69991554f2SKenneth D. Merry #include <cam/scsi/scsi_all.h>
70991554f2SKenneth D. Merry #include <cam/scsi/scsi_message.h>
71991554f2SKenneth D. Merry #if __FreeBSD_version >= 900026
72991554f2SKenneth D. Merry #include <cam/scsi/smp_all.h>
73991554f2SKenneth D. Merry #endif
74991554f2SKenneth D. Merry 
7567feec50SStephen McConnell #include <dev/nvme/nvme.h>
7667feec50SStephen McConnell 
77991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_type.h>
78991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2.h>
79991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_ioc.h>
80991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_sas.h>
8167feec50SStephen McConnell #include <dev/mpr/mpi/mpi2_pci.h>
82991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_cnfg.h>
83991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_init.h>
84991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_tool.h>
85991554f2SKenneth D. Merry #include <dev/mpr/mpr_ioctl.h>
86991554f2SKenneth D. Merry #include <dev/mpr/mprvar.h>
87991554f2SKenneth D. Merry #include <dev/mpr/mpr_table.h>
88991554f2SKenneth D. Merry #include <dev/mpr/mpr_sas.h>
89991554f2SKenneth D. Merry 
90991554f2SKenneth D. Merry #define MPRSAS_DISCOVERY_TIMEOUT	20
91991554f2SKenneth D. Merry #define MPRSAS_MAX_DISCOVERY_TIMEOUTS	10 /* 200 seconds */
92991554f2SKenneth D. Merry 
93991554f2SKenneth D. Merry /*
94991554f2SKenneth D. Merry  * static array to check SCSI OpCode for EEDP protection bits
95991554f2SKenneth D. Merry  */
96991554f2SKenneth D. Merry #define	PRO_R MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP
97991554f2SKenneth D. Merry #define	PRO_W MPI2_SCSIIO_EEDPFLAGS_INSERT_OP
98991554f2SKenneth D. Merry #define	PRO_V MPI2_SCSIIO_EEDPFLAGS_INSERT_OP
99991554f2SKenneth D. Merry static uint8_t op_code_prot[256] = {
100991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
101991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
102991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
103991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
104991554f2SKenneth D. Merry 	0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
105991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
106991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
107991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
108991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
109991554f2SKenneth D. Merry 	0, 0, 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
110991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V,
111991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
112991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
113991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
114991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
115991554f2SKenneth D. Merry 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
116991554f2SKenneth D. Merry };
117991554f2SKenneth D. Merry 
118991554f2SKenneth D. Merry MALLOC_DEFINE(M_MPRSAS, "MPRSAS", "MPR SAS memory");
119991554f2SKenneth D. Merry 
120991554f2SKenneth D. Merry static void mprsas_remove_device(struct mpr_softc *, struct mpr_command *);
121991554f2SKenneth D. Merry static void mprsas_remove_complete(struct mpr_softc *, struct mpr_command *);
122991554f2SKenneth D. Merry static void mprsas_action(struct cam_sim *sim, union ccb *ccb);
123991554f2SKenneth D. Merry static void mprsas_poll(struct cam_sim *sim);
124991554f2SKenneth D. Merry static void mprsas_scsiio_timeout(void *data);
1257a2a6a1aSStephen McConnell static void mprsas_abort_complete(struct mpr_softc *sc, struct mpr_command *cm);
126991554f2SKenneth D. Merry static void mprsas_action_scsiio(struct mprsas_softc *, union ccb *);
127991554f2SKenneth D. Merry static void mprsas_scsiio_complete(struct mpr_softc *, struct mpr_command *);
128991554f2SKenneth D. Merry static void mprsas_action_resetdev(struct mprsas_softc *, union ccb *);
1297a2a6a1aSStephen McConnell static void mprsas_resetdev_complete(struct mpr_softc *, struct mpr_command *);
130991554f2SKenneth D. Merry static int mprsas_send_abort(struct mpr_softc *sc, struct mpr_command *tm,
131991554f2SKenneth D. Merry     struct mpr_command *cm);
132991554f2SKenneth D. Merry static void mprsas_async(void *callback_arg, uint32_t code,
133991554f2SKenneth D. Merry     struct cam_path *path, void *arg);
134991554f2SKenneth D. Merry #if (__FreeBSD_version < 901503) || \
135991554f2SKenneth D. Merry     ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000006))
136991554f2SKenneth D. Merry static void mprsas_check_eedp(struct mpr_softc *sc, struct cam_path *path,
137991554f2SKenneth D. Merry     struct ccb_getdev *cgd);
138991554f2SKenneth D. Merry static void mprsas_read_cap_done(struct cam_periph *periph,
139991554f2SKenneth D. Merry     union ccb *done_ccb);
140991554f2SKenneth D. Merry #endif
141991554f2SKenneth D. Merry static int mprsas_send_portenable(struct mpr_softc *sc);
142991554f2SKenneth D. Merry static void mprsas_portenable_complete(struct mpr_softc *sc,
143991554f2SKenneth D. Merry     struct mpr_command *cm);
144991554f2SKenneth D. Merry 
145991554f2SKenneth D. Merry #if __FreeBSD_version >= 900026
1467a2a6a1aSStephen McConnell static void mprsas_smpio_complete(struct mpr_softc *sc, struct mpr_command *cm);
1477a2a6a1aSStephen McConnell static void mprsas_send_smpcmd(struct mprsas_softc *sassc, union ccb *ccb,
1487a2a6a1aSStephen McConnell     uint64_t sasaddr);
149a2c14879SStephen McConnell static void mprsas_action_smpio(struct mprsas_softc *sassc, union ccb *ccb);
150a2c14879SStephen McConnell #endif //FreeBSD_version >= 900026
151991554f2SKenneth D. Merry 
152991554f2SKenneth D. Merry struct mprsas_target *
153991554f2SKenneth D. Merry mprsas_find_target_by_handle(struct mprsas_softc *sassc, int start,
154991554f2SKenneth D. Merry     uint16_t handle)
155991554f2SKenneth D. Merry {
156991554f2SKenneth D. Merry 	struct mprsas_target *target;
157991554f2SKenneth D. Merry 	int i;
158991554f2SKenneth D. Merry 
159991554f2SKenneth D. Merry 	for (i = start; i < sassc->maxtargets; i++) {
160991554f2SKenneth D. Merry 		target = &sassc->targets[i];
161991554f2SKenneth D. Merry 		if (target->handle == handle)
162991554f2SKenneth D. Merry 			return (target);
163991554f2SKenneth D. Merry 	}
164991554f2SKenneth D. Merry 
165991554f2SKenneth D. Merry 	return (NULL);
166991554f2SKenneth D. Merry }
167991554f2SKenneth D. Merry 
168991554f2SKenneth D. Merry /* we need to freeze the simq during attach and diag reset, to avoid failing
169991554f2SKenneth D. Merry  * commands before device handles have been found by discovery.  Since
170991554f2SKenneth D. Merry  * discovery involves reading config pages and possibly sending commands,
171991554f2SKenneth D. Merry  * discovery actions may continue even after we receive the end of discovery
172991554f2SKenneth D. Merry  * event, so refcount discovery actions instead of assuming we can unfreeze
173991554f2SKenneth D. Merry  * the simq when we get the event.
174991554f2SKenneth D. Merry  */
175991554f2SKenneth D. Merry void
176991554f2SKenneth D. Merry mprsas_startup_increment(struct mprsas_softc *sassc)
177991554f2SKenneth D. Merry {
178991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sassc->sc);
179991554f2SKenneth D. Merry 
180991554f2SKenneth D. Merry 	if ((sassc->flags & MPRSAS_IN_STARTUP) != 0) {
181991554f2SKenneth D. Merry 		if (sassc->startup_refcount++ == 0) {
182991554f2SKenneth D. Merry 			/* just starting, freeze the simq */
183991554f2SKenneth D. Merry 			mpr_dprint(sassc->sc, MPR_INIT,
184991554f2SKenneth D. Merry 			    "%s freezing simq\n", __func__);
185a371d6f9SKenneth D. Merry #if (__FreeBSD_version >= 1000039) || \
186a371d6f9SKenneth D. Merry     ((__FreeBSD_version < 1000000) && (__FreeBSD_version >= 902502))
187991554f2SKenneth D. Merry 			xpt_hold_boot();
188991554f2SKenneth D. Merry #endif
189991554f2SKenneth D. Merry 			xpt_freeze_simq(sassc->sim, 1);
190991554f2SKenneth D. Merry 		}
191991554f2SKenneth D. Merry 		mpr_dprint(sassc->sc, MPR_INIT, "%s refcount %u\n", __func__,
192991554f2SKenneth D. Merry 		    sassc->startup_refcount);
193991554f2SKenneth D. Merry 	}
194991554f2SKenneth D. Merry }
195991554f2SKenneth D. Merry 
196991554f2SKenneth D. Merry void
197991554f2SKenneth D. Merry mprsas_release_simq_reinit(struct mprsas_softc *sassc)
198991554f2SKenneth D. Merry {
199991554f2SKenneth D. Merry 	if (sassc->flags & MPRSAS_QUEUE_FROZEN) {
200991554f2SKenneth D. Merry 		sassc->flags &= ~MPRSAS_QUEUE_FROZEN;
201991554f2SKenneth D. Merry 		xpt_release_simq(sassc->sim, 1);
202991554f2SKenneth D. Merry 		mpr_dprint(sassc->sc, MPR_INFO, "Unfreezing SIM queue\n");
203991554f2SKenneth D. Merry 	}
204991554f2SKenneth D. Merry }
205991554f2SKenneth D. Merry 
206991554f2SKenneth D. Merry void
207991554f2SKenneth D. Merry mprsas_startup_decrement(struct mprsas_softc *sassc)
208991554f2SKenneth D. Merry {
209991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sassc->sc);
210991554f2SKenneth D. Merry 
211991554f2SKenneth D. Merry 	if ((sassc->flags & MPRSAS_IN_STARTUP) != 0) {
212991554f2SKenneth D. Merry 		if (--sassc->startup_refcount == 0) {
213991554f2SKenneth D. Merry 			/* finished all discovery-related actions, release
214991554f2SKenneth D. Merry 			 * the simq and rescan for the latest topology.
215991554f2SKenneth D. Merry 			 */
216991554f2SKenneth D. Merry 			mpr_dprint(sassc->sc, MPR_INIT,
217991554f2SKenneth D. Merry 			    "%s releasing simq\n", __func__);
218991554f2SKenneth D. Merry 			sassc->flags &= ~MPRSAS_IN_STARTUP;
219991554f2SKenneth D. Merry 			xpt_release_simq(sassc->sim, 1);
220a371d6f9SKenneth D. Merry #if (__FreeBSD_version >= 1000039) || \
221a371d6f9SKenneth D. Merry     ((__FreeBSD_version < 1000000) && (__FreeBSD_version >= 902502))
222991554f2SKenneth D. Merry 			xpt_release_boot();
223991554f2SKenneth D. Merry #else
224991554f2SKenneth D. Merry 			mprsas_rescan_target(sassc->sc, NULL);
225991554f2SKenneth D. Merry #endif
226991554f2SKenneth D. Merry 		}
227991554f2SKenneth D. Merry 		mpr_dprint(sassc->sc, MPR_INIT, "%s refcount %u\n", __func__,
228991554f2SKenneth D. Merry 		    sassc->startup_refcount);
229991554f2SKenneth D. Merry 	}
230991554f2SKenneth D. Merry }
231991554f2SKenneth D. Merry 
232b7f1ee79SScott Long /*
233b7f1ee79SScott Long  * The firmware requires us to stop sending commands when we're doing task
234b7f1ee79SScott Long  * management.
235991554f2SKenneth D. Merry  * use.
236b7f1ee79SScott Long  * XXX The logic for serializing the device has been made lazy and moved to
237b7f1ee79SScott Long  * mprsas_prepare_for_tm().
238991554f2SKenneth D. Merry  */
239991554f2SKenneth D. Merry struct mpr_command *
240991554f2SKenneth D. Merry mprsas_alloc_tm(struct mpr_softc *sc)
241991554f2SKenneth D. Merry {
24246b9415fSScott Long 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
243991554f2SKenneth D. Merry 	struct mpr_command *tm;
244991554f2SKenneth D. Merry 
245991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
246991554f2SKenneth D. Merry 	tm = mpr_alloc_high_priority_command(sc);
24746b9415fSScott Long 	if (tm == NULL)
24846b9415fSScott Long 		return (NULL);
24946b9415fSScott Long 
25046b9415fSScott Long 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
25146b9415fSScott Long 	req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT;
252991554f2SKenneth D. Merry 	return tm;
253991554f2SKenneth D. Merry }
254991554f2SKenneth D. Merry 
255991554f2SKenneth D. Merry void
256991554f2SKenneth D. Merry mprsas_free_tm(struct mpr_softc *sc, struct mpr_command *tm)
257991554f2SKenneth D. Merry {
25888619392SStephen McConnell 	int target_id = 0xFFFFFFFF;
25988619392SStephen McConnell 
260a2c14879SStephen McConnell 	MPR_FUNCTRACE(sc);
261991554f2SKenneth D. Merry 	if (tm == NULL)
262991554f2SKenneth D. Merry 		return;
263991554f2SKenneth D. Merry 
264a2c14879SStephen McConnell 	/*
265a2c14879SStephen McConnell 	 * For TM's the devq is frozen for the device.  Unfreeze it here and
266a2c14879SStephen McConnell 	 * free the resources used for freezing the devq.  Must clear the
267a2c14879SStephen McConnell 	 * INRESET flag as well or scsi I/O will not work.
268991554f2SKenneth D. Merry 	 */
269a2c14879SStephen McConnell 	if (tm->cm_targ != NULL) {
270a2c14879SStephen McConnell 		tm->cm_targ->flags &= ~MPRSAS_TARGET_INRESET;
27188619392SStephen McConnell 		target_id = tm->cm_targ->tid;
272991554f2SKenneth D. Merry 	}
273a2c14879SStephen McConnell 	if (tm->cm_ccb) {
274a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_INFO, "Unfreezing devq for target ID %d\n",
27588619392SStephen McConnell 		    target_id);
276a2c14879SStephen McConnell 		xpt_release_devq(tm->cm_ccb->ccb_h.path, 1, TRUE);
277a2c14879SStephen McConnell 		xpt_free_path(tm->cm_ccb->ccb_h.path);
278a2c14879SStephen McConnell 		xpt_free_ccb(tm->cm_ccb);
279a2c14879SStephen McConnell 	}
280991554f2SKenneth D. Merry 
281991554f2SKenneth D. Merry 	mpr_free_high_priority_command(sc, tm);
282991554f2SKenneth D. Merry }
283991554f2SKenneth D. Merry 
284991554f2SKenneth D. Merry void
285991554f2SKenneth D. Merry mprsas_rescan_target(struct mpr_softc *sc, struct mprsas_target *targ)
286991554f2SKenneth D. Merry {
287991554f2SKenneth D. Merry 	struct mprsas_softc *sassc = sc->sassc;
288991554f2SKenneth D. Merry 	path_id_t pathid;
289991554f2SKenneth D. Merry 	target_id_t targetid;
290991554f2SKenneth D. Merry 	union ccb *ccb;
291991554f2SKenneth D. Merry 
292991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
293991554f2SKenneth D. Merry 	pathid = cam_sim_path(sassc->sim);
294991554f2SKenneth D. Merry 	if (targ == NULL)
295991554f2SKenneth D. Merry 		targetid = CAM_TARGET_WILDCARD;
296991554f2SKenneth D. Merry 	else
297991554f2SKenneth D. Merry 		targetid = targ - sassc->targets;
298991554f2SKenneth D. Merry 
299991554f2SKenneth D. Merry 	/*
300991554f2SKenneth D. Merry 	 * Allocate a CCB and schedule a rescan.
301991554f2SKenneth D. Merry 	 */
302991554f2SKenneth D. Merry 	ccb = xpt_alloc_ccb_nowait();
303991554f2SKenneth D. Merry 	if (ccb == NULL) {
304991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "unable to alloc CCB for rescan\n");
305991554f2SKenneth D. Merry 		return;
306991554f2SKenneth D. Merry 	}
307991554f2SKenneth D. Merry 
308a2c14879SStephen McConnell 	if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid, targetid,
309a2c14879SStephen McConnell 	    CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
310991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "unable to create path for rescan\n");
311991554f2SKenneth D. Merry 		xpt_free_ccb(ccb);
312991554f2SKenneth D. Merry 		return;
313991554f2SKenneth D. Merry 	}
314991554f2SKenneth D. Merry 
315991554f2SKenneth D. Merry 	if (targetid == CAM_TARGET_WILDCARD)
316991554f2SKenneth D. Merry 		ccb->ccb_h.func_code = XPT_SCAN_BUS;
317991554f2SKenneth D. Merry 	else
318991554f2SKenneth D. Merry 		ccb->ccb_h.func_code = XPT_SCAN_TGT;
319991554f2SKenneth D. Merry 
320991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_TRACE, "%s targetid %u\n", __func__, targetid);
321991554f2SKenneth D. Merry 	xpt_rescan(ccb);
322991554f2SKenneth D. Merry }
323991554f2SKenneth D. Merry 
324991554f2SKenneth D. Merry static void
325991554f2SKenneth D. Merry mprsas_log_command(struct mpr_command *cm, u_int level, const char *fmt, ...)
326991554f2SKenneth D. Merry {
327991554f2SKenneth D. Merry 	struct sbuf sb;
328991554f2SKenneth D. Merry 	va_list ap;
329991554f2SKenneth D. Merry 	char str[192];
330991554f2SKenneth D. Merry 	char path_str[64];
331991554f2SKenneth D. Merry 
332991554f2SKenneth D. Merry 	if (cm == NULL)
333991554f2SKenneth D. Merry 		return;
334991554f2SKenneth D. Merry 
335991554f2SKenneth D. Merry 	/* No need to be in here if debugging isn't enabled */
336991554f2SKenneth D. Merry 	if ((cm->cm_sc->mpr_debug & level) == 0)
337991554f2SKenneth D. Merry 		return;
338991554f2SKenneth D. Merry 
339991554f2SKenneth D. Merry 	sbuf_new(&sb, str, sizeof(str), 0);
340991554f2SKenneth D. Merry 
341991554f2SKenneth D. Merry 	va_start(ap, fmt);
342991554f2SKenneth D. Merry 
343991554f2SKenneth D. Merry 	if (cm->cm_ccb != NULL) {
344991554f2SKenneth D. Merry 		xpt_path_string(cm->cm_ccb->csio.ccb_h.path, path_str,
345991554f2SKenneth D. Merry 		    sizeof(path_str));
346991554f2SKenneth D. Merry 		sbuf_cat(&sb, path_str);
347991554f2SKenneth D. Merry 		if (cm->cm_ccb->ccb_h.func_code == XPT_SCSI_IO) {
348991554f2SKenneth D. Merry 			scsi_command_string(&cm->cm_ccb->csio, &sb);
349991554f2SKenneth D. Merry 			sbuf_printf(&sb, "length %d ",
350991554f2SKenneth D. Merry 			    cm->cm_ccb->csio.dxfer_len);
351991554f2SKenneth D. Merry 		}
352991554f2SKenneth D. Merry 	} else {
353991554f2SKenneth D. Merry 		sbuf_printf(&sb, "(noperiph:%s%d:%u:%u:%u): ",
354991554f2SKenneth D. Merry 		    cam_sim_name(cm->cm_sc->sassc->sim),
355991554f2SKenneth D. Merry 		    cam_sim_unit(cm->cm_sc->sassc->sim),
356991554f2SKenneth D. Merry 		    cam_sim_bus(cm->cm_sc->sassc->sim),
357991554f2SKenneth D. Merry 		    cm->cm_targ ? cm->cm_targ->tid : 0xFFFFFFFF,
358991554f2SKenneth D. Merry 		    cm->cm_lun);
359991554f2SKenneth D. Merry 	}
360991554f2SKenneth D. Merry 
361991554f2SKenneth D. Merry 	sbuf_printf(&sb, "SMID %u ", cm->cm_desc.Default.SMID);
362991554f2SKenneth D. Merry 	sbuf_vprintf(&sb, fmt, ap);
363991554f2SKenneth D. Merry 	sbuf_finish(&sb);
364c11c484fSScott Long 	mpr_print_field(cm->cm_sc, "%s", sbuf_data(&sb));
365991554f2SKenneth D. Merry 
366991554f2SKenneth D. Merry 	va_end(ap);
367991554f2SKenneth D. Merry }
368991554f2SKenneth D. Merry 
369991554f2SKenneth D. Merry static void
370991554f2SKenneth D. Merry mprsas_remove_volume(struct mpr_softc *sc, struct mpr_command *tm)
371991554f2SKenneth D. Merry {
372991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
373991554f2SKenneth D. Merry 	struct mprsas_target *targ;
374991554f2SKenneth D. Merry 	uint16_t handle;
375991554f2SKenneth D. Merry 
376991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
377991554f2SKenneth D. Merry 
378991554f2SKenneth D. Merry 	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
379991554f2SKenneth D. Merry 	handle = (uint16_t)(uintptr_t)tm->cm_complete_data;
380991554f2SKenneth D. Merry 	targ = tm->cm_targ;
381991554f2SKenneth D. Merry 
382991554f2SKenneth D. Merry 	if (reply == NULL) {
383991554f2SKenneth D. Merry 		/* XXX retry the remove after the diag reset completes? */
384991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_FAULT, "%s NULL reply resetting device "
385991554f2SKenneth D. Merry 		    "0x%04x\n", __func__, handle);
386991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
387991554f2SKenneth D. Merry 		return;
388991554f2SKenneth D. Merry 	}
389991554f2SKenneth D. Merry 
390d3f6eabfSStephen McConnell 	if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) !=
391d3f6eabfSStephen McConnell 	    MPI2_IOCSTATUS_SUCCESS) {
39258581c13SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "IOCStatus = 0x%x while resetting "
393d3f6eabfSStephen McConnell 		    "device 0x%x\n", le16toh(reply->IOCStatus), handle);
394991554f2SKenneth D. Merry 	}
395991554f2SKenneth D. Merry 
396991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO, "Reset aborted %u commands\n",
397d3f6eabfSStephen McConnell 	    le32toh(reply->TerminationCount));
398991554f2SKenneth D. Merry 	mpr_free_reply(sc, tm->cm_reply_data);
399991554f2SKenneth D. Merry 	tm->cm_reply = NULL;	/* Ensures the reply won't get re-freed */
400991554f2SKenneth D. Merry 
401991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO, "clearing target %u handle 0x%04x\n",
402991554f2SKenneth D. Merry 	    targ->tid, handle);
403991554f2SKenneth D. Merry 
404991554f2SKenneth D. Merry 	/*
405991554f2SKenneth D. Merry 	 * Don't clear target if remove fails because things will get confusing.
406991554f2SKenneth D. Merry 	 * Leave the devname and sasaddr intact so that we know to avoid reusing
407991554f2SKenneth D. Merry 	 * this target id if possible, and so we can assign the same target id
408991554f2SKenneth D. Merry 	 * to this device if it comes back in the future.
409991554f2SKenneth D. Merry 	 */
410d3f6eabfSStephen McConnell 	if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) ==
411d3f6eabfSStephen McConnell 	    MPI2_IOCSTATUS_SUCCESS) {
412991554f2SKenneth D. Merry 		targ = tm->cm_targ;
413991554f2SKenneth D. Merry 		targ->handle = 0x0;
414991554f2SKenneth D. Merry 		targ->encl_handle = 0x0;
415991554f2SKenneth D. Merry 		targ->encl_level_valid = 0x0;
416991554f2SKenneth D. Merry 		targ->encl_level = 0x0;
417991554f2SKenneth D. Merry 		targ->connector_name[0] = ' ';
418991554f2SKenneth D. Merry 		targ->connector_name[1] = ' ';
419991554f2SKenneth D. Merry 		targ->connector_name[2] = ' ';
420991554f2SKenneth D. Merry 		targ->connector_name[3] = ' ';
421991554f2SKenneth D. Merry 		targ->encl_slot = 0x0;
422991554f2SKenneth D. Merry 		targ->exp_dev_handle = 0x0;
423991554f2SKenneth D. Merry 		targ->phy_num = 0x0;
424991554f2SKenneth D. Merry 		targ->linkrate = 0x0;
425991554f2SKenneth D. Merry 		targ->devinfo = 0x0;
426991554f2SKenneth D. Merry 		targ->flags = 0x0;
427991554f2SKenneth D. Merry 		targ->scsi_req_desc_type = 0;
428991554f2SKenneth D. Merry 	}
429991554f2SKenneth D. Merry 
430991554f2SKenneth D. Merry 	mprsas_free_tm(sc, tm);
431991554f2SKenneth D. Merry }
432991554f2SKenneth D. Merry 
433991554f2SKenneth D. Merry 
434991554f2SKenneth D. Merry /*
435991554f2SKenneth D. Merry  * No Need to call "MPI2_SAS_OP_REMOVE_DEVICE" For Volume removal.
436991554f2SKenneth D. Merry  * Otherwise Volume Delete is same as Bare Drive Removal.
437991554f2SKenneth D. Merry  */
438991554f2SKenneth D. Merry void
439991554f2SKenneth D. Merry mprsas_prepare_volume_remove(struct mprsas_softc *sassc, uint16_t handle)
440991554f2SKenneth D. Merry {
441991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
442991554f2SKenneth D. Merry 	struct mpr_softc *sc;
443991554f2SKenneth D. Merry 	struct mpr_command *cm;
444991554f2SKenneth D. Merry 	struct mprsas_target *targ = NULL;
445991554f2SKenneth D. Merry 
446991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sassc->sc);
447991554f2SKenneth D. Merry 	sc = sassc->sc;
448991554f2SKenneth D. Merry 
449991554f2SKenneth D. Merry 	targ = mprsas_find_target_by_handle(sassc, 0, handle);
450991554f2SKenneth D. Merry 	if (targ == NULL) {
451991554f2SKenneth D. Merry 		/* FIXME: what is the action? */
452991554f2SKenneth D. Merry 		/* We don't know about this device? */
453991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR,
454991554f2SKenneth D. Merry 		   "%s %d : invalid handle 0x%x \n", __func__,__LINE__, handle);
455991554f2SKenneth D. Merry 		return;
456991554f2SKenneth D. Merry 	}
457991554f2SKenneth D. Merry 
458991554f2SKenneth D. Merry 	targ->flags |= MPRSAS_TARGET_INREMOVAL;
459991554f2SKenneth D. Merry 
460991554f2SKenneth D. Merry 	cm = mprsas_alloc_tm(sc);
461991554f2SKenneth D. Merry 	if (cm == NULL) {
462991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR,
463991554f2SKenneth D. Merry 		    "%s: command alloc failure\n", __func__);
464991554f2SKenneth D. Merry 		return;
465991554f2SKenneth D. Merry 	}
466991554f2SKenneth D. Merry 
467991554f2SKenneth D. Merry 	mprsas_rescan_target(sc, targ);
468991554f2SKenneth D. Merry 
469991554f2SKenneth D. Merry 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req;
470991554f2SKenneth D. Merry 	req->DevHandle = targ->handle;
471991554f2SKenneth D. Merry 	req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
472991554f2SKenneth D. Merry 
473*89d1c21fSKashyap D Desai 	if (!targ->is_nvme || sc->custom_nvme_tm_handling) {
474991554f2SKenneth D. Merry 		/* SAS Hard Link Reset / SATA Link Reset */
475991554f2SKenneth D. Merry 		req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
476*89d1c21fSKashyap D Desai 	} else {
477*89d1c21fSKashyap D Desai 		/* PCIe Protocol Level Reset*/
478*89d1c21fSKashyap D Desai 		req->MsgFlags =
479*89d1c21fSKashyap D Desai 		    MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE;
480*89d1c21fSKashyap D Desai 	}
481991554f2SKenneth D. Merry 
482991554f2SKenneth D. Merry 	cm->cm_targ = targ;
483991554f2SKenneth D. Merry 	cm->cm_data = NULL;
484991554f2SKenneth D. Merry 	cm->cm_complete = mprsas_remove_volume;
485991554f2SKenneth D. Merry 	cm->cm_complete_data = (void *)(uintptr_t)handle;
486a2c14879SStephen McConnell 
487a2c14879SStephen McConnell 	mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n",
488a2c14879SStephen McConnell 	    __func__, targ->tid);
489a2c14879SStephen McConnell 	mprsas_prepare_for_tm(sc, cm, targ, CAM_LUN_WILDCARD);
490a2c14879SStephen McConnell 
491991554f2SKenneth D. Merry 	mpr_map_command(sc, cm);
492991554f2SKenneth D. Merry }
493991554f2SKenneth D. Merry 
494991554f2SKenneth D. Merry /*
49567feec50SStephen McConnell  * The firmware performs debounce on the link to avoid transient link errors
49667feec50SStephen McConnell  * and false removals.  When it does decide that link has been lost and a
49767feec50SStephen McConnell  * device needs to go away, it expects that the host will perform a target reset
49867feec50SStephen McConnell  * and then an op remove.  The reset has the side-effect of aborting any
49967feec50SStephen McConnell  * outstanding requests for the device, which is required for the op-remove to
50067feec50SStephen McConnell  * succeed.  It's not clear if the host should check for the device coming back
50167feec50SStephen McConnell  * alive after the reset.
502991554f2SKenneth D. Merry  */
503991554f2SKenneth D. Merry void
504991554f2SKenneth D. Merry mprsas_prepare_remove(struct mprsas_softc *sassc, uint16_t handle)
505991554f2SKenneth D. Merry {
506991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
507991554f2SKenneth D. Merry 	struct mpr_softc *sc;
50846b9415fSScott Long 	struct mpr_command *tm;
509991554f2SKenneth D. Merry 	struct mprsas_target *targ = NULL;
510991554f2SKenneth D. Merry 
511991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sassc->sc);
512991554f2SKenneth D. Merry 
513991554f2SKenneth D. Merry 	sc = sassc->sc;
514991554f2SKenneth D. Merry 
515991554f2SKenneth D. Merry 	targ = mprsas_find_target_by_handle(sassc, 0, handle);
516991554f2SKenneth D. Merry 	if (targ == NULL) {
517991554f2SKenneth D. Merry 		/* FIXME: what is the action? */
518991554f2SKenneth D. Merry 		/* We don't know about this device? */
519991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s : invalid handle 0x%x \n",
520991554f2SKenneth D. Merry 		    __func__, handle);
521991554f2SKenneth D. Merry 		return;
522991554f2SKenneth D. Merry 	}
523991554f2SKenneth D. Merry 
524991554f2SKenneth D. Merry 	targ->flags |= MPRSAS_TARGET_INREMOVAL;
525991554f2SKenneth D. Merry 
52646b9415fSScott Long 	tm = mprsas_alloc_tm(sc);
52746b9415fSScott Long 	if (tm == NULL) {
528991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s: command alloc failure\n",
529991554f2SKenneth D. Merry 		    __func__);
530991554f2SKenneth D. Merry 		return;
531991554f2SKenneth D. Merry 	}
532991554f2SKenneth D. Merry 
533991554f2SKenneth D. Merry 	mprsas_rescan_target(sc, targ);
534991554f2SKenneth D. Merry 
53546b9415fSScott Long 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
536991554f2SKenneth D. Merry 	memset(req, 0, sizeof(*req));
537991554f2SKenneth D. Merry 	req->DevHandle = htole16(targ->handle);
538991554f2SKenneth D. Merry 	req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
539991554f2SKenneth D. Merry 
540991554f2SKenneth D. Merry 	/* SAS Hard Link Reset / SATA Link Reset */
541991554f2SKenneth D. Merry 	req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
542991554f2SKenneth D. Merry 
54346b9415fSScott Long 	tm->cm_targ = targ;
54446b9415fSScott Long 	tm->cm_data = NULL;
54546b9415fSScott Long 	tm->cm_complete = mprsas_remove_device;
54646b9415fSScott Long 	tm->cm_complete_data = (void *)(uintptr_t)handle;
547a2c14879SStephen McConnell 
548a2c14879SStephen McConnell 	mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n",
549a2c14879SStephen McConnell 	    __func__, targ->tid);
55046b9415fSScott Long 	mprsas_prepare_for_tm(sc, tm, targ, CAM_LUN_WILDCARD);
551a2c14879SStephen McConnell 
55246b9415fSScott Long 	mpr_map_command(sc, tm);
553991554f2SKenneth D. Merry }
554991554f2SKenneth D. Merry 
555991554f2SKenneth D. Merry static void
556991554f2SKenneth D. Merry mprsas_remove_device(struct mpr_softc *sc, struct mpr_command *tm)
557991554f2SKenneth D. Merry {
558991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
559991554f2SKenneth D. Merry 	MPI2_SAS_IOUNIT_CONTROL_REQUEST *req;
560991554f2SKenneth D. Merry 	struct mprsas_target *targ;
561991554f2SKenneth D. Merry 	struct mpr_command *next_cm;
562991554f2SKenneth D. Merry 	uint16_t handle;
563991554f2SKenneth D. Merry 
564991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
565991554f2SKenneth D. Merry 
566991554f2SKenneth D. Merry 	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
567991554f2SKenneth D. Merry 	handle = (uint16_t)(uintptr_t)tm->cm_complete_data;
568991554f2SKenneth D. Merry 	targ = tm->cm_targ;
569991554f2SKenneth D. Merry 
570991554f2SKenneth D. Merry 	/*
571991554f2SKenneth D. Merry 	 * Currently there should be no way we can hit this case.  It only
572991554f2SKenneth D. Merry 	 * happens when we have a failure to allocate chain frames, and
573991554f2SKenneth D. Merry 	 * task management commands don't have S/G lists.
574991554f2SKenneth D. Merry 	 */
575991554f2SKenneth D. Merry 	if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
576991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for remove of "
577991554f2SKenneth D. Merry 		    "handle %#04x! This should not happen!\n", __func__,
578991554f2SKenneth D. Merry 		    tm->cm_flags, handle);
579991554f2SKenneth D. Merry 	}
580991554f2SKenneth D. Merry 
581991554f2SKenneth D. Merry 	if (reply == NULL) {
582991554f2SKenneth D. Merry 		/* XXX retry the remove after the diag reset completes? */
583991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_FAULT, "%s NULL reply resetting device "
584991554f2SKenneth D. Merry 		    "0x%04x\n", __func__, handle);
585991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
586991554f2SKenneth D. Merry 		return;
587991554f2SKenneth D. Merry 	}
588991554f2SKenneth D. Merry 
589d3f6eabfSStephen McConnell 	if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) !=
590d3f6eabfSStephen McConnell 	    MPI2_IOCSTATUS_SUCCESS) {
59158581c13SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "IOCStatus = 0x%x while resetting "
592991554f2SKenneth D. Merry 		    "device 0x%x\n", le16toh(reply->IOCStatus), handle);
593991554f2SKenneth D. Merry 	}
594991554f2SKenneth D. Merry 
595991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO, "Reset aborted %u commands\n",
596991554f2SKenneth D. Merry 	    le32toh(reply->TerminationCount));
597991554f2SKenneth D. Merry 	mpr_free_reply(sc, tm->cm_reply_data);
598991554f2SKenneth D. Merry 	tm->cm_reply = NULL;	/* Ensures the reply won't get re-freed */
599991554f2SKenneth D. Merry 
600991554f2SKenneth D. Merry 	/* Reuse the existing command */
601991554f2SKenneth D. Merry 	req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)tm->cm_req;
602991554f2SKenneth D. Merry 	memset(req, 0, sizeof(*req));
603991554f2SKenneth D. Merry 	req->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL;
604991554f2SKenneth D. Merry 	req->Operation = MPI2_SAS_OP_REMOVE_DEVICE;
605991554f2SKenneth D. Merry 	req->DevHandle = htole16(handle);
606991554f2SKenneth D. Merry 	tm->cm_data = NULL;
607991554f2SKenneth D. Merry 	tm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
608991554f2SKenneth D. Merry 	tm->cm_complete = mprsas_remove_complete;
609991554f2SKenneth D. Merry 	tm->cm_complete_data = (void *)(uintptr_t)handle;
610991554f2SKenneth D. Merry 
611991554f2SKenneth D. Merry 	mpr_map_command(sc, tm);
612991554f2SKenneth D. Merry 
613a2c14879SStephen McConnell 	mpr_dprint(sc, MPR_INFO, "clearing target %u handle 0x%04x\n",
614991554f2SKenneth D. Merry 	    targ->tid, handle);
615991554f2SKenneth D. Merry 	if (targ->encl_level_valid) {
616a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_INFO, "At enclosure level %d, slot %d, "
617991554f2SKenneth D. Merry 		    "connector name (%4s)\n", targ->encl_level, targ->encl_slot,
618991554f2SKenneth D. Merry 		    targ->connector_name);
619991554f2SKenneth D. Merry 	}
620991554f2SKenneth D. Merry 	TAILQ_FOREACH_SAFE(tm, &targ->commands, cm_link, next_cm) {
621991554f2SKenneth D. Merry 		union ccb *ccb;
622991554f2SKenneth D. Merry 
623991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_XINFO, "Completing missed command %p\n", tm);
624991554f2SKenneth D. Merry 		ccb = tm->cm_complete_data;
625a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
626991554f2SKenneth D. Merry 		mprsas_scsiio_complete(sc, tm);
627991554f2SKenneth D. Merry 	}
628991554f2SKenneth D. Merry }
629991554f2SKenneth D. Merry 
630991554f2SKenneth D. Merry static void
631991554f2SKenneth D. Merry mprsas_remove_complete(struct mpr_softc *sc, struct mpr_command *tm)
632991554f2SKenneth D. Merry {
633991554f2SKenneth D. Merry 	MPI2_SAS_IOUNIT_CONTROL_REPLY *reply;
634991554f2SKenneth D. Merry 	uint16_t handle;
635991554f2SKenneth D. Merry 	struct mprsas_target *targ;
636991554f2SKenneth D. Merry 	struct mprsas_lun *lun;
637991554f2SKenneth D. Merry 
638991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
639991554f2SKenneth D. Merry 
640991554f2SKenneth D. Merry 	reply = (MPI2_SAS_IOUNIT_CONTROL_REPLY *)tm->cm_reply;
641991554f2SKenneth D. Merry 	handle = (uint16_t)(uintptr_t)tm->cm_complete_data;
642991554f2SKenneth D. Merry 
643991554f2SKenneth D. Merry 	/*
644991554f2SKenneth D. Merry 	 * Currently there should be no way we can hit this case.  It only
645991554f2SKenneth D. Merry 	 * happens when we have a failure to allocate chain frames, and
646991554f2SKenneth D. Merry 	 * task management commands don't have S/G lists.
647991554f2SKenneth D. Merry 	 */
648991554f2SKenneth D. Merry 	if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
649991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_XINFO, "%s: cm_flags = %#x for remove of "
650991554f2SKenneth D. Merry 		    "handle %#04x! This should not happen!\n", __func__,
651991554f2SKenneth D. Merry 		    tm->cm_flags, handle);
652991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
653991554f2SKenneth D. Merry 		return;
654991554f2SKenneth D. Merry 	}
655991554f2SKenneth D. Merry 
656991554f2SKenneth D. Merry 	if (reply == NULL) {
657991554f2SKenneth D. Merry 		/* most likely a chip reset */
658991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_FAULT, "%s NULL reply removing device "
659991554f2SKenneth D. Merry 		    "0x%04x\n", __func__, handle);
660991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
661991554f2SKenneth D. Merry 		return;
662991554f2SKenneth D. Merry 	}
663991554f2SKenneth D. Merry 
664991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO, "%s on handle 0x%04x, IOCStatus= 0x%x\n",
665991554f2SKenneth D. Merry 	    __func__, handle, le16toh(reply->IOCStatus));
666991554f2SKenneth D. Merry 
667991554f2SKenneth D. Merry 	/*
668991554f2SKenneth D. Merry 	 * Don't clear target if remove fails because things will get confusing.
669991554f2SKenneth D. Merry 	 * Leave the devname and sasaddr intact so that we know to avoid reusing
670991554f2SKenneth D. Merry 	 * this target id if possible, and so we can assign the same target id
671991554f2SKenneth D. Merry 	 * to this device if it comes back in the future.
672991554f2SKenneth D. Merry 	 */
673d3f6eabfSStephen McConnell 	if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) ==
674d3f6eabfSStephen McConnell 	    MPI2_IOCSTATUS_SUCCESS) {
675991554f2SKenneth D. Merry 		targ = tm->cm_targ;
676991554f2SKenneth D. Merry 		targ->handle = 0x0;
677991554f2SKenneth D. Merry 		targ->encl_handle = 0x0;
678991554f2SKenneth D. Merry 		targ->encl_level_valid = 0x0;
679991554f2SKenneth D. Merry 		targ->encl_level = 0x0;
680991554f2SKenneth D. Merry 		targ->connector_name[0] = ' ';
681991554f2SKenneth D. Merry 		targ->connector_name[1] = ' ';
682991554f2SKenneth D. Merry 		targ->connector_name[2] = ' ';
683991554f2SKenneth D. Merry 		targ->connector_name[3] = ' ';
684991554f2SKenneth D. Merry 		targ->encl_slot = 0x0;
685991554f2SKenneth D. Merry 		targ->exp_dev_handle = 0x0;
686991554f2SKenneth D. Merry 		targ->phy_num = 0x0;
687991554f2SKenneth D. Merry 		targ->linkrate = 0x0;
688991554f2SKenneth D. Merry 		targ->devinfo = 0x0;
689991554f2SKenneth D. Merry 		targ->flags = 0x0;
690991554f2SKenneth D. Merry 		targ->scsi_req_desc_type = 0;
691991554f2SKenneth D. Merry 
692991554f2SKenneth D. Merry 		while (!SLIST_EMPTY(&targ->luns)) {
693991554f2SKenneth D. Merry 			lun = SLIST_FIRST(&targ->luns);
694991554f2SKenneth D. Merry 			SLIST_REMOVE_HEAD(&targ->luns, lun_link);
695991554f2SKenneth D. Merry 			free(lun, M_MPR);
696991554f2SKenneth D. Merry 		}
697991554f2SKenneth D. Merry 	}
698991554f2SKenneth D. Merry 
699991554f2SKenneth D. Merry 	mprsas_free_tm(sc, tm);
700991554f2SKenneth D. Merry }
701991554f2SKenneth D. Merry 
702991554f2SKenneth D. Merry static int
703991554f2SKenneth D. Merry mprsas_register_events(struct mpr_softc *sc)
704991554f2SKenneth D. Merry {
705991554f2SKenneth D. Merry 	uint8_t events[16];
706991554f2SKenneth D. Merry 
707991554f2SKenneth D. Merry 	bzero(events, 16);
708991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE);
709991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_SAS_DISCOVERY);
710991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE);
711991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE);
712991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW);
713991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST);
714991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE);
715991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST);
716991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_IR_VOLUME);
717991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_IR_PHYSICAL_DISK);
718991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_IR_OPERATION_STATUS);
719991554f2SKenneth D. Merry 	setbit(events, MPI2_EVENT_TEMP_THRESHOLD);
7205f5baf0eSAlexander Motin 	setbit(events, MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR);
72167feec50SStephen McConnell 	if (sc->facts->MsgVersion >= MPI2_VERSION_02_06) {
7222bbc5fcbSStephen McConnell 		setbit(events, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION);
72367feec50SStephen McConnell 		if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) {
72467feec50SStephen McConnell 			setbit(events, MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE);
72567feec50SStephen McConnell 			setbit(events, MPI2_EVENT_PCIE_ENUMERATION);
72667feec50SStephen McConnell 			setbit(events, MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST);
72767feec50SStephen McConnell 		}
72867feec50SStephen McConnell 	}
729991554f2SKenneth D. Merry 
730991554f2SKenneth D. Merry 	mpr_register_events(sc, events, mprsas_evt_handler, NULL,
731991554f2SKenneth D. Merry 	    &sc->sassc->mprsas_eh);
732991554f2SKenneth D. Merry 
733991554f2SKenneth D. Merry 	return (0);
734991554f2SKenneth D. Merry }
735991554f2SKenneth D. Merry 
736991554f2SKenneth D. Merry int
737991554f2SKenneth D. Merry mpr_attach_sas(struct mpr_softc *sc)
738991554f2SKenneth D. Merry {
739991554f2SKenneth D. Merry 	struct mprsas_softc *sassc;
740991554f2SKenneth D. Merry 	cam_status status;
74162a09ee9SAlexander Motin 	int unit, error = 0, reqs;
742991554f2SKenneth D. Merry 
743991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
744757ff642SScott Long 	mpr_dprint(sc, MPR_INIT, "%s entered\n", __func__);
745991554f2SKenneth D. Merry 
746991554f2SKenneth D. Merry 	sassc = malloc(sizeof(struct mprsas_softc), M_MPR, M_WAITOK|M_ZERO);
747991554f2SKenneth D. Merry 	if (!sassc) {
748757ff642SScott Long 		mpr_dprint(sc, MPR_INIT|MPR_ERROR,
749757ff642SScott Long 		    "Cannot allocate SAS subsystem memory\n");
750991554f2SKenneth D. Merry 		return (ENOMEM);
751991554f2SKenneth D. Merry 	}
752991554f2SKenneth D. Merry 
753991554f2SKenneth D. Merry 	/*
754a2c14879SStephen McConnell 	 * XXX MaxTargets could change during a reinit.  Since we don't
755991554f2SKenneth D. Merry 	 * resize the targets[] array during such an event, cache the value
756991554f2SKenneth D. Merry 	 * of MaxTargets here so that we don't get into trouble later.  This
757991554f2SKenneth D. Merry 	 * should move into the reinit logic.
758991554f2SKenneth D. Merry 	 */
759327f2e6cSStephen McConnell 	sassc->maxtargets = sc->facts->MaxTargets + sc->facts->MaxVolumes;
760991554f2SKenneth D. Merry 	sassc->targets = malloc(sizeof(struct mprsas_target) *
761991554f2SKenneth D. Merry 	    sassc->maxtargets, M_MPR, M_WAITOK|M_ZERO);
762991554f2SKenneth D. Merry 	if (!sassc->targets) {
763757ff642SScott Long 		mpr_dprint(sc, MPR_INIT|MPR_ERROR,
764757ff642SScott Long 		    "Cannot allocate SAS target memory\n");
765991554f2SKenneth D. Merry 		free(sassc, M_MPR);
766991554f2SKenneth D. Merry 		return (ENOMEM);
767991554f2SKenneth D. Merry 	}
768991554f2SKenneth D. Merry 	sc->sassc = sassc;
769991554f2SKenneth D. Merry 	sassc->sc = sc;
770991554f2SKenneth D. Merry 
77162a09ee9SAlexander Motin 	reqs = sc->num_reqs - sc->num_prireqs - 1;
77262a09ee9SAlexander Motin 	if ((sassc->devq = cam_simq_alloc(reqs)) == NULL) {
773757ff642SScott Long 		mpr_dprint(sc, MPR_INIT|MPR_ERROR, "Cannot allocate SIMQ\n");
774991554f2SKenneth D. Merry 		error = ENOMEM;
775991554f2SKenneth D. Merry 		goto out;
776991554f2SKenneth D. Merry 	}
777991554f2SKenneth D. Merry 
778991554f2SKenneth D. Merry 	unit = device_get_unit(sc->mpr_dev);
779991554f2SKenneth D. Merry 	sassc->sim = cam_sim_alloc(mprsas_action, mprsas_poll, "mpr", sassc,
78062a09ee9SAlexander Motin 	    unit, &sc->mpr_mtx, reqs, reqs, sassc->devq);
781991554f2SKenneth D. Merry 	if (sassc->sim == NULL) {
782757ff642SScott Long 		mpr_dprint(sc, MPR_INIT|MPR_ERROR, "Cannot allocate SIM\n");
783991554f2SKenneth D. Merry 		error = EINVAL;
784991554f2SKenneth D. Merry 		goto out;
785991554f2SKenneth D. Merry 	}
786991554f2SKenneth D. Merry 
787991554f2SKenneth D. Merry 	TAILQ_INIT(&sassc->ev_queue);
788991554f2SKenneth D. Merry 
789991554f2SKenneth D. Merry 	/* Initialize taskqueue for Event Handling */
790991554f2SKenneth D. Merry 	TASK_INIT(&sassc->ev_task, 0, mprsas_firmware_event_work, sc);
791991554f2SKenneth D. Merry 	sassc->ev_tq = taskqueue_create("mpr_taskq", M_NOWAIT | M_ZERO,
792991554f2SKenneth D. Merry 	    taskqueue_thread_enqueue, &sassc->ev_tq);
793c2a0f07aSAlexander Motin 	taskqueue_start_threads(&sassc->ev_tq, 1, PRIBIO, "%s taskq",
794991554f2SKenneth D. Merry 	    device_get_nameunit(sc->mpr_dev));
795991554f2SKenneth D. Merry 
796991554f2SKenneth D. Merry 	mpr_lock(sc);
797991554f2SKenneth D. Merry 
798991554f2SKenneth D. Merry 	/*
799991554f2SKenneth D. Merry 	 * XXX There should be a bus for every port on the adapter, but since
800991554f2SKenneth D. Merry 	 * we're just going to fake the topology for now, we'll pretend that
801991554f2SKenneth D. Merry 	 * everything is just a target on a single bus.
802991554f2SKenneth D. Merry 	 */
803991554f2SKenneth D. Merry 	if ((error = xpt_bus_register(sassc->sim, sc->mpr_dev, 0)) != 0) {
804757ff642SScott Long 		mpr_dprint(sc, MPR_INIT|MPR_ERROR,
805757ff642SScott Long 		    "Error %d registering SCSI bus\n", error);
806991554f2SKenneth D. Merry 		mpr_unlock(sc);
807991554f2SKenneth D. Merry 		goto out;
808991554f2SKenneth D. Merry 	}
809991554f2SKenneth D. Merry 
810991554f2SKenneth D. Merry 	/*
811a2c14879SStephen McConnell 	 * Assume that discovery events will start right away.
812991554f2SKenneth D. Merry 	 *
813991554f2SKenneth D. Merry 	 * Hold off boot until discovery is complete.
814991554f2SKenneth D. Merry 	 */
815991554f2SKenneth D. Merry 	sassc->flags |= MPRSAS_IN_STARTUP | MPRSAS_IN_DISCOVERY;
816991554f2SKenneth D. Merry 	sc->sassc->startup_refcount = 0;
817991554f2SKenneth D. Merry 	mprsas_startup_increment(sassc);
818991554f2SKenneth D. Merry 
819a2c14879SStephen McConnell 	callout_init(&sassc->discovery_callout, 1 /*mpsafe*/);
820991554f2SKenneth D. Merry 
821991554f2SKenneth D. Merry 	/*
822991554f2SKenneth D. Merry 	 * Register for async events so we can determine the EEDP
823991554f2SKenneth D. Merry 	 * capabilities of devices.
824991554f2SKenneth D. Merry 	 */
825991554f2SKenneth D. Merry 	status = xpt_create_path(&sassc->path, /*periph*/NULL,
826991554f2SKenneth D. Merry 	    cam_sim_path(sc->sassc->sim), CAM_TARGET_WILDCARD,
827991554f2SKenneth D. Merry 	    CAM_LUN_WILDCARD);
828991554f2SKenneth D. Merry 	if (status != CAM_REQ_CMP) {
829757ff642SScott Long 		mpr_dprint(sc, MPR_INIT|MPR_ERROR,
830757ff642SScott Long 		    "Error %#x creating sim path\n", status);
831991554f2SKenneth D. Merry 		sassc->path = NULL;
832991554f2SKenneth D. Merry 	} else {
833991554f2SKenneth D. Merry 		int event;
834991554f2SKenneth D. Merry 
835991554f2SKenneth D. Merry #if (__FreeBSD_version >= 1000006) || \
836991554f2SKenneth D. Merry     ((__FreeBSD_version >= 901503) && (__FreeBSD_version < 1000000))
837991554f2SKenneth D. Merry 		event = AC_ADVINFO_CHANGED | AC_FOUND_DEVICE;
838991554f2SKenneth D. Merry #else
839991554f2SKenneth D. Merry 		event = AC_FOUND_DEVICE;
840991554f2SKenneth D. Merry #endif
84107aa4de1SKenneth D. Merry 
84207aa4de1SKenneth D. Merry 		/*
84307aa4de1SKenneth D. Merry 		 * Prior to the CAM locking improvements, we can't call
84407aa4de1SKenneth D. Merry 		 * xpt_register_async() with a particular path specified.
84507aa4de1SKenneth D. Merry 		 *
84607aa4de1SKenneth D. Merry 		 * If a path isn't specified, xpt_register_async() will
84707aa4de1SKenneth D. Merry 		 * generate a wildcard path and acquire the XPT lock while
84807aa4de1SKenneth D. Merry 		 * it calls xpt_action() to execute the XPT_SASYNC_CB CCB.
84907aa4de1SKenneth D. Merry 		 * It will then drop the XPT lock once that is done.
85007aa4de1SKenneth D. Merry 		 *
85107aa4de1SKenneth D. Merry 		 * If a path is specified for xpt_register_async(), it will
85207aa4de1SKenneth D. Merry 		 * not acquire and drop the XPT lock around the call to
85307aa4de1SKenneth D. Merry 		 * xpt_action().  xpt_action() asserts that the caller
85407aa4de1SKenneth D. Merry 		 * holds the SIM lock, so the SIM lock has to be held when
85507aa4de1SKenneth D. Merry 		 * calling xpt_register_async() when the path is specified.
85607aa4de1SKenneth D. Merry 		 *
85707aa4de1SKenneth D. Merry 		 * But xpt_register_async calls xpt_for_all_devices(),
85807aa4de1SKenneth D. Merry 		 * which calls xptbustraverse(), which will acquire each
85907aa4de1SKenneth D. Merry 		 * SIM lock.  When it traverses our particular bus, it will
86007aa4de1SKenneth D. Merry 		 * necessarily acquire the SIM lock, which will lead to a
86107aa4de1SKenneth D. Merry 		 * recursive lock acquisition.
86207aa4de1SKenneth D. Merry 		 *
86307aa4de1SKenneth D. Merry 		 * The CAM locking changes fix this problem by acquiring
86407aa4de1SKenneth D. Merry 		 * the XPT topology lock around bus traversal in
86507aa4de1SKenneth D. Merry 		 * xptbustraverse(), so the caller can hold the SIM lock
86607aa4de1SKenneth D. Merry 		 * and it does not cause a recursive lock acquisition.
86707aa4de1SKenneth D. Merry 		 *
86807aa4de1SKenneth D. Merry 		 * These __FreeBSD_version values are approximate, especially
86907aa4de1SKenneth D. Merry 		 * for stable/10, which is two months later than the actual
87007aa4de1SKenneth D. Merry 		 * change.
87107aa4de1SKenneth D. Merry 		 */
87207aa4de1SKenneth D. Merry 
87307aa4de1SKenneth D. Merry #if (__FreeBSD_version < 1000703) || \
87407aa4de1SKenneth D. Merry     ((__FreeBSD_version >= 1100000) && (__FreeBSD_version < 1100002))
87507aa4de1SKenneth D. Merry 		mpr_unlock(sc);
87607aa4de1SKenneth D. Merry 		status = xpt_register_async(event, mprsas_async, sc,
87707aa4de1SKenneth D. Merry 					    NULL);
87807aa4de1SKenneth D. Merry 		mpr_lock(sc);
87907aa4de1SKenneth D. Merry #else
880991554f2SKenneth D. Merry 		status = xpt_register_async(event, mprsas_async, sc,
881991554f2SKenneth D. Merry 					    sassc->path);
88207aa4de1SKenneth D. Merry #endif
88307aa4de1SKenneth D. Merry 
884991554f2SKenneth D. Merry 		if (status != CAM_REQ_CMP) {
885991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_ERROR,
886991554f2SKenneth D. Merry 			    "Error %#x registering async handler for "
887991554f2SKenneth D. Merry 			    "AC_ADVINFO_CHANGED events\n", status);
888991554f2SKenneth D. Merry 			xpt_free_path(sassc->path);
889991554f2SKenneth D. Merry 			sassc->path = NULL;
890991554f2SKenneth D. Merry 		}
891991554f2SKenneth D. Merry 	}
892991554f2SKenneth D. Merry 	if (status != CAM_REQ_CMP) {
893991554f2SKenneth D. Merry 		/*
894991554f2SKenneth D. Merry 		 * EEDP use is the exception, not the rule.
895991554f2SKenneth D. Merry 		 * Warn the user, but do not fail to attach.
896991554f2SKenneth D. Merry 		 */
897991554f2SKenneth D. Merry 		mpr_printf(sc, "EEDP capabilities disabled.\n");
898991554f2SKenneth D. Merry 	}
899991554f2SKenneth D. Merry 
900991554f2SKenneth D. Merry 	mpr_unlock(sc);
901991554f2SKenneth D. Merry 
902991554f2SKenneth D. Merry 	mprsas_register_events(sc);
903991554f2SKenneth D. Merry out:
904991554f2SKenneth D. Merry 	if (error)
905991554f2SKenneth D. Merry 		mpr_detach_sas(sc);
906757ff642SScott Long 
907757ff642SScott Long 	mpr_dprint(sc, MPR_INIT, "%s exit, error= %d\n", __func__, error);
908991554f2SKenneth D. Merry 	return (error);
909991554f2SKenneth D. Merry }
910991554f2SKenneth D. Merry 
911991554f2SKenneth D. Merry int
912991554f2SKenneth D. Merry mpr_detach_sas(struct mpr_softc *sc)
913991554f2SKenneth D. Merry {
914991554f2SKenneth D. Merry 	struct mprsas_softc *sassc;
915991554f2SKenneth D. Merry 	struct mprsas_lun *lun, *lun_tmp;
916991554f2SKenneth D. Merry 	struct mprsas_target *targ;
917991554f2SKenneth D. Merry 	int i;
918991554f2SKenneth D. Merry 
919991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
920991554f2SKenneth D. Merry 
921991554f2SKenneth D. Merry 	if (sc->sassc == NULL)
922991554f2SKenneth D. Merry 		return (0);
923991554f2SKenneth D. Merry 
924991554f2SKenneth D. Merry 	sassc = sc->sassc;
925991554f2SKenneth D. Merry 	mpr_deregister_events(sc, sassc->mprsas_eh);
926991554f2SKenneth D. Merry 
927991554f2SKenneth D. Merry 	/*
928991554f2SKenneth D. Merry 	 * Drain and free the event handling taskqueue with the lock
929991554f2SKenneth D. Merry 	 * unheld so that any parallel processing tasks drain properly
930991554f2SKenneth D. Merry 	 * without deadlocking.
931991554f2SKenneth D. Merry 	 */
932991554f2SKenneth D. Merry 	if (sassc->ev_tq != NULL)
933991554f2SKenneth D. Merry 		taskqueue_free(sassc->ev_tq);
934991554f2SKenneth D. Merry 
935991554f2SKenneth D. Merry 	/* Make sure CAM doesn't wedge if we had to bail out early. */
936991554f2SKenneth D. Merry 	mpr_lock(sc);
937991554f2SKenneth D. Merry 
9383c5ac992SScott Long 	while (sassc->startup_refcount != 0)
9393c5ac992SScott Long 		mprsas_startup_decrement(sassc);
9403c5ac992SScott Long 
941991554f2SKenneth D. Merry 	/* Deregister our async handler */
942991554f2SKenneth D. Merry 	if (sassc->path != NULL) {
943991554f2SKenneth D. Merry 		xpt_register_async(0, mprsas_async, sc, sassc->path);
944991554f2SKenneth D. Merry 		xpt_free_path(sassc->path);
945991554f2SKenneth D. Merry 		sassc->path = NULL;
946991554f2SKenneth D. Merry 	}
947991554f2SKenneth D. Merry 
948991554f2SKenneth D. Merry 	if (sassc->flags & MPRSAS_IN_STARTUP)
949991554f2SKenneth D. Merry 		xpt_release_simq(sassc->sim, 1);
950991554f2SKenneth D. Merry 
951991554f2SKenneth D. Merry 	if (sassc->sim != NULL) {
952991554f2SKenneth D. Merry 		xpt_bus_deregister(cam_sim_path(sassc->sim));
953991554f2SKenneth D. Merry 		cam_sim_free(sassc->sim, FALSE);
954991554f2SKenneth D. Merry 	}
955991554f2SKenneth D. Merry 
956991554f2SKenneth D. Merry 	mpr_unlock(sc);
957991554f2SKenneth D. Merry 
958991554f2SKenneth D. Merry 	if (sassc->devq != NULL)
959991554f2SKenneth D. Merry 		cam_simq_free(sassc->devq);
960991554f2SKenneth D. Merry 
961991554f2SKenneth D. Merry 	for (i = 0; i < sassc->maxtargets; i++) {
962991554f2SKenneth D. Merry 		targ = &sassc->targets[i];
963991554f2SKenneth D. Merry 		SLIST_FOREACH_SAFE(lun, &targ->luns, lun_link, lun_tmp) {
964991554f2SKenneth D. Merry 			free(lun, M_MPR);
965991554f2SKenneth D. Merry 		}
966991554f2SKenneth D. Merry 	}
967991554f2SKenneth D. Merry 	free(sassc->targets, M_MPR);
968991554f2SKenneth D. Merry 	free(sassc, M_MPR);
969991554f2SKenneth D. Merry 	sc->sassc = NULL;
970991554f2SKenneth D. Merry 
971991554f2SKenneth D. Merry 	return (0);
972991554f2SKenneth D. Merry }
973991554f2SKenneth D. Merry 
974991554f2SKenneth D. Merry void
975991554f2SKenneth D. Merry mprsas_discovery_end(struct mprsas_softc *sassc)
976991554f2SKenneth D. Merry {
977991554f2SKenneth D. Merry 	struct mpr_softc *sc = sassc->sc;
978991554f2SKenneth D. Merry 
979991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
980991554f2SKenneth D. Merry 
981991554f2SKenneth D. Merry 	if (sassc->flags & MPRSAS_DISCOVERY_TIMEOUT_PENDING)
982991554f2SKenneth D. Merry 		callout_stop(&sassc->discovery_callout);
983991554f2SKenneth D. Merry 
984327f2e6cSStephen McConnell 	/*
985327f2e6cSStephen McConnell 	 * After discovery has completed, check the mapping table for any
986327f2e6cSStephen McConnell 	 * missing devices and update their missing counts. Only do this once
987327f2e6cSStephen McConnell 	 * whenever the driver is initialized so that missing counts aren't
988327f2e6cSStephen McConnell 	 * updated unnecessarily. Note that just because discovery has
989327f2e6cSStephen McConnell 	 * completed doesn't mean that events have been processed yet. The
990327f2e6cSStephen McConnell 	 * check_devices function is a callout timer that checks if ALL devices
991327f2e6cSStephen McConnell 	 * are missing. If so, it will wait a little longer for events to
992327f2e6cSStephen McConnell 	 * complete and keep resetting itself until some device in the mapping
993327f2e6cSStephen McConnell 	 * table is not missing, meaning that event processing has started.
994327f2e6cSStephen McConnell 	 */
995327f2e6cSStephen McConnell 	if (sc->track_mapping_events) {
996327f2e6cSStephen McConnell 		mpr_dprint(sc, MPR_XINFO | MPR_MAPPING, "Discovery has "
997327f2e6cSStephen McConnell 		    "completed. Check for missing devices in the mapping "
998327f2e6cSStephen McConnell 		    "table.\n");
999327f2e6cSStephen McConnell 		callout_reset(&sc->device_check_callout,
1000327f2e6cSStephen McConnell 		    MPR_MISSING_CHECK_DELAY * hz, mpr_mapping_check_devices,
1001327f2e6cSStephen McConnell 		    sc);
1002327f2e6cSStephen McConnell 	}
1003991554f2SKenneth D. Merry }
1004991554f2SKenneth D. Merry 
1005991554f2SKenneth D. Merry static void
1006991554f2SKenneth D. Merry mprsas_action(struct cam_sim *sim, union ccb *ccb)
1007991554f2SKenneth D. Merry {
1008991554f2SKenneth D. Merry 	struct mprsas_softc *sassc;
1009991554f2SKenneth D. Merry 
1010991554f2SKenneth D. Merry 	sassc = cam_sim_softc(sim);
1011991554f2SKenneth D. Merry 
1012991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sassc->sc);
1013a2c14879SStephen McConnell 	mpr_dprint(sassc->sc, MPR_TRACE, "ccb func_code 0x%x\n",
1014991554f2SKenneth D. Merry 	    ccb->ccb_h.func_code);
1015991554f2SKenneth D. Merry 	mtx_assert(&sassc->sc->mpr_mtx, MA_OWNED);
1016991554f2SKenneth D. Merry 
1017991554f2SKenneth D. Merry 	switch (ccb->ccb_h.func_code) {
1018991554f2SKenneth D. Merry 	case XPT_PATH_INQ:
1019991554f2SKenneth D. Merry 	{
1020991554f2SKenneth D. Merry 		struct ccb_pathinq *cpi = &ccb->cpi;
102132b0a21eSStephen McConnell 		struct mpr_softc *sc = sassc->sc;
1022991554f2SKenneth D. Merry 
1023991554f2SKenneth D. Merry 		cpi->version_num = 1;
1024991554f2SKenneth D. Merry 		cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16;
1025991554f2SKenneth D. Merry 		cpi->target_sprt = 0;
1026a371d6f9SKenneth D. Merry #if (__FreeBSD_version >= 1000039) || \
1027a371d6f9SKenneth D. Merry     ((__FreeBSD_version < 1000000) && (__FreeBSD_version >= 902502))
1028991554f2SKenneth D. Merry 		cpi->hba_misc = PIM_NOBUSRESET | PIM_UNMAPPED | PIM_NOSCAN;
1029991554f2SKenneth D. Merry #else
1030991554f2SKenneth D. Merry 		cpi->hba_misc = PIM_NOBUSRESET | PIM_UNMAPPED;
1031991554f2SKenneth D. Merry #endif
1032991554f2SKenneth D. Merry 		cpi->hba_eng_cnt = 0;
1033991554f2SKenneth D. Merry 		cpi->max_target = sassc->maxtargets - 1;
1034991554f2SKenneth D. Merry 		cpi->max_lun = 255;
1035327f2e6cSStephen McConnell 
1036327f2e6cSStephen McConnell 		/*
1037327f2e6cSStephen McConnell 		 * initiator_id is set here to an ID outside the set of valid
1038327f2e6cSStephen McConnell 		 * target IDs (including volumes).
1039327f2e6cSStephen McConnell 		 */
1040327f2e6cSStephen McConnell 		cpi->initiator_id = sassc->maxtargets;
10414195c7deSAlan Somers 		strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
10424195c7deSAlan Somers 		strlcpy(cpi->hba_vid, "Avago Tech", HBA_IDLEN);
10434195c7deSAlan Somers 		strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
1044991554f2SKenneth D. Merry 		cpi->unit_number = cam_sim_unit(sim);
1045991554f2SKenneth D. Merry 		cpi->bus_id = cam_sim_bus(sim);
1046991554f2SKenneth D. Merry 		/*
1047991554f2SKenneth D. Merry 		 * XXXSLM-I think this needs to change based on config page or
1048991554f2SKenneth D. Merry 		 * something instead of hardcoded to 150000.
1049991554f2SKenneth D. Merry 		 */
1050991554f2SKenneth D. Merry 		cpi->base_transfer_speed = 150000;
1051991554f2SKenneth D. Merry 		cpi->transport = XPORT_SAS;
1052991554f2SKenneth D. Merry 		cpi->transport_version = 0;
1053991554f2SKenneth D. Merry 		cpi->protocol = PROTO_SCSI;
1054991554f2SKenneth D. Merry 		cpi->protocol_version = SCSI_REV_SPC;
10554f5d6573SAlexander Motin 		cpi->maxio = sc->maxio;
1056a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
1057991554f2SKenneth D. Merry 		break;
1058991554f2SKenneth D. Merry 	}
1059991554f2SKenneth D. Merry 	case XPT_GET_TRAN_SETTINGS:
1060991554f2SKenneth D. Merry 	{
1061991554f2SKenneth D. Merry 		struct ccb_trans_settings	*cts;
1062991554f2SKenneth D. Merry 		struct ccb_trans_settings_sas	*sas;
1063991554f2SKenneth D. Merry 		struct ccb_trans_settings_scsi	*scsi;
1064991554f2SKenneth D. Merry 		struct mprsas_target *targ;
1065991554f2SKenneth D. Merry 
1066991554f2SKenneth D. Merry 		cts = &ccb->cts;
1067991554f2SKenneth D. Merry 		sas = &cts->xport_specific.sas;
1068991554f2SKenneth D. Merry 		scsi = &cts->proto_specific.scsi;
1069991554f2SKenneth D. Merry 
1070991554f2SKenneth D. Merry 		KASSERT(cts->ccb_h.target_id < sassc->maxtargets,
1071991554f2SKenneth D. Merry 		    ("Target %d out of bounds in XPT_GET_TRAN_SETTINGS\n",
1072991554f2SKenneth D. Merry 		    cts->ccb_h.target_id));
1073991554f2SKenneth D. Merry 		targ = &sassc->targets[cts->ccb_h.target_id];
1074991554f2SKenneth D. Merry 		if (targ->handle == 0x0) {
1075a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
1076991554f2SKenneth D. Merry 			break;
1077991554f2SKenneth D. Merry 		}
1078991554f2SKenneth D. Merry 
1079991554f2SKenneth D. Merry 		cts->protocol_version = SCSI_REV_SPC2;
1080991554f2SKenneth D. Merry 		cts->transport = XPORT_SAS;
1081991554f2SKenneth D. Merry 		cts->transport_version = 0;
1082991554f2SKenneth D. Merry 
1083991554f2SKenneth D. Merry 		sas->valid = CTS_SAS_VALID_SPEED;
1084991554f2SKenneth D. Merry 		switch (targ->linkrate) {
1085991554f2SKenneth D. Merry 		case 0x08:
1086991554f2SKenneth D. Merry 			sas->bitrate = 150000;
1087991554f2SKenneth D. Merry 			break;
1088991554f2SKenneth D. Merry 		case 0x09:
1089991554f2SKenneth D. Merry 			sas->bitrate = 300000;
1090991554f2SKenneth D. Merry 			break;
1091991554f2SKenneth D. Merry 		case 0x0a:
1092991554f2SKenneth D. Merry 			sas->bitrate = 600000;
1093991554f2SKenneth D. Merry 			break;
109482315915SAlexander Motin 		case 0x0b:
109582315915SAlexander Motin 			sas->bitrate = 1200000;
109682315915SAlexander Motin 			break;
1097991554f2SKenneth D. Merry 		default:
1098991554f2SKenneth D. Merry 			sas->valid = 0;
1099991554f2SKenneth D. Merry 		}
1100991554f2SKenneth D. Merry 
1101991554f2SKenneth D. Merry 		cts->protocol = PROTO_SCSI;
1102991554f2SKenneth D. Merry 		scsi->valid = CTS_SCSI_VALID_TQ;
1103991554f2SKenneth D. Merry 		scsi->flags = CTS_SCSI_FLAGS_TAG_ENB;
1104991554f2SKenneth D. Merry 
1105a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
1106991554f2SKenneth D. Merry 		break;
1107991554f2SKenneth D. Merry 	}
1108991554f2SKenneth D. Merry 	case XPT_CALC_GEOMETRY:
1109991554f2SKenneth D. Merry 		cam_calc_geometry(&ccb->ccg, /*extended*/1);
1110a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
1111991554f2SKenneth D. Merry 		break;
1112991554f2SKenneth D. Merry 	case XPT_RESET_DEV:
11137a2a6a1aSStephen McConnell 		mpr_dprint(sassc->sc, MPR_XINFO, "mprsas_action "
11147a2a6a1aSStephen McConnell 		    "XPT_RESET_DEV\n");
1115991554f2SKenneth D. Merry 		mprsas_action_resetdev(sassc, ccb);
1116991554f2SKenneth D. Merry 		return;
1117991554f2SKenneth D. Merry 	case XPT_RESET_BUS:
1118991554f2SKenneth D. Merry 	case XPT_ABORT:
1119991554f2SKenneth D. Merry 	case XPT_TERM_IO:
11207a2a6a1aSStephen McConnell 		mpr_dprint(sassc->sc, MPR_XINFO, "mprsas_action faking success "
11217a2a6a1aSStephen McConnell 		    "for abort or reset\n");
1122a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
1123991554f2SKenneth D. Merry 		break;
1124991554f2SKenneth D. Merry 	case XPT_SCSI_IO:
1125991554f2SKenneth D. Merry 		mprsas_action_scsiio(sassc, ccb);
1126991554f2SKenneth D. Merry 		return;
1127991554f2SKenneth D. Merry #if __FreeBSD_version >= 900026
1128991554f2SKenneth D. Merry 	case XPT_SMP_IO:
1129991554f2SKenneth D. Merry 		mprsas_action_smpio(sassc, ccb);
1130991554f2SKenneth D. Merry 		return;
1131991554f2SKenneth D. Merry #endif
1132991554f2SKenneth D. Merry 	default:
1133a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_FUNC_NOTAVAIL);
1134991554f2SKenneth D. Merry 		break;
1135991554f2SKenneth D. Merry 	}
1136991554f2SKenneth D. Merry 	xpt_done(ccb);
1137991554f2SKenneth D. Merry 
1138991554f2SKenneth D. Merry }
1139991554f2SKenneth D. Merry 
1140991554f2SKenneth D. Merry static void
1141991554f2SKenneth D. Merry mprsas_announce_reset(struct mpr_softc *sc, uint32_t ac_code,
1142991554f2SKenneth D. Merry     target_id_t target_id, lun_id_t lun_id)
1143991554f2SKenneth D. Merry {
1144991554f2SKenneth D. Merry 	path_id_t path_id = cam_sim_path(sc->sassc->sim);
1145991554f2SKenneth D. Merry 	struct cam_path *path;
1146991554f2SKenneth D. Merry 
1147991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO, "%s code %x target %d lun %jx\n", __func__,
1148991554f2SKenneth D. Merry 	    ac_code, target_id, (uintmax_t)lun_id);
1149991554f2SKenneth D. Merry 
1150991554f2SKenneth D. Merry 	if (xpt_create_path(&path, NULL,
1151991554f2SKenneth D. Merry 		path_id, target_id, lun_id) != CAM_REQ_CMP) {
1152991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "unable to create path for reset "
1153991554f2SKenneth D. Merry 		    "notification\n");
1154991554f2SKenneth D. Merry 		return;
1155991554f2SKenneth D. Merry 	}
1156991554f2SKenneth D. Merry 
1157991554f2SKenneth D. Merry 	xpt_async(ac_code, path, NULL);
1158991554f2SKenneth D. Merry 	xpt_free_path(path);
1159991554f2SKenneth D. Merry }
1160991554f2SKenneth D. Merry 
1161991554f2SKenneth D. Merry static void
1162991554f2SKenneth D. Merry mprsas_complete_all_commands(struct mpr_softc *sc)
1163991554f2SKenneth D. Merry {
1164991554f2SKenneth D. Merry 	struct mpr_command *cm;
1165991554f2SKenneth D. Merry 	int i;
1166991554f2SKenneth D. Merry 	int completed;
1167991554f2SKenneth D. Merry 
1168991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
1169991554f2SKenneth D. Merry 	mtx_assert(&sc->mpr_mtx, MA_OWNED);
1170991554f2SKenneth D. Merry 
1171991554f2SKenneth D. Merry 	/* complete all commands with a NULL reply */
1172991554f2SKenneth D. Merry 	for (i = 1; i < sc->num_reqs; i++) {
1173991554f2SKenneth D. Merry 		cm = &sc->commands[i];
1174f0779b04SScott Long 		if (cm->cm_state == MPR_CM_STATE_FREE)
1175f0779b04SScott Long 			continue;
1176f0779b04SScott Long 
1177f0779b04SScott Long 		cm->cm_state = MPR_CM_STATE_BUSY;
1178991554f2SKenneth D. Merry 		cm->cm_reply = NULL;
1179991554f2SKenneth D. Merry 		completed = 0;
1180991554f2SKenneth D. Merry 
11818277ce2bSConrad Meyer 		if (cm->cm_flags & MPR_CM_FLAGS_SATA_ID_TIMEOUT) {
11828277ce2bSConrad Meyer 			MPASS(cm->cm_data);
11838277ce2bSConrad Meyer 			free(cm->cm_data, M_MPR);
11848277ce2bSConrad Meyer 			cm->cm_data = NULL;
11858277ce2bSConrad Meyer 		}
11868277ce2bSConrad Meyer 
1187991554f2SKenneth D. Merry 		if (cm->cm_flags & MPR_CM_FLAGS_POLLED)
1188991554f2SKenneth D. Merry 			cm->cm_flags |= MPR_CM_FLAGS_COMPLETE;
1189991554f2SKenneth D. Merry 
1190991554f2SKenneth D. Merry 		if (cm->cm_complete != NULL) {
1191991554f2SKenneth D. Merry 			mprsas_log_command(cm, MPR_RECOVERY,
11927a2a6a1aSStephen McConnell 			    "completing cm %p state %x ccb %p for diag reset\n",
11937a2a6a1aSStephen McConnell 			    cm, cm->cm_state, cm->cm_ccb);
1194991554f2SKenneth D. Merry 			cm->cm_complete(sc, cm);
1195991554f2SKenneth D. Merry 			completed = 1;
1196f0779b04SScott Long 		} else if (cm->cm_flags & MPR_CM_FLAGS_WAKEUP) {
1197991554f2SKenneth D. Merry 			mprsas_log_command(cm, MPR_RECOVERY,
1198991554f2SKenneth D. Merry 			    "waking up cm %p state %x ccb %p for diag reset\n",
1199991554f2SKenneth D. Merry 			    cm, cm->cm_state, cm->cm_ccb);
1200991554f2SKenneth D. Merry 			wakeup(cm);
1201991554f2SKenneth D. Merry 			completed = 1;
1202991554f2SKenneth D. Merry 		}
1203991554f2SKenneth D. Merry 
1204991554f2SKenneth D. Merry 		if ((completed == 0) && (cm->cm_state != MPR_CM_STATE_FREE)) {
1205991554f2SKenneth D. Merry 			/* this should never happen, but if it does, log */
1206991554f2SKenneth D. Merry 			mprsas_log_command(cm, MPR_RECOVERY,
1207991554f2SKenneth D. Merry 			    "cm %p state %x flags 0x%x ccb %p during diag "
1208991554f2SKenneth D. Merry 			    "reset\n", cm, cm->cm_state, cm->cm_flags,
1209991554f2SKenneth D. Merry 			    cm->cm_ccb);
1210991554f2SKenneth D. Merry 		}
1211991554f2SKenneth D. Merry 	}
1212f0779b04SScott Long 
1213f0779b04SScott Long 	sc->io_cmds_active = 0;
1214991554f2SKenneth D. Merry }
1215991554f2SKenneth D. Merry 
1216991554f2SKenneth D. Merry void
1217991554f2SKenneth D. Merry mprsas_handle_reinit(struct mpr_softc *sc)
1218991554f2SKenneth D. Merry {
1219991554f2SKenneth D. Merry 	int i;
1220991554f2SKenneth D. Merry 
1221991554f2SKenneth D. Merry 	/* Go back into startup mode and freeze the simq, so that CAM
1222991554f2SKenneth D. Merry 	 * doesn't send any commands until after we've rediscovered all
1223991554f2SKenneth D. Merry 	 * targets and found the proper device handles for them.
1224991554f2SKenneth D. Merry 	 *
1225991554f2SKenneth D. Merry 	 * After the reset, portenable will trigger discovery, and after all
1226991554f2SKenneth D. Merry 	 * discovery-related activities have finished, the simq will be
1227991554f2SKenneth D. Merry 	 * released.
1228991554f2SKenneth D. Merry 	 */
1229991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_INIT, "%s startup\n", __func__);
1230991554f2SKenneth D. Merry 	sc->sassc->flags |= MPRSAS_IN_STARTUP;
1231991554f2SKenneth D. Merry 	sc->sassc->flags |= MPRSAS_IN_DISCOVERY;
1232991554f2SKenneth D. Merry 	mprsas_startup_increment(sc->sassc);
1233991554f2SKenneth D. Merry 
1234991554f2SKenneth D. Merry 	/* notify CAM of a bus reset */
1235991554f2SKenneth D. Merry 	mprsas_announce_reset(sc, AC_BUS_RESET, CAM_TARGET_WILDCARD,
1236991554f2SKenneth D. Merry 	    CAM_LUN_WILDCARD);
1237991554f2SKenneth D. Merry 
1238991554f2SKenneth D. Merry 	/* complete and cleanup after all outstanding commands */
1239991554f2SKenneth D. Merry 	mprsas_complete_all_commands(sc);
1240991554f2SKenneth D. Merry 
1241a2c14879SStephen McConnell 	mpr_dprint(sc, MPR_INIT, "%s startup %u after command completion\n",
1242a2c14879SStephen McConnell 	    __func__, sc->sassc->startup_refcount);
1243991554f2SKenneth D. Merry 
1244991554f2SKenneth D. Merry 	/* zero all the target handles, since they may change after the
1245991554f2SKenneth D. Merry 	 * reset, and we have to rediscover all the targets and use the new
1246991554f2SKenneth D. Merry 	 * handles.
1247991554f2SKenneth D. Merry 	 */
1248991554f2SKenneth D. Merry 	for (i = 0; i < sc->sassc->maxtargets; i++) {
1249991554f2SKenneth D. Merry 		if (sc->sassc->targets[i].outstanding != 0)
1250991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_INIT, "target %u outstanding %u\n",
1251991554f2SKenneth D. Merry 			    i, sc->sassc->targets[i].outstanding);
1252991554f2SKenneth D. Merry 		sc->sassc->targets[i].handle = 0x0;
1253991554f2SKenneth D. Merry 		sc->sassc->targets[i].exp_dev_handle = 0x0;
1254991554f2SKenneth D. Merry 		sc->sassc->targets[i].outstanding = 0;
1255991554f2SKenneth D. Merry 		sc->sassc->targets[i].flags = MPRSAS_TARGET_INDIAGRESET;
1256991554f2SKenneth D. Merry 	}
1257991554f2SKenneth D. Merry }
1258991554f2SKenneth D. Merry static void
1259991554f2SKenneth D. Merry mprsas_tm_timeout(void *data)
1260991554f2SKenneth D. Merry {
1261991554f2SKenneth D. Merry 	struct mpr_command *tm = data;
1262991554f2SKenneth D. Merry 	struct mpr_softc *sc = tm->cm_sc;
1263991554f2SKenneth D. Merry 
1264991554f2SKenneth D. Merry 	mtx_assert(&sc->mpr_mtx, MA_OWNED);
1265991554f2SKenneth D. Merry 
12667a2a6a1aSStephen McConnell 	mprsas_log_command(tm, MPR_INFO|MPR_RECOVERY, "task mgmt %p timed "
12677a2a6a1aSStephen McConnell 	    "out\n", tm);
1268f0779b04SScott Long 
1269f0779b04SScott Long 	KASSERT(tm->cm_state == MPR_CM_STATE_INQUEUE,
1270f0779b04SScott Long 	    ("command not inqueue\n"));
1271f0779b04SScott Long 
1272f0779b04SScott Long 	tm->cm_state = MPR_CM_STATE_BUSY;
1273991554f2SKenneth D. Merry 	mpr_reinit(sc);
1274991554f2SKenneth D. Merry }
1275991554f2SKenneth D. Merry 
1276991554f2SKenneth D. Merry static void
12777a2a6a1aSStephen McConnell mprsas_logical_unit_reset_complete(struct mpr_softc *sc, struct mpr_command *tm)
1278991554f2SKenneth D. Merry {
1279991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
1280991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
1281991554f2SKenneth D. Merry 	unsigned int cm_count = 0;
1282991554f2SKenneth D. Merry 	struct mpr_command *cm;
1283991554f2SKenneth D. Merry 	struct mprsas_target *targ;
1284991554f2SKenneth D. Merry 
1285991554f2SKenneth D. Merry 	callout_stop(&tm->cm_callout);
1286991554f2SKenneth D. Merry 
1287991554f2SKenneth D. Merry 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
1288991554f2SKenneth D. Merry 	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
1289991554f2SKenneth D. Merry 	targ = tm->cm_targ;
1290991554f2SKenneth D. Merry 
1291991554f2SKenneth D. Merry 	/*
1292991554f2SKenneth D. Merry 	 * Currently there should be no way we can hit this case.  It only
1293991554f2SKenneth D. Merry 	 * happens when we have a failure to allocate chain frames, and
1294991554f2SKenneth D. Merry 	 * task management commands don't have S/G lists.
1295991554f2SKenneth D. Merry 	 */
1296991554f2SKenneth D. Merry 	if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
12976eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY|MPR_ERROR,
12986eea4f46SScott Long 		    "%s: cm_flags = %#x for LUN reset! "
1299991554f2SKenneth D. Merry 		    "This should not happen!\n", __func__, tm->cm_flags);
1300991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
1301991554f2SKenneth D. Merry 		return;
1302991554f2SKenneth D. Merry 	}
1303991554f2SKenneth D. Merry 
1304991554f2SKenneth D. Merry 	if (reply == NULL) {
13056eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY, "NULL reset reply for tm %p\n",
13066eea4f46SScott Long 		    tm);
1307991554f2SKenneth D. Merry 		if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) {
1308991554f2SKenneth D. Merry 			/* this completion was due to a reset, just cleanup */
13096eea4f46SScott Long 			mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing "
13106eea4f46SScott Long 			    "reset, ignoring NULL LUN reset reply\n");
1311991554f2SKenneth D. Merry 			targ->tm = NULL;
1312991554f2SKenneth D. Merry 			mprsas_free_tm(sc, tm);
1313991554f2SKenneth D. Merry 		}
1314991554f2SKenneth D. Merry 		else {
1315991554f2SKenneth D. Merry 			/* we should have gotten a reply. */
13166eea4f46SScott Long 			mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on "
13176eea4f46SScott Long 			    "LUN reset attempt, resetting controller\n");
1318991554f2SKenneth D. Merry 			mpr_reinit(sc);
1319991554f2SKenneth D. Merry 		}
1320991554f2SKenneth D. Merry 		return;
1321991554f2SKenneth D. Merry 	}
1322991554f2SKenneth D. Merry 
13236eea4f46SScott Long 	mpr_dprint(sc, MPR_RECOVERY,
1324991554f2SKenneth D. Merry 	    "logical unit reset status 0x%x code 0x%x count %u\n",
1325991554f2SKenneth D. Merry 	    le16toh(reply->IOCStatus), le32toh(reply->ResponseCode),
1326991554f2SKenneth D. Merry 	    le32toh(reply->TerminationCount));
1327991554f2SKenneth D. Merry 
13286eea4f46SScott Long 	/*
13296eea4f46SScott Long 	 * See if there are any outstanding commands for this LUN.
1330991554f2SKenneth D. Merry 	 * This could be made more efficient by using a per-LU data
1331991554f2SKenneth D. Merry 	 * structure of some sort.
1332991554f2SKenneth D. Merry 	 */
1333991554f2SKenneth D. Merry 	TAILQ_FOREACH(cm, &targ->commands, cm_link) {
1334991554f2SKenneth D. Merry 		if (cm->cm_lun == tm->cm_lun)
1335991554f2SKenneth D. Merry 			cm_count++;
1336991554f2SKenneth D. Merry 	}
1337991554f2SKenneth D. Merry 
1338991554f2SKenneth D. Merry 	if (cm_count == 0) {
13396eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY|MPR_INFO,
13406eea4f46SScott Long 		    "Finished recovery after LUN reset for target %u\n",
13416eea4f46SScott Long 		    targ->tid);
1342991554f2SKenneth D. Merry 
13436eea4f46SScott Long 		mprsas_announce_reset(sc, AC_SENT_BDR, targ->tid,
1344991554f2SKenneth D. Merry 		    tm->cm_lun);
1345991554f2SKenneth D. Merry 
13466eea4f46SScott Long 		/*
13476eea4f46SScott Long 		 * We've finished recovery for this logical unit.  check and
1348991554f2SKenneth D. Merry 		 * see if some other logical unit has a timedout command
1349991554f2SKenneth D. Merry 		 * that needs to be processed.
1350991554f2SKenneth D. Merry 		 */
1351991554f2SKenneth D. Merry 		cm = TAILQ_FIRST(&targ->timedout_commands);
1352991554f2SKenneth D. Merry 		if (cm) {
13536eea4f46SScott Long 			mpr_dprint(sc, MPR_INFO|MPR_RECOVERY,
13546eea4f46SScott Long 			   "More commands to abort for target %u\n", targ->tid);
1355991554f2SKenneth D. Merry 			mprsas_send_abort(sc, tm, cm);
13566eea4f46SScott Long 		} else {
1357991554f2SKenneth D. Merry 			targ->tm = NULL;
1358991554f2SKenneth D. Merry 			mprsas_free_tm(sc, tm);
1359991554f2SKenneth D. Merry 		}
13606eea4f46SScott Long 	} else {
1361991554f2SKenneth D. Merry 		/* if we still have commands for this LUN, the reset
1362991554f2SKenneth D. Merry 		 * effectively failed, regardless of the status reported.
1363991554f2SKenneth D. Merry 		 * Escalate to a target reset.
1364991554f2SKenneth D. Merry 		 */
13656eea4f46SScott Long 		mpr_dprint(sc, MPR_INFO|MPR_RECOVERY,
13666eea4f46SScott Long 		    "logical unit reset complete for target %u, but still "
13676eea4f46SScott Long 		    "have %u command(s), sending target reset\n", targ->tid,
13686eea4f46SScott Long 		    cm_count);
1369*89d1c21fSKashyap D Desai 		if (!targ->is_nvme || sc->custom_nvme_tm_handling)
1370991554f2SKenneth D. Merry 			mprsas_send_reset(sc, tm,
1371991554f2SKenneth D. Merry 			    MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET);
1372*89d1c21fSKashyap D Desai 		else
1373*89d1c21fSKashyap D Desai 			mpr_reinit(sc);
1374991554f2SKenneth D. Merry 	}
1375991554f2SKenneth D. Merry }
1376991554f2SKenneth D. Merry 
1377991554f2SKenneth D. Merry static void
1378991554f2SKenneth D. Merry mprsas_target_reset_complete(struct mpr_softc *sc, struct mpr_command *tm)
1379991554f2SKenneth D. Merry {
1380991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
1381991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
1382991554f2SKenneth D. Merry 	struct mprsas_target *targ;
1383991554f2SKenneth D. Merry 
1384991554f2SKenneth D. Merry 	callout_stop(&tm->cm_callout);
1385991554f2SKenneth D. Merry 
1386991554f2SKenneth D. Merry 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
1387991554f2SKenneth D. Merry 	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
1388991554f2SKenneth D. Merry 	targ = tm->cm_targ;
1389991554f2SKenneth D. Merry 
1390991554f2SKenneth D. Merry 	/*
1391991554f2SKenneth D. Merry 	 * Currently there should be no way we can hit this case.  It only
1392991554f2SKenneth D. Merry 	 * happens when we have a failure to allocate chain frames, and
1393991554f2SKenneth D. Merry 	 * task management commands don't have S/G lists.
1394991554f2SKenneth D. Merry 	 */
1395991554f2SKenneth D. Merry 	if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
1396a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for target "
1397a2c14879SStephen McConnell 		    "reset! This should not happen!\n", __func__, tm->cm_flags);
1398991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
1399991554f2SKenneth D. Merry 		return;
1400991554f2SKenneth D. Merry 	}
1401991554f2SKenneth D. Merry 
1402991554f2SKenneth D. Merry 	if (reply == NULL) {
14036eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY,
14046eea4f46SScott Long 		    "NULL target reset reply for tm %p TaskMID %u\n",
14056eea4f46SScott Long 		    tm, le16toh(req->TaskMID));
1406991554f2SKenneth D. Merry 		if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) {
1407991554f2SKenneth D. Merry 			/* this completion was due to a reset, just cleanup */
14086eea4f46SScott Long 			mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing "
14096eea4f46SScott Long 			    "reset, ignoring NULL target reset reply\n");
1410991554f2SKenneth D. Merry 			targ->tm = NULL;
1411991554f2SKenneth D. Merry 			mprsas_free_tm(sc, tm);
1412991554f2SKenneth D. Merry 		}
1413991554f2SKenneth D. Merry 		else {
1414991554f2SKenneth D. Merry 			/* we should have gotten a reply. */
14156eea4f46SScott Long 			mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on "
14166eea4f46SScott Long 			    "target reset attempt, resetting controller\n");
1417991554f2SKenneth D. Merry 			mpr_reinit(sc);
1418991554f2SKenneth D. Merry 		}
1419991554f2SKenneth D. Merry 		return;
1420991554f2SKenneth D. Merry 	}
1421991554f2SKenneth D. Merry 
14226eea4f46SScott Long 	mpr_dprint(sc, MPR_RECOVERY,
1423991554f2SKenneth D. Merry 	    "target reset status 0x%x code 0x%x count %u\n",
1424991554f2SKenneth D. Merry 	    le16toh(reply->IOCStatus), le32toh(reply->ResponseCode),
1425991554f2SKenneth D. Merry 	    le32toh(reply->TerminationCount));
1426991554f2SKenneth D. Merry 
1427991554f2SKenneth D. Merry 	if (targ->outstanding == 0) {
14286eea4f46SScott Long 		/*
14296eea4f46SScott Long 		 * We've finished recovery for this target and all
1430991554f2SKenneth D. Merry 		 * of its logical units.
1431991554f2SKenneth D. Merry 		 */
14326eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY|MPR_INFO,
14336eea4f46SScott Long 		    "Finished reset recovery for target %u\n", targ->tid);
1434991554f2SKenneth D. Merry 
1435991554f2SKenneth D. Merry 		mprsas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid,
1436991554f2SKenneth D. Merry 		    CAM_LUN_WILDCARD);
1437991554f2SKenneth D. Merry 
1438991554f2SKenneth D. Merry 		targ->tm = NULL;
1439991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
14406eea4f46SScott Long 	} else {
14416eea4f46SScott Long 		/*
14426eea4f46SScott Long 		 * After a target reset, if this target still has
1443991554f2SKenneth D. Merry 		 * outstanding commands, the reset effectively failed,
1444991554f2SKenneth D. Merry 		 * regardless of the status reported.  escalate.
1445991554f2SKenneth D. Merry 		 */
14466eea4f46SScott Long 		mpr_dprint(sc, MPR_INFO|MPR_RECOVERY,
14476eea4f46SScott Long 		    "Target reset complete for target %u, but still have %u "
14486eea4f46SScott Long 		    "command(s), resetting controller\n", targ->tid,
14496eea4f46SScott Long 		    targ->outstanding);
1450991554f2SKenneth D. Merry 		mpr_reinit(sc);
1451991554f2SKenneth D. Merry 	}
1452991554f2SKenneth D. Merry }
1453991554f2SKenneth D. Merry 
1454991554f2SKenneth D. Merry #define MPR_RESET_TIMEOUT 30
1455991554f2SKenneth D. Merry 
1456a2c14879SStephen McConnell int
1457991554f2SKenneth D. Merry mprsas_send_reset(struct mpr_softc *sc, struct mpr_command *tm, uint8_t type)
1458991554f2SKenneth D. Merry {
1459991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
1460991554f2SKenneth D. Merry 	struct mprsas_target *target;
1461*89d1c21fSKashyap D Desai 	int err, timeout;
1462991554f2SKenneth D. Merry 
1463991554f2SKenneth D. Merry 	target = tm->cm_targ;
1464991554f2SKenneth D. Merry 	if (target->handle == 0) {
1465a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "%s null devhandle for target_id "
1466a2c14879SStephen McConnell 		    "%d\n", __func__, target->tid);
1467991554f2SKenneth D. Merry 		return -1;
1468991554f2SKenneth D. Merry 	}
1469991554f2SKenneth D. Merry 
1470991554f2SKenneth D. Merry 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
1471991554f2SKenneth D. Merry 	req->DevHandle = htole16(target->handle);
1472991554f2SKenneth D. Merry 	req->TaskType = type;
1473991554f2SKenneth D. Merry 
1474*89d1c21fSKashyap D Desai 	if (!target->is_nvme || sc->custom_nvme_tm_handling) {
1475*89d1c21fSKashyap D Desai 		timeout = MPR_RESET_TIMEOUT;
1476*89d1c21fSKashyap D Desai 		/*
1477*89d1c21fSKashyap D Desai 		 * Target reset method =
1478*89d1c21fSKashyap D Desai 		 *     SAS Hard Link Reset / SATA Link Reset
1479*89d1c21fSKashyap D Desai 		 */
1480*89d1c21fSKashyap D Desai 		req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
1481*89d1c21fSKashyap D Desai 	} else {
1482*89d1c21fSKashyap D Desai 		timeout = (target->controller_reset_timeout) ? (
1483*89d1c21fSKashyap D Desai 		    target->controller_reset_timeout) : (MPR_RESET_TIMEOUT);
1484*89d1c21fSKashyap D Desai 		/* PCIe Protocol Level Reset*/
1485*89d1c21fSKashyap D Desai 		req->MsgFlags =
1486*89d1c21fSKashyap D Desai 		    MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE;
1487*89d1c21fSKashyap D Desai 	}
1488*89d1c21fSKashyap D Desai 
1489991554f2SKenneth D. Merry 	if (type == MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET) {
1490991554f2SKenneth D. Merry 		/* XXX Need to handle invalid LUNs */
1491991554f2SKenneth D. Merry 		MPR_SET_LUN(req->LUN, tm->cm_lun);
1492991554f2SKenneth D. Merry 		tm->cm_targ->logical_unit_resets++;
14936eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY|MPR_INFO,
14946eea4f46SScott Long 		    "Sending logical unit reset to target %u lun %d\n",
14956eea4f46SScott Long 		    target->tid, tm->cm_lun);
1496991554f2SKenneth D. Merry 		tm->cm_complete = mprsas_logical_unit_reset_complete;
1497a2c14879SStephen McConnell 		mprsas_prepare_for_tm(sc, tm, target, tm->cm_lun);
14986eea4f46SScott Long 	} else if (type == MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET) {
1499991554f2SKenneth D. Merry 		tm->cm_targ->target_resets++;
15006eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY|MPR_INFO,
15016eea4f46SScott Long 		    "Sending target reset to target %u\n", target->tid);
1502991554f2SKenneth D. Merry 		tm->cm_complete = mprsas_target_reset_complete;
1503a2c14879SStephen McConnell 		mprsas_prepare_for_tm(sc, tm, target, CAM_LUN_WILDCARD);
1504991554f2SKenneth D. Merry 	}
1505991554f2SKenneth D. Merry 	else {
1506991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "unexpected reset type 0x%x\n", type);
1507991554f2SKenneth D. Merry 		return -1;
1508991554f2SKenneth D. Merry 	}
1509991554f2SKenneth D. Merry 
1510991554f2SKenneth D. Merry 	if (target->encl_level_valid) {
15116eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY|MPR_INFO,
15126eea4f46SScott Long 		    "At enclosure level %d, slot %d, connector name (%4s)\n",
15136eea4f46SScott Long 		    target->encl_level, target->encl_slot,
15146eea4f46SScott Long 		    target->connector_name);
1515991554f2SKenneth D. Merry 	}
1516991554f2SKenneth D. Merry 
1517991554f2SKenneth D. Merry 	tm->cm_data = NULL;
1518991554f2SKenneth D. Merry 	tm->cm_complete_data = (void *)tm;
1519991554f2SKenneth D. Merry 
1520*89d1c21fSKashyap D Desai 	callout_reset(&tm->cm_callout, timeout * hz,
1521991554f2SKenneth D. Merry 	    mprsas_tm_timeout, tm);
1522991554f2SKenneth D. Merry 
1523991554f2SKenneth D. Merry 	err = mpr_map_command(sc, tm);
1524991554f2SKenneth D. Merry 	if (err)
15256eea4f46SScott Long 		mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY,
1526a2c14879SStephen McConnell 		    "error %d sending reset type %u\n", err, type);
1527991554f2SKenneth D. Merry 
1528991554f2SKenneth D. Merry 	return err;
1529991554f2SKenneth D. Merry }
1530991554f2SKenneth D. Merry 
1531991554f2SKenneth D. Merry 
1532991554f2SKenneth D. Merry static void
1533991554f2SKenneth D. Merry mprsas_abort_complete(struct mpr_softc *sc, struct mpr_command *tm)
1534991554f2SKenneth D. Merry {
1535991554f2SKenneth D. Merry 	struct mpr_command *cm;
1536991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REPLY *reply;
1537991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
1538991554f2SKenneth D. Merry 	struct mprsas_target *targ;
1539991554f2SKenneth D. Merry 
1540991554f2SKenneth D. Merry 	callout_stop(&tm->cm_callout);
1541991554f2SKenneth D. Merry 
1542991554f2SKenneth D. Merry 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
1543991554f2SKenneth D. Merry 	reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
1544991554f2SKenneth D. Merry 	targ = tm->cm_targ;
1545991554f2SKenneth D. Merry 
1546991554f2SKenneth D. Merry 	/*
1547991554f2SKenneth D. Merry 	 * Currently there should be no way we can hit this case.  It only
1548991554f2SKenneth D. Merry 	 * happens when we have a failure to allocate chain frames, and
1549991554f2SKenneth D. Merry 	 * task management commands don't have S/G lists.
1550991554f2SKenneth D. Merry 	 */
1551991554f2SKenneth D. Merry 	if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
15526eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY|MPR_ERROR,
1553991554f2SKenneth D. Merry 		    "cm_flags = %#x for abort %p TaskMID %u!\n",
1554991554f2SKenneth D. Merry 		    tm->cm_flags, tm, le16toh(req->TaskMID));
1555991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
1556991554f2SKenneth D. Merry 		return;
1557991554f2SKenneth D. Merry 	}
1558991554f2SKenneth D. Merry 
1559991554f2SKenneth D. Merry 	if (reply == NULL) {
15606eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY,
1561991554f2SKenneth D. Merry 		    "NULL abort reply for tm %p TaskMID %u\n",
1562991554f2SKenneth D. Merry 		    tm, le16toh(req->TaskMID));
1563991554f2SKenneth D. Merry 		if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) {
1564991554f2SKenneth D. Merry 			/* this completion was due to a reset, just cleanup */
15656eea4f46SScott Long 			mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing "
15666eea4f46SScott Long 			    "reset, ignoring NULL abort reply\n");
1567991554f2SKenneth D. Merry 			targ->tm = NULL;
1568991554f2SKenneth D. Merry 			mprsas_free_tm(sc, tm);
15696eea4f46SScott Long 		} else {
1570991554f2SKenneth D. Merry 			/* we should have gotten a reply. */
15716eea4f46SScott Long 			mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on "
15726eea4f46SScott Long 			    "abort attempt, resetting controller\n");
1573991554f2SKenneth D. Merry 			mpr_reinit(sc);
1574991554f2SKenneth D. Merry 		}
1575991554f2SKenneth D. Merry 		return;
1576991554f2SKenneth D. Merry 	}
1577991554f2SKenneth D. Merry 
15786eea4f46SScott Long 	mpr_dprint(sc, MPR_RECOVERY,
1579991554f2SKenneth D. Merry 	    "abort TaskMID %u status 0x%x code 0x%x count %u\n",
1580991554f2SKenneth D. Merry 	    le16toh(req->TaskMID),
1581991554f2SKenneth D. Merry 	    le16toh(reply->IOCStatus), le32toh(reply->ResponseCode),
1582991554f2SKenneth D. Merry 	    le32toh(reply->TerminationCount));
1583991554f2SKenneth D. Merry 
1584991554f2SKenneth D. Merry 	cm = TAILQ_FIRST(&tm->cm_targ->timedout_commands);
1585991554f2SKenneth D. Merry 	if (cm == NULL) {
15866eea4f46SScott Long 		/*
15876eea4f46SScott Long 		 * if there are no more timedout commands, we're done with
1588991554f2SKenneth D. Merry 		 * error recovery for this target.
1589991554f2SKenneth D. Merry 		 */
15906eea4f46SScott Long 		mpr_dprint(sc, MPR_INFO|MPR_RECOVERY,
15916eea4f46SScott Long 		    "Finished abort recovery for target %u\n", targ->tid);
1592991554f2SKenneth D. Merry 		targ->tm = NULL;
1593991554f2SKenneth D. Merry 		mprsas_free_tm(sc, tm);
15946eea4f46SScott Long 	} else if (le16toh(req->TaskMID) != cm->cm_desc.Default.SMID) {
1595991554f2SKenneth D. Merry 		/* abort success, but we have more timedout commands to abort */
15966eea4f46SScott Long 		mpr_dprint(sc, MPR_INFO|MPR_RECOVERY,
15976eea4f46SScott Long 		    "Continuing abort recovery for target %u\n", targ->tid);
1598991554f2SKenneth D. Merry 		mprsas_send_abort(sc, tm, cm);
15996eea4f46SScott Long 	} else {
16006eea4f46SScott Long 		/*
16016eea4f46SScott Long 		 * we didn't get a command completion, so the abort
1602991554f2SKenneth D. Merry 		 * failed as far as we're concerned.  escalate.
1603991554f2SKenneth D. Merry 		 */
16046eea4f46SScott Long 		mpr_dprint(sc, MPR_INFO|MPR_RECOVERY,
16056eea4f46SScott Long 		    "Abort failed for target %u, sending logical unit reset\n",
16066eea4f46SScott Long 		    targ->tid);
1607991554f2SKenneth D. Merry 
1608991554f2SKenneth D. Merry 		mprsas_send_reset(sc, tm,
1609991554f2SKenneth D. Merry 		    MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET);
1610991554f2SKenneth D. Merry 	}
1611991554f2SKenneth D. Merry }
1612991554f2SKenneth D. Merry 
1613991554f2SKenneth D. Merry #define MPR_ABORT_TIMEOUT 5
1614991554f2SKenneth D. Merry 
1615991554f2SKenneth D. Merry static int
1616991554f2SKenneth D. Merry mprsas_send_abort(struct mpr_softc *sc, struct mpr_command *tm,
1617991554f2SKenneth D. Merry     struct mpr_command *cm)
1618991554f2SKenneth D. Merry {
1619991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
1620991554f2SKenneth D. Merry 	struct mprsas_target *targ;
1621*89d1c21fSKashyap D Desai 	int err, timeout;
1622991554f2SKenneth D. Merry 
1623991554f2SKenneth D. Merry 	targ = cm->cm_targ;
1624991554f2SKenneth D. Merry 	if (targ->handle == 0) {
16256eea4f46SScott Long 		mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY,
16266eea4f46SScott Long 		   "%s null devhandle for target_id %d\n",
1627991554f2SKenneth D. Merry 		    __func__, cm->cm_ccb->ccb_h.target_id);
1628991554f2SKenneth D. Merry 		return -1;
1629991554f2SKenneth D. Merry 	}
1630991554f2SKenneth D. Merry 
1631855fe445SScott Long 	mprsas_log_command(cm, MPR_RECOVERY|MPR_INFO,
1632991554f2SKenneth D. Merry 	    "Aborting command %p\n", cm);
1633991554f2SKenneth D. Merry 
1634991554f2SKenneth D. Merry 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
1635991554f2SKenneth D. Merry 	req->DevHandle = htole16(targ->handle);
1636991554f2SKenneth D. Merry 	req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK;
1637991554f2SKenneth D. Merry 
1638991554f2SKenneth D. Merry 	/* XXX Need to handle invalid LUNs */
1639991554f2SKenneth D. Merry 	MPR_SET_LUN(req->LUN, cm->cm_ccb->ccb_h.target_lun);
1640991554f2SKenneth D. Merry 
1641991554f2SKenneth D. Merry 	req->TaskMID = htole16(cm->cm_desc.Default.SMID);
1642991554f2SKenneth D. Merry 
1643991554f2SKenneth D. Merry 	tm->cm_data = NULL;
1644991554f2SKenneth D. Merry 	tm->cm_complete = mprsas_abort_complete;
1645991554f2SKenneth D. Merry 	tm->cm_complete_data = (void *)tm;
1646991554f2SKenneth D. Merry 	tm->cm_targ = cm->cm_targ;
1647991554f2SKenneth D. Merry 	tm->cm_lun = cm->cm_lun;
1648991554f2SKenneth D. Merry 
1649*89d1c21fSKashyap D Desai 	if (!targ->is_nvme || sc->custom_nvme_tm_handling)
1650*89d1c21fSKashyap D Desai 		timeout	= MPR_ABORT_TIMEOUT;
1651*89d1c21fSKashyap D Desai 	else
1652*89d1c21fSKashyap D Desai 		timeout = sc->nvme_abort_timeout;
1653*89d1c21fSKashyap D Desai 
1654*89d1c21fSKashyap D Desai 	callout_reset(&tm->cm_callout, timeout * hz,
1655991554f2SKenneth D. Merry 	    mprsas_tm_timeout, tm);
1656991554f2SKenneth D. Merry 
1657991554f2SKenneth D. Merry 	targ->aborts++;
1658991554f2SKenneth D. Merry 
1659a2c14879SStephen McConnell 	mprsas_prepare_for_tm(sc, tm, targ, tm->cm_lun);
1660a2c14879SStephen McConnell 
1661991554f2SKenneth D. Merry 	err = mpr_map_command(sc, tm);
1662991554f2SKenneth D. Merry 	if (err)
16636eea4f46SScott Long 		mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY,
1664991554f2SKenneth D. Merry 		    "error %d sending abort for cm %p SMID %u\n",
1665991554f2SKenneth D. Merry 		    err, cm, req->TaskMID);
1666991554f2SKenneth D. Merry 	return err;
1667991554f2SKenneth D. Merry }
1668991554f2SKenneth D. Merry 
1669991554f2SKenneth D. Merry static void
1670991554f2SKenneth D. Merry mprsas_scsiio_timeout(void *data)
1671991554f2SKenneth D. Merry {
16726eea4f46SScott Long 	sbintime_t elapsed, now;
16736eea4f46SScott Long 	union ccb *ccb;
1674991554f2SKenneth D. Merry 	struct mpr_softc *sc;
1675991554f2SKenneth D. Merry 	struct mpr_command *cm;
1676991554f2SKenneth D. Merry 	struct mprsas_target *targ;
1677991554f2SKenneth D. Merry 
1678991554f2SKenneth D. Merry 	cm = (struct mpr_command *)data;
1679991554f2SKenneth D. Merry 	sc = cm->cm_sc;
16806eea4f46SScott Long 	ccb = cm->cm_ccb;
16816eea4f46SScott Long 	now = sbinuptime();
1682991554f2SKenneth D. Merry 
1683991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
1684991554f2SKenneth D. Merry 	mtx_assert(&sc->mpr_mtx, MA_OWNED);
1685991554f2SKenneth D. Merry 
16866eea4f46SScott Long 	mpr_dprint(sc, MPR_XINFO|MPR_RECOVERY, "Timeout checking cm %p\n", cm);
1687991554f2SKenneth D. Merry 
1688991554f2SKenneth D. Merry 	/*
1689991554f2SKenneth D. Merry 	 * Run the interrupt handler to make sure it's not pending.  This
1690991554f2SKenneth D. Merry 	 * isn't perfect because the command could have already completed
1691991554f2SKenneth D. Merry 	 * and been re-used, though this is unlikely.
1692991554f2SKenneth D. Merry 	 */
1693991554f2SKenneth D. Merry 	mpr_intr_locked(sc);
1694f0779b04SScott Long 	if (cm->cm_state != MPR_CM_STATE_INQUEUE) {
1695991554f2SKenneth D. Merry 		mprsas_log_command(cm, MPR_XINFO,
1696991554f2SKenneth D. Merry 		    "SCSI command %p almost timed out\n", cm);
1697991554f2SKenneth D. Merry 		return;
1698991554f2SKenneth D. Merry 	}
1699991554f2SKenneth D. Merry 
1700991554f2SKenneth D. Merry 	if (cm->cm_ccb == NULL) {
1701991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "command timeout with NULL ccb\n");
1702991554f2SKenneth D. Merry 		return;
1703991554f2SKenneth D. Merry 	}
1704991554f2SKenneth D. Merry 
1705991554f2SKenneth D. Merry 	targ = cm->cm_targ;
1706991554f2SKenneth D. Merry 	targ->timeouts++;
1707991554f2SKenneth D. Merry 
17086eea4f46SScott Long 	elapsed = now - ccb->ccb_h.qos.sim_data;
17096eea4f46SScott Long 	mprsas_log_command(cm, MPR_INFO|MPR_RECOVERY,
17106eea4f46SScott Long 	    "Command timeout on target %u(0x%04x), %d set, %d.%d elapsed\n",
17116eea4f46SScott Long 	    targ->tid, targ->handle, ccb->ccb_h.timeout,
17126eea4f46SScott Long 	    sbintime_getsec(elapsed), elapsed & 0xffffffff);
1713991554f2SKenneth D. Merry 	if (targ->encl_level_valid) {
17146eea4f46SScott Long 		mpr_dprint(sc, MPR_INFO|MPR_RECOVERY,
17156eea4f46SScott Long 		    "At enclosure level %d, slot %d, connector name (%4s)\n",
17166eea4f46SScott Long 		    targ->encl_level, targ->encl_slot, targ->connector_name);
1717991554f2SKenneth D. Merry 	}
1718991554f2SKenneth D. Merry 
1719991554f2SKenneth D. Merry 	/* XXX first, check the firmware state, to see if it's still
1720991554f2SKenneth D. Merry 	 * operational.  if not, do a diag reset.
1721991554f2SKenneth D. Merry 	 */
1722a2c14879SStephen McConnell 	mprsas_set_ccbstatus(cm->cm_ccb, CAM_CMD_TIMEOUT);
1723991554f2SKenneth D. Merry 	cm->cm_state = MPR_CM_STATE_TIMEDOUT;
1724991554f2SKenneth D. Merry 	TAILQ_INSERT_TAIL(&targ->timedout_commands, cm, cm_recovery);
1725991554f2SKenneth D. Merry 
1726991554f2SKenneth D. Merry 	if (targ->tm != NULL) {
1727991554f2SKenneth D. Merry 		/* target already in recovery, just queue up another
1728991554f2SKenneth D. Merry 		 * timedout command to be processed later.
1729991554f2SKenneth D. Merry 		 */
1730991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_RECOVERY, "queued timedout cm %p for "
1731991554f2SKenneth D. Merry 		    "processing by tm %p\n", cm, targ->tm);
1732991554f2SKenneth D. Merry 	}
1733991554f2SKenneth D. Merry 	else if ((targ->tm = mprsas_alloc_tm(sc)) != NULL) {
1734991554f2SKenneth D. Merry 
1735991554f2SKenneth D. Merry 		/* start recovery by aborting the first timedout command */
17366eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY|MPR_INFO,
17376eea4f46SScott Long 		    "Sending abort to target %u for SMID %d\n", targ->tid,
17386eea4f46SScott Long 		    cm->cm_desc.Default.SMID);
17396eea4f46SScott Long 		mpr_dprint(sc, MPR_RECOVERY, "timedout cm %p allocated tm %p\n",
17406eea4f46SScott Long 		    cm, targ->tm);
1741991554f2SKenneth D. Merry 		mprsas_send_abort(sc, targ->tm, cm);
1742991554f2SKenneth D. Merry 	}
1743991554f2SKenneth D. Merry 	else {
1744991554f2SKenneth D. Merry 		/* XXX queue this target up for recovery once a TM becomes
1745991554f2SKenneth D. Merry 		 * available.  The firmware only has a limited number of
1746991554f2SKenneth D. Merry 		 * HighPriority credits for the high priority requests used
1747991554f2SKenneth D. Merry 		 * for task management, and we ran out.
1748991554f2SKenneth D. Merry 		 *
1749991554f2SKenneth D. Merry 		 * Isilon: don't worry about this for now, since we have
1750991554f2SKenneth D. Merry 		 * more credits than disks in an enclosure, and limit
1751991554f2SKenneth D. Merry 		 * ourselves to one TM per target for recovery.
1752991554f2SKenneth D. Merry 		 */
17536eea4f46SScott Long 		mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY,
17546eea4f46SScott Long 		    "timedout cm %p failed to allocate a tm\n", cm);
1755991554f2SKenneth D. Merry 	}
1756991554f2SKenneth D. Merry }
1757991554f2SKenneth D. Merry 
175867feec50SStephen McConnell /**
175967feec50SStephen McConnell  * mprsas_build_nvme_unmap - Build Native NVMe DSM command equivalent
176067feec50SStephen McConnell  *			     to SCSI Unmap.
176167feec50SStephen McConnell  * Return 0 - for success,
176267feec50SStephen McConnell  *	  1 - to immediately return back the command with success status to CAM
176367feec50SStephen McConnell  *	  negative value - to fallback to firmware path i.e. issue scsi unmap
176467feec50SStephen McConnell  *			   to FW without any translation.
176567feec50SStephen McConnell  */
176667feec50SStephen McConnell static int
176767feec50SStephen McConnell mprsas_build_nvme_unmap(struct mpr_softc *sc, struct mpr_command *cm,
176867feec50SStephen McConnell     union ccb *ccb, struct mprsas_target *targ)
176967feec50SStephen McConnell {
177067feec50SStephen McConnell 	Mpi26NVMeEncapsulatedRequest_t *req = NULL;
177167feec50SStephen McConnell 	struct ccb_scsiio *csio;
177267feec50SStephen McConnell 	struct unmap_parm_list *plist;
177367feec50SStephen McConnell 	struct nvme_dsm_range *nvme_dsm_ranges = NULL;
177467feec50SStephen McConnell 	struct nvme_command *c;
177567feec50SStephen McConnell 	int i, res;
177667feec50SStephen McConnell 	uint16_t ndesc, list_len, data_length;
177767feec50SStephen McConnell 	struct mpr_prp_page *prp_page_info;
177867feec50SStephen McConnell 	uint64_t nvme_dsm_ranges_dma_handle;
177967feec50SStephen McConnell 
178067feec50SStephen McConnell 	csio = &ccb->csio;
178167feec50SStephen McConnell #if __FreeBSD_version >= 1100103
178267feec50SStephen McConnell 	list_len = (scsiio_cdb_ptr(csio)[7] << 8 | scsiio_cdb_ptr(csio)[8]);
178367feec50SStephen McConnell #else
178467feec50SStephen McConnell 	if (csio->ccb_h.flags & CAM_CDB_POINTER) {
178567feec50SStephen McConnell 		list_len = (ccb->csio.cdb_io.cdb_ptr[7] << 8 |
178667feec50SStephen McConnell 		    ccb->csio.cdb_io.cdb_ptr[8]);
178767feec50SStephen McConnell 	} else {
178867feec50SStephen McConnell 		list_len = (ccb->csio.cdb_io.cdb_bytes[7] << 8 |
178967feec50SStephen McConnell 		    ccb->csio.cdb_io.cdb_bytes[8]);
179067feec50SStephen McConnell 	}
179167feec50SStephen McConnell #endif
179267feec50SStephen McConnell 	if (!list_len) {
179367feec50SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "Parameter list length is Zero\n");
179467feec50SStephen McConnell 		return -EINVAL;
179567feec50SStephen McConnell 	}
179667feec50SStephen McConnell 
179767feec50SStephen McConnell 	plist = malloc(csio->dxfer_len, M_MPR, M_ZERO|M_NOWAIT);
179867feec50SStephen McConnell 	if (!plist) {
179967feec50SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "Unable to allocate memory to "
180067feec50SStephen McConnell 		    "save UNMAP data\n");
180167feec50SStephen McConnell 		return -ENOMEM;
180267feec50SStephen McConnell 	}
180367feec50SStephen McConnell 
180467feec50SStephen McConnell 	/* Copy SCSI unmap data to a local buffer */
180567feec50SStephen McConnell 	bcopy(csio->data_ptr, plist, csio->dxfer_len);
180667feec50SStephen McConnell 
180767feec50SStephen McConnell 	/* return back the unmap command to CAM with success status,
180867feec50SStephen McConnell 	 * if number of descripts is zero.
180967feec50SStephen McConnell 	 */
181067feec50SStephen McConnell 	ndesc = be16toh(plist->unmap_blk_desc_data_len) >> 4;
181167feec50SStephen McConnell 	if (!ndesc) {
181267feec50SStephen McConnell 		mpr_dprint(sc, MPR_XINFO, "Number of descriptors in "
181367feec50SStephen McConnell 		    "UNMAP cmd is Zero\n");
181467feec50SStephen McConnell 		res = 1;
181567feec50SStephen McConnell 		goto out;
181667feec50SStephen McConnell 	}
181767feec50SStephen McConnell 
181867feec50SStephen McConnell 	data_length = ndesc * sizeof(struct nvme_dsm_range);
181967feec50SStephen McConnell 	if (data_length > targ->MDTS) {
182067feec50SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "data length: %d is greater than "
182167feec50SStephen McConnell 		    "Device's MDTS: %d\n", data_length, targ->MDTS);
182267feec50SStephen McConnell 		res = -EINVAL;
182367feec50SStephen McConnell 		goto out;
182467feec50SStephen McConnell 	}
182567feec50SStephen McConnell 
182667feec50SStephen McConnell 	prp_page_info = mpr_alloc_prp_page(sc);
182767feec50SStephen McConnell 	KASSERT(prp_page_info != NULL, ("%s: There is no PRP Page for "
182867feec50SStephen McConnell 	    "UNMAP command.\n", __func__));
182967feec50SStephen McConnell 
183067feec50SStephen McConnell 	/*
183167feec50SStephen McConnell 	 * Insert the allocated PRP page into the command's PRP page list. This
183267feec50SStephen McConnell 	 * will be freed when the command is freed.
183367feec50SStephen McConnell 	 */
183467feec50SStephen McConnell 	TAILQ_INSERT_TAIL(&cm->cm_prp_page_list, prp_page_info, prp_page_link);
183567feec50SStephen McConnell 
183667feec50SStephen McConnell 	nvme_dsm_ranges = (struct nvme_dsm_range *)prp_page_info->prp_page;
183767feec50SStephen McConnell 	nvme_dsm_ranges_dma_handle = prp_page_info->prp_page_busaddr;
183867feec50SStephen McConnell 
183967feec50SStephen McConnell 	bzero(nvme_dsm_ranges, data_length);
184067feec50SStephen McConnell 
184167feec50SStephen McConnell 	/* Convert SCSI unmap's descriptor data to NVMe DSM specific Range data
184267feec50SStephen McConnell 	 * for each descriptors contained in SCSI UNMAP data.
184367feec50SStephen McConnell 	 */
184467feec50SStephen McConnell 	for (i = 0; i < ndesc; i++) {
184567feec50SStephen McConnell 		nvme_dsm_ranges[i].length =
184667feec50SStephen McConnell 		    htole32(be32toh(plist->desc[i].nlb));
184767feec50SStephen McConnell 		nvme_dsm_ranges[i].starting_lba =
184867feec50SStephen McConnell 		    htole64(be64toh(plist->desc[i].slba));
184967feec50SStephen McConnell 		nvme_dsm_ranges[i].attributes = 0;
185067feec50SStephen McConnell 	}
185167feec50SStephen McConnell 
185267feec50SStephen McConnell 	/* Build MPI2.6's NVMe Encapsulated Request Message */
185367feec50SStephen McConnell 	req = (Mpi26NVMeEncapsulatedRequest_t *)cm->cm_req;
185467feec50SStephen McConnell 	bzero(req, sizeof(*req));
185567feec50SStephen McConnell 	req->DevHandle = htole16(targ->handle);
185667feec50SStephen McConnell 	req->Function = MPI2_FUNCTION_NVME_ENCAPSULATED;
185767feec50SStephen McConnell 	req->Flags = MPI26_NVME_FLAGS_WRITE;
185867feec50SStephen McConnell 	req->ErrorResponseBaseAddress.High =
185967feec50SStephen McConnell 	    htole32((uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32));
186067feec50SStephen McConnell 	req->ErrorResponseBaseAddress.Low =
186167feec50SStephen McConnell 	    htole32(cm->cm_sense_busaddr);
186267feec50SStephen McConnell 	req->ErrorResponseAllocationLength =
186367feec50SStephen McConnell 	    htole16(sizeof(struct nvme_completion));
186467feec50SStephen McConnell 	req->EncapsulatedCommandLength =
186567feec50SStephen McConnell 	    htole16(sizeof(struct nvme_command));
186667feec50SStephen McConnell 	req->DataLength = htole32(data_length);
186767feec50SStephen McConnell 
186867feec50SStephen McConnell 	/* Build NVMe DSM command */
186967feec50SStephen McConnell 	c = (struct nvme_command *) req->NVMe_Command;
18709544e6dcSChuck Tuffli 	c->opc = NVME_OPC_DATASET_MANAGEMENT;
187167feec50SStephen McConnell 	c->nsid = htole32(csio->ccb_h.target_lun + 1);
187267feec50SStephen McConnell 	c->cdw10 = htole32(ndesc - 1);
187367feec50SStephen McConnell 	c->cdw11 = htole32(NVME_DSM_ATTR_DEALLOCATE);
187467feec50SStephen McConnell 
187567feec50SStephen McConnell 	cm->cm_length = data_length;
187667feec50SStephen McConnell 	cm->cm_data = NULL;
187767feec50SStephen McConnell 
187867feec50SStephen McConnell 	cm->cm_complete = mprsas_scsiio_complete;
187967feec50SStephen McConnell 	cm->cm_complete_data = ccb;
188067feec50SStephen McConnell 	cm->cm_targ = targ;
188167feec50SStephen McConnell 	cm->cm_lun = csio->ccb_h.target_lun;
188267feec50SStephen McConnell 	cm->cm_ccb = ccb;
188367feec50SStephen McConnell 
188467feec50SStephen McConnell 	cm->cm_desc.Default.RequestFlags =
188567feec50SStephen McConnell 	    MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED;
188667feec50SStephen McConnell 
18876eea4f46SScott Long 	csio->ccb_h.qos.sim_data = sbinuptime();
188867feec50SStephen McConnell #if __FreeBSD_version >= 1000029
188967feec50SStephen McConnell 	callout_reset_sbt(&cm->cm_callout, SBT_1MS * ccb->ccb_h.timeout, 0,
189067feec50SStephen McConnell 	    mprsas_scsiio_timeout, cm, 0);
189167feec50SStephen McConnell #else //__FreeBSD_version < 1000029
189267feec50SStephen McConnell 	callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000,
189367feec50SStephen McConnell 	    mprsas_scsiio_timeout, cm);
189467feec50SStephen McConnell #endif //__FreeBSD_version >= 1000029
189567feec50SStephen McConnell 
189667feec50SStephen McConnell 	targ->issued++;
189767feec50SStephen McConnell 	targ->outstanding++;
189867feec50SStephen McConnell 	TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link);
189967feec50SStephen McConnell 	ccb->ccb_h.status |= CAM_SIM_QUEUED;
190067feec50SStephen McConnell 
190167feec50SStephen McConnell 	mprsas_log_command(cm, MPR_XINFO, "%s cm %p ccb %p outstanding %u\n",
190267feec50SStephen McConnell 	    __func__, cm, ccb, targ->outstanding);
190367feec50SStephen McConnell 
190418982e8fSStephen McConnell 	mpr_build_nvme_prp(sc, cm, req,
190518982e8fSStephen McConnell 	    (void *)(uintptr_t)nvme_dsm_ranges_dma_handle, 0, data_length);
190667feec50SStephen McConnell 	mpr_map_command(sc, cm);
190767feec50SStephen McConnell 
190867feec50SStephen McConnell out:
190967feec50SStephen McConnell 	free(plist, M_MPR);
191067feec50SStephen McConnell 	return 0;
191167feec50SStephen McConnell }
191267feec50SStephen McConnell 
1913991554f2SKenneth D. Merry static void
1914991554f2SKenneth D. Merry mprsas_action_scsiio(struct mprsas_softc *sassc, union ccb *ccb)
1915991554f2SKenneth D. Merry {
1916991554f2SKenneth D. Merry 	MPI2_SCSI_IO_REQUEST *req;
1917991554f2SKenneth D. Merry 	struct ccb_scsiio *csio;
1918991554f2SKenneth D. Merry 	struct mpr_softc *sc;
1919991554f2SKenneth D. Merry 	struct mprsas_target *targ;
1920991554f2SKenneth D. Merry 	struct mprsas_lun *lun;
1921991554f2SKenneth D. Merry 	struct mpr_command *cm;
192267feec50SStephen McConnell 	uint8_t i, lba_byte, *ref_tag_addr, scsi_opcode;
1923991554f2SKenneth D. Merry 	uint16_t eedp_flags;
1924991554f2SKenneth D. Merry 	uint32_t mpi_control;
192567feec50SStephen McConnell 	int rc;
1926991554f2SKenneth D. Merry 
1927991554f2SKenneth D. Merry 	sc = sassc->sc;
1928991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
1929991554f2SKenneth D. Merry 	mtx_assert(&sc->mpr_mtx, MA_OWNED);
1930991554f2SKenneth D. Merry 
1931991554f2SKenneth D. Merry 	csio = &ccb->csio;
1932a2c14879SStephen McConnell 	KASSERT(csio->ccb_h.target_id < sassc->maxtargets,
1933a2c14879SStephen McConnell 	    ("Target %d out of bounds in XPT_SCSI_IO\n",
1934a2c14879SStephen McConnell 	     csio->ccb_h.target_id));
1935991554f2SKenneth D. Merry 	targ = &sassc->targets[csio->ccb_h.target_id];
1936991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_TRACE, "ccb %p target flag %x\n", ccb, targ->flags);
1937991554f2SKenneth D. Merry 	if (targ->handle == 0x0) {
1938991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s NULL handle for target %u\n",
1939991554f2SKenneth D. Merry 		    __func__, csio->ccb_h.target_id);
1940a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
1941991554f2SKenneth D. Merry 		xpt_done(ccb);
1942991554f2SKenneth D. Merry 		return;
1943991554f2SKenneth D. Merry 	}
1944991554f2SKenneth D. Merry 	if (targ->flags & MPR_TARGET_FLAGS_RAID_COMPONENT) {
1945a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "%s Raid component no SCSI IO "
1946991554f2SKenneth D. Merry 		    "supported %u\n", __func__, csio->ccb_h.target_id);
1947a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
1948991554f2SKenneth D. Merry 		xpt_done(ccb);
1949991554f2SKenneth D. Merry 		return;
1950991554f2SKenneth D. Merry 	}
1951991554f2SKenneth D. Merry 	/*
1952991554f2SKenneth D. Merry 	 * Sometimes, it is possible to get a command that is not "In
1953991554f2SKenneth D. Merry 	 * Progress" and was actually aborted by the upper layer.  Check for
1954991554f2SKenneth D. Merry 	 * this here and complete the command without error.
1955991554f2SKenneth D. Merry 	 */
1956a2c14879SStephen McConnell 	if (mprsas_get_ccbstatus(ccb) != CAM_REQ_INPROG) {
1957991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_TRACE, "%s Command is not in progress for "
1958991554f2SKenneth D. Merry 		    "target %u\n", __func__, csio->ccb_h.target_id);
1959991554f2SKenneth D. Merry 		xpt_done(ccb);
1960991554f2SKenneth D. Merry 		return;
1961991554f2SKenneth D. Merry 	}
1962991554f2SKenneth D. Merry 	/*
1963991554f2SKenneth D. Merry 	 * If devinfo is 0 this will be a volume.  In that case don't tell CAM
1964991554f2SKenneth D. Merry 	 * that the volume has timed out.  We want volumes to be enumerated
1965991554f2SKenneth D. Merry 	 * until they are deleted/removed, not just failed.
1966991554f2SKenneth D. Merry 	 */
1967991554f2SKenneth D. Merry 	if (targ->flags & MPRSAS_TARGET_INREMOVAL) {
1968991554f2SKenneth D. Merry 		if (targ->devinfo == 0)
1969a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
1970991554f2SKenneth D. Merry 		else
1971a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_SEL_TIMEOUT);
1972991554f2SKenneth D. Merry 		xpt_done(ccb);
1973991554f2SKenneth D. Merry 		return;
1974991554f2SKenneth D. Merry 	}
1975991554f2SKenneth D. Merry 
1976991554f2SKenneth D. Merry 	if ((sc->mpr_flags & MPR_FLAGS_SHUTDOWN) != 0) {
1977a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_INFO, "%s shutting down\n", __func__);
1978a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
1979a2c14879SStephen McConnell 		xpt_done(ccb);
1980a2c14879SStephen McConnell 		return;
1981a2c14879SStephen McConnell 	}
1982a2c14879SStephen McConnell 
1983a2c14879SStephen McConnell 	/*
1984a2c14879SStephen McConnell 	 * If target has a reset in progress, freeze the devq and return.  The
1985a2c14879SStephen McConnell 	 * devq will be released when the TM reset is finished.
1986a2c14879SStephen McConnell 	 */
1987a2c14879SStephen McConnell 	if (targ->flags & MPRSAS_TARGET_INRESET) {
1988a2c14879SStephen McConnell 		ccb->ccb_h.status = CAM_BUSY | CAM_DEV_QFRZN;
1989a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_INFO, "%s: Freezing devq for target ID %d\n",
1990a2c14879SStephen McConnell 		    __func__, targ->tid);
1991a2c14879SStephen McConnell 		xpt_freeze_devq(ccb->ccb_h.path, 1);
1992991554f2SKenneth D. Merry 		xpt_done(ccb);
1993991554f2SKenneth D. Merry 		return;
1994991554f2SKenneth D. Merry 	}
1995991554f2SKenneth D. Merry 
1996991554f2SKenneth D. Merry 	cm = mpr_alloc_command(sc);
1997991554f2SKenneth D. Merry 	if (cm == NULL || (sc->mpr_flags & MPR_FLAGS_DIAGRESET)) {
1998991554f2SKenneth D. Merry 		if (cm != NULL) {
1999991554f2SKenneth D. Merry 			mpr_free_command(sc, cm);
2000991554f2SKenneth D. Merry 		}
2001991554f2SKenneth D. Merry 		if ((sassc->flags & MPRSAS_QUEUE_FROZEN) == 0) {
2002991554f2SKenneth D. Merry 			xpt_freeze_simq(sassc->sim, 1);
2003991554f2SKenneth D. Merry 			sassc->flags |= MPRSAS_QUEUE_FROZEN;
2004991554f2SKenneth D. Merry 		}
2005991554f2SKenneth D. Merry 		ccb->ccb_h.status &= ~CAM_SIM_QUEUED;
2006991554f2SKenneth D. Merry 		ccb->ccb_h.status |= CAM_REQUEUE_REQ;
2007991554f2SKenneth D. Merry 		xpt_done(ccb);
2008991554f2SKenneth D. Merry 		return;
2009991554f2SKenneth D. Merry 	}
2010991554f2SKenneth D. Merry 
201167feec50SStephen McConnell 	/* For NVME device's issue UNMAP command directly to NVME drives by
201267feec50SStephen McConnell 	 * constructing equivalent native NVMe DataSetManagement command.
201367feec50SStephen McConnell 	 */
201467feec50SStephen McConnell #if __FreeBSD_version >= 1100103
201567feec50SStephen McConnell 	scsi_opcode = scsiio_cdb_ptr(csio)[0];
201667feec50SStephen McConnell #else
201767feec50SStephen McConnell 	if (csio->ccb_h.flags & CAM_CDB_POINTER)
201867feec50SStephen McConnell 		scsi_opcode = csio->cdb_io.cdb_ptr[0];
201967feec50SStephen McConnell 	else
202067feec50SStephen McConnell 		scsi_opcode = csio->cdb_io.cdb_bytes[0];
202167feec50SStephen McConnell #endif
202267feec50SStephen McConnell 	if (scsi_opcode == UNMAP &&
202367feec50SStephen McConnell 	    targ->is_nvme &&
202467feec50SStephen McConnell 	    (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) {
202567feec50SStephen McConnell 		rc = mprsas_build_nvme_unmap(sc, cm, ccb, targ);
202667feec50SStephen McConnell 		if (rc == 1) { /* return command to CAM with success status */
202767feec50SStephen McConnell 			mpr_free_command(sc, cm);
202867feec50SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
202967feec50SStephen McConnell 			xpt_done(ccb);
203067feec50SStephen McConnell 			return;
203167feec50SStephen McConnell 		} else if (!rc) /* Issued NVMe Encapsulated Request Message */
203267feec50SStephen McConnell 			return;
203367feec50SStephen McConnell 	}
203467feec50SStephen McConnell 
2035991554f2SKenneth D. Merry 	req = (MPI2_SCSI_IO_REQUEST *)cm->cm_req;
2036991554f2SKenneth D. Merry 	bzero(req, sizeof(*req));
2037991554f2SKenneth D. Merry 	req->DevHandle = htole16(targ->handle);
2038991554f2SKenneth D. Merry 	req->Function = MPI2_FUNCTION_SCSI_IO_REQUEST;
2039991554f2SKenneth D. Merry 	req->MsgFlags = 0;
2040991554f2SKenneth D. Merry 	req->SenseBufferLowAddress = htole32(cm->cm_sense_busaddr);
2041991554f2SKenneth D. Merry 	req->SenseBufferLength = MPR_SENSE_LEN;
2042991554f2SKenneth D. Merry 	req->SGLFlags = 0;
2043991554f2SKenneth D. Merry 	req->ChainOffset = 0;
2044991554f2SKenneth D. Merry 	req->SGLOffset0 = 24;	/* 32bit word offset to the SGL */
2045991554f2SKenneth D. Merry 	req->SGLOffset1= 0;
2046991554f2SKenneth D. Merry 	req->SGLOffset2= 0;
2047991554f2SKenneth D. Merry 	req->SGLOffset3= 0;
2048991554f2SKenneth D. Merry 	req->SkipCount = 0;
2049991554f2SKenneth D. Merry 	req->DataLength = htole32(csio->dxfer_len);
2050991554f2SKenneth D. Merry 	req->BidirectionalDataLength = 0;
2051991554f2SKenneth D. Merry 	req->IoFlags = htole16(csio->cdb_len);
2052991554f2SKenneth D. Merry 	req->EEDPFlags = 0;
2053991554f2SKenneth D. Merry 
2054991554f2SKenneth D. Merry 	/* Note: BiDirectional transfers are not supported */
2055991554f2SKenneth D. Merry 	switch (csio->ccb_h.flags & CAM_DIR_MASK) {
2056991554f2SKenneth D. Merry 	case CAM_DIR_IN:
2057991554f2SKenneth D. Merry 		mpi_control = MPI2_SCSIIO_CONTROL_READ;
2058991554f2SKenneth D. Merry 		cm->cm_flags |= MPR_CM_FLAGS_DATAIN;
2059991554f2SKenneth D. Merry 		break;
2060991554f2SKenneth D. Merry 	case CAM_DIR_OUT:
2061991554f2SKenneth D. Merry 		mpi_control = MPI2_SCSIIO_CONTROL_WRITE;
2062991554f2SKenneth D. Merry 		cm->cm_flags |= MPR_CM_FLAGS_DATAOUT;
2063991554f2SKenneth D. Merry 		break;
2064991554f2SKenneth D. Merry 	case CAM_DIR_NONE:
2065991554f2SKenneth D. Merry 	default:
2066991554f2SKenneth D. Merry 		mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER;
2067991554f2SKenneth D. Merry 		break;
2068991554f2SKenneth D. Merry 	}
2069991554f2SKenneth D. Merry 
2070991554f2SKenneth D. Merry 	if (csio->cdb_len == 32)
2071991554f2SKenneth D. Merry 		mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT;
2072991554f2SKenneth D. Merry 	/*
2073991554f2SKenneth D. Merry 	 * It looks like the hardware doesn't require an explicit tag
2074991554f2SKenneth D. Merry 	 * number for each transaction.  SAM Task Management not supported
2075991554f2SKenneth D. Merry 	 * at the moment.
2076991554f2SKenneth D. Merry 	 */
2077991554f2SKenneth D. Merry 	switch (csio->tag_action) {
2078991554f2SKenneth D. Merry 	case MSG_HEAD_OF_Q_TAG:
2079991554f2SKenneth D. Merry 		mpi_control |= MPI2_SCSIIO_CONTROL_HEADOFQ;
2080991554f2SKenneth D. Merry 		break;
2081991554f2SKenneth D. Merry 	case MSG_ORDERED_Q_TAG:
2082991554f2SKenneth D. Merry 		mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ;
2083991554f2SKenneth D. Merry 		break;
2084991554f2SKenneth D. Merry 	case MSG_ACA_TASK:
2085991554f2SKenneth D. Merry 		mpi_control |= MPI2_SCSIIO_CONTROL_ACAQ;
2086991554f2SKenneth D. Merry 		break;
2087991554f2SKenneth D. Merry 	case CAM_TAG_ACTION_NONE:
2088991554f2SKenneth D. Merry 	case MSG_SIMPLE_Q_TAG:
2089991554f2SKenneth D. Merry 	default:
2090991554f2SKenneth D. Merry 		mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ;
2091991554f2SKenneth D. Merry 		break;
2092991554f2SKenneth D. Merry 	}
2093991554f2SKenneth D. Merry 	mpi_control |= sc->mapping_table[csio->ccb_h.target_id].TLR_bits;
2094991554f2SKenneth D. Merry 	req->Control = htole32(mpi_control);
2095991554f2SKenneth D. Merry 
2096991554f2SKenneth D. Merry 	if (MPR_SET_LUN(req->LUN, csio->ccb_h.target_lun) != 0) {
2097991554f2SKenneth D. Merry 		mpr_free_command(sc, cm);
2098a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_LUN_INVALID);
2099991554f2SKenneth D. Merry 		xpt_done(ccb);
2100991554f2SKenneth D. Merry 		return;
2101991554f2SKenneth D. Merry 	}
2102991554f2SKenneth D. Merry 
2103991554f2SKenneth D. Merry 	if (csio->ccb_h.flags & CAM_CDB_POINTER)
2104991554f2SKenneth D. Merry 		bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len);
2105fa699bb2SAlan Somers 	else {
2106fa699bb2SAlan Somers 		KASSERT(csio->cdb_len <= IOCDBLEN,
210767feec50SStephen McConnell 		    ("cdb_len %d is greater than IOCDBLEN but CAM_CDB_POINTER "
210867feec50SStephen McConnell 		    "is not set", csio->cdb_len));
2109991554f2SKenneth D. Merry 		bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len);
2110fa699bb2SAlan Somers 	}
2111991554f2SKenneth D. Merry 	req->IoFlags = htole16(csio->cdb_len);
2112991554f2SKenneth D. Merry 
2113991554f2SKenneth D. Merry 	/*
2114991554f2SKenneth D. Merry 	 * Check if EEDP is supported and enabled.  If it is then check if the
2115991554f2SKenneth D. Merry 	 * SCSI opcode could be using EEDP.  If so, make sure the LUN exists and
2116991554f2SKenneth D. Merry 	 * is formatted for EEDP support.  If all of this is true, set CDB up
2117991554f2SKenneth D. Merry 	 * for EEDP transfer.
2118991554f2SKenneth D. Merry 	 */
2119991554f2SKenneth D. Merry 	eedp_flags = op_code_prot[req->CDB.CDB32[0]];
2120991554f2SKenneth D. Merry 	if (sc->eedp_enabled && eedp_flags) {
2121991554f2SKenneth D. Merry 		SLIST_FOREACH(lun, &targ->luns, lun_link) {
2122991554f2SKenneth D. Merry 			if (lun->lun_id == csio->ccb_h.target_lun) {
2123991554f2SKenneth D. Merry 				break;
2124991554f2SKenneth D. Merry 			}
2125991554f2SKenneth D. Merry 		}
2126991554f2SKenneth D. Merry 
2127991554f2SKenneth D. Merry 		if ((lun != NULL) && (lun->eedp_formatted)) {
2128991554f2SKenneth D. Merry 			req->EEDPBlockSize = htole16(lun->eedp_block_size);
2129991554f2SKenneth D. Merry 			eedp_flags |= (MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG |
2130991554f2SKenneth D. Merry 			    MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG |
2131991554f2SKenneth D. Merry 			    MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD);
213267feec50SStephen McConnell 			if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) {
213367feec50SStephen McConnell 				eedp_flags |=
213467feec50SStephen McConnell 				    MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE;
213567feec50SStephen McConnell 			}
2136991554f2SKenneth D. Merry 			req->EEDPFlags = htole16(eedp_flags);
2137991554f2SKenneth D. Merry 
2138991554f2SKenneth D. Merry 			/*
2139991554f2SKenneth D. Merry 			 * If CDB less than 32, fill in Primary Ref Tag with
2140991554f2SKenneth D. Merry 			 * low 4 bytes of LBA.  If CDB is 32, tag stuff is
2141991554f2SKenneth D. Merry 			 * already there.  Also, set protection bit.  FreeBSD
2142991554f2SKenneth D. Merry 			 * currently does not support CDBs bigger than 16, but
2143991554f2SKenneth D. Merry 			 * the code doesn't hurt, and will be here for the
2144991554f2SKenneth D. Merry 			 * future.
2145991554f2SKenneth D. Merry 			 */
2146991554f2SKenneth D. Merry 			if (csio->cdb_len != 32) {
2147991554f2SKenneth D. Merry 				lba_byte = (csio->cdb_len == 16) ? 6 : 2;
2148991554f2SKenneth D. Merry 				ref_tag_addr = (uint8_t *)&req->CDB.EEDP32.
2149991554f2SKenneth D. Merry 				    PrimaryReferenceTag;
2150991554f2SKenneth D. Merry 				for (i = 0; i < 4; i++) {
2151991554f2SKenneth D. Merry 					*ref_tag_addr =
2152991554f2SKenneth D. Merry 					    req->CDB.CDB32[lba_byte + i];
2153991554f2SKenneth D. Merry 					ref_tag_addr++;
2154991554f2SKenneth D. Merry 				}
2155991554f2SKenneth D. Merry 				req->CDB.EEDP32.PrimaryReferenceTag =
2156991554f2SKenneth D. Merry 				    htole32(req->
2157991554f2SKenneth D. Merry 				    CDB.EEDP32.PrimaryReferenceTag);
2158991554f2SKenneth D. Merry 				req->CDB.EEDP32.PrimaryApplicationTagMask =
2159991554f2SKenneth D. Merry 				    0xFFFF;
21608881681bSKenneth D. Merry 				req->CDB.CDB32[1] =
21618881681bSKenneth D. Merry 				    (req->CDB.CDB32[1] & 0x1F) | 0x20;
2162991554f2SKenneth D. Merry 			} else {
2163991554f2SKenneth D. Merry 				eedp_flags |=
2164991554f2SKenneth D. Merry 				    MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG;
2165991554f2SKenneth D. Merry 				req->EEDPFlags = htole16(eedp_flags);
2166991554f2SKenneth D. Merry 				req->CDB.CDB32[10] = (req->CDB.CDB32[10] &
2167991554f2SKenneth D. Merry 				    0x1F) | 0x20;
2168991554f2SKenneth D. Merry 			}
2169991554f2SKenneth D. Merry 		}
2170991554f2SKenneth D. Merry 	}
2171991554f2SKenneth D. Merry 
2172991554f2SKenneth D. Merry 	cm->cm_length = csio->dxfer_len;
2173991554f2SKenneth D. Merry 	if (cm->cm_length != 0) {
2174991554f2SKenneth D. Merry 		cm->cm_data = ccb;
2175991554f2SKenneth D. Merry 		cm->cm_flags |= MPR_CM_FLAGS_USE_CCB;
2176991554f2SKenneth D. Merry 	} else {
2177991554f2SKenneth D. Merry 		cm->cm_data = NULL;
2178991554f2SKenneth D. Merry 	}
2179991554f2SKenneth D. Merry 	cm->cm_sge = &req->SGL;
2180991554f2SKenneth D. Merry 	cm->cm_sglsize = (32 - 24) * 4;
2181991554f2SKenneth D. Merry 	cm->cm_complete = mprsas_scsiio_complete;
2182991554f2SKenneth D. Merry 	cm->cm_complete_data = ccb;
2183991554f2SKenneth D. Merry 	cm->cm_targ = targ;
2184991554f2SKenneth D. Merry 	cm->cm_lun = csio->ccb_h.target_lun;
2185991554f2SKenneth D. Merry 	cm->cm_ccb = ccb;
2186991554f2SKenneth D. Merry 	/*
2187991554f2SKenneth D. Merry 	 * If using FP desc type, need to set a bit in IoFlags (SCSI IO is 0)
2188991554f2SKenneth D. Merry 	 * and set descriptor type.
2189991554f2SKenneth D. Merry 	 */
2190991554f2SKenneth D. Merry 	if (targ->scsi_req_desc_type ==
2191991554f2SKenneth D. Merry 	    MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO) {
2192991554f2SKenneth D. Merry 		req->IoFlags |= MPI25_SCSIIO_IOFLAGS_FAST_PATH;
2193991554f2SKenneth D. Merry 		cm->cm_desc.FastPathSCSIIO.RequestFlags =
2194991554f2SKenneth D. Merry 		    MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO;
219567feec50SStephen McConnell 		if (!sc->atomic_desc_capable) {
219667feec50SStephen McConnell 			cm->cm_desc.FastPathSCSIIO.DevHandle =
219767feec50SStephen McConnell 			    htole16(targ->handle);
219867feec50SStephen McConnell 		}
2199991554f2SKenneth D. Merry 	} else {
2200991554f2SKenneth D. Merry 		cm->cm_desc.SCSIIO.RequestFlags =
2201991554f2SKenneth D. Merry 		    MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO;
220267feec50SStephen McConnell 		if (!sc->atomic_desc_capable)
2203991554f2SKenneth D. Merry 			cm->cm_desc.SCSIIO.DevHandle = htole16(targ->handle);
2204991554f2SKenneth D. Merry 	}
2205991554f2SKenneth D. Merry 
22066eea4f46SScott Long 	csio->ccb_h.qos.sim_data = sbinuptime();
2207407073a0SStephen McConnell #if __FreeBSD_version >= 1000029
220885c9dd9dSSteven Hartland 	callout_reset_sbt(&cm->cm_callout, SBT_1MS * ccb->ccb_h.timeout, 0,
220985c9dd9dSSteven Hartland 	    mprsas_scsiio_timeout, cm, 0);
2210407073a0SStephen McConnell #else //__FreeBSD_version < 1000029
2211407073a0SStephen McConnell 	callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000,
2212407073a0SStephen McConnell 	    mprsas_scsiio_timeout, cm);
2213407073a0SStephen McConnell #endif //__FreeBSD_version >= 1000029
2214991554f2SKenneth D. Merry 
2215991554f2SKenneth D. Merry 	targ->issued++;
2216991554f2SKenneth D. Merry 	targ->outstanding++;
2217991554f2SKenneth D. Merry 	TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link);
2218991554f2SKenneth D. Merry 	ccb->ccb_h.status |= CAM_SIM_QUEUED;
2219991554f2SKenneth D. Merry 
2220991554f2SKenneth D. Merry 	mprsas_log_command(cm, MPR_XINFO, "%s cm %p ccb %p outstanding %u\n",
2221991554f2SKenneth D. Merry 	    __func__, cm, ccb, targ->outstanding);
2222991554f2SKenneth D. Merry 
2223991554f2SKenneth D. Merry 	mpr_map_command(sc, cm);
2224991554f2SKenneth D. Merry 	return;
2225991554f2SKenneth D. Merry }
2226991554f2SKenneth D. Merry 
2227991554f2SKenneth D. Merry /**
2228991554f2SKenneth D. Merry  * mpr_sc_failed_io_info - translated non-succesfull SCSI_IO request
2229991554f2SKenneth D. Merry  */
2230991554f2SKenneth D. Merry static void
2231991554f2SKenneth D. Merry mpr_sc_failed_io_info(struct mpr_softc *sc, struct ccb_scsiio *csio,
2232991554f2SKenneth D. Merry     Mpi2SCSIIOReply_t *mpi_reply, struct mprsas_target *targ)
2233991554f2SKenneth D. Merry {
2234991554f2SKenneth D. Merry 	u32 response_info;
2235991554f2SKenneth D. Merry 	u8 *response_bytes;
2236991554f2SKenneth D. Merry 	u16 ioc_status = le16toh(mpi_reply->IOCStatus) &
2237991554f2SKenneth D. Merry 	    MPI2_IOCSTATUS_MASK;
2238991554f2SKenneth D. Merry 	u8 scsi_state = mpi_reply->SCSIState;
2239991554f2SKenneth D. Merry 	u8 scsi_status = mpi_reply->SCSIStatus;
2240991554f2SKenneth D. Merry 	char *desc_ioc_state = NULL;
2241991554f2SKenneth D. Merry 	char *desc_scsi_status = NULL;
2242991554f2SKenneth D. Merry 	u32 log_info = le32toh(mpi_reply->IOCLogInfo);
2243991554f2SKenneth D. Merry 
2244991554f2SKenneth D. Merry 	if (log_info == 0x31170000)
2245991554f2SKenneth D. Merry 		return;
2246991554f2SKenneth D. Merry 
22472bf620cbSScott Long 	desc_ioc_state = mpr_describe_table(mpr_iocstatus_string,
22482bf620cbSScott Long 	     ioc_status);
22492bf620cbSScott Long 	desc_scsi_status = mpr_describe_table(mpr_scsi_status_string,
22502bf620cbSScott Long 	    scsi_status);
2251991554f2SKenneth D. Merry 
2252991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO, "\thandle(0x%04x), ioc_status(%s)(0x%04x)\n",
2253991554f2SKenneth D. Merry 	    le16toh(mpi_reply->DevHandle), desc_ioc_state, ioc_status);
2254991554f2SKenneth D. Merry 	if (targ->encl_level_valid) {
2255991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_XINFO, "At enclosure level %d, slot %d, "
2256991554f2SKenneth D. Merry 		    "connector name (%4s)\n", targ->encl_level, targ->encl_slot,
2257991554f2SKenneth D. Merry 		    targ->connector_name);
2258991554f2SKenneth D. Merry 	}
22592bf620cbSScott Long 
22602bf620cbSScott Long 	/*
22612bf620cbSScott Long 	 * We can add more detail about underflow data here
2262991554f2SKenneth D. Merry 	 * TO-DO
22632bf620cbSScott Long 	 */
2264991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO, "\tscsi_status(%s)(0x%02x), "
22652bf620cbSScott Long 	    "scsi_state %b\n", desc_scsi_status, scsi_status,
22662bf620cbSScott Long 	    scsi_state, "\20" "\1AutosenseValid" "\2AutosenseFailed"
22672bf620cbSScott Long 	    "\3NoScsiStatus" "\4Terminated" "\5Response InfoValid");
2268991554f2SKenneth D. Merry 
2269991554f2SKenneth D. Merry 	if (sc->mpr_debug & MPR_XINFO &&
2270991554f2SKenneth D. Merry 	    scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
2271991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_XINFO, "-> Sense Buffer Data : Start :\n");
2272991554f2SKenneth D. Merry 		scsi_sense_print(csio);
2273991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_XINFO, "-> Sense Buffer Data : End :\n");
2274991554f2SKenneth D. Merry 	}
2275991554f2SKenneth D. Merry 
2276991554f2SKenneth D. Merry 	if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) {
2277991554f2SKenneth D. Merry 		response_info = le32toh(mpi_reply->ResponseInfo);
2278991554f2SKenneth D. Merry 		response_bytes = (u8 *)&response_info;
22792bf620cbSScott Long 		mpr_dprint(sc, MPR_XINFO, "response code(0x%01x): %s\n",
22802bf620cbSScott Long 		    response_bytes[0],
22812bf620cbSScott Long 		    mpr_describe_table(mpr_scsi_taskmgmt_string,
22822bf620cbSScott Long 		    response_bytes[0]));
2283991554f2SKenneth D. Merry 	}
2284991554f2SKenneth D. Merry }
2285991554f2SKenneth D. Merry 
228667feec50SStephen McConnell /** mprsas_nvme_trans_status_code
228767feec50SStephen McConnell  *
228867feec50SStephen McConnell  * Convert Native NVMe command error status to
228967feec50SStephen McConnell  * equivalent SCSI error status.
229067feec50SStephen McConnell  *
229167feec50SStephen McConnell  * Returns appropriate scsi_status
229267feec50SStephen McConnell  */
229367feec50SStephen McConnell static u8
22940d787e9bSWojciech Macek mprsas_nvme_trans_status_code(uint16_t nvme_status,
229567feec50SStephen McConnell     struct mpr_command *cm)
229667feec50SStephen McConnell {
229767feec50SStephen McConnell 	u8 status = MPI2_SCSI_STATUS_GOOD;
229867feec50SStephen McConnell 	int skey, asc, ascq;
229967feec50SStephen McConnell 	union ccb *ccb = cm->cm_complete_data;
230067feec50SStephen McConnell 	int returned_sense_len;
23010d787e9bSWojciech Macek 	uint8_t sct, sc;
23020d787e9bSWojciech Macek 
23030d787e9bSWojciech Macek 	sct = NVME_STATUS_GET_SCT(nvme_status);
23040d787e9bSWojciech Macek 	sc = NVME_STATUS_GET_SC(nvme_status);
230567feec50SStephen McConnell 
230667feec50SStephen McConnell 	status = MPI2_SCSI_STATUS_CHECK_CONDITION;
230767feec50SStephen McConnell 	skey = SSD_KEY_ILLEGAL_REQUEST;
230867feec50SStephen McConnell 	asc = SCSI_ASC_NO_SENSE;
230967feec50SStephen McConnell 	ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
231067feec50SStephen McConnell 
23110d787e9bSWojciech Macek 	switch (sct) {
231267feec50SStephen McConnell 	case NVME_SCT_GENERIC:
23130d787e9bSWojciech Macek 		switch (sc) {
231467feec50SStephen McConnell 		case NVME_SC_SUCCESS:
231567feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_GOOD;
231667feec50SStephen McConnell 			skey = SSD_KEY_NO_SENSE;
231767feec50SStephen McConnell 			asc = SCSI_ASC_NO_SENSE;
231867feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
231967feec50SStephen McConnell 			break;
232067feec50SStephen McConnell 		case NVME_SC_INVALID_OPCODE:
232167feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
232267feec50SStephen McConnell 			skey = SSD_KEY_ILLEGAL_REQUEST;
232367feec50SStephen McConnell 			asc = SCSI_ASC_ILLEGAL_COMMAND;
232467feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
232567feec50SStephen McConnell 			break;
232667feec50SStephen McConnell 		case NVME_SC_INVALID_FIELD:
232767feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
232867feec50SStephen McConnell 			skey = SSD_KEY_ILLEGAL_REQUEST;
232967feec50SStephen McConnell 			asc = SCSI_ASC_INVALID_CDB;
233067feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
233167feec50SStephen McConnell 			break;
233267feec50SStephen McConnell 		case NVME_SC_DATA_TRANSFER_ERROR:
233367feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
233467feec50SStephen McConnell 			skey = SSD_KEY_MEDIUM_ERROR;
233567feec50SStephen McConnell 			asc = SCSI_ASC_NO_SENSE;
233667feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
233767feec50SStephen McConnell 			break;
233867feec50SStephen McConnell 		case NVME_SC_ABORTED_POWER_LOSS:
233967feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_TASK_ABORTED;
234067feec50SStephen McConnell 			skey = SSD_KEY_ABORTED_COMMAND;
234167feec50SStephen McConnell 			asc = SCSI_ASC_WARNING;
234267feec50SStephen McConnell 			ascq = SCSI_ASCQ_POWER_LOSS_EXPECTED;
234367feec50SStephen McConnell 			break;
234467feec50SStephen McConnell 		case NVME_SC_INTERNAL_DEVICE_ERROR:
234567feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
234667feec50SStephen McConnell 			skey = SSD_KEY_HARDWARE_ERROR;
234767feec50SStephen McConnell 			asc = SCSI_ASC_INTERNAL_TARGET_FAILURE;
234867feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
234967feec50SStephen McConnell 			break;
235067feec50SStephen McConnell 		case NVME_SC_ABORTED_BY_REQUEST:
235167feec50SStephen McConnell 		case NVME_SC_ABORTED_SQ_DELETION:
235267feec50SStephen McConnell 		case NVME_SC_ABORTED_FAILED_FUSED:
235367feec50SStephen McConnell 		case NVME_SC_ABORTED_MISSING_FUSED:
235467feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_TASK_ABORTED;
235567feec50SStephen McConnell 			skey = SSD_KEY_ABORTED_COMMAND;
235667feec50SStephen McConnell 			asc = SCSI_ASC_NO_SENSE;
235767feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
235867feec50SStephen McConnell 			break;
235967feec50SStephen McConnell 		case NVME_SC_INVALID_NAMESPACE_OR_FORMAT:
236067feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
236167feec50SStephen McConnell 			skey = SSD_KEY_ILLEGAL_REQUEST;
236267feec50SStephen McConnell 			asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID;
236367feec50SStephen McConnell 			ascq = SCSI_ASCQ_INVALID_LUN_ID;
236467feec50SStephen McConnell 			break;
236567feec50SStephen McConnell 		case NVME_SC_LBA_OUT_OF_RANGE:
236667feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
236767feec50SStephen McConnell 			skey = SSD_KEY_ILLEGAL_REQUEST;
236867feec50SStephen McConnell 			asc = SCSI_ASC_ILLEGAL_BLOCK;
236967feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
237067feec50SStephen McConnell 			break;
237167feec50SStephen McConnell 		case NVME_SC_CAPACITY_EXCEEDED:
237267feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
237367feec50SStephen McConnell 			skey = SSD_KEY_MEDIUM_ERROR;
237467feec50SStephen McConnell 			asc = SCSI_ASC_NO_SENSE;
237567feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
237667feec50SStephen McConnell 			break;
237767feec50SStephen McConnell 		case NVME_SC_NAMESPACE_NOT_READY:
237867feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
237967feec50SStephen McConnell 			skey = SSD_KEY_NOT_READY;
238067feec50SStephen McConnell 			asc = SCSI_ASC_LUN_NOT_READY;
238167feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
238267feec50SStephen McConnell 			break;
238367feec50SStephen McConnell 		}
238467feec50SStephen McConnell 		break;
238567feec50SStephen McConnell 	case NVME_SCT_COMMAND_SPECIFIC:
23860d787e9bSWojciech Macek 		switch (sc) {
238767feec50SStephen McConnell 		case NVME_SC_INVALID_FORMAT:
238867feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
238967feec50SStephen McConnell 			skey = SSD_KEY_ILLEGAL_REQUEST;
239067feec50SStephen McConnell 			asc = SCSI_ASC_FORMAT_COMMAND_FAILED;
239167feec50SStephen McConnell 			ascq = SCSI_ASCQ_FORMAT_COMMAND_FAILED;
239267feec50SStephen McConnell 			break;
239367feec50SStephen McConnell 		case NVME_SC_CONFLICTING_ATTRIBUTES:
239467feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
239567feec50SStephen McConnell 			skey = SSD_KEY_ILLEGAL_REQUEST;
239667feec50SStephen McConnell 			asc = SCSI_ASC_INVALID_CDB;
239767feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
239867feec50SStephen McConnell 			break;
239967feec50SStephen McConnell 		}
240067feec50SStephen McConnell 		break;
240167feec50SStephen McConnell 	case NVME_SCT_MEDIA_ERROR:
24020d787e9bSWojciech Macek 		switch (sc) {
240367feec50SStephen McConnell 		case NVME_SC_WRITE_FAULTS:
240467feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
240567feec50SStephen McConnell 			skey = SSD_KEY_MEDIUM_ERROR;
240667feec50SStephen McConnell 			asc = SCSI_ASC_PERIPHERAL_DEV_WRITE_FAULT;
240767feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
240867feec50SStephen McConnell 			break;
240967feec50SStephen McConnell 		case NVME_SC_UNRECOVERED_READ_ERROR:
241067feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
241167feec50SStephen McConnell 			skey = SSD_KEY_MEDIUM_ERROR;
241267feec50SStephen McConnell 			asc = SCSI_ASC_UNRECOVERED_READ_ERROR;
241367feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
241467feec50SStephen McConnell 			break;
241567feec50SStephen McConnell 		case NVME_SC_GUARD_CHECK_ERROR:
241667feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
241767feec50SStephen McConnell 			skey = SSD_KEY_MEDIUM_ERROR;
241867feec50SStephen McConnell 			asc = SCSI_ASC_LOG_BLOCK_GUARD_CHECK_FAILED;
241967feec50SStephen McConnell 			ascq = SCSI_ASCQ_LOG_BLOCK_GUARD_CHECK_FAILED;
242067feec50SStephen McConnell 			break;
242167feec50SStephen McConnell 		case NVME_SC_APPLICATION_TAG_CHECK_ERROR:
242267feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
242367feec50SStephen McConnell 			skey = SSD_KEY_MEDIUM_ERROR;
242467feec50SStephen McConnell 			asc = SCSI_ASC_LOG_BLOCK_APPTAG_CHECK_FAILED;
242567feec50SStephen McConnell 			ascq = SCSI_ASCQ_LOG_BLOCK_APPTAG_CHECK_FAILED;
242667feec50SStephen McConnell 			break;
242767feec50SStephen McConnell 		case NVME_SC_REFERENCE_TAG_CHECK_ERROR:
242867feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
242967feec50SStephen McConnell 			skey = SSD_KEY_MEDIUM_ERROR;
243067feec50SStephen McConnell 			asc = SCSI_ASC_LOG_BLOCK_REFTAG_CHECK_FAILED;
243167feec50SStephen McConnell 			ascq = SCSI_ASCQ_LOG_BLOCK_REFTAG_CHECK_FAILED;
243267feec50SStephen McConnell 			break;
243367feec50SStephen McConnell 		case NVME_SC_COMPARE_FAILURE:
243467feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
243567feec50SStephen McConnell 			skey = SSD_KEY_MISCOMPARE;
243667feec50SStephen McConnell 			asc = SCSI_ASC_MISCOMPARE_DURING_VERIFY;
243767feec50SStephen McConnell 			ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE;
243867feec50SStephen McConnell 			break;
243967feec50SStephen McConnell 		case NVME_SC_ACCESS_DENIED:
244067feec50SStephen McConnell 			status = MPI2_SCSI_STATUS_CHECK_CONDITION;
244167feec50SStephen McConnell 			skey = SSD_KEY_ILLEGAL_REQUEST;
244267feec50SStephen McConnell 			asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID;
244367feec50SStephen McConnell 			ascq = SCSI_ASCQ_INVALID_LUN_ID;
244467feec50SStephen McConnell 			break;
244567feec50SStephen McConnell 		}
244667feec50SStephen McConnell 		break;
244767feec50SStephen McConnell 	}
244867feec50SStephen McConnell 
244967feec50SStephen McConnell 	returned_sense_len = sizeof(struct scsi_sense_data);
245067feec50SStephen McConnell 	if (returned_sense_len < ccb->csio.sense_len)
245167feec50SStephen McConnell 		ccb->csio.sense_resid = ccb->csio.sense_len -
245267feec50SStephen McConnell 		    returned_sense_len;
245367feec50SStephen McConnell 	else
245467feec50SStephen McConnell 		ccb->csio.sense_resid = 0;
245567feec50SStephen McConnell 
245667feec50SStephen McConnell 	scsi_set_sense_data(&ccb->csio.sense_data, SSD_TYPE_FIXED,
245767feec50SStephen McConnell 	    1, skey, asc, ascq, SSD_ELEM_NONE);
245867feec50SStephen McConnell 	ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
245967feec50SStephen McConnell 
246067feec50SStephen McConnell 	return status;
246167feec50SStephen McConnell }
246267feec50SStephen McConnell 
246367feec50SStephen McConnell /** mprsas_complete_nvme_unmap
246467feec50SStephen McConnell  *
246567feec50SStephen McConnell  * Complete native NVMe command issued using NVMe Encapsulated
246667feec50SStephen McConnell  * Request Message.
246767feec50SStephen McConnell  */
246867feec50SStephen McConnell static u8
246967feec50SStephen McConnell mprsas_complete_nvme_unmap(struct mpr_softc *sc, struct mpr_command *cm)
247067feec50SStephen McConnell {
247167feec50SStephen McConnell 	Mpi26NVMeEncapsulatedErrorReply_t *mpi_reply;
247267feec50SStephen McConnell 	struct nvme_completion *nvme_completion = NULL;
247367feec50SStephen McConnell 	u8 scsi_status = MPI2_SCSI_STATUS_GOOD;
247467feec50SStephen McConnell 
247567feec50SStephen McConnell 	mpi_reply =(Mpi26NVMeEncapsulatedErrorReply_t *)cm->cm_reply;
247667feec50SStephen McConnell 	if (le16toh(mpi_reply->ErrorResponseCount)){
247767feec50SStephen McConnell 		nvme_completion = (struct nvme_completion *)cm->cm_sense;
247867feec50SStephen McConnell 		scsi_status = mprsas_nvme_trans_status_code(
247967feec50SStephen McConnell 		    nvme_completion->status, cm);
248067feec50SStephen McConnell 	}
248167feec50SStephen McConnell 	return scsi_status;
248267feec50SStephen McConnell }
248367feec50SStephen McConnell 
2484991554f2SKenneth D. Merry static void
2485991554f2SKenneth D. Merry mprsas_scsiio_complete(struct mpr_softc *sc, struct mpr_command *cm)
2486991554f2SKenneth D. Merry {
2487991554f2SKenneth D. Merry 	MPI2_SCSI_IO_REPLY *rep;
2488991554f2SKenneth D. Merry 	union ccb *ccb;
2489991554f2SKenneth D. Merry 	struct ccb_scsiio *csio;
2490991554f2SKenneth D. Merry 	struct mprsas_softc *sassc;
2491991554f2SKenneth D. Merry 	struct scsi_vpd_supported_page_list *vpd_list = NULL;
249267feec50SStephen McConnell 	u8 *TLR_bits, TLR_on, *scsi_cdb;
2493991554f2SKenneth D. Merry 	int dir = 0, i;
2494991554f2SKenneth D. Merry 	u16 alloc_len;
2495a2c14879SStephen McConnell 	struct mprsas_target *target;
2496a2c14879SStephen McConnell 	target_id_t target_id;
2497991554f2SKenneth D. Merry 
2498991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
2499991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_TRACE,
2500991554f2SKenneth D. Merry 	    "cm %p SMID %u ccb %p reply %p outstanding %u\n", cm,
2501991554f2SKenneth D. Merry 	    cm->cm_desc.Default.SMID, cm->cm_ccb, cm->cm_reply,
2502991554f2SKenneth D. Merry 	    cm->cm_targ->outstanding);
2503991554f2SKenneth D. Merry 
2504991554f2SKenneth D. Merry 	callout_stop(&cm->cm_callout);
2505991554f2SKenneth D. Merry 	mtx_assert(&sc->mpr_mtx, MA_OWNED);
2506991554f2SKenneth D. Merry 
2507991554f2SKenneth D. Merry 	sassc = sc->sassc;
2508991554f2SKenneth D. Merry 	ccb = cm->cm_complete_data;
2509991554f2SKenneth D. Merry 	csio = &ccb->csio;
2510a2c14879SStephen McConnell 	target_id = csio->ccb_h.target_id;
2511991554f2SKenneth D. Merry 	rep = (MPI2_SCSI_IO_REPLY *)cm->cm_reply;
2512991554f2SKenneth D. Merry 	/*
2513991554f2SKenneth D. Merry 	 * XXX KDM if the chain allocation fails, does it matter if we do
2514991554f2SKenneth D. Merry 	 * the sync and unload here?  It is simpler to do it in every case,
2515991554f2SKenneth D. Merry 	 * assuming it doesn't cause problems.
2516991554f2SKenneth D. Merry 	 */
2517991554f2SKenneth D. Merry 	if (cm->cm_data != NULL) {
2518991554f2SKenneth D. Merry 		if (cm->cm_flags & MPR_CM_FLAGS_DATAIN)
2519991554f2SKenneth D. Merry 			dir = BUS_DMASYNC_POSTREAD;
2520991554f2SKenneth D. Merry 		else if (cm->cm_flags & MPR_CM_FLAGS_DATAOUT)
2521991554f2SKenneth D. Merry 			dir = BUS_DMASYNC_POSTWRITE;
2522991554f2SKenneth D. Merry 		bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir);
2523991554f2SKenneth D. Merry 		bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
2524991554f2SKenneth D. Merry 	}
2525991554f2SKenneth D. Merry 
2526991554f2SKenneth D. Merry 	cm->cm_targ->completed++;
2527991554f2SKenneth D. Merry 	cm->cm_targ->outstanding--;
2528991554f2SKenneth D. Merry 	TAILQ_REMOVE(&cm->cm_targ->commands, cm, cm_link);
2529991554f2SKenneth D. Merry 	ccb->ccb_h.status &= ~(CAM_STATUS_MASK | CAM_SIM_QUEUED);
2530991554f2SKenneth D. Merry 
2531991554f2SKenneth D. Merry 	if (cm->cm_state == MPR_CM_STATE_TIMEDOUT) {
2532991554f2SKenneth D. Merry 		TAILQ_REMOVE(&cm->cm_targ->timedout_commands, cm, cm_recovery);
2533f0779b04SScott Long 		cm->cm_state = MPR_CM_STATE_BUSY;
2534991554f2SKenneth D. Merry 		if (cm->cm_reply != NULL)
2535991554f2SKenneth D. Merry 			mprsas_log_command(cm, MPR_RECOVERY,
2536991554f2SKenneth D. Merry 			    "completed timedout cm %p ccb %p during recovery "
2537991554f2SKenneth D. Merry 			    "ioc %x scsi %x state %x xfer %u\n", cm, cm->cm_ccb,
2538991554f2SKenneth D. Merry 			    le16toh(rep->IOCStatus), rep->SCSIStatus,
2539991554f2SKenneth D. Merry 			    rep->SCSIState, le32toh(rep->TransferCount));
2540991554f2SKenneth D. Merry 		else
2541991554f2SKenneth D. Merry 			mprsas_log_command(cm, MPR_RECOVERY,
2542991554f2SKenneth D. Merry 			    "completed timedout cm %p ccb %p during recovery\n",
2543991554f2SKenneth D. Merry 			    cm, cm->cm_ccb);
2544991554f2SKenneth D. Merry 	} else if (cm->cm_targ->tm != NULL) {
2545991554f2SKenneth D. Merry 		if (cm->cm_reply != NULL)
2546991554f2SKenneth D. Merry 			mprsas_log_command(cm, MPR_RECOVERY,
2547991554f2SKenneth D. Merry 			    "completed cm %p ccb %p during recovery "
2548991554f2SKenneth D. Merry 			    "ioc %x scsi %x state %x xfer %u\n",
2549991554f2SKenneth D. Merry 			    cm, cm->cm_ccb, le16toh(rep->IOCStatus),
2550991554f2SKenneth D. Merry 			    rep->SCSIStatus, rep->SCSIState,
2551991554f2SKenneth D. Merry 			    le32toh(rep->TransferCount));
2552991554f2SKenneth D. Merry 		else
2553991554f2SKenneth D. Merry 			mprsas_log_command(cm, MPR_RECOVERY,
2554991554f2SKenneth D. Merry 			    "completed cm %p ccb %p during recovery\n",
2555991554f2SKenneth D. Merry 			    cm, cm->cm_ccb);
2556991554f2SKenneth D. Merry 	} else if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) {
2557991554f2SKenneth D. Merry 		mprsas_log_command(cm, MPR_RECOVERY,
2558991554f2SKenneth D. Merry 		    "reset completed cm %p ccb %p\n", cm, cm->cm_ccb);
2559991554f2SKenneth D. Merry 	}
2560991554f2SKenneth D. Merry 
2561991554f2SKenneth D. Merry 	if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
2562991554f2SKenneth D. Merry 		/*
2563991554f2SKenneth D. Merry 		 * We ran into an error after we tried to map the command,
2564991554f2SKenneth D. Merry 		 * so we're getting a callback without queueing the command
2565991554f2SKenneth D. Merry 		 * to the hardware.  So we set the status here, and it will
2566991554f2SKenneth D. Merry 		 * be retained below.  We'll go through the "fast path",
2567991554f2SKenneth D. Merry 		 * because there can be no reply when we haven't actually
2568991554f2SKenneth D. Merry 		 * gone out to the hardware.
2569991554f2SKenneth D. Merry 		 */
2570a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQUEUE_REQ);
2571991554f2SKenneth D. Merry 
2572991554f2SKenneth D. Merry 		/*
2573991554f2SKenneth D. Merry 		 * Currently the only error included in the mask is
2574991554f2SKenneth D. Merry 		 * MPR_CM_FLAGS_CHAIN_FAILED, which means we're out of
2575991554f2SKenneth D. Merry 		 * chain frames.  We need to freeze the queue until we get
2576991554f2SKenneth D. Merry 		 * a command that completed without this error, which will
2577991554f2SKenneth D. Merry 		 * hopefully have some chain frames attached that we can
2578991554f2SKenneth D. Merry 		 * use.  If we wanted to get smarter about it, we would
2579991554f2SKenneth D. Merry 		 * only unfreeze the queue in this condition when we're
2580991554f2SKenneth D. Merry 		 * sure that we're getting some chain frames back.  That's
2581991554f2SKenneth D. Merry 		 * probably unnecessary.
2582991554f2SKenneth D. Merry 		 */
2583991554f2SKenneth D. Merry 		if ((sassc->flags & MPRSAS_QUEUE_FROZEN) == 0) {
2584991554f2SKenneth D. Merry 			xpt_freeze_simq(sassc->sim, 1);
2585991554f2SKenneth D. Merry 			sassc->flags |= MPRSAS_QUEUE_FROZEN;
25866c85e33eSScott Long 			mpr_dprint(sc, MPR_XINFO, "Error sending command, "
2587991554f2SKenneth D. Merry 			    "freezing SIM queue\n");
2588991554f2SKenneth D. Merry 		}
2589991554f2SKenneth D. Merry 	}
2590991554f2SKenneth D. Merry 
2591991554f2SKenneth D. Merry 	/*
259267feec50SStephen McConnell 	 * Point to the SCSI CDB, which is dependent on the CAM_CDB_POINTER
259367feec50SStephen McConnell 	 * flag, and use it in a few places in the rest of this function for
259467feec50SStephen McConnell 	 * convenience. Use the macro if available.
259567feec50SStephen McConnell 	 */
259667feec50SStephen McConnell #if __FreeBSD_version >= 1100103
259767feec50SStephen McConnell 	scsi_cdb = scsiio_cdb_ptr(csio);
259867feec50SStephen McConnell #else
259967feec50SStephen McConnell 	if (csio->ccb_h.flags & CAM_CDB_POINTER)
260067feec50SStephen McConnell 		scsi_cdb = csio->cdb_io.cdb_ptr;
260167feec50SStephen McConnell 	else
260267feec50SStephen McConnell 		scsi_cdb = csio->cdb_io.cdb_bytes;
260367feec50SStephen McConnell #endif
260467feec50SStephen McConnell 
260567feec50SStephen McConnell 	/*
2606991554f2SKenneth D. Merry 	 * If this is a Start Stop Unit command and it was issued by the driver
2607991554f2SKenneth D. Merry 	 * during shutdown, decrement the refcount to account for all of the
2608991554f2SKenneth D. Merry 	 * commands that were sent.  All SSU commands should be completed before
2609991554f2SKenneth D. Merry 	 * shutdown completes, meaning SSU_refcount will be 0 after SSU_started
2610991554f2SKenneth D. Merry 	 * is TRUE.
2611991554f2SKenneth D. Merry 	 */
261267feec50SStephen McConnell 	if (sc->SSU_started && (scsi_cdb[0] == START_STOP_UNIT)) {
2613991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_INFO, "Decrementing SSU count.\n");
2614991554f2SKenneth D. Merry 		sc->SSU_refcount--;
2615991554f2SKenneth D. Merry 	}
2616991554f2SKenneth D. Merry 
2617991554f2SKenneth D. Merry 	/* Take the fast path to completion */
2618991554f2SKenneth D. Merry 	if (cm->cm_reply == NULL) {
2619a2c14879SStephen McConnell 		if (mprsas_get_ccbstatus(ccb) == CAM_REQ_INPROG) {
2620991554f2SKenneth D. Merry 			if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0)
2621a2c14879SStephen McConnell 				mprsas_set_ccbstatus(ccb, CAM_SCSI_BUS_RESET);
2622991554f2SKenneth D. Merry 			else {
2623a2c14879SStephen McConnell 				mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
2624a2c14879SStephen McConnell 				csio->scsi_status = SCSI_STATUS_OK;
2625991554f2SKenneth D. Merry 			}
2626991554f2SKenneth D. Merry 			if (sassc->flags & MPRSAS_QUEUE_FROZEN) {
2627991554f2SKenneth D. Merry 				ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
2628991554f2SKenneth D. Merry 				sassc->flags &= ~MPRSAS_QUEUE_FROZEN;
2629991554f2SKenneth D. Merry 				mpr_dprint(sc, MPR_XINFO,
2630991554f2SKenneth D. Merry 				    "Unfreezing SIM queue\n");
2631991554f2SKenneth D. Merry 			}
2632991554f2SKenneth D. Merry 		}
2633991554f2SKenneth D. Merry 
2634991554f2SKenneth D. Merry 		/*
2635991554f2SKenneth D. Merry 		 * There are two scenarios where the status won't be
2636991554f2SKenneth D. Merry 		 * CAM_REQ_CMP.  The first is if MPR_CM_FLAGS_ERROR_MASK is
2637991554f2SKenneth D. Merry 		 * set, the second is in the MPR_FLAGS_DIAGRESET above.
2638991554f2SKenneth D. Merry 		 */
2639a2c14879SStephen McConnell 		if (mprsas_get_ccbstatus(ccb) != CAM_REQ_CMP) {
2640991554f2SKenneth D. Merry 			/*
2641991554f2SKenneth D. Merry 			 * Freeze the dev queue so that commands are
2642a2c14879SStephen McConnell 			 * executed in the correct order after error
2643991554f2SKenneth D. Merry 			 * recovery.
2644991554f2SKenneth D. Merry 			 */
2645991554f2SKenneth D. Merry 			ccb->ccb_h.status |= CAM_DEV_QFRZN;
2646991554f2SKenneth D. Merry 			xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
2647991554f2SKenneth D. Merry 		}
2648991554f2SKenneth D. Merry 		mpr_free_command(sc, cm);
2649991554f2SKenneth D. Merry 		xpt_done(ccb);
2650991554f2SKenneth D. Merry 		return;
2651991554f2SKenneth D. Merry 	}
2652991554f2SKenneth D. Merry 
265367feec50SStephen McConnell 	target = &sassc->targets[target_id];
265467feec50SStephen McConnell 	if (scsi_cdb[0] == UNMAP &&
265567feec50SStephen McConnell 	    target->is_nvme &&
265667feec50SStephen McConnell 	    (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) {
265767feec50SStephen McConnell 		rep->SCSIStatus = mprsas_complete_nvme_unmap(sc, cm);
265867feec50SStephen McConnell 		csio->scsi_status = rep->SCSIStatus;
265967feec50SStephen McConnell 	}
266067feec50SStephen McConnell 
2661991554f2SKenneth D. Merry 	mprsas_log_command(cm, MPR_XINFO,
2662991554f2SKenneth D. Merry 	    "ioc %x scsi %x state %x xfer %u\n",
2663991554f2SKenneth D. Merry 	    le16toh(rep->IOCStatus), rep->SCSIStatus, rep->SCSIState,
2664991554f2SKenneth D. Merry 	    le32toh(rep->TransferCount));
2665991554f2SKenneth D. Merry 
2666991554f2SKenneth D. Merry 	switch (le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK) {
2667991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN:
2668991554f2SKenneth D. Merry 		csio->resid = cm->cm_length - le32toh(rep->TransferCount);
2669991554f2SKenneth D. Merry 		/* FALLTHROUGH */
2670991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SUCCESS:
2671991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR:
2672991554f2SKenneth D. Merry 		if ((le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK) ==
2673991554f2SKenneth D. Merry 		    MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR)
2674991554f2SKenneth D. Merry 			mprsas_log_command(cm, MPR_XINFO, "recovered error\n");
2675991554f2SKenneth D. Merry 
2676991554f2SKenneth D. Merry 		/* Completion failed at the transport level. */
2677991554f2SKenneth D. Merry 		if (rep->SCSIState & (MPI2_SCSI_STATE_NO_SCSI_STATUS |
2678991554f2SKenneth D. Merry 		    MPI2_SCSI_STATE_TERMINATED)) {
2679a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR);
2680991554f2SKenneth D. Merry 			break;
2681991554f2SKenneth D. Merry 		}
2682991554f2SKenneth D. Merry 
2683991554f2SKenneth D. Merry 		/* In a modern packetized environment, an autosense failure
2684991554f2SKenneth D. Merry 		 * implies that there's not much else that can be done to
2685991554f2SKenneth D. Merry 		 * recover the command.
2686991554f2SKenneth D. Merry 		 */
2687991554f2SKenneth D. Merry 		if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_FAILED) {
2688a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_AUTOSENSE_FAIL);
2689991554f2SKenneth D. Merry 			break;
2690991554f2SKenneth D. Merry 		}
2691991554f2SKenneth D. Merry 
2692991554f2SKenneth D. Merry 		/*
2693991554f2SKenneth D. Merry 		 * CAM doesn't care about SAS Response Info data, but if this is
2694991554f2SKenneth D. Merry 		 * the state check if TLR should be done.  If not, clear the
2695991554f2SKenneth D. Merry 		 * TLR_bits for the target.
2696991554f2SKenneth D. Merry 		 */
2697991554f2SKenneth D. Merry 		if ((rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) &&
2698991554f2SKenneth D. Merry 		    ((le32toh(rep->ResponseInfo) & MPI2_SCSI_RI_MASK_REASONCODE)
2699991554f2SKenneth D. Merry 		    == MPR_SCSI_RI_INVALID_FRAME)) {
2700a2c14879SStephen McConnell 			sc->mapping_table[target_id].TLR_bits =
2701991554f2SKenneth D. Merry 			    (u8)MPI2_SCSIIO_CONTROL_NO_TLR;
2702991554f2SKenneth D. Merry 		}
2703991554f2SKenneth D. Merry 
2704991554f2SKenneth D. Merry 		/*
2705991554f2SKenneth D. Merry 		 * Intentionally override the normal SCSI status reporting
2706991554f2SKenneth D. Merry 		 * for these two cases.  These are likely to happen in a
2707991554f2SKenneth D. Merry 		 * multi-initiator environment, and we want to make sure that
2708991554f2SKenneth D. Merry 		 * CAM retries these commands rather than fail them.
2709991554f2SKenneth D. Merry 		 */
2710991554f2SKenneth D. Merry 		if ((rep->SCSIStatus == MPI2_SCSI_STATUS_COMMAND_TERMINATED) ||
2711991554f2SKenneth D. Merry 		    (rep->SCSIStatus == MPI2_SCSI_STATUS_TASK_ABORTED)) {
2712a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_ABORTED);
2713991554f2SKenneth D. Merry 			break;
2714991554f2SKenneth D. Merry 		}
2715991554f2SKenneth D. Merry 
2716991554f2SKenneth D. Merry 		/* Handle normal status and sense */
2717991554f2SKenneth D. Merry 		csio->scsi_status = rep->SCSIStatus;
2718991554f2SKenneth D. Merry 		if (rep->SCSIStatus == MPI2_SCSI_STATUS_GOOD)
2719a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
2720991554f2SKenneth D. Merry 		else
2721a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_SCSI_STATUS_ERROR);
2722991554f2SKenneth D. Merry 
2723991554f2SKenneth D. Merry 		if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) {
2724991554f2SKenneth D. Merry 			int sense_len, returned_sense_len;
2725991554f2SKenneth D. Merry 
2726991554f2SKenneth D. Merry 			returned_sense_len = min(le32toh(rep->SenseCount),
2727991554f2SKenneth D. Merry 			    sizeof(struct scsi_sense_data));
2728991554f2SKenneth D. Merry 			if (returned_sense_len < csio->sense_len)
2729991554f2SKenneth D. Merry 				csio->sense_resid = csio->sense_len -
2730991554f2SKenneth D. Merry 				    returned_sense_len;
2731991554f2SKenneth D. Merry 			else
2732991554f2SKenneth D. Merry 				csio->sense_resid = 0;
2733991554f2SKenneth D. Merry 
2734991554f2SKenneth D. Merry 			sense_len = min(returned_sense_len,
2735991554f2SKenneth D. Merry 			    csio->sense_len - csio->sense_resid);
2736991554f2SKenneth D. Merry 			bzero(&csio->sense_data, sizeof(csio->sense_data));
2737991554f2SKenneth D. Merry 			bcopy(cm->cm_sense, &csio->sense_data, sense_len);
2738991554f2SKenneth D. Merry 			ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
2739991554f2SKenneth D. Merry 		}
2740991554f2SKenneth D. Merry 
2741991554f2SKenneth D. Merry 		/*
2742991554f2SKenneth D. Merry 		 * Check if this is an INQUIRY command.  If it's a VPD inquiry,
2743991554f2SKenneth D. Merry 		 * and it's page code 0 (Supported Page List), and there is
2744991554f2SKenneth D. Merry 		 * inquiry data, and this is for a sequential access device, and
2745991554f2SKenneth D. Merry 		 * the device is an SSP target, and TLR is supported by the
2746991554f2SKenneth D. Merry 		 * controller, turn the TLR_bits value ON if page 0x90 is
2747991554f2SKenneth D. Merry 		 * supported.
2748991554f2SKenneth D. Merry 		 */
274967feec50SStephen McConnell 		if ((scsi_cdb[0] == INQUIRY) &&
275067feec50SStephen McConnell 		    (scsi_cdb[1] & SI_EVPD) &&
275167feec50SStephen McConnell 		    (scsi_cdb[2] == SVPD_SUPPORTED_PAGE_LIST) &&
2752991554f2SKenneth D. Merry 		    ((csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) &&
2753d2b4e18bSKenneth D. Merry 		    (csio->data_ptr != NULL) &&
2754d2b4e18bSKenneth D. Merry 		    ((csio->data_ptr[0] & 0x1f) == T_SEQUENTIAL) &&
2755d2b4e18bSKenneth D. Merry 		    (sc->control_TLR) &&
2756a2c14879SStephen McConnell 		    (sc->mapping_table[target_id].device_info &
2757991554f2SKenneth D. Merry 		    MPI2_SAS_DEVICE_INFO_SSP_TARGET)) {
2758991554f2SKenneth D. Merry 			vpd_list = (struct scsi_vpd_supported_page_list *)
2759991554f2SKenneth D. Merry 			    csio->data_ptr;
2760a2c14879SStephen McConnell 			TLR_bits = &sc->mapping_table[target_id].TLR_bits;
2761991554f2SKenneth D. Merry 			*TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR;
2762991554f2SKenneth D. Merry 			TLR_on = (u8)MPI2_SCSIIO_CONTROL_TLR_ON;
276367feec50SStephen McConnell 			alloc_len = ((u16)scsi_cdb[3] << 8) + scsi_cdb[4];
2764d2b4e18bSKenneth D. Merry 			alloc_len -= csio->resid;
2765991554f2SKenneth D. Merry 			for (i = 0; i < MIN(vpd_list->length, alloc_len); i++) {
2766991554f2SKenneth D. Merry 				if (vpd_list->list[i] == 0x90) {
2767991554f2SKenneth D. Merry 					*TLR_bits = TLR_on;
2768991554f2SKenneth D. Merry 					break;
2769991554f2SKenneth D. Merry 				}
2770991554f2SKenneth D. Merry 			}
2771991554f2SKenneth D. Merry 		}
2772a2c14879SStephen McConnell 
2773a2c14879SStephen McConnell 		/*
2774a2c14879SStephen McConnell 		 * If this is a SATA direct-access end device, mark it so that
2775a2c14879SStephen McConnell 		 * a SCSI StartStopUnit command will be sent to it when the
2776a2c14879SStephen McConnell 		 * driver is being shutdown.
2777a2c14879SStephen McConnell 		 */
277867feec50SStephen McConnell 		if ((scsi_cdb[0] == INQUIRY) &&
2779fa699bb2SAlan Somers 		    (csio->data_ptr != NULL) &&
2780a2c14879SStephen McConnell 		    ((csio->data_ptr[0] & 0x1f) == T_DIRECT) &&
2781a2c14879SStephen McConnell 		    (sc->mapping_table[target_id].device_info &
2782a2c14879SStephen McConnell 		    MPI2_SAS_DEVICE_INFO_SATA_DEVICE) &&
2783a2c14879SStephen McConnell 		    ((sc->mapping_table[target_id].device_info &
2784a2c14879SStephen McConnell 		    MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) ==
2785a2c14879SStephen McConnell 		    MPI2_SAS_DEVICE_INFO_END_DEVICE)) {
2786a2c14879SStephen McConnell 			target = &sassc->targets[target_id];
2787a2c14879SStephen McConnell 			target->supports_SSU = TRUE;
2788a2c14879SStephen McConnell 			mpr_dprint(sc, MPR_XINFO, "Target %d supports SSU\n",
2789a2c14879SStephen McConnell 			    target_id);
2790a2c14879SStephen McConnell 		}
2791991554f2SKenneth D. Merry 		break;
2792991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE:
2793991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE:
2794991554f2SKenneth D. Merry 		/*
2795991554f2SKenneth D. Merry 		 * If devinfo is 0 this will be a volume.  In that case don't
2796991554f2SKenneth D. Merry 		 * tell CAM that the volume is not there.  We want volumes to
2797991554f2SKenneth D. Merry 		 * be enumerated until they are deleted/removed, not just
2798991554f2SKenneth D. Merry 		 * failed.
2799991554f2SKenneth D. Merry 		 */
2800991554f2SKenneth D. Merry 		if (cm->cm_targ->devinfo == 0)
2801a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
2802991554f2SKenneth D. Merry 		else
2803a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
2804991554f2SKenneth D. Merry 		break;
2805991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_INVALID_SGL:
2806991554f2SKenneth D. Merry 		mpr_print_scsiio_cmd(sc, cm);
2807a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_UNREC_HBA_ERROR);
2808991554f2SKenneth D. Merry 		break;
2809991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED:
2810991554f2SKenneth D. Merry 		/*
2811991554f2SKenneth D. Merry 		 * This is one of the responses that comes back when an I/O
2812991554f2SKenneth D. Merry 		 * has been aborted.  If it is because of a timeout that we
2813991554f2SKenneth D. Merry 		 * initiated, just set the status to CAM_CMD_TIMEOUT.
2814991554f2SKenneth D. Merry 		 * Otherwise set it to CAM_REQ_ABORTED.  The effect on the
2815991554f2SKenneth D. Merry 		 * command is the same (it gets retried, subject to the
2816991554f2SKenneth D. Merry 		 * retry counter), the only difference is what gets printed
2817991554f2SKenneth D. Merry 		 * on the console.
2818991554f2SKenneth D. Merry 		 */
2819991554f2SKenneth D. Merry 		if (cm->cm_state == MPR_CM_STATE_TIMEDOUT)
2820a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_CMD_TIMEOUT);
2821991554f2SKenneth D. Merry 		else
2822a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_ABORTED);
2823991554f2SKenneth D. Merry 		break;
2824991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN:
2825991554f2SKenneth D. Merry 		/* resid is ignored for this condition */
2826991554f2SKenneth D. Merry 		csio->resid = 0;
2827a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_DATA_RUN_ERR);
2828991554f2SKenneth D. Merry 		break;
2829991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED:
2830991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED:
2831991554f2SKenneth D. Merry 		/*
28326adfa7edSAlan Somers 		 * These can sometimes be transient transport-related
28336adfa7edSAlan Somers 		 * errors, and sometimes persistent drive-related errors.
28346adfa7edSAlan Somers 		 * We used to retry these without decrementing the retry
28356adfa7edSAlan Somers 		 * count by returning CAM_REQUEUE_REQ.  Unfortunately, if
28366adfa7edSAlan Somers 		 * we hit a persistent drive problem that returns one of
28376adfa7edSAlan Somers 		 * these error codes, we would retry indefinitely.  So,
28386adfa7edSAlan Somers 		 * return CAM_REQ_CMP_ERROR so that we decrement the retry
28396adfa7edSAlan Somers 		 * count and avoid infinite retries.  We're taking the
28406adfa7edSAlan Somers 		 * potential risk of flagging false failures in the event
28416adfa7edSAlan Somers 		 * of a topology-related error (e.g. a SAS expander problem
28426adfa7edSAlan Somers 		 * causes a command addressed to a drive to fail), but
28436adfa7edSAlan Somers 		 * avoiding getting into an infinite retry loop.
2844991554f2SKenneth D. Merry 		 */
28456adfa7edSAlan Somers 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR);
28466eea4f46SScott Long 		mpr_dprint(sc, MPR_INFO,
28472bf620cbSScott Long 		    "Controller reported %s tgt %u SMID %u loginfo %x\n",
28482bf620cbSScott Long 		    mpr_describe_table(mpr_iocstatus_string,
28492bf620cbSScott Long 		    le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK),
28502bf620cbSScott Long 		    target_id, cm->cm_desc.Default.SMID,
28516eea4f46SScott Long 		    le32toh(rep->IOCLogInfo));
28526eea4f46SScott Long 		mpr_dprint(sc, MPR_XINFO,
28536eea4f46SScott Long 		    "SCSIStatus %x SCSIState %x xfercount %u\n",
2854694cb8b8SScott Long 		    rep->SCSIStatus, rep->SCSIState,
2855991554f2SKenneth D. Merry 		    le32toh(rep->TransferCount));
2856991554f2SKenneth D. Merry 		break;
2857991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_INVALID_FUNCTION:
2858991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_INTERNAL_ERROR:
2859991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_INVALID_VPID:
2860991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_INVALID_FIELD:
2861991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_INVALID_STATE:
2862991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED:
2863991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR:
2864991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR:
2865991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH:
2866991554f2SKenneth D. Merry 	case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED:
2867991554f2SKenneth D. Merry 	default:
2868991554f2SKenneth D. Merry 		mprsas_log_command(cm, MPR_XINFO,
2869694cb8b8SScott Long 		    "completed ioc %x loginfo %x scsi %x state %x xfer %u\n",
2870694cb8b8SScott Long 		    le16toh(rep->IOCStatus), le32toh(rep->IOCLogInfo),
2871694cb8b8SScott Long 		    rep->SCSIStatus, rep->SCSIState,
2872991554f2SKenneth D. Merry 		    le32toh(rep->TransferCount));
2873991554f2SKenneth D. Merry 		csio->resid = cm->cm_length;
287467feec50SStephen McConnell 
287567feec50SStephen McConnell 		if (scsi_cdb[0] == UNMAP &&
287667feec50SStephen McConnell 		    target->is_nvme &&
287767feec50SStephen McConnell 		    (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR)
287867feec50SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
287967feec50SStephen McConnell 		else
2880a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR);
288167feec50SStephen McConnell 
2882991554f2SKenneth D. Merry 		break;
2883991554f2SKenneth D. Merry 	}
2884991554f2SKenneth D. Merry 
2885991554f2SKenneth D. Merry 	mpr_sc_failed_io_info(sc, csio, rep, cm->cm_targ);
2886991554f2SKenneth D. Merry 
2887991554f2SKenneth D. Merry 	if (sassc->flags & MPRSAS_QUEUE_FROZEN) {
2888991554f2SKenneth D. Merry 		ccb->ccb_h.status |= CAM_RELEASE_SIMQ;
2889991554f2SKenneth D. Merry 		sassc->flags &= ~MPRSAS_QUEUE_FROZEN;
2890991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_XINFO, "Command completed, unfreezing SIM "
2891991554f2SKenneth D. Merry 		    "queue\n");
2892991554f2SKenneth D. Merry 	}
2893991554f2SKenneth D. Merry 
2894a2c14879SStephen McConnell 	if (mprsas_get_ccbstatus(ccb) != CAM_REQ_CMP) {
2895991554f2SKenneth D. Merry 		ccb->ccb_h.status |= CAM_DEV_QFRZN;
2896991554f2SKenneth D. Merry 		xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1);
2897991554f2SKenneth D. Merry 	}
2898991554f2SKenneth D. Merry 
2899991554f2SKenneth D. Merry 	mpr_free_command(sc, cm);
2900991554f2SKenneth D. Merry 	xpt_done(ccb);
2901991554f2SKenneth D. Merry }
2902991554f2SKenneth D. Merry 
2903991554f2SKenneth D. Merry #if __FreeBSD_version >= 900026
2904991554f2SKenneth D. Merry static void
2905991554f2SKenneth D. Merry mprsas_smpio_complete(struct mpr_softc *sc, struct mpr_command *cm)
2906991554f2SKenneth D. Merry {
2907991554f2SKenneth D. Merry 	MPI2_SMP_PASSTHROUGH_REPLY *rpl;
2908991554f2SKenneth D. Merry 	MPI2_SMP_PASSTHROUGH_REQUEST *req;
2909991554f2SKenneth D. Merry 	uint64_t sasaddr;
2910991554f2SKenneth D. Merry 	union ccb *ccb;
2911991554f2SKenneth D. Merry 
2912991554f2SKenneth D. Merry 	ccb = cm->cm_complete_data;
2913991554f2SKenneth D. Merry 
2914991554f2SKenneth D. Merry 	/*
2915991554f2SKenneth D. Merry 	 * Currently there should be no way we can hit this case.  It only
2916991554f2SKenneth D. Merry 	 * happens when we have a failure to allocate chain frames, and SMP
2917991554f2SKenneth D. Merry 	 * commands require two S/G elements only.  That should be handled
2918991554f2SKenneth D. Merry 	 * in the standard request size.
2919991554f2SKenneth D. Merry 	 */
2920991554f2SKenneth D. Merry 	if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
2921a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x on SMP "
2922a2c14879SStephen McConnell 		    "request!\n", __func__, cm->cm_flags);
2923a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR);
2924991554f2SKenneth D. Merry 		goto bailout;
2925991554f2SKenneth D. Merry         }
2926991554f2SKenneth D. Merry 
2927991554f2SKenneth D. Merry 	rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply;
2928991554f2SKenneth D. Merry 	if (rpl == NULL) {
2929991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s: NULL cm_reply!\n", __func__);
2930a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR);
2931991554f2SKenneth D. Merry 		goto bailout;
2932991554f2SKenneth D. Merry 	}
2933991554f2SKenneth D. Merry 
2934991554f2SKenneth D. Merry 	req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req;
2935991554f2SKenneth D. Merry 	sasaddr = le32toh(req->SASAddress.Low);
2936991554f2SKenneth D. Merry 	sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32;
2937991554f2SKenneth D. Merry 
2938991554f2SKenneth D. Merry 	if ((le16toh(rpl->IOCStatus) & MPI2_IOCSTATUS_MASK) !=
2939991554f2SKenneth D. Merry 	    MPI2_IOCSTATUS_SUCCESS ||
2940991554f2SKenneth D. Merry 	    rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) {
2941991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_XINFO, "%s: IOCStatus %04x SASStatus %02x\n",
2942991554f2SKenneth D. Merry 		    __func__, le16toh(rpl->IOCStatus), rpl->SASStatus);
2943a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR);
2944991554f2SKenneth D. Merry 		goto bailout;
2945991554f2SKenneth D. Merry 	}
2946991554f2SKenneth D. Merry 
2947a2c14879SStephen McConnell 	mpr_dprint(sc, MPR_XINFO, "%s: SMP request to SAS address %#jx "
2948a2c14879SStephen McConnell 	    "completed successfully\n", __func__, (uintmax_t)sasaddr);
2949991554f2SKenneth D. Merry 
2950991554f2SKenneth D. Merry 	if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED)
2951a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
2952991554f2SKenneth D. Merry 	else
2953a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_SMP_STATUS_ERROR);
2954991554f2SKenneth D. Merry 
2955991554f2SKenneth D. Merry bailout:
2956991554f2SKenneth D. Merry 	/*
2957991554f2SKenneth D. Merry 	 * We sync in both directions because we had DMAs in the S/G list
2958991554f2SKenneth D. Merry 	 * in both directions.
2959991554f2SKenneth D. Merry 	 */
2960991554f2SKenneth D. Merry 	bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap,
2961991554f2SKenneth D. Merry 			BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
2962991554f2SKenneth D. Merry 	bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap);
2963991554f2SKenneth D. Merry 	mpr_free_command(sc, cm);
2964991554f2SKenneth D. Merry 	xpt_done(ccb);
2965991554f2SKenneth D. Merry }
2966991554f2SKenneth D. Merry 
2967991554f2SKenneth D. Merry static void
29687a2a6a1aSStephen McConnell mprsas_send_smpcmd(struct mprsas_softc *sassc, union ccb *ccb, uint64_t sasaddr)
2969991554f2SKenneth D. Merry {
2970991554f2SKenneth D. Merry 	struct mpr_command *cm;
2971991554f2SKenneth D. Merry 	uint8_t *request, *response;
2972991554f2SKenneth D. Merry 	MPI2_SMP_PASSTHROUGH_REQUEST *req;
2973991554f2SKenneth D. Merry 	struct mpr_softc *sc;
2974991554f2SKenneth D. Merry 	struct sglist *sg;
2975991554f2SKenneth D. Merry 	int error;
2976991554f2SKenneth D. Merry 
2977991554f2SKenneth D. Merry 	sc = sassc->sc;
2978991554f2SKenneth D. Merry 	sg = NULL;
2979991554f2SKenneth D. Merry 	error = 0;
2980991554f2SKenneth D. Merry 
2981c503306dSKenneth D. Merry #if (__FreeBSD_version >= 1000028) || \
2982c503306dSKenneth D. Merry     ((__FreeBSD_version >= 902001) && (__FreeBSD_version < 1000000))
2983991554f2SKenneth D. Merry 	switch (ccb->ccb_h.flags & CAM_DATA_MASK) {
2984991554f2SKenneth D. Merry 	case CAM_DATA_PADDR:
2985991554f2SKenneth D. Merry 	case CAM_DATA_SG_PADDR:
2986991554f2SKenneth D. Merry 		/*
2987991554f2SKenneth D. Merry 		 * XXX We don't yet support physical addresses here.
2988991554f2SKenneth D. Merry 		 */
2989991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s: physical addresses not "
2990991554f2SKenneth D. Merry 		    "supported\n", __func__);
2991a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID);
2992991554f2SKenneth D. Merry 		xpt_done(ccb);
2993991554f2SKenneth D. Merry 		return;
2994991554f2SKenneth D. Merry 	case CAM_DATA_SG:
2995991554f2SKenneth D. Merry 		/*
2996991554f2SKenneth D. Merry 		 * The chip does not support more than one buffer for the
2997991554f2SKenneth D. Merry 		 * request or response.
2998991554f2SKenneth D. Merry 		 */
2999991554f2SKenneth D. Merry 		if ((ccb->smpio.smp_request_sglist_cnt > 1)
3000991554f2SKenneth D. Merry 		    || (ccb->smpio.smp_response_sglist_cnt > 1)) {
30017a2a6a1aSStephen McConnell 			mpr_dprint(sc, MPR_ERROR, "%s: multiple request or "
30027a2a6a1aSStephen McConnell 			    "response buffer segments not supported for SMP\n",
30037a2a6a1aSStephen McConnell 			    __func__);
3004a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID);
3005991554f2SKenneth D. Merry 			xpt_done(ccb);
3006991554f2SKenneth D. Merry 			return;
3007991554f2SKenneth D. Merry 		}
3008991554f2SKenneth D. Merry 
3009991554f2SKenneth D. Merry 		/*
3010991554f2SKenneth D. Merry 		 * The CAM_SCATTER_VALID flag was originally implemented
3011991554f2SKenneth D. Merry 		 * for the XPT_SCSI_IO CCB, which only has one data pointer.
3012991554f2SKenneth D. Merry 		 * We have two.  So, just take that flag to mean that we
3013991554f2SKenneth D. Merry 		 * might have S/G lists, and look at the S/G segment count
3014991554f2SKenneth D. Merry 		 * to figure out whether that is the case for each individual
3015991554f2SKenneth D. Merry 		 * buffer.
3016991554f2SKenneth D. Merry 		 */
3017991554f2SKenneth D. Merry 		if (ccb->smpio.smp_request_sglist_cnt != 0) {
3018991554f2SKenneth D. Merry 			bus_dma_segment_t *req_sg;
3019991554f2SKenneth D. Merry 
3020991554f2SKenneth D. Merry 			req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request;
3021991554f2SKenneth D. Merry 			request = (uint8_t *)(uintptr_t)req_sg[0].ds_addr;
3022991554f2SKenneth D. Merry 		} else
3023991554f2SKenneth D. Merry 			request = ccb->smpio.smp_request;
3024991554f2SKenneth D. Merry 
3025991554f2SKenneth D. Merry 		if (ccb->smpio.smp_response_sglist_cnt != 0) {
3026991554f2SKenneth D. Merry 			bus_dma_segment_t *rsp_sg;
3027991554f2SKenneth D. Merry 
3028991554f2SKenneth D. Merry 			rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response;
3029991554f2SKenneth D. Merry 			response = (uint8_t *)(uintptr_t)rsp_sg[0].ds_addr;
3030991554f2SKenneth D. Merry 		} else
3031991554f2SKenneth D. Merry 			response = ccb->smpio.smp_response;
3032991554f2SKenneth D. Merry 		break;
3033991554f2SKenneth D. Merry 	case CAM_DATA_VADDR:
3034991554f2SKenneth D. Merry 		request = ccb->smpio.smp_request;
3035991554f2SKenneth D. Merry 		response = ccb->smpio.smp_response;
3036991554f2SKenneth D. Merry 		break;
3037991554f2SKenneth D. Merry 	default:
3038a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID);
3039991554f2SKenneth D. Merry 		xpt_done(ccb);
3040991554f2SKenneth D. Merry 		return;
3041991554f2SKenneth D. Merry 	}
3042c503306dSKenneth D. Merry #else /* __FreeBSD_version < 1000028 */
3043991554f2SKenneth D. Merry 	/*
3044991554f2SKenneth D. Merry 	 * XXX We don't yet support physical addresses here.
3045991554f2SKenneth D. Merry 	 */
3046991554f2SKenneth D. Merry 	if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) {
3047a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "%s: physical addresses not "
3048a2c14879SStephen McConnell 		    "supported\n", __func__);
3049a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID);
3050991554f2SKenneth D. Merry 		xpt_done(ccb);
3051991554f2SKenneth D. Merry 		return;
3052991554f2SKenneth D. Merry 	}
3053991554f2SKenneth D. Merry 
3054991554f2SKenneth D. Merry 	/*
3055991554f2SKenneth D. Merry 	 * If the user wants to send an S/G list, check to make sure they
3056991554f2SKenneth D. Merry 	 * have single buffers.
3057991554f2SKenneth D. Merry 	 */
3058991554f2SKenneth D. Merry 	if (ccb->ccb_h.flags & CAM_SCATTER_VALID) {
3059991554f2SKenneth D. Merry 		/*
3060991554f2SKenneth D. Merry 		 * The chip does not support more than one buffer for the
3061991554f2SKenneth D. Merry 		 * request or response.
3062991554f2SKenneth D. Merry 		 */
3063991554f2SKenneth D. Merry 	 	if ((ccb->smpio.smp_request_sglist_cnt > 1)
3064991554f2SKenneth D. Merry 		  || (ccb->smpio.smp_response_sglist_cnt > 1)) {
3065991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_ERROR, "%s: multiple request or "
3066991554f2SKenneth D. Merry 			    "response buffer segments not supported for SMP\n",
3067991554f2SKenneth D. Merry 			    __func__);
3068a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID);
3069991554f2SKenneth D. Merry 			xpt_done(ccb);
3070991554f2SKenneth D. Merry 			return;
3071991554f2SKenneth D. Merry 		}
3072991554f2SKenneth D. Merry 
3073991554f2SKenneth D. Merry 		/*
3074991554f2SKenneth D. Merry 		 * The CAM_SCATTER_VALID flag was originally implemented
3075991554f2SKenneth D. Merry 		 * for the XPT_SCSI_IO CCB, which only has one data pointer.
3076991554f2SKenneth D. Merry 		 * We have two.  So, just take that flag to mean that we
3077991554f2SKenneth D. Merry 		 * might have S/G lists, and look at the S/G segment count
3078991554f2SKenneth D. Merry 		 * to figure out whether that is the case for each individual
3079991554f2SKenneth D. Merry 		 * buffer.
3080991554f2SKenneth D. Merry 		 */
3081991554f2SKenneth D. Merry 		if (ccb->smpio.smp_request_sglist_cnt != 0) {
3082991554f2SKenneth D. Merry 			bus_dma_segment_t *req_sg;
3083991554f2SKenneth D. Merry 
3084991554f2SKenneth D. Merry 			req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request;
3085c503306dSKenneth D. Merry 			request = (uint8_t *)(uintptr_t)req_sg[0].ds_addr;
3086991554f2SKenneth D. Merry 		} else
3087991554f2SKenneth D. Merry 			request = ccb->smpio.smp_request;
3088991554f2SKenneth D. Merry 
3089991554f2SKenneth D. Merry 		if (ccb->smpio.smp_response_sglist_cnt != 0) {
3090991554f2SKenneth D. Merry 			bus_dma_segment_t *rsp_sg;
3091991554f2SKenneth D. Merry 
3092991554f2SKenneth D. Merry 			rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response;
3093c503306dSKenneth D. Merry 			response = (uint8_t *)(uintptr_t)rsp_sg[0].ds_addr;
3094991554f2SKenneth D. Merry 		} else
3095991554f2SKenneth D. Merry 			response = ccb->smpio.smp_response;
3096991554f2SKenneth D. Merry 	} else {
3097991554f2SKenneth D. Merry 		request = ccb->smpio.smp_request;
3098991554f2SKenneth D. Merry 		response = ccb->smpio.smp_response;
3099991554f2SKenneth D. Merry 	}
3100c503306dSKenneth D. Merry #endif /* __FreeBSD_version < 1000028 */
3101991554f2SKenneth D. Merry 
3102991554f2SKenneth D. Merry 	cm = mpr_alloc_command(sc);
3103991554f2SKenneth D. Merry 	if (cm == NULL) {
31047a2a6a1aSStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "%s: cannot allocate command\n",
31057a2a6a1aSStephen McConnell 		    __func__);
3106a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL);
3107991554f2SKenneth D. Merry 		xpt_done(ccb);
3108991554f2SKenneth D. Merry 		return;
3109991554f2SKenneth D. Merry 	}
3110991554f2SKenneth D. Merry 
3111991554f2SKenneth D. Merry 	req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req;
3112991554f2SKenneth D. Merry 	bzero(req, sizeof(*req));
3113991554f2SKenneth D. Merry 	req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH;
3114991554f2SKenneth D. Merry 
3115991554f2SKenneth D. Merry 	/* Allow the chip to use any route to this SAS address. */
3116991554f2SKenneth D. Merry 	req->PhysicalPort = 0xff;
3117991554f2SKenneth D. Merry 
3118991554f2SKenneth D. Merry 	req->RequestDataLength = htole16(ccb->smpio.smp_request_len);
3119991554f2SKenneth D. Merry 	req->SGLFlags =
3120991554f2SKenneth D. Merry 	    MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI;
3121991554f2SKenneth D. Merry 
3122991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO, "%s: sending SMP request to SAS address "
3123991554f2SKenneth D. Merry 	    "%#jx\n", __func__, (uintmax_t)sasaddr);
3124991554f2SKenneth D. Merry 
3125991554f2SKenneth D. Merry 	mpr_init_sge(cm, req, &req->SGL);
3126991554f2SKenneth D. Merry 
3127991554f2SKenneth D. Merry 	/*
3128991554f2SKenneth D. Merry 	 * Set up a uio to pass into mpr_map_command().  This allows us to
3129991554f2SKenneth D. Merry 	 * do one map command, and one busdma call in there.
3130991554f2SKenneth D. Merry 	 */
3131991554f2SKenneth D. Merry 	cm->cm_uio.uio_iov = cm->cm_iovec;
3132991554f2SKenneth D. Merry 	cm->cm_uio.uio_iovcnt = 2;
3133991554f2SKenneth D. Merry 	cm->cm_uio.uio_segflg = UIO_SYSSPACE;
3134991554f2SKenneth D. Merry 
3135991554f2SKenneth D. Merry 	/*
3136991554f2SKenneth D. Merry 	 * The read/write flag isn't used by busdma, but set it just in
3137991554f2SKenneth D. Merry 	 * case.  This isn't exactly accurate, either, since we're going in
3138991554f2SKenneth D. Merry 	 * both directions.
3139991554f2SKenneth D. Merry 	 */
3140991554f2SKenneth D. Merry 	cm->cm_uio.uio_rw = UIO_WRITE;
3141991554f2SKenneth D. Merry 
3142991554f2SKenneth D. Merry 	cm->cm_iovec[0].iov_base = request;
3143991554f2SKenneth D. Merry 	cm->cm_iovec[0].iov_len = le16toh(req->RequestDataLength);
3144991554f2SKenneth D. Merry 	cm->cm_iovec[1].iov_base = response;
3145991554f2SKenneth D. Merry 	cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len;
3146991554f2SKenneth D. Merry 
3147991554f2SKenneth D. Merry 	cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len +
3148991554f2SKenneth D. Merry 			       cm->cm_iovec[1].iov_len;
3149991554f2SKenneth D. Merry 
3150991554f2SKenneth D. Merry 	/*
3151991554f2SKenneth D. Merry 	 * Trigger a warning message in mpr_data_cb() for the user if we
3152991554f2SKenneth D. Merry 	 * wind up exceeding two S/G segments.  The chip expects one
3153991554f2SKenneth D. Merry 	 * segment for the request and another for the response.
3154991554f2SKenneth D. Merry 	 */
3155991554f2SKenneth D. Merry 	cm->cm_max_segs = 2;
3156991554f2SKenneth D. Merry 
3157991554f2SKenneth D. Merry 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
3158991554f2SKenneth D. Merry 	cm->cm_complete = mprsas_smpio_complete;
3159991554f2SKenneth D. Merry 	cm->cm_complete_data = ccb;
3160991554f2SKenneth D. Merry 
3161991554f2SKenneth D. Merry 	/*
3162991554f2SKenneth D. Merry 	 * Tell the mapping code that we're using a uio, and that this is
3163991554f2SKenneth D. Merry 	 * an SMP passthrough request.  There is a little special-case
3164991554f2SKenneth D. Merry 	 * logic there (in mpr_data_cb()) to handle the bidirectional
3165991554f2SKenneth D. Merry 	 * transfer.
3166991554f2SKenneth D. Merry 	 */
3167991554f2SKenneth D. Merry 	cm->cm_flags |= MPR_CM_FLAGS_USE_UIO | MPR_CM_FLAGS_SMP_PASS |
3168991554f2SKenneth D. Merry 			MPR_CM_FLAGS_DATAIN | MPR_CM_FLAGS_DATAOUT;
3169991554f2SKenneth D. Merry 
3170991554f2SKenneth D. Merry 	/* The chip data format is little endian. */
3171991554f2SKenneth D. Merry 	req->SASAddress.High = htole32(sasaddr >> 32);
3172991554f2SKenneth D. Merry 	req->SASAddress.Low = htole32(sasaddr);
3173991554f2SKenneth D. Merry 
3174991554f2SKenneth D. Merry 	/*
3175991554f2SKenneth D. Merry 	 * XXX Note that we don't have a timeout/abort mechanism here.
3176991554f2SKenneth D. Merry 	 * From the manual, it looks like task management requests only
3177991554f2SKenneth D. Merry 	 * work for SCSI IO and SATA passthrough requests.  We may need to
3178991554f2SKenneth D. Merry 	 * have a mechanism to retry requests in the event of a chip reset
3179991554f2SKenneth D. Merry 	 * at least.  Hopefully the chip will insure that any errors short
3180991554f2SKenneth D. Merry 	 * of that are relayed back to the driver.
3181991554f2SKenneth D. Merry 	 */
3182991554f2SKenneth D. Merry 	error = mpr_map_command(sc, cm);
3183991554f2SKenneth D. Merry 	if ((error != 0) && (error != EINPROGRESS)) {
3184991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s: error %d returned from "
3185991554f2SKenneth D. Merry 		    "mpr_map_command()\n", __func__, error);
3186991554f2SKenneth D. Merry 		goto bailout_error;
3187991554f2SKenneth D. Merry 	}
3188991554f2SKenneth D. Merry 
3189991554f2SKenneth D. Merry 	return;
3190991554f2SKenneth D. Merry 
3191991554f2SKenneth D. Merry bailout_error:
3192991554f2SKenneth D. Merry 	mpr_free_command(sc, cm);
3193a2c14879SStephen McConnell 	mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL);
3194991554f2SKenneth D. Merry 	xpt_done(ccb);
3195991554f2SKenneth D. Merry 	return;
3196991554f2SKenneth D. Merry }
3197991554f2SKenneth D. Merry 
3198991554f2SKenneth D. Merry static void
3199991554f2SKenneth D. Merry mprsas_action_smpio(struct mprsas_softc *sassc, union ccb *ccb)
3200991554f2SKenneth D. Merry {
3201991554f2SKenneth D. Merry 	struct mpr_softc *sc;
3202991554f2SKenneth D. Merry 	struct mprsas_target *targ;
3203991554f2SKenneth D. Merry 	uint64_t sasaddr = 0;
3204991554f2SKenneth D. Merry 
3205991554f2SKenneth D. Merry 	sc = sassc->sc;
3206991554f2SKenneth D. Merry 
3207991554f2SKenneth D. Merry 	/*
3208991554f2SKenneth D. Merry 	 * Make sure the target exists.
3209991554f2SKenneth D. Merry 	 */
3210991554f2SKenneth D. Merry 	KASSERT(ccb->ccb_h.target_id < sassc->maxtargets,
3211991554f2SKenneth D. Merry 	    ("Target %d out of bounds in XPT_SMP_IO\n", ccb->ccb_h.target_id));
3212991554f2SKenneth D. Merry 	targ = &sassc->targets[ccb->ccb_h.target_id];
3213991554f2SKenneth D. Merry 	if (targ->handle == 0x0) {
3214991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s: target %d does not exist!\n",
3215991554f2SKenneth D. Merry 		    __func__, ccb->ccb_h.target_id);
3216a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_SEL_TIMEOUT);
3217991554f2SKenneth D. Merry 		xpt_done(ccb);
3218991554f2SKenneth D. Merry 		return;
3219991554f2SKenneth D. Merry 	}
3220991554f2SKenneth D. Merry 
3221991554f2SKenneth D. Merry 	/*
3222991554f2SKenneth D. Merry 	 * If this device has an embedded SMP target, we'll talk to it
3223991554f2SKenneth D. Merry 	 * directly.
3224991554f2SKenneth D. Merry 	 * figure out what the expander's address is.
3225991554f2SKenneth D. Merry 	 */
3226991554f2SKenneth D. Merry 	if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0)
3227991554f2SKenneth D. Merry 		sasaddr = targ->sasaddr;
3228991554f2SKenneth D. Merry 
3229991554f2SKenneth D. Merry 	/*
3230991554f2SKenneth D. Merry 	 * If we don't have a SAS address for the expander yet, try
3231991554f2SKenneth D. Merry 	 * grabbing it from the page 0x83 information cached in the
3232991554f2SKenneth D. Merry 	 * transport layer for this target.  LSI expanders report the
3233991554f2SKenneth D. Merry 	 * expander SAS address as the port-associated SAS address in
3234991554f2SKenneth D. Merry 	 * Inquiry VPD page 0x83.  Maxim expanders don't report it in page
3235991554f2SKenneth D. Merry 	 * 0x83.
3236991554f2SKenneth D. Merry 	 *
3237991554f2SKenneth D. Merry 	 * XXX KDM disable this for now, but leave it commented out so that
3238991554f2SKenneth D. Merry 	 * it is obvious that this is another possible way to get the SAS
3239991554f2SKenneth D. Merry 	 * address.
3240991554f2SKenneth D. Merry 	 *
3241991554f2SKenneth D. Merry 	 * The parent handle method below is a little more reliable, and
3242991554f2SKenneth D. Merry 	 * the other benefit is that it works for devices other than SES
3243991554f2SKenneth D. Merry 	 * devices.  So you can send a SMP request to a da(4) device and it
3244991554f2SKenneth D. Merry 	 * will get routed to the expander that device is attached to.
3245991554f2SKenneth D. Merry 	 * (Assuming the da(4) device doesn't contain an SMP target...)
3246991554f2SKenneth D. Merry 	 */
3247991554f2SKenneth D. Merry #if 0
3248991554f2SKenneth D. Merry 	if (sasaddr == 0)
3249991554f2SKenneth D. Merry 		sasaddr = xpt_path_sas_addr(ccb->ccb_h.path);
3250991554f2SKenneth D. Merry #endif
3251991554f2SKenneth D. Merry 
3252991554f2SKenneth D. Merry 	/*
3253991554f2SKenneth D. Merry 	 * If we still don't have a SAS address for the expander, look for
3254991554f2SKenneth D. Merry 	 * the parent device of this device, which is probably the expander.
3255991554f2SKenneth D. Merry 	 */
3256991554f2SKenneth D. Merry 	if (sasaddr == 0) {
3257991554f2SKenneth D. Merry #ifdef OLD_MPR_PROBE
3258991554f2SKenneth D. Merry 		struct mprsas_target *parent_target;
3259991554f2SKenneth D. Merry #endif
3260991554f2SKenneth D. Merry 
3261991554f2SKenneth D. Merry 		if (targ->parent_handle == 0x0) {
3262991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_ERROR, "%s: handle %d does not have "
3263991554f2SKenneth D. Merry 			    "a valid parent handle!\n", __func__, targ->handle);
3264a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
3265991554f2SKenneth D. Merry 			goto bailout;
3266991554f2SKenneth D. Merry 		}
3267991554f2SKenneth D. Merry #ifdef OLD_MPR_PROBE
3268991554f2SKenneth D. Merry 		parent_target = mprsas_find_target_by_handle(sassc, 0,
3269991554f2SKenneth D. Merry 		    targ->parent_handle);
3270991554f2SKenneth D. Merry 
3271991554f2SKenneth D. Merry 		if (parent_target == NULL) {
3272991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_ERROR, "%s: handle %d does not have "
3273991554f2SKenneth D. Merry 			    "a valid parent target!\n", __func__, targ->handle);
3274a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
3275991554f2SKenneth D. Merry 			goto bailout;
3276991554f2SKenneth D. Merry 		}
3277991554f2SKenneth D. Merry 
3278991554f2SKenneth D. Merry 		if ((parent_target->devinfo &
3279991554f2SKenneth D. Merry 		     MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) {
3280991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent %d "
3281991554f2SKenneth D. Merry 			    "does not have an SMP target!\n", __func__,
3282991554f2SKenneth D. Merry 			    targ->handle, parent_target->handle);
3283a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
3284991554f2SKenneth D. Merry 			goto bailout;
3285991554f2SKenneth D. Merry 		}
3286991554f2SKenneth D. Merry 
3287991554f2SKenneth D. Merry 		sasaddr = parent_target->sasaddr;
3288991554f2SKenneth D. Merry #else /* OLD_MPR_PROBE */
3289991554f2SKenneth D. Merry 		if ((targ->parent_devinfo &
3290991554f2SKenneth D. Merry 		     MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) {
3291991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent %d "
3292991554f2SKenneth D. Merry 			    "does not have an SMP target!\n", __func__,
3293991554f2SKenneth D. Merry 			    targ->handle, targ->parent_handle);
3294a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
3295991554f2SKenneth D. Merry 			goto bailout;
3296991554f2SKenneth D. Merry 
3297991554f2SKenneth D. Merry 		}
3298991554f2SKenneth D. Merry 		if (targ->parent_sasaddr == 0x0) {
3299991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent handle "
3300991554f2SKenneth D. Merry 			    "%d does not have a valid SAS address!\n", __func__,
3301991554f2SKenneth D. Merry 			    targ->handle, targ->parent_handle);
3302a2c14879SStephen McConnell 			mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
3303991554f2SKenneth D. Merry 			goto bailout;
3304991554f2SKenneth D. Merry 		}
3305991554f2SKenneth D. Merry 
3306991554f2SKenneth D. Merry 		sasaddr = targ->parent_sasaddr;
3307991554f2SKenneth D. Merry #endif /* OLD_MPR_PROBE */
3308991554f2SKenneth D. Merry 
3309991554f2SKenneth D. Merry 	}
3310991554f2SKenneth D. Merry 
3311991554f2SKenneth D. Merry 	if (sasaddr == 0) {
3312991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_INFO, "%s: unable to find SAS address for "
3313991554f2SKenneth D. Merry 		    "handle %d\n", __func__, targ->handle);
3314a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE);
3315991554f2SKenneth D. Merry 		goto bailout;
3316991554f2SKenneth D. Merry 	}
3317991554f2SKenneth D. Merry 	mprsas_send_smpcmd(sassc, ccb, sasaddr);
3318991554f2SKenneth D. Merry 
3319991554f2SKenneth D. Merry 	return;
3320991554f2SKenneth D. Merry 
3321991554f2SKenneth D. Merry bailout:
3322991554f2SKenneth D. Merry 	xpt_done(ccb);
3323991554f2SKenneth D. Merry 
3324991554f2SKenneth D. Merry }
3325991554f2SKenneth D. Merry #endif //__FreeBSD_version >= 900026
3326991554f2SKenneth D. Merry 
3327991554f2SKenneth D. Merry static void
3328991554f2SKenneth D. Merry mprsas_action_resetdev(struct mprsas_softc *sassc, union ccb *ccb)
3329991554f2SKenneth D. Merry {
3330991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REQUEST *req;
3331991554f2SKenneth D. Merry 	struct mpr_softc *sc;
3332991554f2SKenneth D. Merry 	struct mpr_command *tm;
3333991554f2SKenneth D. Merry 	struct mprsas_target *targ;
3334991554f2SKenneth D. Merry 
3335991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sassc->sc);
3336991554f2SKenneth D. Merry 	mtx_assert(&sassc->sc->mpr_mtx, MA_OWNED);
3337991554f2SKenneth D. Merry 
33387a2a6a1aSStephen McConnell 	KASSERT(ccb->ccb_h.target_id < sassc->maxtargets, ("Target %d out of "
33397a2a6a1aSStephen McConnell 	    "bounds in XPT_RESET_DEV\n", ccb->ccb_h.target_id));
3340991554f2SKenneth D. Merry 	sc = sassc->sc;
33413921a9f7SScott Long 	tm = mprsas_alloc_tm(sc);
3342991554f2SKenneth D. Merry 	if (tm == NULL) {
33437a2a6a1aSStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "command alloc failure in "
33447a2a6a1aSStephen McConnell 		    "mprsas_action_resetdev\n");
3345a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL);
3346991554f2SKenneth D. Merry 		xpt_done(ccb);
3347991554f2SKenneth D. Merry 		return;
3348991554f2SKenneth D. Merry 	}
3349991554f2SKenneth D. Merry 
3350991554f2SKenneth D. Merry 	targ = &sassc->targets[ccb->ccb_h.target_id];
3351991554f2SKenneth D. Merry 	req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
3352991554f2SKenneth D. Merry 	req->DevHandle = htole16(targ->handle);
3353991554f2SKenneth D. Merry 	req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET;
3354991554f2SKenneth D. Merry 
3355*89d1c21fSKashyap D Desai 	if (!targ->is_nvme || sc->custom_nvme_tm_handling) {
3356991554f2SKenneth D. Merry 		/* SAS Hard Link Reset / SATA Link Reset */
3357991554f2SKenneth D. Merry 		req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET;
3358*89d1c21fSKashyap D Desai 	} else {
3359*89d1c21fSKashyap D Desai 		/* PCIe Protocol Level Reset*/
3360*89d1c21fSKashyap D Desai 		req->MsgFlags =
3361*89d1c21fSKashyap D Desai 		    MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE;
3362*89d1c21fSKashyap D Desai 	}
3363991554f2SKenneth D. Merry 
3364991554f2SKenneth D. Merry 	tm->cm_data = NULL;
3365991554f2SKenneth D. Merry 	tm->cm_complete = mprsas_resetdev_complete;
3366991554f2SKenneth D. Merry 	tm->cm_complete_data = ccb;
3367a2c14879SStephen McConnell 
3368a2c14879SStephen McConnell 	mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n",
3369a2c14879SStephen McConnell 	    __func__, targ->tid);
3370991554f2SKenneth D. Merry 	tm->cm_targ = targ;
3371a2c14879SStephen McConnell 
337246b9415fSScott Long 	mprsas_prepare_for_tm(sc, tm, targ, CAM_LUN_WILDCARD);
3373991554f2SKenneth D. Merry 	mpr_map_command(sc, tm);
3374991554f2SKenneth D. Merry }
3375991554f2SKenneth D. Merry 
3376991554f2SKenneth D. Merry static void
3377991554f2SKenneth D. Merry mprsas_resetdev_complete(struct mpr_softc *sc, struct mpr_command *tm)
3378991554f2SKenneth D. Merry {
3379991554f2SKenneth D. Merry 	MPI2_SCSI_TASK_MANAGE_REPLY *resp;
3380991554f2SKenneth D. Merry 	union ccb *ccb;
3381991554f2SKenneth D. Merry 
3382991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
3383991554f2SKenneth D. Merry 	mtx_assert(&sc->mpr_mtx, MA_OWNED);
3384991554f2SKenneth D. Merry 
3385991554f2SKenneth D. Merry 	resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply;
3386991554f2SKenneth D. Merry 	ccb = tm->cm_complete_data;
3387991554f2SKenneth D. Merry 
3388991554f2SKenneth D. Merry 	/*
3389991554f2SKenneth D. Merry 	 * Currently there should be no way we can hit this case.  It only
3390991554f2SKenneth D. Merry 	 * happens when we have a failure to allocate chain frames, and
3391991554f2SKenneth D. Merry 	 * task management commands don't have S/G lists.
3392991554f2SKenneth D. Merry 	 */
3393991554f2SKenneth D. Merry 	if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
3394991554f2SKenneth D. Merry 		MPI2_SCSI_TASK_MANAGE_REQUEST *req;
3395991554f2SKenneth D. Merry 
3396991554f2SKenneth D. Merry 		req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req;
3397991554f2SKenneth D. Merry 
3398991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for reset of "
3399991554f2SKenneth D. Merry 		    "handle %#04x! This should not happen!\n", __func__,
3400991554f2SKenneth D. Merry 		    tm->cm_flags, req->DevHandle);
3401a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR);
3402991554f2SKenneth D. Merry 		goto bailout;
3403991554f2SKenneth D. Merry 	}
3404991554f2SKenneth D. Merry 
34057a2a6a1aSStephen McConnell 	mpr_dprint(sc, MPR_XINFO, "%s: IOCStatus = 0x%x ResponseCode = 0x%x\n",
34067a2a6a1aSStephen McConnell 	    __func__, le16toh(resp->IOCStatus), le32toh(resp->ResponseCode));
3407991554f2SKenneth D. Merry 
3408991554f2SKenneth D. Merry 	if (le32toh(resp->ResponseCode) == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) {
3409a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP);
3410991554f2SKenneth D. Merry 		mprsas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid,
3411991554f2SKenneth D. Merry 		    CAM_LUN_WILDCARD);
3412991554f2SKenneth D. Merry 	}
3413991554f2SKenneth D. Merry 	else
3414a2c14879SStephen McConnell 		mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR);
3415991554f2SKenneth D. Merry 
3416991554f2SKenneth D. Merry bailout:
3417991554f2SKenneth D. Merry 
3418991554f2SKenneth D. Merry 	mprsas_free_tm(sc, tm);
3419991554f2SKenneth D. Merry 	xpt_done(ccb);
3420991554f2SKenneth D. Merry }
3421991554f2SKenneth D. Merry 
3422991554f2SKenneth D. Merry static void
3423991554f2SKenneth D. Merry mprsas_poll(struct cam_sim *sim)
3424991554f2SKenneth D. Merry {
3425991554f2SKenneth D. Merry 	struct mprsas_softc *sassc;
3426991554f2SKenneth D. Merry 
3427991554f2SKenneth D. Merry 	sassc = cam_sim_softc(sim);
3428991554f2SKenneth D. Merry 
3429991554f2SKenneth D. Merry 	if (sassc->sc->mpr_debug & MPR_TRACE) {
3430991554f2SKenneth D. Merry 		/* frequent debug messages during a panic just slow
3431991554f2SKenneth D. Merry 		 * everything down too much.
3432991554f2SKenneth D. Merry 		 */
3433a2c14879SStephen McConnell 		mpr_dprint(sassc->sc, MPR_XINFO, "%s clearing MPR_TRACE\n",
3434a2c14879SStephen McConnell 		    __func__);
3435991554f2SKenneth D. Merry 		sassc->sc->mpr_debug &= ~MPR_TRACE;
3436991554f2SKenneth D. Merry 	}
3437991554f2SKenneth D. Merry 
3438991554f2SKenneth D. Merry 	mpr_intr_locked(sassc->sc);
3439991554f2SKenneth D. Merry }
3440991554f2SKenneth D. Merry 
3441991554f2SKenneth D. Merry static void
3442991554f2SKenneth D. Merry mprsas_async(void *callback_arg, uint32_t code, struct cam_path *path,
3443991554f2SKenneth D. Merry     void *arg)
3444991554f2SKenneth D. Merry {
3445991554f2SKenneth D. Merry 	struct mpr_softc *sc;
3446991554f2SKenneth D. Merry 
3447991554f2SKenneth D. Merry 	sc = (struct mpr_softc *)callback_arg;
3448991554f2SKenneth D. Merry 
3449991554f2SKenneth D. Merry 	switch (code) {
3450991554f2SKenneth D. Merry #if (__FreeBSD_version >= 1000006) || \
3451991554f2SKenneth D. Merry     ((__FreeBSD_version >= 901503) && (__FreeBSD_version < 1000000))
3452991554f2SKenneth D. Merry 	case AC_ADVINFO_CHANGED: {
3453991554f2SKenneth D. Merry 		struct mprsas_target *target;
3454991554f2SKenneth D. Merry 		struct mprsas_softc *sassc;
3455991554f2SKenneth D. Merry 		struct scsi_read_capacity_data_long rcap_buf;
3456991554f2SKenneth D. Merry 		struct ccb_dev_advinfo cdai;
3457991554f2SKenneth D. Merry 		struct mprsas_lun *lun;
3458991554f2SKenneth D. Merry 		lun_id_t lunid;
3459991554f2SKenneth D. Merry 		int found_lun;
3460991554f2SKenneth D. Merry 		uintptr_t buftype;
3461991554f2SKenneth D. Merry 
3462991554f2SKenneth D. Merry 		buftype = (uintptr_t)arg;
3463991554f2SKenneth D. Merry 
3464991554f2SKenneth D. Merry 		found_lun = 0;
3465991554f2SKenneth D. Merry 		sassc = sc->sassc;
3466991554f2SKenneth D. Merry 
3467991554f2SKenneth D. Merry 		/*
3468991554f2SKenneth D. Merry 		 * We're only interested in read capacity data changes.
3469991554f2SKenneth D. Merry 		 */
3470991554f2SKenneth D. Merry 		if (buftype != CDAI_TYPE_RCAPLONG)
3471991554f2SKenneth D. Merry 			break;
3472991554f2SKenneth D. Merry 
3473991554f2SKenneth D. Merry 		/*
347407aa4de1SKenneth D. Merry 		 * See the comment in mpr_attach_sas() for a detailed
347507aa4de1SKenneth D. Merry 		 * explanation.  In these versions of FreeBSD we register
347607aa4de1SKenneth D. Merry 		 * for all events and filter out the events that don't
347707aa4de1SKenneth D. Merry 		 * apply to us.
347807aa4de1SKenneth D. Merry 		 */
347907aa4de1SKenneth D. Merry #if (__FreeBSD_version < 1000703) || \
348007aa4de1SKenneth D. Merry     ((__FreeBSD_version >= 1100000) && (__FreeBSD_version < 1100002))
348107aa4de1SKenneth D. Merry 		if (xpt_path_path_id(path) != sassc->sim->path_id)
348207aa4de1SKenneth D. Merry 			break;
348307aa4de1SKenneth D. Merry #endif
348407aa4de1SKenneth D. Merry 
348507aa4de1SKenneth D. Merry 		/*
3486991554f2SKenneth D. Merry 		 * We should have a handle for this, but check to make sure.
3487991554f2SKenneth D. Merry 		 */
3488991554f2SKenneth D. Merry 		KASSERT(xpt_path_target_id(path) < sassc->maxtargets,
3489991554f2SKenneth D. Merry 		    ("Target %d out of bounds in mprsas_async\n",
3490991554f2SKenneth D. Merry 		    xpt_path_target_id(path)));
3491991554f2SKenneth D. Merry 		target = &sassc->targets[xpt_path_target_id(path)];
3492991554f2SKenneth D. Merry 		if (target->handle == 0)
3493991554f2SKenneth D. Merry 			break;
3494991554f2SKenneth D. Merry 
3495991554f2SKenneth D. Merry 		lunid = xpt_path_lun_id(path);
3496991554f2SKenneth D. Merry 
3497991554f2SKenneth D. Merry 		SLIST_FOREACH(lun, &target->luns, lun_link) {
3498991554f2SKenneth D. Merry 			if (lun->lun_id == lunid) {
3499991554f2SKenneth D. Merry 				found_lun = 1;
3500991554f2SKenneth D. Merry 				break;
3501991554f2SKenneth D. Merry 			}
3502991554f2SKenneth D. Merry 		}
3503991554f2SKenneth D. Merry 
3504991554f2SKenneth D. Merry 		if (found_lun == 0) {
3505991554f2SKenneth D. Merry 			lun = malloc(sizeof(struct mprsas_lun), M_MPR,
3506991554f2SKenneth D. Merry 			    M_NOWAIT | M_ZERO);
3507991554f2SKenneth D. Merry 			if (lun == NULL) {
3508991554f2SKenneth D. Merry 				mpr_dprint(sc, MPR_ERROR, "Unable to alloc "
3509991554f2SKenneth D. Merry 				    "LUN for EEDP support.\n");
3510991554f2SKenneth D. Merry 				break;
3511991554f2SKenneth D. Merry 			}
3512991554f2SKenneth D. Merry 			lun->lun_id = lunid;
3513991554f2SKenneth D. Merry 			SLIST_INSERT_HEAD(&target->luns, lun, lun_link);
3514991554f2SKenneth D. Merry 		}
3515991554f2SKenneth D. Merry 
3516991554f2SKenneth D. Merry 		bzero(&rcap_buf, sizeof(rcap_buf));
3517991554f2SKenneth D. Merry 		xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL);
3518991554f2SKenneth D. Merry 		cdai.ccb_h.func_code = XPT_DEV_ADVINFO;
3519991554f2SKenneth D. Merry 		cdai.ccb_h.flags = CAM_DIR_IN;
3520991554f2SKenneth D. Merry 		cdai.buftype = CDAI_TYPE_RCAPLONG;
35216c0541faSKenneth D. Merry #if (__FreeBSD_version >= 1100061) || \
35226c0541faSKenneth D. Merry     ((__FreeBSD_version >= 1001510) && (__FreeBSD_version < 1100000))
3523e8577fb4SKenneth D. Merry 		cdai.flags = CDAI_FLAG_NONE;
3524e8577fb4SKenneth D. Merry #else
3525991554f2SKenneth D. Merry 		cdai.flags = 0;
3526e8577fb4SKenneth D. Merry #endif
3527991554f2SKenneth D. Merry 		cdai.bufsiz = sizeof(rcap_buf);
3528991554f2SKenneth D. Merry 		cdai.buf = (uint8_t *)&rcap_buf;
3529991554f2SKenneth D. Merry 		xpt_action((union ccb *)&cdai);
3530991554f2SKenneth D. Merry 		if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0)
3531991554f2SKenneth D. Merry 			cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE);
3532991554f2SKenneth D. Merry 
3533a2c14879SStephen McConnell 		if ((mprsas_get_ccbstatus((union ccb *)&cdai) == CAM_REQ_CMP)
3534991554f2SKenneth D. Merry 		    && (rcap_buf.prot & SRC16_PROT_EN)) {
35358881681bSKenneth D. Merry 			switch (rcap_buf.prot & SRC16_P_TYPE) {
35368881681bSKenneth D. Merry 			case SRC16_PTYPE_1:
35378881681bSKenneth D. Merry 			case SRC16_PTYPE_3:
3538991554f2SKenneth D. Merry 				lun->eedp_formatted = TRUE;
35398881681bSKenneth D. Merry 				lun->eedp_block_size =
35408881681bSKenneth D. Merry 				    scsi_4btoul(rcap_buf.length);
35418881681bSKenneth D. Merry 				break;
35428881681bSKenneth D. Merry 			case SRC16_PTYPE_2:
35438881681bSKenneth D. Merry 			default:
35448881681bSKenneth D. Merry 				lun->eedp_formatted = FALSE;
35458881681bSKenneth D. Merry 				lun->eedp_block_size = 0;
35468881681bSKenneth D. Merry 				break;
35478881681bSKenneth D. Merry 			}
3548991554f2SKenneth D. Merry 		} else {
3549991554f2SKenneth D. Merry 			lun->eedp_formatted = FALSE;
3550991554f2SKenneth D. Merry 			lun->eedp_block_size = 0;
3551991554f2SKenneth D. Merry 		}
3552991554f2SKenneth D. Merry 		break;
3553991554f2SKenneth D. Merry 	}
3554991554f2SKenneth D. Merry #endif
3555991554f2SKenneth D. Merry 	case AC_FOUND_DEVICE: {
3556991554f2SKenneth D. Merry 		struct ccb_getdev *cgd;
3557991554f2SKenneth D. Merry 
355807aa4de1SKenneth D. Merry 		/*
355907aa4de1SKenneth D. Merry 		 * See the comment in mpr_attach_sas() for a detailed
356007aa4de1SKenneth D. Merry 		 * explanation.  In these versions of FreeBSD we register
356107aa4de1SKenneth D. Merry 		 * for all events and filter out the events that don't
356207aa4de1SKenneth D. Merry 		 * apply to us.
356307aa4de1SKenneth D. Merry 		 */
356407aa4de1SKenneth D. Merry #if (__FreeBSD_version < 1000703) || \
356507aa4de1SKenneth D. Merry     ((__FreeBSD_version >= 1100000) && (__FreeBSD_version < 1100002))
356607aa4de1SKenneth D. Merry 		if (xpt_path_path_id(path) != sc->sassc->sim->path_id)
356707aa4de1SKenneth D. Merry 			break;
356807aa4de1SKenneth D. Merry #endif
356907aa4de1SKenneth D. Merry 
3570991554f2SKenneth D. Merry 		cgd = arg;
3571991554f2SKenneth D. Merry #if (__FreeBSD_version < 901503) || \
3572991554f2SKenneth D. Merry     ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000006))
3573991554f2SKenneth D. Merry 		mprsas_check_eedp(sc, path, cgd);
3574991554f2SKenneth D. Merry #endif
3575991554f2SKenneth D. Merry 		break;
3576991554f2SKenneth D. Merry 	}
3577991554f2SKenneth D. Merry 	default:
3578991554f2SKenneth D. Merry 		break;
3579991554f2SKenneth D. Merry 	}
3580991554f2SKenneth D. Merry }
3581991554f2SKenneth D. Merry 
3582991554f2SKenneth D. Merry #if (__FreeBSD_version < 901503) || \
3583991554f2SKenneth D. Merry     ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000006))
3584991554f2SKenneth D. Merry static void
3585991554f2SKenneth D. Merry mprsas_check_eedp(struct mpr_softc *sc, struct cam_path *path,
3586991554f2SKenneth D. Merry     struct ccb_getdev *cgd)
3587991554f2SKenneth D. Merry {
3588991554f2SKenneth D. Merry 	struct mprsas_softc *sassc = sc->sassc;
3589991554f2SKenneth D. Merry 	struct ccb_scsiio *csio;
3590991554f2SKenneth D. Merry 	struct scsi_read_capacity_16 *scsi_cmd;
3591991554f2SKenneth D. Merry 	struct scsi_read_capacity_eedp *rcap_buf;
3592991554f2SKenneth D. Merry 	path_id_t pathid;
3593991554f2SKenneth D. Merry 	target_id_t targetid;
3594991554f2SKenneth D. Merry 	lun_id_t lunid;
3595991554f2SKenneth D. Merry 	union ccb *ccb;
3596991554f2SKenneth D. Merry 	struct cam_path *local_path;
3597991554f2SKenneth D. Merry 	struct mprsas_target *target;
3598991554f2SKenneth D. Merry 	struct mprsas_lun *lun;
3599991554f2SKenneth D. Merry 	uint8_t	found_lun;
3600991554f2SKenneth D. Merry 	char path_str[64];
3601991554f2SKenneth D. Merry 
3602991554f2SKenneth D. Merry 	pathid = cam_sim_path(sassc->sim);
3603991554f2SKenneth D. Merry 	targetid = xpt_path_target_id(path);
3604991554f2SKenneth D. Merry 	lunid = xpt_path_lun_id(path);
3605991554f2SKenneth D. Merry 
36067a2a6a1aSStephen McConnell 	KASSERT(targetid < sassc->maxtargets, ("Target %d out of bounds in "
36077a2a6a1aSStephen McConnell 	    "mprsas_check_eedp\n", targetid));
3608991554f2SKenneth D. Merry 	target = &sassc->targets[targetid];
3609991554f2SKenneth D. Merry 	if (target->handle == 0x0)
3610991554f2SKenneth D. Merry 		return;
3611991554f2SKenneth D. Merry 
3612991554f2SKenneth D. Merry 	/*
3613991554f2SKenneth D. Merry 	 * Determine if the device is EEDP capable.
3614991554f2SKenneth D. Merry 	 *
3615991554f2SKenneth D. Merry 	 * If this flag is set in the inquiry data, the device supports
3616991554f2SKenneth D. Merry 	 * protection information, and must support the 16 byte read capacity
36177a2a6a1aSStephen McConnell 	 * command, otherwise continue without sending read cap 16.
3618991554f2SKenneth D. Merry 	 */
3619991554f2SKenneth D. Merry 	if ((cgd->inq_data.spc3_flags & SPC3_SID_PROTECT) == 0)
3620991554f2SKenneth D. Merry 		return;
3621991554f2SKenneth D. Merry 
3622991554f2SKenneth D. Merry 	/*
3623991554f2SKenneth D. Merry 	 * Issue a READ CAPACITY 16 command.  This info is used to determine if
3624991554f2SKenneth D. Merry 	 * the LUN is formatted for EEDP support.
3625991554f2SKenneth D. Merry 	 */
3626991554f2SKenneth D. Merry 	ccb = xpt_alloc_ccb_nowait();
3627991554f2SKenneth D. Merry 	if (ccb == NULL) {
3628991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "Unable to alloc CCB for EEDP "
3629991554f2SKenneth D. Merry 		    "support.\n");
3630991554f2SKenneth D. Merry 		return;
3631991554f2SKenneth D. Merry 	}
3632991554f2SKenneth D. Merry 
36337a2a6a1aSStephen McConnell 	if (xpt_create_path(&local_path, xpt_periph, pathid, targetid, lunid) !=
36347a2a6a1aSStephen McConnell 	    CAM_REQ_CMP) {
3635991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "Unable to create path for EEDP "
36367a2a6a1aSStephen McConnell 		    "support.\n");
3637991554f2SKenneth D. Merry 		xpt_free_ccb(ccb);
3638991554f2SKenneth D. Merry 		return;
3639991554f2SKenneth D. Merry 	}
3640991554f2SKenneth D. Merry 
3641991554f2SKenneth D. Merry 	/*
3642991554f2SKenneth D. Merry 	 * If LUN is already in list, don't create a new one.
3643991554f2SKenneth D. Merry 	 */
3644991554f2SKenneth D. Merry 	found_lun = FALSE;
3645991554f2SKenneth D. Merry 	SLIST_FOREACH(lun, &target->luns, lun_link) {
3646991554f2SKenneth D. Merry 		if (lun->lun_id == lunid) {
3647991554f2SKenneth D. Merry 			found_lun = TRUE;
3648991554f2SKenneth D. Merry 			break;
3649991554f2SKenneth D. Merry 		}
3650991554f2SKenneth D. Merry 	}
3651991554f2SKenneth D. Merry 	if (!found_lun) {
3652991554f2SKenneth D. Merry 		lun = malloc(sizeof(struct mprsas_lun), M_MPR,
3653991554f2SKenneth D. Merry 		    M_NOWAIT | M_ZERO);
3654991554f2SKenneth D. Merry 		if (lun == NULL) {
3655991554f2SKenneth D. Merry 			mpr_dprint(sc, MPR_ERROR, "Unable to alloc LUN for "
3656991554f2SKenneth D. Merry 			    "EEDP support.\n");
3657991554f2SKenneth D. Merry 			xpt_free_path(local_path);
3658991554f2SKenneth D. Merry 			xpt_free_ccb(ccb);
3659991554f2SKenneth D. Merry 			return;
3660991554f2SKenneth D. Merry 		}
3661991554f2SKenneth D. Merry 		lun->lun_id = lunid;
3662991554f2SKenneth D. Merry 		SLIST_INSERT_HEAD(&target->luns, lun, lun_link);
3663991554f2SKenneth D. Merry 	}
3664991554f2SKenneth D. Merry 
3665991554f2SKenneth D. Merry 	xpt_path_string(local_path, path_str, sizeof(path_str));
3666991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_INFO, "Sending read cap: path %s handle %d\n",
3667991554f2SKenneth D. Merry 	    path_str, target->handle);
3668991554f2SKenneth D. Merry 
3669991554f2SKenneth D. Merry 	/*
3670991554f2SKenneth D. Merry 	 * Issue a READ CAPACITY 16 command for the LUN.  The
3671991554f2SKenneth D. Merry 	 * mprsas_read_cap_done function will load the read cap info into the
3672991554f2SKenneth D. Merry 	 * LUN struct.
3673991554f2SKenneth D. Merry 	 */
3674991554f2SKenneth D. Merry 	rcap_buf = malloc(sizeof(struct scsi_read_capacity_eedp), M_MPR,
3675991554f2SKenneth D. Merry 	    M_NOWAIT | M_ZERO);
3676991554f2SKenneth D. Merry 	if (rcap_buf == NULL) {
3677a2c14879SStephen McConnell 		mpr_dprint(sc, MPR_ERROR, "Unable to alloc read capacity "
3678991554f2SKenneth D. Merry 		    "buffer for EEDP support.\n");
3679991554f2SKenneth D. Merry 		xpt_free_path(ccb->ccb_h.path);
3680991554f2SKenneth D. Merry 		xpt_free_ccb(ccb);
3681991554f2SKenneth D. Merry 		return;
3682991554f2SKenneth D. Merry 	}
3683991554f2SKenneth D. Merry 	xpt_setup_ccb(&ccb->ccb_h, local_path, CAM_PRIORITY_XPT);
3684991554f2SKenneth D. Merry 	csio = &ccb->csio;
3685991554f2SKenneth D. Merry 	csio->ccb_h.func_code = XPT_SCSI_IO;
3686991554f2SKenneth D. Merry 	csio->ccb_h.flags = CAM_DIR_IN;
3687991554f2SKenneth D. Merry 	csio->ccb_h.retry_count = 4;
3688991554f2SKenneth D. Merry 	csio->ccb_h.cbfcnp = mprsas_read_cap_done;
3689991554f2SKenneth D. Merry 	csio->ccb_h.timeout = 60000;
3690991554f2SKenneth D. Merry 	csio->data_ptr = (uint8_t *)rcap_buf;
3691991554f2SKenneth D. Merry 	csio->dxfer_len = sizeof(struct scsi_read_capacity_eedp);
3692991554f2SKenneth D. Merry 	csio->sense_len = MPR_SENSE_LEN;
3693991554f2SKenneth D. Merry 	csio->cdb_len = sizeof(*scsi_cmd);
3694991554f2SKenneth D. Merry 	csio->tag_action = MSG_SIMPLE_Q_TAG;
3695991554f2SKenneth D. Merry 
3696991554f2SKenneth D. Merry 	scsi_cmd = (struct scsi_read_capacity_16 *)&csio->cdb_io.cdb_bytes;
3697991554f2SKenneth D. Merry 	bzero(scsi_cmd, sizeof(*scsi_cmd));
3698991554f2SKenneth D. Merry 	scsi_cmd->opcode = 0x9E;
3699991554f2SKenneth D. Merry 	scsi_cmd->service_action = SRC16_SERVICE_ACTION;
3700991554f2SKenneth D. Merry 	((uint8_t *)scsi_cmd)[13] = sizeof(struct scsi_read_capacity_eedp);
3701991554f2SKenneth D. Merry 
3702991554f2SKenneth D. Merry 	ccb->ccb_h.ppriv_ptr1 = sassc;
3703991554f2SKenneth D. Merry 	xpt_action(ccb);
3704991554f2SKenneth D. Merry }
3705991554f2SKenneth D. Merry 
3706991554f2SKenneth D. Merry static void
3707991554f2SKenneth D. Merry mprsas_read_cap_done(struct cam_periph *periph, union ccb *done_ccb)
3708991554f2SKenneth D. Merry {
3709991554f2SKenneth D. Merry 	struct mprsas_softc *sassc;
3710991554f2SKenneth D. Merry 	struct mprsas_target *target;
3711991554f2SKenneth D. Merry 	struct mprsas_lun *lun;
3712991554f2SKenneth D. Merry 	struct scsi_read_capacity_eedp *rcap_buf;
3713991554f2SKenneth D. Merry 
3714991554f2SKenneth D. Merry 	if (done_ccb == NULL)
3715991554f2SKenneth D. Merry 		return;
3716991554f2SKenneth D. Merry 
3717991554f2SKenneth D. Merry 	/* Driver need to release devq, it Scsi command is
3718991554f2SKenneth D. Merry 	 * generated by driver internally.
3719991554f2SKenneth D. Merry 	 * Currently there is a single place where driver
3720991554f2SKenneth D. Merry 	 * calls scsi command internally. In future if driver
3721991554f2SKenneth D. Merry 	 * calls more scsi command internally, it needs to release
3722991554f2SKenneth D. Merry 	 * devq internally, since those command will not go back to
3723991554f2SKenneth D. Merry 	 * cam_periph.
3724991554f2SKenneth D. Merry 	 */
3725991554f2SKenneth D. Merry 	if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) ) {
3726991554f2SKenneth D. Merry         	done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN;
3727991554f2SKenneth D. Merry 		xpt_release_devq(done_ccb->ccb_h.path,
3728991554f2SKenneth D. Merry 			       	/*count*/ 1, /*run_queue*/TRUE);
3729991554f2SKenneth D. Merry 	}
3730991554f2SKenneth D. Merry 
3731991554f2SKenneth D. Merry 	rcap_buf = (struct scsi_read_capacity_eedp *)done_ccb->csio.data_ptr;
3732991554f2SKenneth D. Merry 
3733991554f2SKenneth D. Merry 	/*
3734991554f2SKenneth D. Merry 	 * Get the LUN ID for the path and look it up in the LUN list for the
3735991554f2SKenneth D. Merry 	 * target.
3736991554f2SKenneth D. Merry 	 */
3737991554f2SKenneth D. Merry 	sassc = (struct mprsas_softc *)done_ccb->ccb_h.ppriv_ptr1;
37387a2a6a1aSStephen McConnell 	KASSERT(done_ccb->ccb_h.target_id < sassc->maxtargets, ("Target %d out "
37397a2a6a1aSStephen McConnell 	    "of bounds in mprsas_read_cap_done\n", done_ccb->ccb_h.target_id));
3740991554f2SKenneth D. Merry 	target = &sassc->targets[done_ccb->ccb_h.target_id];
3741991554f2SKenneth D. Merry 	SLIST_FOREACH(lun, &target->luns, lun_link) {
3742991554f2SKenneth D. Merry 		if (lun->lun_id != done_ccb->ccb_h.target_lun)
3743991554f2SKenneth D. Merry 			continue;
3744991554f2SKenneth D. Merry 
3745991554f2SKenneth D. Merry 		/*
3746991554f2SKenneth D. Merry 		 * Got the LUN in the target's LUN list.  Fill it in with EEDP
3747991554f2SKenneth D. Merry 		 * info. If the READ CAP 16 command had some SCSI error (common
3748991554f2SKenneth D. Merry 		 * if command is not supported), mark the lun as not supporting
3749991554f2SKenneth D. Merry 		 * EEDP and set the block size to 0.
3750991554f2SKenneth D. Merry 		 */
3751a2c14879SStephen McConnell 		if ((mprsas_get_ccbstatus(done_ccb) != CAM_REQ_CMP) ||
3752a2c14879SStephen McConnell 		    (done_ccb->csio.scsi_status != SCSI_STATUS_OK)) {
3753991554f2SKenneth D. Merry 			lun->eedp_formatted = FALSE;
3754991554f2SKenneth D. Merry 			lun->eedp_block_size = 0;
3755991554f2SKenneth D. Merry 			break;
3756991554f2SKenneth D. Merry 		}
3757991554f2SKenneth D. Merry 
3758991554f2SKenneth D. Merry 		if (rcap_buf->protect & 0x01) {
3759a2c14879SStephen McConnell 			mpr_dprint(sassc->sc, MPR_INFO, "LUN %d for target ID "
3760a2c14879SStephen McConnell 			    "%d is formatted for EEDP support.\n",
3761a2c14879SStephen McConnell 			    done_ccb->ccb_h.target_lun,
3762991554f2SKenneth D. Merry 			    done_ccb->ccb_h.target_id);
3763991554f2SKenneth D. Merry 			lun->eedp_formatted = TRUE;
3764991554f2SKenneth D. Merry 			lun->eedp_block_size = scsi_4btoul(rcap_buf->length);
3765991554f2SKenneth D. Merry 		}
3766991554f2SKenneth D. Merry 		break;
3767991554f2SKenneth D. Merry 	}
3768991554f2SKenneth D. Merry 
3769991554f2SKenneth D. Merry 	// Finished with this CCB and path.
3770991554f2SKenneth D. Merry 	free(rcap_buf, M_MPR);
3771991554f2SKenneth D. Merry 	xpt_free_path(done_ccb->ccb_h.path);
3772991554f2SKenneth D. Merry 	xpt_free_ccb(done_ccb);
3773991554f2SKenneth D. Merry }
3774991554f2SKenneth D. Merry #endif /* (__FreeBSD_version < 901503) || \
3775991554f2SKenneth D. Merry           ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000006)) */
3776991554f2SKenneth D. Merry 
3777a2c14879SStephen McConnell /*
3778a2c14879SStephen McConnell  * Set the INRESET flag for this target so that no I/O will be sent to
3779a2c14879SStephen McConnell  * the target until the reset has completed.  If an I/O request does
3780a2c14879SStephen McConnell  * happen, the devq will be frozen.  The CCB holds the path which is
3781a2c14879SStephen McConnell  * used to release the devq.  The devq is released and the CCB is freed
3782a2c14879SStephen McConnell  * when the TM completes.
3783a2c14879SStephen McConnell  */
3784b7f1ee79SScott Long void
3785b7f1ee79SScott Long mprsas_prepare_for_tm(struct mpr_softc *sc, struct mpr_command *tm,
3786b7f1ee79SScott Long     struct mprsas_target *target, lun_id_t lun_id)
3787b7f1ee79SScott Long {
3788b7f1ee79SScott Long 	union ccb *ccb;
3789b7f1ee79SScott Long 	path_id_t path_id;
3790b7f1ee79SScott Long 
3791a2c14879SStephen McConnell 	ccb = xpt_alloc_ccb_nowait();
3792a2c14879SStephen McConnell 	if (ccb) {
3793a2c14879SStephen McConnell 		path_id = cam_sim_path(sc->sassc->sim);
3794a2c14879SStephen McConnell 		if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, path_id,
3795a2c14879SStephen McConnell 		    target->tid, lun_id) != CAM_REQ_CMP) {
3796a2c14879SStephen McConnell 			xpt_free_ccb(ccb);
3797a2c14879SStephen McConnell 		} else {
3798a2c14879SStephen McConnell 			tm->cm_ccb = ccb;
3799a2c14879SStephen McConnell 			tm->cm_targ = target;
3800a2c14879SStephen McConnell 			target->flags |= MPRSAS_TARGET_INRESET;
3801a2c14879SStephen McConnell 		}
3802a2c14879SStephen McConnell 	}
3803a2c14879SStephen McConnell }
3804a2c14879SStephen McConnell 
3805991554f2SKenneth D. Merry int
3806991554f2SKenneth D. Merry mprsas_startup(struct mpr_softc *sc)
3807991554f2SKenneth D. Merry {
3808991554f2SKenneth D. Merry 	/*
3809991554f2SKenneth D. Merry 	 * Send the port enable message and set the wait_for_port_enable flag.
3810991554f2SKenneth D. Merry 	 * This flag helps to keep the simq frozen until all discovery events
3811991554f2SKenneth D. Merry 	 * are processed.
3812991554f2SKenneth D. Merry 	 */
3813991554f2SKenneth D. Merry 	sc->wait_for_port_enable = 1;
3814991554f2SKenneth D. Merry 	mprsas_send_portenable(sc);
3815991554f2SKenneth D. Merry 	return (0);
3816991554f2SKenneth D. Merry }
3817991554f2SKenneth D. Merry 
3818991554f2SKenneth D. Merry static int
3819991554f2SKenneth D. Merry mprsas_send_portenable(struct mpr_softc *sc)
3820991554f2SKenneth D. Merry {
3821991554f2SKenneth D. Merry 	MPI2_PORT_ENABLE_REQUEST *request;
3822991554f2SKenneth D. Merry 	struct mpr_command *cm;
3823991554f2SKenneth D. Merry 
3824991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
3825991554f2SKenneth D. Merry 
3826991554f2SKenneth D. Merry 	if ((cm = mpr_alloc_command(sc)) == NULL)
3827991554f2SKenneth D. Merry 		return (EBUSY);
3828991554f2SKenneth D. Merry 	request = (MPI2_PORT_ENABLE_REQUEST *)cm->cm_req;
3829991554f2SKenneth D. Merry 	request->Function = MPI2_FUNCTION_PORT_ENABLE;
3830991554f2SKenneth D. Merry 	request->MsgFlags = 0;
3831991554f2SKenneth D. Merry 	request->VP_ID = 0;
3832991554f2SKenneth D. Merry 	cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE;
3833991554f2SKenneth D. Merry 	cm->cm_complete = mprsas_portenable_complete;
3834991554f2SKenneth D. Merry 	cm->cm_data = NULL;
3835991554f2SKenneth D. Merry 	cm->cm_sge = NULL;
3836991554f2SKenneth D. Merry 
3837991554f2SKenneth D. Merry 	mpr_map_command(sc, cm);
3838991554f2SKenneth D. Merry 	mpr_dprint(sc, MPR_XINFO,
3839991554f2SKenneth D. Merry 	    "mpr_send_portenable finished cm %p req %p complete %p\n",
3840991554f2SKenneth D. Merry 	    cm, cm->cm_req, cm->cm_complete);
3841991554f2SKenneth D. Merry 	return (0);
3842991554f2SKenneth D. Merry }
3843991554f2SKenneth D. Merry 
3844991554f2SKenneth D. Merry static void
3845991554f2SKenneth D. Merry mprsas_portenable_complete(struct mpr_softc *sc, struct mpr_command *cm)
3846991554f2SKenneth D. Merry {
3847991554f2SKenneth D. Merry 	MPI2_PORT_ENABLE_REPLY *reply;
3848991554f2SKenneth D. Merry 	struct mprsas_softc *sassc;
3849991554f2SKenneth D. Merry 
3850991554f2SKenneth D. Merry 	MPR_FUNCTRACE(sc);
3851991554f2SKenneth D. Merry 	sassc = sc->sassc;
3852991554f2SKenneth D. Merry 
3853991554f2SKenneth D. Merry 	/*
3854991554f2SKenneth D. Merry 	 * Currently there should be no way we can hit this case.  It only
3855991554f2SKenneth D. Merry 	 * happens when we have a failure to allocate chain frames, and
3856991554f2SKenneth D. Merry 	 * port enable commands don't have S/G lists.
3857991554f2SKenneth D. Merry 	 */
3858991554f2SKenneth D. Merry 	if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) {
3859991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for port enable! "
3860991554f2SKenneth D. Merry 		    "This should not happen!\n", __func__, cm->cm_flags);
3861991554f2SKenneth D. Merry 	}
3862991554f2SKenneth D. Merry 
3863991554f2SKenneth D. Merry 	reply = (MPI2_PORT_ENABLE_REPLY *)cm->cm_reply;
3864991554f2SKenneth D. Merry 	if (reply == NULL)
3865991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_FAULT, "Portenable NULL reply\n");
3866991554f2SKenneth D. Merry 	else if (le16toh(reply->IOCStatus & MPI2_IOCSTATUS_MASK) !=
3867991554f2SKenneth D. Merry 	    MPI2_IOCSTATUS_SUCCESS)
3868991554f2SKenneth D. Merry 		mpr_dprint(sc, MPR_FAULT, "Portenable failed\n");
3869991554f2SKenneth D. Merry 
3870991554f2SKenneth D. Merry 	mpr_free_command(sc, cm);
3871991554f2SKenneth D. Merry 	/*
3872991554f2SKenneth D. Merry 	 * Done waiting for port enable to complete.  Decrement the refcount.
3873991554f2SKenneth D. Merry 	 * If refcount is 0, discovery is complete and a rescan of the bus can
3874991554f2SKenneth D. Merry 	 * take place.
3875991554f2SKenneth D. Merry 	 */
3876991554f2SKenneth D. Merry 	sc->wait_for_port_enable = 0;
3877991554f2SKenneth D. Merry 	sc->port_enable_complete = 1;
3878991554f2SKenneth D. Merry 	wakeup(&sc->port_enable_complete);
3879991554f2SKenneth D. Merry 	mprsas_startup_decrement(sassc);
3880991554f2SKenneth D. Merry }
3881991554f2SKenneth D. Merry 
3882991554f2SKenneth D. Merry int
3883991554f2SKenneth D. Merry mprsas_check_id(struct mprsas_softc *sassc, int id)
3884991554f2SKenneth D. Merry {
3885991554f2SKenneth D. Merry 	struct mpr_softc *sc = sassc->sc;
3886991554f2SKenneth D. Merry 	char *ids;
3887991554f2SKenneth D. Merry 	char *name;
3888991554f2SKenneth D. Merry 
3889991554f2SKenneth D. Merry 	ids = &sc->exclude_ids[0];
3890991554f2SKenneth D. Merry 	while((name = strsep(&ids, ",")) != NULL) {
3891991554f2SKenneth D. Merry 		if (name[0] == '\0')
3892991554f2SKenneth D. Merry 			continue;
3893991554f2SKenneth D. Merry 		if (strtol(name, NULL, 0) == (long)id)
3894991554f2SKenneth D. Merry 			return (1);
3895991554f2SKenneth D. Merry 	}
3896991554f2SKenneth D. Merry 
3897991554f2SKenneth D. Merry 	return (0);
3898991554f2SKenneth D. Merry }
3899a2c14879SStephen McConnell 
3900a2c14879SStephen McConnell void
3901a2c14879SStephen McConnell mprsas_realloc_targets(struct mpr_softc *sc, int maxtargets)
3902a2c14879SStephen McConnell {
3903a2c14879SStephen McConnell 	struct mprsas_softc *sassc;
3904a2c14879SStephen McConnell 	struct mprsas_lun *lun, *lun_tmp;
3905a2c14879SStephen McConnell 	struct mprsas_target *targ;
3906a2c14879SStephen McConnell 	int i;
3907a2c14879SStephen McConnell 
3908a2c14879SStephen McConnell 	sassc = sc->sassc;
3909a2c14879SStephen McConnell 	/*
3910a2c14879SStephen McConnell 	 * The number of targets is based on IOC Facts, so free all of
3911a2c14879SStephen McConnell 	 * the allocated LUNs for each target and then the target buffer
3912a2c14879SStephen McConnell 	 * itself.
3913a2c14879SStephen McConnell 	 */
3914a2c14879SStephen McConnell 	for (i=0; i< maxtargets; i++) {
3915a2c14879SStephen McConnell 		targ = &sassc->targets[i];
3916a2c14879SStephen McConnell 		SLIST_FOREACH_SAFE(lun, &targ->luns, lun_link, lun_tmp) {
3917a2c14879SStephen McConnell 			free(lun, M_MPR);
3918a2c14879SStephen McConnell 		}
3919a2c14879SStephen McConnell 	}
3920a2c14879SStephen McConnell 	free(sassc->targets, M_MPR);
3921a2c14879SStephen McConnell 
3922a2c14879SStephen McConnell 	sassc->targets = malloc(sizeof(struct mprsas_target) * maxtargets,
3923a2c14879SStephen McConnell 	    M_MPR, M_WAITOK|M_ZERO);
3924a2c14879SStephen McConnell 	if (!sassc->targets) {
3925a2c14879SStephen McConnell 		panic("%s failed to alloc targets with error %d\n",
3926a2c14879SStephen McConnell 		    __func__, ENOMEM);
3927a2c14879SStephen McConnell 	}
3928a2c14879SStephen McConnell }
3929