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 546b23587SKashyap D Desai * Copyright 2000-2020 Broadcom Inc. 6991554f2SKenneth D. Merry * All rights reserved. 7991554f2SKenneth D. Merry * 8991554f2SKenneth D. Merry * Redistribution and use in source and binary forms, with or without 9991554f2SKenneth D. Merry * modification, are permitted provided that the following conditions 10991554f2SKenneth D. Merry * are met: 11991554f2SKenneth D. Merry * 1. Redistributions of source code must retain the above copyright 12991554f2SKenneth D. Merry * notice, this list of conditions and the following disclaimer. 13991554f2SKenneth D. Merry * 2. Redistributions in binary form must reproduce the above copyright 14991554f2SKenneth D. Merry * notice, this list of conditions and the following disclaimer in the 15991554f2SKenneth D. Merry * documentation and/or other materials provided with the distribution. 16991554f2SKenneth D. Merry * 17991554f2SKenneth D. Merry * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18991554f2SKenneth D. Merry * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19991554f2SKenneth D. Merry * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20991554f2SKenneth D. Merry * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21991554f2SKenneth D. Merry * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22991554f2SKenneth D. Merry * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23991554f2SKenneth D. Merry * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24991554f2SKenneth D. Merry * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25991554f2SKenneth D. Merry * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26991554f2SKenneth D. Merry * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27991554f2SKenneth D. Merry * SUCH DAMAGE. 28a2c14879SStephen McConnell * 2946b23587SKashyap D Desai * Broadcom Inc. (LSI) MPT-Fusion Host Adapter FreeBSD 30a2c14879SStephen McConnell * 31991554f2SKenneth D. Merry */ 32991554f2SKenneth D. Merry 33991554f2SKenneth D. Merry #include <sys/cdefs.h> 34991554f2SKenneth D. Merry __FBSDID("$FreeBSD$"); 35991554f2SKenneth D. Merry 36a2c14879SStephen McConnell /* Communications core for Avago Technologies (LSI) MPT3 */ 37991554f2SKenneth D. Merry 38991554f2SKenneth D. Merry /* TODO Move headers to mprvar */ 39991554f2SKenneth D. Merry #include <sys/types.h> 40991554f2SKenneth D. Merry #include <sys/param.h> 41991554f2SKenneth D. Merry #include <sys/systm.h> 42991554f2SKenneth D. Merry #include <sys/kernel.h> 43991554f2SKenneth D. Merry #include <sys/selinfo.h> 44991554f2SKenneth D. Merry #include <sys/module.h> 45991554f2SKenneth D. Merry #include <sys/bus.h> 46991554f2SKenneth D. Merry #include <sys/conf.h> 47991554f2SKenneth D. Merry #include <sys/bio.h> 48991554f2SKenneth D. Merry #include <sys/malloc.h> 49991554f2SKenneth D. Merry #include <sys/uio.h> 50991554f2SKenneth D. Merry #include <sys/sysctl.h> 51991554f2SKenneth D. Merry #include <sys/endian.h> 52991554f2SKenneth D. Merry #include <sys/queue.h> 53991554f2SKenneth D. Merry #include <sys/kthread.h> 54991554f2SKenneth D. Merry #include <sys/taskqueue.h> 55991554f2SKenneth D. Merry #include <sys/sbuf.h> 56991554f2SKenneth D. Merry 57991554f2SKenneth D. Merry #include <machine/bus.h> 58991554f2SKenneth D. Merry #include <machine/resource.h> 59991554f2SKenneth D. Merry #include <sys/rman.h> 60991554f2SKenneth D. Merry 61991554f2SKenneth D. Merry #include <machine/stdarg.h> 62991554f2SKenneth D. Merry 63991554f2SKenneth D. Merry #include <cam/cam.h> 64991554f2SKenneth D. Merry #include <cam/cam_ccb.h> 65991554f2SKenneth D. Merry #include <cam/cam_debug.h> 66991554f2SKenneth D. Merry #include <cam/cam_sim.h> 67991554f2SKenneth D. Merry #include <cam/cam_xpt_sim.h> 68991554f2SKenneth D. Merry #include <cam/cam_xpt_periph.h> 69991554f2SKenneth D. Merry #include <cam/cam_periph.h> 70991554f2SKenneth D. Merry #include <cam/scsi/scsi_all.h> 71991554f2SKenneth D. Merry #include <cam/scsi/scsi_message.h> 72991554f2SKenneth D. Merry #include <cam/scsi/smp_all.h> 73991554f2SKenneth D. Merry 7467feec50SStephen McConnell #include <dev/nvme/nvme.h> 7567feec50SStephen McConnell 76991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_type.h> 77991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2.h> 78991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_ioc.h> 79991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_sas.h> 8067feec50SStephen McConnell #include <dev/mpr/mpi/mpi2_pci.h> 81991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_cnfg.h> 82991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_init.h> 83991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_tool.h> 84991554f2SKenneth D. Merry #include <dev/mpr/mpr_ioctl.h> 85991554f2SKenneth D. Merry #include <dev/mpr/mprvar.h> 86991554f2SKenneth D. Merry #include <dev/mpr/mpr_table.h> 87991554f2SKenneth D. Merry #include <dev/mpr/mpr_sas.h> 88991554f2SKenneth D. Merry 89991554f2SKenneth D. Merry #define MPRSAS_DISCOVERY_TIMEOUT 20 90991554f2SKenneth D. Merry #define MPRSAS_MAX_DISCOVERY_TIMEOUTS 10 /* 200 seconds */ 91991554f2SKenneth D. Merry 92991554f2SKenneth D. Merry /* 93991554f2SKenneth D. Merry * static array to check SCSI OpCode for EEDP protection bits 94991554f2SKenneth D. Merry */ 95991554f2SKenneth D. Merry #define PRO_R MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP 96991554f2SKenneth D. Merry #define PRO_W MPI2_SCSIIO_EEDPFLAGS_INSERT_OP 97991554f2SKenneth D. Merry #define PRO_V MPI2_SCSIIO_EEDPFLAGS_INSERT_OP 98991554f2SKenneth D. Merry static uint8_t op_code_prot[256] = { 99991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V, 102991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103991554f2SKenneth D. Merry 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104991554f2SKenneth D. Merry 0, 0, 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, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V, 108991554f2SKenneth D. Merry 0, 0, 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V, 110991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 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 }; 116991554f2SKenneth D. Merry 117991554f2SKenneth D. Merry MALLOC_DEFINE(M_MPRSAS, "MPRSAS", "MPR SAS memory"); 118991554f2SKenneth D. Merry 119991554f2SKenneth D. Merry static void mprsas_remove_device(struct mpr_softc *, struct mpr_command *); 120991554f2SKenneth D. Merry static void mprsas_remove_complete(struct mpr_softc *, struct mpr_command *); 121991554f2SKenneth D. Merry static void mprsas_action(struct cam_sim *sim, union ccb *ccb); 122991554f2SKenneth D. Merry static void mprsas_poll(struct cam_sim *sim); 123991554f2SKenneth D. Merry static void mprsas_scsiio_timeout(void *data); 1247a2a6a1aSStephen McConnell static void mprsas_abort_complete(struct mpr_softc *sc, struct mpr_command *cm); 125991554f2SKenneth D. Merry static void mprsas_action_scsiio(struct mprsas_softc *, union ccb *); 126991554f2SKenneth D. Merry static void mprsas_scsiio_complete(struct mpr_softc *, struct mpr_command *); 127991554f2SKenneth D. Merry static void mprsas_action_resetdev(struct mprsas_softc *, union ccb *); 1287a2a6a1aSStephen McConnell static void mprsas_resetdev_complete(struct mpr_softc *, struct mpr_command *); 129991554f2SKenneth D. Merry static int mprsas_send_abort(struct mpr_softc *sc, struct mpr_command *tm, 130991554f2SKenneth D. Merry struct mpr_command *cm); 131991554f2SKenneth D. Merry static void mprsas_async(void *callback_arg, uint32_t code, 132991554f2SKenneth D. Merry struct cam_path *path, void *arg); 133991554f2SKenneth D. Merry static int mprsas_send_portenable(struct mpr_softc *sc); 134991554f2SKenneth D. Merry static void mprsas_portenable_complete(struct mpr_softc *sc, 135991554f2SKenneth D. Merry struct mpr_command *cm); 136991554f2SKenneth D. Merry 1377a2a6a1aSStephen McConnell static void mprsas_smpio_complete(struct mpr_softc *sc, struct mpr_command *cm); 1387a2a6a1aSStephen McConnell static void mprsas_send_smpcmd(struct mprsas_softc *sassc, union ccb *ccb, 1397a2a6a1aSStephen McConnell uint64_t sasaddr); 140a2c14879SStephen McConnell static void mprsas_action_smpio(struct mprsas_softc *sassc, union ccb *ccb); 141991554f2SKenneth D. Merry 142991554f2SKenneth D. Merry struct mprsas_target * 143991554f2SKenneth D. Merry mprsas_find_target_by_handle(struct mprsas_softc *sassc, int start, 144991554f2SKenneth D. Merry uint16_t handle) 145991554f2SKenneth D. Merry { 146991554f2SKenneth D. Merry struct mprsas_target *target; 147991554f2SKenneth D. Merry int i; 148991554f2SKenneth D. Merry 149991554f2SKenneth D. Merry for (i = start; i < sassc->maxtargets; i++) { 150991554f2SKenneth D. Merry target = &sassc->targets[i]; 151991554f2SKenneth D. Merry if (target->handle == handle) 152991554f2SKenneth D. Merry return (target); 153991554f2SKenneth D. Merry } 154991554f2SKenneth D. Merry 155991554f2SKenneth D. Merry return (NULL); 156991554f2SKenneth D. Merry } 157991554f2SKenneth D. Merry 158991554f2SKenneth D. Merry /* we need to freeze the simq during attach and diag reset, to avoid failing 159991554f2SKenneth D. Merry * commands before device handles have been found by discovery. Since 160991554f2SKenneth D. Merry * discovery involves reading config pages and possibly sending commands, 161991554f2SKenneth D. Merry * discovery actions may continue even after we receive the end of discovery 162991554f2SKenneth D. Merry * event, so refcount discovery actions instead of assuming we can unfreeze 163991554f2SKenneth D. Merry * the simq when we get the event. 164991554f2SKenneth D. Merry */ 165991554f2SKenneth D. Merry void 166991554f2SKenneth D. Merry mprsas_startup_increment(struct mprsas_softc *sassc) 167991554f2SKenneth D. Merry { 168991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 169991554f2SKenneth D. Merry 170991554f2SKenneth D. Merry if ((sassc->flags & MPRSAS_IN_STARTUP) != 0) { 171991554f2SKenneth D. Merry if (sassc->startup_refcount++ == 0) { 172991554f2SKenneth D. Merry /* just starting, freeze the simq */ 173991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INIT, 174991554f2SKenneth D. Merry "%s freezing simq\n", __func__); 175991554f2SKenneth D. Merry xpt_hold_boot(); 176991554f2SKenneth D. Merry xpt_freeze_simq(sassc->sim, 1); 177991554f2SKenneth D. Merry } 178991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INIT, "%s refcount %u\n", __func__, 179991554f2SKenneth D. Merry sassc->startup_refcount); 180991554f2SKenneth D. Merry } 181991554f2SKenneth D. Merry } 182991554f2SKenneth D. Merry 183991554f2SKenneth D. Merry void 184991554f2SKenneth D. Merry mprsas_release_simq_reinit(struct mprsas_softc *sassc) 185991554f2SKenneth D. Merry { 186991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_QUEUE_FROZEN) { 187991554f2SKenneth D. Merry sassc->flags &= ~MPRSAS_QUEUE_FROZEN; 188991554f2SKenneth D. Merry xpt_release_simq(sassc->sim, 1); 189991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INFO, "Unfreezing SIM queue\n"); 190991554f2SKenneth D. Merry } 191991554f2SKenneth D. Merry } 192991554f2SKenneth D. Merry 193991554f2SKenneth D. Merry void 194991554f2SKenneth D. Merry mprsas_startup_decrement(struct mprsas_softc *sassc) 195991554f2SKenneth D. Merry { 196991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 197991554f2SKenneth D. Merry 198991554f2SKenneth D. Merry if ((sassc->flags & MPRSAS_IN_STARTUP) != 0) { 199991554f2SKenneth D. Merry if (--sassc->startup_refcount == 0) { 200991554f2SKenneth D. Merry /* finished all discovery-related actions, release 201991554f2SKenneth D. Merry * the simq and rescan for the latest topology. 202991554f2SKenneth D. Merry */ 203991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INIT, 204991554f2SKenneth D. Merry "%s releasing simq\n", __func__); 205991554f2SKenneth D. Merry sassc->flags &= ~MPRSAS_IN_STARTUP; 206991554f2SKenneth D. Merry xpt_release_simq(sassc->sim, 1); 207991554f2SKenneth D. Merry xpt_release_boot(); 208991554f2SKenneth D. Merry } 209991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INIT, "%s refcount %u\n", __func__, 210991554f2SKenneth D. Merry sassc->startup_refcount); 211991554f2SKenneth D. Merry } 212991554f2SKenneth D. Merry } 213991554f2SKenneth D. Merry 214b7f1ee79SScott Long /* 215b7f1ee79SScott Long * The firmware requires us to stop sending commands when we're doing task 216b7f1ee79SScott Long * management. 217991554f2SKenneth D. Merry * use. 218b7f1ee79SScott Long * XXX The logic for serializing the device has been made lazy and moved to 219b7f1ee79SScott Long * mprsas_prepare_for_tm(). 220991554f2SKenneth D. Merry */ 221991554f2SKenneth D. Merry struct mpr_command * 222991554f2SKenneth D. Merry mprsas_alloc_tm(struct mpr_softc *sc) 223991554f2SKenneth D. Merry { 22446b9415fSScott Long MPI2_SCSI_TASK_MANAGE_REQUEST *req; 225991554f2SKenneth D. Merry struct mpr_command *tm; 226991554f2SKenneth D. Merry 227991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 228991554f2SKenneth D. Merry tm = mpr_alloc_high_priority_command(sc); 22946b9415fSScott Long if (tm == NULL) 23046b9415fSScott Long return (NULL); 23146b9415fSScott Long 23246b9415fSScott Long req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 23346b9415fSScott Long req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; 234991554f2SKenneth D. Merry return tm; 235991554f2SKenneth D. Merry } 236991554f2SKenneth D. Merry 237991554f2SKenneth D. Merry void 238991554f2SKenneth D. Merry mprsas_free_tm(struct mpr_softc *sc, struct mpr_command *tm) 239991554f2SKenneth D. Merry { 24088619392SStephen McConnell 241a2c14879SStephen McConnell MPR_FUNCTRACE(sc); 242991554f2SKenneth D. Merry if (tm == NULL) 243991554f2SKenneth D. Merry return; 244991554f2SKenneth D. Merry 245a2c14879SStephen McConnell /* 246a2c14879SStephen McConnell * For TM's the devq is frozen for the device. Unfreeze it here and 247a2c14879SStephen McConnell * free the resources used for freezing the devq. Must clear the 248a2c14879SStephen McConnell * INRESET flag as well or scsi I/O will not work. 249991554f2SKenneth D. Merry */ 250a2c14879SStephen McConnell if (tm->cm_ccb) { 251a8837c77SWarner Losh mpr_dprint(sc, MPR_XINFO | MPR_RECOVERY, 252a8837c77SWarner Losh "Unfreezing devq for target ID %d\n", 2539781c28cSAlexander Motin tm->cm_targ->tid); 2549781c28cSAlexander Motin tm->cm_targ->flags &= ~MPRSAS_TARGET_INRESET; 255a2c14879SStephen McConnell xpt_release_devq(tm->cm_ccb->ccb_h.path, 1, TRUE); 256a2c14879SStephen McConnell xpt_free_path(tm->cm_ccb->ccb_h.path); 257a2c14879SStephen McConnell xpt_free_ccb(tm->cm_ccb); 258a2c14879SStephen McConnell } 259991554f2SKenneth D. Merry 260991554f2SKenneth D. Merry mpr_free_high_priority_command(sc, tm); 261991554f2SKenneth D. Merry } 262991554f2SKenneth D. Merry 263991554f2SKenneth D. Merry void 264991554f2SKenneth D. Merry mprsas_rescan_target(struct mpr_softc *sc, struct mprsas_target *targ) 265991554f2SKenneth D. Merry { 266991554f2SKenneth D. Merry struct mprsas_softc *sassc = sc->sassc; 267991554f2SKenneth D. Merry path_id_t pathid; 268991554f2SKenneth D. Merry target_id_t targetid; 269991554f2SKenneth D. Merry union ccb *ccb; 270991554f2SKenneth D. Merry 271991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 272991554f2SKenneth D. Merry pathid = cam_sim_path(sassc->sim); 273991554f2SKenneth D. Merry if (targ == NULL) 274991554f2SKenneth D. Merry targetid = CAM_TARGET_WILDCARD; 275991554f2SKenneth D. Merry else 276991554f2SKenneth D. Merry targetid = targ - sassc->targets; 277991554f2SKenneth D. Merry 278991554f2SKenneth D. Merry /* 279991554f2SKenneth D. Merry * Allocate a CCB and schedule a rescan. 280991554f2SKenneth D. Merry */ 281991554f2SKenneth D. Merry ccb = xpt_alloc_ccb_nowait(); 282991554f2SKenneth D. Merry if (ccb == NULL) { 283991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "unable to alloc CCB for rescan\n"); 284991554f2SKenneth D. Merry return; 285991554f2SKenneth D. Merry } 286991554f2SKenneth D. Merry 287a2c14879SStephen McConnell if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid, targetid, 288a2c14879SStephen McConnell CAM_LUN_WILDCARD) != CAM_REQ_CMP) { 289991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "unable to create path for rescan\n"); 290991554f2SKenneth D. Merry xpt_free_ccb(ccb); 291991554f2SKenneth D. Merry return; 292991554f2SKenneth D. Merry } 293991554f2SKenneth D. Merry 294991554f2SKenneth D. Merry if (targetid == CAM_TARGET_WILDCARD) 295991554f2SKenneth D. Merry ccb->ccb_h.func_code = XPT_SCAN_BUS; 296991554f2SKenneth D. Merry else 297991554f2SKenneth D. Merry ccb->ccb_h.func_code = XPT_SCAN_TGT; 298991554f2SKenneth D. Merry 299991554f2SKenneth D. Merry mpr_dprint(sc, MPR_TRACE, "%s targetid %u\n", __func__, targetid); 300991554f2SKenneth D. Merry xpt_rescan(ccb); 301991554f2SKenneth D. Merry } 302991554f2SKenneth D. Merry 303991554f2SKenneth D. Merry static void 304991554f2SKenneth D. Merry mprsas_log_command(struct mpr_command *cm, u_int level, const char *fmt, ...) 305991554f2SKenneth D. Merry { 306991554f2SKenneth D. Merry struct sbuf sb; 307991554f2SKenneth D. Merry va_list ap; 308a2386b6fSAlexander Motin char str[224]; 309991554f2SKenneth D. Merry char path_str[64]; 310991554f2SKenneth D. Merry 311991554f2SKenneth D. Merry if (cm == NULL) 312991554f2SKenneth D. Merry return; 313991554f2SKenneth D. Merry 314991554f2SKenneth D. Merry /* No need to be in here if debugging isn't enabled */ 315991554f2SKenneth D. Merry if ((cm->cm_sc->mpr_debug & level) == 0) 316991554f2SKenneth D. Merry return; 317991554f2SKenneth D. Merry 318991554f2SKenneth D. Merry sbuf_new(&sb, str, sizeof(str), 0); 319991554f2SKenneth D. Merry 320991554f2SKenneth D. Merry va_start(ap, fmt); 321991554f2SKenneth D. Merry 322991554f2SKenneth D. Merry if (cm->cm_ccb != NULL) { 323991554f2SKenneth D. Merry xpt_path_string(cm->cm_ccb->csio.ccb_h.path, path_str, 324991554f2SKenneth D. Merry sizeof(path_str)); 325991554f2SKenneth D. Merry sbuf_cat(&sb, path_str); 326991554f2SKenneth D. Merry if (cm->cm_ccb->ccb_h.func_code == XPT_SCSI_IO) { 327991554f2SKenneth D. Merry scsi_command_string(&cm->cm_ccb->csio, &sb); 328991554f2SKenneth D. Merry sbuf_printf(&sb, "length %d ", 329991554f2SKenneth D. Merry cm->cm_ccb->csio.dxfer_len); 330991554f2SKenneth D. Merry } 331991554f2SKenneth D. Merry } else { 332991554f2SKenneth D. Merry sbuf_printf(&sb, "(noperiph:%s%d:%u:%u:%u): ", 333991554f2SKenneth D. Merry cam_sim_name(cm->cm_sc->sassc->sim), 334991554f2SKenneth D. Merry cam_sim_unit(cm->cm_sc->sassc->sim), 335991554f2SKenneth D. Merry cam_sim_bus(cm->cm_sc->sassc->sim), 336991554f2SKenneth D. Merry cm->cm_targ ? cm->cm_targ->tid : 0xFFFFFFFF, 337991554f2SKenneth D. Merry cm->cm_lun); 338991554f2SKenneth D. Merry } 339991554f2SKenneth D. Merry 340991554f2SKenneth D. Merry sbuf_printf(&sb, "SMID %u ", cm->cm_desc.Default.SMID); 341991554f2SKenneth D. Merry sbuf_vprintf(&sb, fmt, ap); 342991554f2SKenneth D. Merry sbuf_finish(&sb); 343c11c484fSScott Long mpr_print_field(cm->cm_sc, "%s", sbuf_data(&sb)); 344991554f2SKenneth D. Merry 345991554f2SKenneth D. Merry va_end(ap); 346991554f2SKenneth D. Merry } 347991554f2SKenneth D. Merry 348991554f2SKenneth D. Merry static void 349991554f2SKenneth D. Merry mprsas_remove_volume(struct mpr_softc *sc, struct mpr_command *tm) 350991554f2SKenneth D. Merry { 351991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 352991554f2SKenneth D. Merry struct mprsas_target *targ; 353991554f2SKenneth D. Merry uint16_t handle; 354991554f2SKenneth D. Merry 355991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 356991554f2SKenneth D. Merry 357991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 358991554f2SKenneth D. Merry handle = (uint16_t)(uintptr_t)tm->cm_complete_data; 359991554f2SKenneth D. Merry targ = tm->cm_targ; 360991554f2SKenneth D. Merry 361991554f2SKenneth D. Merry if (reply == NULL) { 362991554f2SKenneth D. Merry /* XXX retry the remove after the diag reset completes? */ 363991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "%s NULL reply resetting device " 364991554f2SKenneth D. Merry "0x%04x\n", __func__, handle); 365991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 366991554f2SKenneth D. Merry return; 367991554f2SKenneth D. Merry } 368991554f2SKenneth D. Merry 369d3f6eabfSStephen McConnell if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != 370d3f6eabfSStephen McConnell MPI2_IOCSTATUS_SUCCESS) { 37158581c13SStephen McConnell mpr_dprint(sc, MPR_ERROR, "IOCStatus = 0x%x while resetting " 372d3f6eabfSStephen McConnell "device 0x%x\n", le16toh(reply->IOCStatus), handle); 373991554f2SKenneth D. Merry } 374991554f2SKenneth D. Merry 375991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "Reset aborted %u commands\n", 376d3f6eabfSStephen McConnell le32toh(reply->TerminationCount)); 377991554f2SKenneth D. Merry mpr_free_reply(sc, tm->cm_reply_data); 378991554f2SKenneth D. Merry tm->cm_reply = NULL; /* Ensures the reply won't get re-freed */ 379991554f2SKenneth D. Merry 380991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "clearing target %u handle 0x%04x\n", 381991554f2SKenneth D. Merry targ->tid, handle); 382991554f2SKenneth D. Merry 383991554f2SKenneth D. Merry /* 384991554f2SKenneth D. Merry * Don't clear target if remove fails because things will get confusing. 385991554f2SKenneth D. Merry * Leave the devname and sasaddr intact so that we know to avoid reusing 386991554f2SKenneth D. Merry * this target id if possible, and so we can assign the same target id 387991554f2SKenneth D. Merry * to this device if it comes back in the future. 388991554f2SKenneth D. Merry */ 389d3f6eabfSStephen McConnell if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) == 390d3f6eabfSStephen McConnell MPI2_IOCSTATUS_SUCCESS) { 391991554f2SKenneth D. Merry targ = tm->cm_targ; 392991554f2SKenneth D. Merry targ->handle = 0x0; 393991554f2SKenneth D. Merry targ->encl_handle = 0x0; 394991554f2SKenneth D. Merry targ->encl_level_valid = 0x0; 395991554f2SKenneth D. Merry targ->encl_level = 0x0; 396991554f2SKenneth D. Merry targ->connector_name[0] = ' '; 397991554f2SKenneth D. Merry targ->connector_name[1] = ' '; 398991554f2SKenneth D. Merry targ->connector_name[2] = ' '; 399991554f2SKenneth D. Merry targ->connector_name[3] = ' '; 400991554f2SKenneth D. Merry targ->encl_slot = 0x0; 401991554f2SKenneth D. Merry targ->exp_dev_handle = 0x0; 402991554f2SKenneth D. Merry targ->phy_num = 0x0; 403991554f2SKenneth D. Merry targ->linkrate = 0x0; 404991554f2SKenneth D. Merry targ->devinfo = 0x0; 405991554f2SKenneth D. Merry targ->flags = 0x0; 406991554f2SKenneth D. Merry targ->scsi_req_desc_type = 0; 407991554f2SKenneth D. Merry } 408991554f2SKenneth D. Merry 409991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 410991554f2SKenneth D. Merry } 411991554f2SKenneth D. Merry 412991554f2SKenneth D. Merry /* 413e3c5965cSAlexander Motin * Retry mprsas_prepare_remove() if some previous attempt failed to allocate 414e3c5965cSAlexander Motin * high priority command due to limit reached. 415e3c5965cSAlexander Motin */ 416e3c5965cSAlexander Motin void 417e3c5965cSAlexander Motin mprsas_prepare_remove_retry(struct mprsas_softc *sassc) 418e3c5965cSAlexander Motin { 419e3c5965cSAlexander Motin struct mprsas_target *target; 420e3c5965cSAlexander Motin int i; 421e3c5965cSAlexander Motin 422e3c5965cSAlexander Motin if ((sassc->flags & MPRSAS_TOREMOVE) == 0) 423e3c5965cSAlexander Motin return; 424e3c5965cSAlexander Motin 425e3c5965cSAlexander Motin for (i = 0; i < sassc->maxtargets; i++) { 426e3c5965cSAlexander Motin target = &sassc->targets[i]; 427e3c5965cSAlexander Motin if ((target->flags & MPRSAS_TARGET_TOREMOVE) == 0) 428e3c5965cSAlexander Motin continue; 429e3c5965cSAlexander Motin if (TAILQ_EMPTY(&sassc->sc->high_priority_req_list)) 430e3c5965cSAlexander Motin return; 431e3c5965cSAlexander Motin target->flags &= ~MPRSAS_TARGET_TOREMOVE; 432e3c5965cSAlexander Motin if (target->flags & MPR_TARGET_FLAGS_VOLUME) 433e3c5965cSAlexander Motin mprsas_prepare_volume_remove(sassc, target->handle); 434e3c5965cSAlexander Motin else 435e3c5965cSAlexander Motin mprsas_prepare_remove(sassc, target->handle); 436e3c5965cSAlexander Motin } 437e3c5965cSAlexander Motin sassc->flags &= ~MPRSAS_TOREMOVE; 438e3c5965cSAlexander Motin } 439e3c5965cSAlexander Motin 440e3c5965cSAlexander Motin /* 441991554f2SKenneth D. Merry * No Need to call "MPI2_SAS_OP_REMOVE_DEVICE" For Volume removal. 442991554f2SKenneth D. Merry * Otherwise Volume Delete is same as Bare Drive Removal. 443991554f2SKenneth D. Merry */ 444991554f2SKenneth D. Merry void 445991554f2SKenneth D. Merry mprsas_prepare_volume_remove(struct mprsas_softc *sassc, uint16_t handle) 446991554f2SKenneth D. Merry { 447991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 448991554f2SKenneth D. Merry struct mpr_softc *sc; 449991554f2SKenneth D. Merry struct mpr_command *cm; 450991554f2SKenneth D. Merry struct mprsas_target *targ = NULL; 451991554f2SKenneth D. Merry 452991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 453991554f2SKenneth D. Merry sc = sassc->sc; 454991554f2SKenneth D. Merry 455991554f2SKenneth D. Merry targ = mprsas_find_target_by_handle(sassc, 0, handle); 456991554f2SKenneth D. Merry if (targ == NULL) { 457991554f2SKenneth D. Merry /* FIXME: what is the action? */ 458991554f2SKenneth D. Merry /* We don't know about this device? */ 459991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, 460991554f2SKenneth D. Merry "%s %d : invalid handle 0x%x \n", __func__,__LINE__, handle); 461991554f2SKenneth D. Merry return; 462991554f2SKenneth D. Merry } 463991554f2SKenneth D. Merry 464991554f2SKenneth D. Merry targ->flags |= MPRSAS_TARGET_INREMOVAL; 465991554f2SKenneth D. Merry 466991554f2SKenneth D. Merry cm = mprsas_alloc_tm(sc); 467991554f2SKenneth D. Merry if (cm == NULL) { 468e3c5965cSAlexander Motin targ->flags |= MPRSAS_TARGET_TOREMOVE; 469e3c5965cSAlexander Motin sassc->flags |= MPRSAS_TOREMOVE; 470991554f2SKenneth D. Merry return; 471991554f2SKenneth D. Merry } 472991554f2SKenneth D. Merry 473991554f2SKenneth D. Merry mprsas_rescan_target(sc, targ); 474991554f2SKenneth D. Merry 475991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req; 476991554f2SKenneth D. Merry req->DevHandle = targ->handle; 477991554f2SKenneth D. Merry req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; 478991554f2SKenneth D. Merry 47989d1c21fSKashyap D Desai if (!targ->is_nvme || sc->custom_nvme_tm_handling) { 480991554f2SKenneth D. Merry /* SAS Hard Link Reset / SATA Link Reset */ 481991554f2SKenneth D. Merry req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; 48289d1c21fSKashyap D Desai } else { 48389d1c21fSKashyap D Desai /* PCIe Protocol Level Reset*/ 48489d1c21fSKashyap D Desai req->MsgFlags = 48589d1c21fSKashyap D Desai MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; 48689d1c21fSKashyap D Desai } 487991554f2SKenneth D. Merry 488991554f2SKenneth D. Merry cm->cm_targ = targ; 489991554f2SKenneth D. Merry cm->cm_data = NULL; 490991554f2SKenneth D. Merry cm->cm_complete = mprsas_remove_volume; 491991554f2SKenneth D. Merry cm->cm_complete_data = (void *)(uintptr_t)handle; 492a2c14879SStephen McConnell 493a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n", 494a2c14879SStephen McConnell __func__, targ->tid); 495a2c14879SStephen McConnell mprsas_prepare_for_tm(sc, cm, targ, CAM_LUN_WILDCARD); 496a2c14879SStephen McConnell 497991554f2SKenneth D. Merry mpr_map_command(sc, cm); 498991554f2SKenneth D. Merry } 499991554f2SKenneth D. Merry 500991554f2SKenneth D. Merry /* 50167feec50SStephen McConnell * The firmware performs debounce on the link to avoid transient link errors 50267feec50SStephen McConnell * and false removals. When it does decide that link has been lost and a 50367feec50SStephen McConnell * device needs to go away, it expects that the host will perform a target reset 50467feec50SStephen McConnell * and then an op remove. The reset has the side-effect of aborting any 50567feec50SStephen McConnell * outstanding requests for the device, which is required for the op-remove to 50667feec50SStephen McConnell * succeed. It's not clear if the host should check for the device coming back 50767feec50SStephen McConnell * alive after the reset. 508991554f2SKenneth D. Merry */ 509991554f2SKenneth D. Merry void 510991554f2SKenneth D. Merry mprsas_prepare_remove(struct mprsas_softc *sassc, uint16_t handle) 511991554f2SKenneth D. Merry { 512991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 513991554f2SKenneth D. Merry struct mpr_softc *sc; 51446b9415fSScott Long struct mpr_command *tm; 515991554f2SKenneth D. Merry struct mprsas_target *targ = NULL; 516991554f2SKenneth D. Merry 517991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 518991554f2SKenneth D. Merry 519991554f2SKenneth D. Merry sc = sassc->sc; 520991554f2SKenneth D. Merry 521991554f2SKenneth D. Merry targ = mprsas_find_target_by_handle(sassc, 0, handle); 522991554f2SKenneth D. Merry if (targ == NULL) { 523991554f2SKenneth D. Merry /* FIXME: what is the action? */ 524991554f2SKenneth D. Merry /* We don't know about this device? */ 525991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s : invalid handle 0x%x \n", 526991554f2SKenneth D. Merry __func__, handle); 527991554f2SKenneth D. Merry return; 528991554f2SKenneth D. Merry } 529991554f2SKenneth D. Merry 530991554f2SKenneth D. Merry targ->flags |= MPRSAS_TARGET_INREMOVAL; 531991554f2SKenneth D. Merry 53246b9415fSScott Long tm = mprsas_alloc_tm(sc); 53346b9415fSScott Long if (tm == NULL) { 534e3c5965cSAlexander Motin targ->flags |= MPRSAS_TARGET_TOREMOVE; 535e3c5965cSAlexander Motin sassc->flags |= MPRSAS_TOREMOVE; 536991554f2SKenneth D. Merry return; 537991554f2SKenneth D. Merry } 538991554f2SKenneth D. Merry 539991554f2SKenneth D. Merry mprsas_rescan_target(sc, targ); 540991554f2SKenneth D. Merry 54146b9415fSScott Long req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 542991554f2SKenneth D. Merry req->DevHandle = htole16(targ->handle); 543991554f2SKenneth D. Merry req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; 544991554f2SKenneth D. Merry 545991554f2SKenneth D. Merry /* SAS Hard Link Reset / SATA Link Reset */ 546991554f2SKenneth D. Merry req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; 547991554f2SKenneth D. Merry 54846b9415fSScott Long tm->cm_targ = targ; 54946b9415fSScott Long tm->cm_data = NULL; 55046b9415fSScott Long tm->cm_complete = mprsas_remove_device; 55146b9415fSScott Long tm->cm_complete_data = (void *)(uintptr_t)handle; 552a2c14879SStephen McConnell 553a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n", 554a2c14879SStephen McConnell __func__, targ->tid); 55546b9415fSScott Long mprsas_prepare_for_tm(sc, tm, targ, CAM_LUN_WILDCARD); 556a2c14879SStephen McConnell 55746b9415fSScott Long mpr_map_command(sc, tm); 558991554f2SKenneth D. Merry } 559991554f2SKenneth D. Merry 560991554f2SKenneth D. Merry static void 561991554f2SKenneth D. Merry mprsas_remove_device(struct mpr_softc *sc, struct mpr_command *tm) 562991554f2SKenneth D. Merry { 563991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 564991554f2SKenneth D. Merry MPI2_SAS_IOUNIT_CONTROL_REQUEST *req; 565991554f2SKenneth D. Merry struct mprsas_target *targ; 566991554f2SKenneth D. Merry uint16_t handle; 567991554f2SKenneth D. Merry 568991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 569991554f2SKenneth D. Merry 570991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 571991554f2SKenneth D. Merry handle = (uint16_t)(uintptr_t)tm->cm_complete_data; 572991554f2SKenneth D. Merry targ = tm->cm_targ; 573991554f2SKenneth D. Merry 574991554f2SKenneth D. Merry /* 575991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 576991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 577991554f2SKenneth D. Merry * task management commands don't have S/G lists. 578991554f2SKenneth D. Merry */ 579991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 580991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for remove of " 581991554f2SKenneth D. Merry "handle %#04x! This should not happen!\n", __func__, 582991554f2SKenneth D. Merry tm->cm_flags, handle); 583991554f2SKenneth D. Merry } 584991554f2SKenneth D. Merry 585991554f2SKenneth D. Merry if (reply == NULL) { 586991554f2SKenneth D. Merry /* XXX retry the remove after the diag reset completes? */ 587991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "%s NULL reply resetting device " 588991554f2SKenneth D. Merry "0x%04x\n", __func__, handle); 589991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 590991554f2SKenneth D. Merry return; 591991554f2SKenneth D. Merry } 592991554f2SKenneth D. Merry 593d3f6eabfSStephen McConnell if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != 594d3f6eabfSStephen McConnell MPI2_IOCSTATUS_SUCCESS) { 59558581c13SStephen McConnell mpr_dprint(sc, MPR_ERROR, "IOCStatus = 0x%x while resetting " 596991554f2SKenneth D. Merry "device 0x%x\n", le16toh(reply->IOCStatus), handle); 597991554f2SKenneth D. Merry } 598991554f2SKenneth D. Merry 599991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "Reset aborted %u commands\n", 600991554f2SKenneth D. Merry le32toh(reply->TerminationCount)); 601991554f2SKenneth D. Merry mpr_free_reply(sc, tm->cm_reply_data); 602991554f2SKenneth D. Merry tm->cm_reply = NULL; /* Ensures the reply won't get re-freed */ 603991554f2SKenneth D. Merry 604991554f2SKenneth D. Merry /* Reuse the existing command */ 605991554f2SKenneth D. Merry req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)tm->cm_req; 606991554f2SKenneth D. Merry memset(req, 0, sizeof(*req)); 607991554f2SKenneth D. Merry req->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; 608991554f2SKenneth D. Merry req->Operation = MPI2_SAS_OP_REMOVE_DEVICE; 609991554f2SKenneth D. Merry req->DevHandle = htole16(handle); 610991554f2SKenneth D. Merry tm->cm_data = NULL; 611991554f2SKenneth D. Merry tm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 612991554f2SKenneth D. Merry tm->cm_complete = mprsas_remove_complete; 613991554f2SKenneth D. Merry tm->cm_complete_data = (void *)(uintptr_t)handle; 614991554f2SKenneth D. Merry 6154c1cdd4aSWarner Losh /* 6164c1cdd4aSWarner Losh * Wait to send the REMOVE_DEVICE until all the commands have cleared. 6174c1cdd4aSWarner Losh * They should be aborted or time out and we'll kick thus off there 6184c1cdd4aSWarner Losh * if so. 6194c1cdd4aSWarner Losh */ 6204c1cdd4aSWarner Losh if (TAILQ_FIRST(&targ->commands) == NULL) { 6214c1cdd4aSWarner Losh mpr_dprint(sc, MPR_INFO, "No pending commands: starting remove_device\n"); 622991554f2SKenneth D. Merry mpr_map_command(sc, tm); 6234c1cdd4aSWarner Losh targ->pending_remove_tm = NULL; 6244c1cdd4aSWarner Losh } else { 6254c1cdd4aSWarner Losh targ->pending_remove_tm = tm; 6264c1cdd4aSWarner Losh } 627991554f2SKenneth D. Merry 628a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "clearing target %u handle 0x%04x\n", 629991554f2SKenneth D. Merry targ->tid, handle); 630991554f2SKenneth D. Merry if (targ->encl_level_valid) { 631a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "At enclosure level %d, slot %d, " 632991554f2SKenneth D. Merry "connector name (%4s)\n", targ->encl_level, targ->encl_slot, 633991554f2SKenneth D. Merry targ->connector_name); 634991554f2SKenneth D. Merry } 635991554f2SKenneth D. Merry } 636991554f2SKenneth D. Merry 637991554f2SKenneth D. Merry static void 638991554f2SKenneth D. Merry mprsas_remove_complete(struct mpr_softc *sc, struct mpr_command *tm) 639991554f2SKenneth D. Merry { 640991554f2SKenneth D. Merry MPI2_SAS_IOUNIT_CONTROL_REPLY *reply; 641991554f2SKenneth D. Merry uint16_t handle; 642991554f2SKenneth D. Merry struct mprsas_target *targ; 643991554f2SKenneth D. Merry struct mprsas_lun *lun; 644991554f2SKenneth D. Merry 645991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 646991554f2SKenneth D. Merry 647991554f2SKenneth D. Merry reply = (MPI2_SAS_IOUNIT_CONTROL_REPLY *)tm->cm_reply; 648991554f2SKenneth D. Merry handle = (uint16_t)(uintptr_t)tm->cm_complete_data; 649991554f2SKenneth D. Merry 6504c1cdd4aSWarner Losh targ = tm->cm_targ; 6514c1cdd4aSWarner Losh 6524c1cdd4aSWarner Losh /* 6534c1cdd4aSWarner Losh * At this point, we should have no pending commands for the target. 6544c1cdd4aSWarner Losh * The remove target has just completed. 6554c1cdd4aSWarner Losh */ 6564c1cdd4aSWarner Losh KASSERT(TAILQ_FIRST(&targ->commands) == NULL, 6574c1cdd4aSWarner Losh ("%s: no commands should be pending\n", __func__)); 6584c1cdd4aSWarner Losh 659991554f2SKenneth D. Merry /* 660991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 661991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 662991554f2SKenneth D. Merry * task management commands don't have S/G lists. 663991554f2SKenneth D. Merry */ 664991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 665991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s: cm_flags = %#x for remove of " 666991554f2SKenneth D. Merry "handle %#04x! This should not happen!\n", __func__, 667991554f2SKenneth D. Merry tm->cm_flags, handle); 668991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 669991554f2SKenneth D. Merry return; 670991554f2SKenneth D. Merry } 671991554f2SKenneth D. Merry 672991554f2SKenneth D. Merry if (reply == NULL) { 673991554f2SKenneth D. Merry /* most likely a chip reset */ 674991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "%s NULL reply removing device " 675991554f2SKenneth D. Merry "0x%04x\n", __func__, handle); 676991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 677991554f2SKenneth D. Merry return; 678991554f2SKenneth D. Merry } 679991554f2SKenneth D. Merry 680991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s on handle 0x%04x, IOCStatus= 0x%x\n", 681991554f2SKenneth D. Merry __func__, handle, le16toh(reply->IOCStatus)); 682991554f2SKenneth D. Merry 683991554f2SKenneth D. Merry /* 684991554f2SKenneth D. Merry * Don't clear target if remove fails because things will get confusing. 685991554f2SKenneth D. Merry * Leave the devname and sasaddr intact so that we know to avoid reusing 686991554f2SKenneth D. Merry * this target id if possible, and so we can assign the same target id 687991554f2SKenneth D. Merry * to this device if it comes back in the future. 688991554f2SKenneth D. Merry */ 689d3f6eabfSStephen McConnell if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) == 690d3f6eabfSStephen McConnell MPI2_IOCSTATUS_SUCCESS) { 691991554f2SKenneth D. Merry targ->handle = 0x0; 692991554f2SKenneth D. Merry targ->encl_handle = 0x0; 693991554f2SKenneth D. Merry targ->encl_level_valid = 0x0; 694991554f2SKenneth D. Merry targ->encl_level = 0x0; 695991554f2SKenneth D. Merry targ->connector_name[0] = ' '; 696991554f2SKenneth D. Merry targ->connector_name[1] = ' '; 697991554f2SKenneth D. Merry targ->connector_name[2] = ' '; 698991554f2SKenneth D. Merry targ->connector_name[3] = ' '; 699991554f2SKenneth D. Merry targ->encl_slot = 0x0; 700991554f2SKenneth D. Merry targ->exp_dev_handle = 0x0; 701991554f2SKenneth D. Merry targ->phy_num = 0x0; 702991554f2SKenneth D. Merry targ->linkrate = 0x0; 703991554f2SKenneth D. Merry targ->devinfo = 0x0; 704991554f2SKenneth D. Merry targ->flags = 0x0; 705991554f2SKenneth D. Merry targ->scsi_req_desc_type = 0; 706991554f2SKenneth D. Merry 707991554f2SKenneth D. Merry while (!SLIST_EMPTY(&targ->luns)) { 708991554f2SKenneth D. Merry lun = SLIST_FIRST(&targ->luns); 709991554f2SKenneth D. Merry SLIST_REMOVE_HEAD(&targ->luns, lun_link); 710991554f2SKenneth D. Merry free(lun, M_MPR); 711991554f2SKenneth D. Merry } 712991554f2SKenneth D. Merry } 713991554f2SKenneth D. Merry 714991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 715991554f2SKenneth D. Merry } 716991554f2SKenneth D. Merry 717991554f2SKenneth D. Merry static int 718991554f2SKenneth D. Merry mprsas_register_events(struct mpr_softc *sc) 719991554f2SKenneth D. Merry { 720991554f2SKenneth D. Merry uint8_t events[16]; 721991554f2SKenneth D. Merry 722991554f2SKenneth D. Merry bzero(events, 16); 723991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); 724991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_DISCOVERY); 725991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE); 726991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE); 727991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW); 728991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST); 729991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE); 730991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST); 731991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_IR_VOLUME); 732991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_IR_PHYSICAL_DISK); 733991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_IR_OPERATION_STATUS); 734991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_TEMP_THRESHOLD); 7355f5baf0eSAlexander Motin setbit(events, MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR); 73667feec50SStephen McConnell if (sc->facts->MsgVersion >= MPI2_VERSION_02_06) { 7372bbc5fcbSStephen McConnell setbit(events, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); 73867feec50SStephen McConnell if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) { 73967feec50SStephen McConnell setbit(events, MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE); 74067feec50SStephen McConnell setbit(events, MPI2_EVENT_PCIE_ENUMERATION); 74167feec50SStephen McConnell setbit(events, MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST); 74267feec50SStephen McConnell } 74367feec50SStephen McConnell } 744991554f2SKenneth D. Merry 745991554f2SKenneth D. Merry mpr_register_events(sc, events, mprsas_evt_handler, NULL, 746991554f2SKenneth D. Merry &sc->sassc->mprsas_eh); 747991554f2SKenneth D. Merry 748991554f2SKenneth D. Merry return (0); 749991554f2SKenneth D. Merry } 750991554f2SKenneth D. Merry 751991554f2SKenneth D. Merry int 752991554f2SKenneth D. Merry mpr_attach_sas(struct mpr_softc *sc) 753991554f2SKenneth D. Merry { 754991554f2SKenneth D. Merry struct mprsas_softc *sassc; 755991554f2SKenneth D. Merry cam_status status; 75662a09ee9SAlexander Motin int unit, error = 0, reqs; 757991554f2SKenneth D. Merry 758991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 759757ff642SScott Long mpr_dprint(sc, MPR_INIT, "%s entered\n", __func__); 760991554f2SKenneth D. Merry 761991554f2SKenneth D. Merry sassc = malloc(sizeof(struct mprsas_softc), M_MPR, M_WAITOK|M_ZERO); 762991554f2SKenneth D. Merry 763991554f2SKenneth D. Merry /* 764a2c14879SStephen McConnell * XXX MaxTargets could change during a reinit. Since we don't 765991554f2SKenneth D. Merry * resize the targets[] array during such an event, cache the value 766991554f2SKenneth D. Merry * of MaxTargets here so that we don't get into trouble later. This 767991554f2SKenneth D. Merry * should move into the reinit logic. 768991554f2SKenneth D. Merry */ 769327f2e6cSStephen McConnell sassc->maxtargets = sc->facts->MaxTargets + sc->facts->MaxVolumes; 770991554f2SKenneth D. Merry sassc->targets = malloc(sizeof(struct mprsas_target) * 771991554f2SKenneth D. Merry sassc->maxtargets, M_MPR, M_WAITOK|M_ZERO); 772991554f2SKenneth D. Merry sc->sassc = sassc; 773991554f2SKenneth D. Merry sassc->sc = sc; 774991554f2SKenneth D. Merry 77562a09ee9SAlexander Motin reqs = sc->num_reqs - sc->num_prireqs - 1; 77662a09ee9SAlexander Motin if ((sassc->devq = cam_simq_alloc(reqs)) == NULL) { 777757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, "Cannot allocate SIMQ\n"); 778991554f2SKenneth D. Merry error = ENOMEM; 779991554f2SKenneth D. Merry goto out; 780991554f2SKenneth D. Merry } 781991554f2SKenneth D. Merry 782991554f2SKenneth D. Merry unit = device_get_unit(sc->mpr_dev); 783991554f2SKenneth D. Merry sassc->sim = cam_sim_alloc(mprsas_action, mprsas_poll, "mpr", sassc, 78462a09ee9SAlexander Motin unit, &sc->mpr_mtx, reqs, reqs, sassc->devq); 785991554f2SKenneth D. Merry if (sassc->sim == NULL) { 786757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, "Cannot allocate SIM\n"); 787991554f2SKenneth D. Merry error = EINVAL; 788991554f2SKenneth D. Merry goto out; 789991554f2SKenneth D. Merry } 790991554f2SKenneth D. Merry 791991554f2SKenneth D. Merry TAILQ_INIT(&sassc->ev_queue); 792991554f2SKenneth D. Merry 793991554f2SKenneth D. Merry /* Initialize taskqueue for Event Handling */ 794991554f2SKenneth D. Merry TASK_INIT(&sassc->ev_task, 0, mprsas_firmware_event_work, sc); 795991554f2SKenneth D. Merry sassc->ev_tq = taskqueue_create("mpr_taskq", M_NOWAIT | M_ZERO, 796991554f2SKenneth D. Merry taskqueue_thread_enqueue, &sassc->ev_tq); 797c2a0f07aSAlexander Motin taskqueue_start_threads(&sassc->ev_tq, 1, PRIBIO, "%s taskq", 798991554f2SKenneth D. Merry device_get_nameunit(sc->mpr_dev)); 799991554f2SKenneth D. Merry 800991554f2SKenneth D. Merry mpr_lock(sc); 801991554f2SKenneth D. Merry 802991554f2SKenneth D. Merry /* 803991554f2SKenneth D. Merry * XXX There should be a bus for every port on the adapter, but since 804991554f2SKenneth D. Merry * we're just going to fake the topology for now, we'll pretend that 805991554f2SKenneth D. Merry * everything is just a target on a single bus. 806991554f2SKenneth D. Merry */ 807991554f2SKenneth D. Merry if ((error = xpt_bus_register(sassc->sim, sc->mpr_dev, 0)) != 0) { 808757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, 809757ff642SScott Long "Error %d registering SCSI bus\n", error); 810991554f2SKenneth D. Merry mpr_unlock(sc); 811991554f2SKenneth D. Merry goto out; 812991554f2SKenneth D. Merry } 813991554f2SKenneth D. Merry 814991554f2SKenneth D. Merry /* 815a2c14879SStephen McConnell * Assume that discovery events will start right away. 816991554f2SKenneth D. Merry * 817991554f2SKenneth D. Merry * Hold off boot until discovery is complete. 818991554f2SKenneth D. Merry */ 819991554f2SKenneth D. Merry sassc->flags |= MPRSAS_IN_STARTUP | MPRSAS_IN_DISCOVERY; 820991554f2SKenneth D. Merry sc->sassc->startup_refcount = 0; 821991554f2SKenneth D. Merry mprsas_startup_increment(sassc); 822991554f2SKenneth D. Merry 82302d81940SAlexander Motin mpr_unlock(sc); 82402d81940SAlexander Motin 825991554f2SKenneth D. Merry /* 826991554f2SKenneth D. Merry * Register for async events so we can determine the EEDP 827991554f2SKenneth D. Merry * capabilities of devices. 828991554f2SKenneth D. Merry */ 829991554f2SKenneth D. Merry status = xpt_create_path(&sassc->path, /*periph*/NULL, 830991554f2SKenneth D. Merry cam_sim_path(sc->sassc->sim), CAM_TARGET_WILDCARD, 831991554f2SKenneth D. Merry CAM_LUN_WILDCARD); 832991554f2SKenneth D. Merry if (status != CAM_REQ_CMP) { 833757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, 834757ff642SScott Long "Error %#x creating sim path\n", status); 835991554f2SKenneth D. Merry sassc->path = NULL; 836991554f2SKenneth D. Merry } else { 837991554f2SKenneth D. Merry int event; 838991554f2SKenneth D. Merry 83902d81940SAlexander Motin event = AC_ADVINFO_CHANGED; 840991554f2SKenneth D. Merry status = xpt_register_async(event, mprsas_async, sc, 841991554f2SKenneth D. Merry sassc->path); 84207aa4de1SKenneth D. Merry 843991554f2SKenneth D. Merry if (status != CAM_REQ_CMP) { 844991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, 845991554f2SKenneth D. Merry "Error %#x registering async handler for " 846991554f2SKenneth D. Merry "AC_ADVINFO_CHANGED events\n", status); 847991554f2SKenneth D. Merry xpt_free_path(sassc->path); 848991554f2SKenneth D. Merry sassc->path = NULL; 849991554f2SKenneth D. Merry } 850991554f2SKenneth D. Merry } 851991554f2SKenneth D. Merry if (status != CAM_REQ_CMP) { 852991554f2SKenneth D. Merry /* 853991554f2SKenneth D. Merry * EEDP use is the exception, not the rule. 854991554f2SKenneth D. Merry * Warn the user, but do not fail to attach. 855991554f2SKenneth D. Merry */ 856991554f2SKenneth D. Merry mpr_printf(sc, "EEDP capabilities disabled.\n"); 857991554f2SKenneth D. Merry } 858991554f2SKenneth D. Merry 859991554f2SKenneth D. Merry mprsas_register_events(sc); 860991554f2SKenneth D. Merry out: 861991554f2SKenneth D. Merry if (error) 862991554f2SKenneth D. Merry mpr_detach_sas(sc); 863757ff642SScott Long 864757ff642SScott Long mpr_dprint(sc, MPR_INIT, "%s exit, error= %d\n", __func__, error); 865991554f2SKenneth D. Merry return (error); 866991554f2SKenneth D. Merry } 867991554f2SKenneth D. Merry 868991554f2SKenneth D. Merry int 869991554f2SKenneth D. Merry mpr_detach_sas(struct mpr_softc *sc) 870991554f2SKenneth D. Merry { 871991554f2SKenneth D. Merry struct mprsas_softc *sassc; 872991554f2SKenneth D. Merry struct mprsas_lun *lun, *lun_tmp; 873991554f2SKenneth D. Merry struct mprsas_target *targ; 874991554f2SKenneth D. Merry int i; 875991554f2SKenneth D. Merry 876991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 877991554f2SKenneth D. Merry 878991554f2SKenneth D. Merry if (sc->sassc == NULL) 879991554f2SKenneth D. Merry return (0); 880991554f2SKenneth D. Merry 881991554f2SKenneth D. Merry sassc = sc->sassc; 882991554f2SKenneth D. Merry mpr_deregister_events(sc, sassc->mprsas_eh); 883991554f2SKenneth D. Merry 884991554f2SKenneth D. Merry /* 885991554f2SKenneth D. Merry * Drain and free the event handling taskqueue with the lock 886991554f2SKenneth D. Merry * unheld so that any parallel processing tasks drain properly 887991554f2SKenneth D. Merry * without deadlocking. 888991554f2SKenneth D. Merry */ 889991554f2SKenneth D. Merry if (sassc->ev_tq != NULL) 890991554f2SKenneth D. Merry taskqueue_free(sassc->ev_tq); 891991554f2SKenneth D. Merry 892991554f2SKenneth D. Merry /* Deregister our async handler */ 893991554f2SKenneth D. Merry if (sassc->path != NULL) { 894991554f2SKenneth D. Merry xpt_register_async(0, mprsas_async, sc, sassc->path); 895991554f2SKenneth D. Merry xpt_free_path(sassc->path); 896991554f2SKenneth D. Merry sassc->path = NULL; 897991554f2SKenneth D. Merry } 898991554f2SKenneth D. Merry 89902d81940SAlexander Motin /* Make sure CAM doesn't wedge if we had to bail out early. */ 90002d81940SAlexander Motin mpr_lock(sc); 90102d81940SAlexander Motin 90202d81940SAlexander Motin while (sassc->startup_refcount != 0) 90302d81940SAlexander Motin mprsas_startup_decrement(sassc); 90402d81940SAlexander Motin 905991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_IN_STARTUP) 906991554f2SKenneth D. Merry xpt_release_simq(sassc->sim, 1); 907991554f2SKenneth D. Merry 908991554f2SKenneth D. Merry if (sassc->sim != NULL) { 909991554f2SKenneth D. Merry xpt_bus_deregister(cam_sim_path(sassc->sim)); 910991554f2SKenneth D. Merry cam_sim_free(sassc->sim, FALSE); 911991554f2SKenneth D. Merry } 912991554f2SKenneth D. Merry 913991554f2SKenneth D. Merry mpr_unlock(sc); 914991554f2SKenneth D. Merry 915991554f2SKenneth D. Merry if (sassc->devq != NULL) 916991554f2SKenneth D. Merry cam_simq_free(sassc->devq); 917991554f2SKenneth D. Merry 918991554f2SKenneth D. Merry for (i = 0; i < sassc->maxtargets; i++) { 919991554f2SKenneth D. Merry targ = &sassc->targets[i]; 920991554f2SKenneth D. Merry SLIST_FOREACH_SAFE(lun, &targ->luns, lun_link, lun_tmp) { 921991554f2SKenneth D. Merry free(lun, M_MPR); 922991554f2SKenneth D. Merry } 923991554f2SKenneth D. Merry } 924991554f2SKenneth D. Merry free(sassc->targets, M_MPR); 925991554f2SKenneth D. Merry free(sassc, M_MPR); 926991554f2SKenneth D. Merry sc->sassc = NULL; 927991554f2SKenneth D. Merry 928991554f2SKenneth D. Merry return (0); 929991554f2SKenneth D. Merry } 930991554f2SKenneth D. Merry 931991554f2SKenneth D. Merry void 932991554f2SKenneth D. Merry mprsas_discovery_end(struct mprsas_softc *sassc) 933991554f2SKenneth D. Merry { 934991554f2SKenneth D. Merry struct mpr_softc *sc = sassc->sc; 935991554f2SKenneth D. Merry 936991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 937991554f2SKenneth D. Merry 938327f2e6cSStephen McConnell /* 939327f2e6cSStephen McConnell * After discovery has completed, check the mapping table for any 940327f2e6cSStephen McConnell * missing devices and update their missing counts. Only do this once 941327f2e6cSStephen McConnell * whenever the driver is initialized so that missing counts aren't 942327f2e6cSStephen McConnell * updated unnecessarily. Note that just because discovery has 943327f2e6cSStephen McConnell * completed doesn't mean that events have been processed yet. The 944327f2e6cSStephen McConnell * check_devices function is a callout timer that checks if ALL devices 945327f2e6cSStephen McConnell * are missing. If so, it will wait a little longer for events to 946327f2e6cSStephen McConnell * complete and keep resetting itself until some device in the mapping 947327f2e6cSStephen McConnell * table is not missing, meaning that event processing has started. 948327f2e6cSStephen McConnell */ 949327f2e6cSStephen McConnell if (sc->track_mapping_events) { 950327f2e6cSStephen McConnell mpr_dprint(sc, MPR_XINFO | MPR_MAPPING, "Discovery has " 951327f2e6cSStephen McConnell "completed. Check for missing devices in the mapping " 952327f2e6cSStephen McConnell "table.\n"); 953327f2e6cSStephen McConnell callout_reset(&sc->device_check_callout, 954327f2e6cSStephen McConnell MPR_MISSING_CHECK_DELAY * hz, mpr_mapping_check_devices, 955327f2e6cSStephen McConnell sc); 956327f2e6cSStephen McConnell } 957991554f2SKenneth D. Merry } 958991554f2SKenneth D. Merry 959991554f2SKenneth D. Merry static void 960991554f2SKenneth D. Merry mprsas_action(struct cam_sim *sim, union ccb *ccb) 961991554f2SKenneth D. Merry { 962991554f2SKenneth D. Merry struct mprsas_softc *sassc; 963991554f2SKenneth D. Merry 964991554f2SKenneth D. Merry sassc = cam_sim_softc(sim); 965991554f2SKenneth D. Merry 966991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 967a2c14879SStephen McConnell mpr_dprint(sassc->sc, MPR_TRACE, "ccb func_code 0x%x\n", 968991554f2SKenneth D. Merry ccb->ccb_h.func_code); 969991554f2SKenneth D. Merry mtx_assert(&sassc->sc->mpr_mtx, MA_OWNED); 970991554f2SKenneth D. Merry 971991554f2SKenneth D. Merry switch (ccb->ccb_h.func_code) { 972991554f2SKenneth D. Merry case XPT_PATH_INQ: 973991554f2SKenneth D. Merry { 974991554f2SKenneth D. Merry struct ccb_pathinq *cpi = &ccb->cpi; 97532b0a21eSStephen McConnell struct mpr_softc *sc = sassc->sc; 976991554f2SKenneth D. Merry 977991554f2SKenneth D. Merry cpi->version_num = 1; 978991554f2SKenneth D. Merry cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; 979991554f2SKenneth D. Merry cpi->target_sprt = 0; 980991554f2SKenneth D. Merry cpi->hba_misc = PIM_NOBUSRESET | PIM_UNMAPPED | PIM_NOSCAN; 981991554f2SKenneth D. Merry cpi->hba_eng_cnt = 0; 982991554f2SKenneth D. Merry cpi->max_target = sassc->maxtargets - 1; 983991554f2SKenneth D. Merry cpi->max_lun = 255; 984327f2e6cSStephen McConnell 985327f2e6cSStephen McConnell /* 986327f2e6cSStephen McConnell * initiator_id is set here to an ID outside the set of valid 987327f2e6cSStephen McConnell * target IDs (including volumes). 988327f2e6cSStephen McConnell */ 989327f2e6cSStephen McConnell cpi->initiator_id = sassc->maxtargets; 9904195c7deSAlan Somers strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 9914195c7deSAlan Somers strlcpy(cpi->hba_vid, "Avago Tech", HBA_IDLEN); 9924195c7deSAlan Somers strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); 993991554f2SKenneth D. Merry cpi->unit_number = cam_sim_unit(sim); 994991554f2SKenneth D. Merry cpi->bus_id = cam_sim_bus(sim); 995991554f2SKenneth D. Merry /* 996991554f2SKenneth D. Merry * XXXSLM-I think this needs to change based on config page or 997991554f2SKenneth D. Merry * something instead of hardcoded to 150000. 998991554f2SKenneth D. Merry */ 999991554f2SKenneth D. Merry cpi->base_transfer_speed = 150000; 1000991554f2SKenneth D. Merry cpi->transport = XPORT_SAS; 1001991554f2SKenneth D. Merry cpi->transport_version = 0; 1002991554f2SKenneth D. Merry cpi->protocol = PROTO_SCSI; 1003991554f2SKenneth D. Merry cpi->protocol_version = SCSI_REV_SPC; 10044f5d6573SAlexander Motin cpi->maxio = sc->maxio; 1005a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1006991554f2SKenneth D. Merry break; 1007991554f2SKenneth D. Merry } 1008991554f2SKenneth D. Merry case XPT_GET_TRAN_SETTINGS: 1009991554f2SKenneth D. Merry { 1010991554f2SKenneth D. Merry struct ccb_trans_settings *cts; 1011991554f2SKenneth D. Merry struct ccb_trans_settings_sas *sas; 1012991554f2SKenneth D. Merry struct ccb_trans_settings_scsi *scsi; 1013991554f2SKenneth D. Merry struct mprsas_target *targ; 1014991554f2SKenneth D. Merry 1015991554f2SKenneth D. Merry cts = &ccb->cts; 1016991554f2SKenneth D. Merry sas = &cts->xport_specific.sas; 1017991554f2SKenneth D. Merry scsi = &cts->proto_specific.scsi; 1018991554f2SKenneth D. Merry 1019991554f2SKenneth D. Merry KASSERT(cts->ccb_h.target_id < sassc->maxtargets, 1020991554f2SKenneth D. Merry ("Target %d out of bounds in XPT_GET_TRAN_SETTINGS\n", 1021991554f2SKenneth D. Merry cts->ccb_h.target_id)); 1022991554f2SKenneth D. Merry targ = &sassc->targets[cts->ccb_h.target_id]; 1023991554f2SKenneth D. Merry if (targ->handle == 0x0) { 1024a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1025991554f2SKenneth D. Merry break; 1026991554f2SKenneth D. Merry } 1027991554f2SKenneth D. Merry 1028991554f2SKenneth D. Merry cts->protocol_version = SCSI_REV_SPC2; 1029991554f2SKenneth D. Merry cts->transport = XPORT_SAS; 1030991554f2SKenneth D. Merry cts->transport_version = 0; 1031991554f2SKenneth D. Merry 1032991554f2SKenneth D. Merry sas->valid = CTS_SAS_VALID_SPEED; 1033991554f2SKenneth D. Merry switch (targ->linkrate) { 1034991554f2SKenneth D. Merry case 0x08: 1035991554f2SKenneth D. Merry sas->bitrate = 150000; 1036991554f2SKenneth D. Merry break; 1037991554f2SKenneth D. Merry case 0x09: 1038991554f2SKenneth D. Merry sas->bitrate = 300000; 1039991554f2SKenneth D. Merry break; 1040991554f2SKenneth D. Merry case 0x0a: 1041991554f2SKenneth D. Merry sas->bitrate = 600000; 1042991554f2SKenneth D. Merry break; 104382315915SAlexander Motin case 0x0b: 104482315915SAlexander Motin sas->bitrate = 1200000; 104582315915SAlexander Motin break; 1046991554f2SKenneth D. Merry default: 1047991554f2SKenneth D. Merry sas->valid = 0; 1048991554f2SKenneth D. Merry } 1049991554f2SKenneth D. Merry 1050991554f2SKenneth D. Merry cts->protocol = PROTO_SCSI; 1051991554f2SKenneth D. Merry scsi->valid = CTS_SCSI_VALID_TQ; 1052991554f2SKenneth D. Merry scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; 1053991554f2SKenneth D. Merry 1054a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1055991554f2SKenneth D. Merry break; 1056991554f2SKenneth D. Merry } 1057991554f2SKenneth D. Merry case XPT_CALC_GEOMETRY: 1058991554f2SKenneth D. Merry cam_calc_geometry(&ccb->ccg, /*extended*/1); 1059a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1060991554f2SKenneth D. Merry break; 1061991554f2SKenneth D. Merry case XPT_RESET_DEV: 10627a2a6a1aSStephen McConnell mpr_dprint(sassc->sc, MPR_XINFO, "mprsas_action " 10637a2a6a1aSStephen McConnell "XPT_RESET_DEV\n"); 1064991554f2SKenneth D. Merry mprsas_action_resetdev(sassc, ccb); 1065991554f2SKenneth D. Merry return; 1066991554f2SKenneth D. Merry case XPT_RESET_BUS: 1067991554f2SKenneth D. Merry case XPT_ABORT: 1068991554f2SKenneth D. Merry case XPT_TERM_IO: 10697a2a6a1aSStephen McConnell mpr_dprint(sassc->sc, MPR_XINFO, "mprsas_action faking success " 10707a2a6a1aSStephen McConnell "for abort or reset\n"); 1071a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1072991554f2SKenneth D. Merry break; 1073991554f2SKenneth D. Merry case XPT_SCSI_IO: 1074991554f2SKenneth D. Merry mprsas_action_scsiio(sassc, ccb); 1075991554f2SKenneth D. Merry return; 1076991554f2SKenneth D. Merry case XPT_SMP_IO: 1077991554f2SKenneth D. Merry mprsas_action_smpio(sassc, ccb); 1078991554f2SKenneth D. Merry return; 1079991554f2SKenneth D. Merry default: 1080a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_FUNC_NOTAVAIL); 1081991554f2SKenneth D. Merry break; 1082991554f2SKenneth D. Merry } 1083991554f2SKenneth D. Merry xpt_done(ccb); 1084991554f2SKenneth D. Merry 1085991554f2SKenneth D. Merry } 1086991554f2SKenneth D. Merry 1087991554f2SKenneth D. Merry static void 1088991554f2SKenneth D. Merry mprsas_announce_reset(struct mpr_softc *sc, uint32_t ac_code, 1089991554f2SKenneth D. Merry target_id_t target_id, lun_id_t lun_id) 1090991554f2SKenneth D. Merry { 1091991554f2SKenneth D. Merry path_id_t path_id = cam_sim_path(sc->sassc->sim); 1092991554f2SKenneth D. Merry struct cam_path *path; 1093991554f2SKenneth D. Merry 1094991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s code %x target %d lun %jx\n", __func__, 1095991554f2SKenneth D. Merry ac_code, target_id, (uintmax_t)lun_id); 1096991554f2SKenneth D. Merry 1097991554f2SKenneth D. Merry if (xpt_create_path(&path, NULL, 1098991554f2SKenneth D. Merry path_id, target_id, lun_id) != CAM_REQ_CMP) { 1099991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "unable to create path for reset " 1100991554f2SKenneth D. Merry "notification\n"); 1101991554f2SKenneth D. Merry return; 1102991554f2SKenneth D. Merry } 1103991554f2SKenneth D. Merry 1104991554f2SKenneth D. Merry xpt_async(ac_code, path, NULL); 1105991554f2SKenneth D. Merry xpt_free_path(path); 1106991554f2SKenneth D. Merry } 1107991554f2SKenneth D. Merry 1108991554f2SKenneth D. Merry static void 1109991554f2SKenneth D. Merry mprsas_complete_all_commands(struct mpr_softc *sc) 1110991554f2SKenneth D. Merry { 1111991554f2SKenneth D. Merry struct mpr_command *cm; 1112991554f2SKenneth D. Merry int i; 1113991554f2SKenneth D. Merry int completed; 1114991554f2SKenneth D. Merry 1115991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 1116991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 1117991554f2SKenneth D. Merry 1118991554f2SKenneth D. Merry /* complete all commands with a NULL reply */ 1119991554f2SKenneth D. Merry for (i = 1; i < sc->num_reqs; i++) { 1120991554f2SKenneth D. Merry cm = &sc->commands[i]; 1121f0779b04SScott Long if (cm->cm_state == MPR_CM_STATE_FREE) 1122f0779b04SScott Long continue; 1123f0779b04SScott Long 1124f0779b04SScott Long cm->cm_state = MPR_CM_STATE_BUSY; 1125991554f2SKenneth D. Merry cm->cm_reply = NULL; 1126991554f2SKenneth D. Merry completed = 0; 1127991554f2SKenneth D. Merry 11288277ce2bSConrad Meyer if (cm->cm_flags & MPR_CM_FLAGS_SATA_ID_TIMEOUT) { 11298277ce2bSConrad Meyer MPASS(cm->cm_data); 11308277ce2bSConrad Meyer free(cm->cm_data, M_MPR); 11318277ce2bSConrad Meyer cm->cm_data = NULL; 11328277ce2bSConrad Meyer } 11338277ce2bSConrad Meyer 1134991554f2SKenneth D. Merry if (cm->cm_flags & MPR_CM_FLAGS_POLLED) 1135991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_COMPLETE; 1136991554f2SKenneth D. Merry 1137991554f2SKenneth D. Merry if (cm->cm_complete != NULL) { 1138991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 11397a2a6a1aSStephen McConnell "completing cm %p state %x ccb %p for diag reset\n", 11407a2a6a1aSStephen McConnell cm, cm->cm_state, cm->cm_ccb); 1141991554f2SKenneth D. Merry cm->cm_complete(sc, cm); 1142991554f2SKenneth D. Merry completed = 1; 1143f0779b04SScott Long } else if (cm->cm_flags & MPR_CM_FLAGS_WAKEUP) { 1144991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 1145991554f2SKenneth D. Merry "waking up cm %p state %x ccb %p for diag reset\n", 1146991554f2SKenneth D. Merry cm, cm->cm_state, cm->cm_ccb); 1147991554f2SKenneth D. Merry wakeup(cm); 1148991554f2SKenneth D. Merry completed = 1; 1149991554f2SKenneth D. Merry } 1150991554f2SKenneth D. Merry 1151991554f2SKenneth D. Merry if ((completed == 0) && (cm->cm_state != MPR_CM_STATE_FREE)) { 1152991554f2SKenneth D. Merry /* this should never happen, but if it does, log */ 1153991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 1154991554f2SKenneth D. Merry "cm %p state %x flags 0x%x ccb %p during diag " 1155991554f2SKenneth D. Merry "reset\n", cm, cm->cm_state, cm->cm_flags, 1156991554f2SKenneth D. Merry cm->cm_ccb); 1157991554f2SKenneth D. Merry } 1158991554f2SKenneth D. Merry } 1159f0779b04SScott Long 1160f0779b04SScott Long sc->io_cmds_active = 0; 1161991554f2SKenneth D. Merry } 1162991554f2SKenneth D. Merry 1163991554f2SKenneth D. Merry void 1164991554f2SKenneth D. Merry mprsas_handle_reinit(struct mpr_softc *sc) 1165991554f2SKenneth D. Merry { 1166991554f2SKenneth D. Merry int i; 1167991554f2SKenneth D. Merry 1168991554f2SKenneth D. Merry /* Go back into startup mode and freeze the simq, so that CAM 1169991554f2SKenneth D. Merry * doesn't send any commands until after we've rediscovered all 1170991554f2SKenneth D. Merry * targets and found the proper device handles for them. 1171991554f2SKenneth D. Merry * 1172991554f2SKenneth D. Merry * After the reset, portenable will trigger discovery, and after all 1173991554f2SKenneth D. Merry * discovery-related activities have finished, the simq will be 1174991554f2SKenneth D. Merry * released. 1175991554f2SKenneth D. Merry */ 1176991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INIT, "%s startup\n", __func__); 1177991554f2SKenneth D. Merry sc->sassc->flags |= MPRSAS_IN_STARTUP; 1178991554f2SKenneth D. Merry sc->sassc->flags |= MPRSAS_IN_DISCOVERY; 1179991554f2SKenneth D. Merry mprsas_startup_increment(sc->sassc); 1180991554f2SKenneth D. Merry 1181991554f2SKenneth D. Merry /* notify CAM of a bus reset */ 1182991554f2SKenneth D. Merry mprsas_announce_reset(sc, AC_BUS_RESET, CAM_TARGET_WILDCARD, 1183991554f2SKenneth D. Merry CAM_LUN_WILDCARD); 1184991554f2SKenneth D. Merry 1185991554f2SKenneth D. Merry /* complete and cleanup after all outstanding commands */ 1186991554f2SKenneth D. Merry mprsas_complete_all_commands(sc); 1187991554f2SKenneth D. Merry 1188a2c14879SStephen McConnell mpr_dprint(sc, MPR_INIT, "%s startup %u after command completion\n", 1189a2c14879SStephen McConnell __func__, sc->sassc->startup_refcount); 1190991554f2SKenneth D. Merry 1191991554f2SKenneth D. Merry /* zero all the target handles, since they may change after the 1192991554f2SKenneth D. Merry * reset, and we have to rediscover all the targets and use the new 1193991554f2SKenneth D. Merry * handles. 1194991554f2SKenneth D. Merry */ 1195991554f2SKenneth D. Merry for (i = 0; i < sc->sassc->maxtargets; i++) { 1196991554f2SKenneth D. Merry if (sc->sassc->targets[i].outstanding != 0) 1197991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INIT, "target %u outstanding %u\n", 1198991554f2SKenneth D. Merry i, sc->sassc->targets[i].outstanding); 1199991554f2SKenneth D. Merry sc->sassc->targets[i].handle = 0x0; 1200991554f2SKenneth D. Merry sc->sassc->targets[i].exp_dev_handle = 0x0; 1201991554f2SKenneth D. Merry sc->sassc->targets[i].outstanding = 0; 1202991554f2SKenneth D. Merry sc->sassc->targets[i].flags = MPRSAS_TARGET_INDIAGRESET; 1203991554f2SKenneth D. Merry } 1204991554f2SKenneth D. Merry } 1205991554f2SKenneth D. Merry static void 1206991554f2SKenneth D. Merry mprsas_tm_timeout(void *data) 1207991554f2SKenneth D. Merry { 1208991554f2SKenneth D. Merry struct mpr_command *tm = data; 1209991554f2SKenneth D. Merry struct mpr_softc *sc = tm->cm_sc; 1210991554f2SKenneth D. Merry 1211991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 1212991554f2SKenneth D. Merry 12137a2a6a1aSStephen McConnell mprsas_log_command(tm, MPR_INFO|MPR_RECOVERY, "task mgmt %p timed " 12147a2a6a1aSStephen McConnell "out\n", tm); 1215f0779b04SScott Long 1216f0779b04SScott Long KASSERT(tm->cm_state == MPR_CM_STATE_INQUEUE, 1217175ad3d0SKenneth D. Merry ("command not inqueue, state = %u\n", tm->cm_state)); 1218f0779b04SScott Long 1219f0779b04SScott Long tm->cm_state = MPR_CM_STATE_BUSY; 1220991554f2SKenneth D. Merry mpr_reinit(sc); 1221991554f2SKenneth D. Merry } 1222991554f2SKenneth D. Merry 1223991554f2SKenneth D. Merry static void 12247a2a6a1aSStephen McConnell mprsas_logical_unit_reset_complete(struct mpr_softc *sc, struct mpr_command *tm) 1225991554f2SKenneth D. Merry { 1226991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 1227991554f2SKenneth D. Merry unsigned int cm_count = 0; 1228991554f2SKenneth D. Merry struct mpr_command *cm; 1229991554f2SKenneth D. Merry struct mprsas_target *targ; 1230991554f2SKenneth D. Merry 1231991554f2SKenneth D. Merry callout_stop(&tm->cm_callout); 1232991554f2SKenneth D. Merry 1233991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 1234991554f2SKenneth D. Merry targ = tm->cm_targ; 1235991554f2SKenneth D. Merry 1236991554f2SKenneth D. Merry /* 1237991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 1238991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 1239991554f2SKenneth D. Merry * task management commands don't have S/G lists. 1240991554f2SKenneth D. Merry */ 1241991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 12426eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_ERROR, 12436eea4f46SScott Long "%s: cm_flags = %#x for LUN reset! " 1244991554f2SKenneth D. Merry "This should not happen!\n", __func__, tm->cm_flags); 1245991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1246991554f2SKenneth D. Merry return; 1247991554f2SKenneth D. Merry } 1248991554f2SKenneth D. Merry 1249991554f2SKenneth D. Merry if (reply == NULL) { 12506eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "NULL reset reply for tm %p\n", 12516eea4f46SScott Long tm); 1252991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) { 1253991554f2SKenneth D. Merry /* this completion was due to a reset, just cleanup */ 12546eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing " 12556eea4f46SScott Long "reset, ignoring NULL LUN reset reply\n"); 1256991554f2SKenneth D. Merry targ->tm = NULL; 1257991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1258991554f2SKenneth D. Merry } 1259991554f2SKenneth D. Merry else { 1260991554f2SKenneth D. Merry /* we should have gotten a reply. */ 12616eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on " 12626eea4f46SScott Long "LUN reset attempt, resetting controller\n"); 1263991554f2SKenneth D. Merry mpr_reinit(sc); 1264991554f2SKenneth D. Merry } 1265991554f2SKenneth D. Merry return; 1266991554f2SKenneth D. Merry } 1267991554f2SKenneth D. Merry 12686eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 1269991554f2SKenneth D. Merry "logical unit reset status 0x%x code 0x%x count %u\n", 1270991554f2SKenneth D. Merry le16toh(reply->IOCStatus), le32toh(reply->ResponseCode), 1271991554f2SKenneth D. Merry le32toh(reply->TerminationCount)); 1272991554f2SKenneth D. Merry 12736eea4f46SScott Long /* 12746eea4f46SScott Long * See if there are any outstanding commands for this LUN. 1275991554f2SKenneth D. Merry * This could be made more efficient by using a per-LU data 1276991554f2SKenneth D. Merry * structure of some sort. 1277991554f2SKenneth D. Merry */ 1278991554f2SKenneth D. Merry TAILQ_FOREACH(cm, &targ->commands, cm_link) { 1279991554f2SKenneth D. Merry if (cm->cm_lun == tm->cm_lun) 1280991554f2SKenneth D. Merry cm_count++; 1281991554f2SKenneth D. Merry } 1282991554f2SKenneth D. Merry 1283991554f2SKenneth D. Merry if (cm_count == 0) { 12846eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 12856eea4f46SScott Long "Finished recovery after LUN reset for target %u\n", 12866eea4f46SScott Long targ->tid); 1287991554f2SKenneth D. Merry 12886eea4f46SScott Long mprsas_announce_reset(sc, AC_SENT_BDR, targ->tid, 1289991554f2SKenneth D. Merry tm->cm_lun); 1290991554f2SKenneth D. Merry 12916eea4f46SScott Long /* 12926eea4f46SScott Long * We've finished recovery for this logical unit. check and 1293991554f2SKenneth D. Merry * see if some other logical unit has a timedout command 1294991554f2SKenneth D. Merry * that needs to be processed. 1295991554f2SKenneth D. Merry */ 1296991554f2SKenneth D. Merry cm = TAILQ_FIRST(&targ->timedout_commands); 1297991554f2SKenneth D. Merry if (cm) { 12986eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 12996eea4f46SScott Long "More commands to abort for target %u\n", targ->tid); 1300991554f2SKenneth D. Merry mprsas_send_abort(sc, tm, cm); 13016eea4f46SScott Long } else { 1302991554f2SKenneth D. Merry targ->tm = NULL; 1303991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1304991554f2SKenneth D. Merry } 13056eea4f46SScott Long } else { 1306991554f2SKenneth D. Merry /* if we still have commands for this LUN, the reset 1307991554f2SKenneth D. Merry * effectively failed, regardless of the status reported. 1308991554f2SKenneth D. Merry * Escalate to a target reset. 1309991554f2SKenneth D. Merry */ 13106eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 13116eea4f46SScott Long "logical unit reset complete for target %u, but still " 13126eea4f46SScott Long "have %u command(s), sending target reset\n", targ->tid, 13136eea4f46SScott Long cm_count); 131489d1c21fSKashyap D Desai if (!targ->is_nvme || sc->custom_nvme_tm_handling) 1315991554f2SKenneth D. Merry mprsas_send_reset(sc, tm, 1316991554f2SKenneth D. Merry MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET); 131789d1c21fSKashyap D Desai else 131889d1c21fSKashyap D Desai mpr_reinit(sc); 1319991554f2SKenneth D. Merry } 1320991554f2SKenneth D. Merry } 1321991554f2SKenneth D. Merry 1322991554f2SKenneth D. Merry static void 1323991554f2SKenneth D. Merry mprsas_target_reset_complete(struct mpr_softc *sc, struct mpr_command *tm) 1324991554f2SKenneth D. Merry { 1325991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 1326991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1327991554f2SKenneth D. Merry struct mprsas_target *targ; 1328991554f2SKenneth D. Merry 1329991554f2SKenneth D. Merry callout_stop(&tm->cm_callout); 1330991554f2SKenneth D. Merry 1331991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1332991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 1333991554f2SKenneth D. Merry targ = tm->cm_targ; 1334991554f2SKenneth D. Merry 1335991554f2SKenneth D. Merry /* 1336991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 1337991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 1338991554f2SKenneth D. Merry * task management commands don't have S/G lists. 1339991554f2SKenneth D. Merry */ 1340991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 1341a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for target " 1342a2c14879SStephen McConnell "reset! This should not happen!\n", __func__, tm->cm_flags); 1343991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1344991554f2SKenneth D. Merry return; 1345991554f2SKenneth D. Merry } 1346991554f2SKenneth D. Merry 1347991554f2SKenneth D. Merry if (reply == NULL) { 13486eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 13496eea4f46SScott Long "NULL target reset reply for tm %p TaskMID %u\n", 13506eea4f46SScott Long tm, le16toh(req->TaskMID)); 1351991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) { 1352991554f2SKenneth D. Merry /* this completion was due to a reset, just cleanup */ 13536eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing " 13546eea4f46SScott Long "reset, ignoring NULL target reset reply\n"); 1355991554f2SKenneth D. Merry targ->tm = NULL; 1356991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1357991554f2SKenneth D. Merry } 1358991554f2SKenneth D. Merry else { 1359991554f2SKenneth D. Merry /* we should have gotten a reply. */ 13606eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on " 13616eea4f46SScott Long "target reset attempt, resetting controller\n"); 1362991554f2SKenneth D. Merry mpr_reinit(sc); 1363991554f2SKenneth D. Merry } 1364991554f2SKenneth D. Merry return; 1365991554f2SKenneth D. Merry } 1366991554f2SKenneth D. Merry 13676eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 1368991554f2SKenneth D. Merry "target reset status 0x%x code 0x%x count %u\n", 1369991554f2SKenneth D. Merry le16toh(reply->IOCStatus), le32toh(reply->ResponseCode), 1370991554f2SKenneth D. Merry le32toh(reply->TerminationCount)); 1371991554f2SKenneth D. Merry 1372991554f2SKenneth D. Merry if (targ->outstanding == 0) { 13736eea4f46SScott Long /* 13746eea4f46SScott Long * We've finished recovery for this target and all 1375991554f2SKenneth D. Merry * of its logical units. 1376991554f2SKenneth D. Merry */ 13776eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 13786eea4f46SScott Long "Finished reset recovery for target %u\n", targ->tid); 1379991554f2SKenneth D. Merry 1380991554f2SKenneth D. Merry mprsas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid, 1381991554f2SKenneth D. Merry CAM_LUN_WILDCARD); 1382991554f2SKenneth D. Merry 1383991554f2SKenneth D. Merry targ->tm = NULL; 1384991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 13856eea4f46SScott Long } else { 13866eea4f46SScott Long /* 13876eea4f46SScott Long * After a target reset, if this target still has 1388991554f2SKenneth D. Merry * outstanding commands, the reset effectively failed, 1389991554f2SKenneth D. Merry * regardless of the status reported. escalate. 1390991554f2SKenneth D. Merry */ 13916eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 13926eea4f46SScott Long "Target reset complete for target %u, but still have %u " 13936eea4f46SScott Long "command(s), resetting controller\n", targ->tid, 13946eea4f46SScott Long targ->outstanding); 1395991554f2SKenneth D. Merry mpr_reinit(sc); 1396991554f2SKenneth D. Merry } 1397991554f2SKenneth D. Merry } 1398991554f2SKenneth D. Merry 1399991554f2SKenneth D. Merry #define MPR_RESET_TIMEOUT 30 1400991554f2SKenneth D. Merry 1401a2c14879SStephen McConnell int 1402991554f2SKenneth D. Merry mprsas_send_reset(struct mpr_softc *sc, struct mpr_command *tm, uint8_t type) 1403991554f2SKenneth D. Merry { 1404991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1405991554f2SKenneth D. Merry struct mprsas_target *target; 140689d1c21fSKashyap D Desai int err, timeout; 1407991554f2SKenneth D. Merry 1408991554f2SKenneth D. Merry target = tm->cm_targ; 1409991554f2SKenneth D. Merry if (target->handle == 0) { 1410a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s null devhandle for target_id " 1411a2c14879SStephen McConnell "%d\n", __func__, target->tid); 1412991554f2SKenneth D. Merry return -1; 1413991554f2SKenneth D. Merry } 1414991554f2SKenneth D. Merry 1415991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1416991554f2SKenneth D. Merry req->DevHandle = htole16(target->handle); 1417991554f2SKenneth D. Merry req->TaskType = type; 1418991554f2SKenneth D. Merry 141989d1c21fSKashyap D Desai if (!target->is_nvme || sc->custom_nvme_tm_handling) { 142089d1c21fSKashyap D Desai timeout = MPR_RESET_TIMEOUT; 142189d1c21fSKashyap D Desai /* 142289d1c21fSKashyap D Desai * Target reset method = 142389d1c21fSKashyap D Desai * SAS Hard Link Reset / SATA Link Reset 142489d1c21fSKashyap D Desai */ 142589d1c21fSKashyap D Desai req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; 142689d1c21fSKashyap D Desai } else { 142789d1c21fSKashyap D Desai timeout = (target->controller_reset_timeout) ? ( 142889d1c21fSKashyap D Desai target->controller_reset_timeout) : (MPR_RESET_TIMEOUT); 142989d1c21fSKashyap D Desai /* PCIe Protocol Level Reset*/ 143089d1c21fSKashyap D Desai req->MsgFlags = 143189d1c21fSKashyap D Desai MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; 143289d1c21fSKashyap D Desai } 143389d1c21fSKashyap D Desai 1434991554f2SKenneth D. Merry if (type == MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET) { 1435991554f2SKenneth D. Merry /* XXX Need to handle invalid LUNs */ 1436991554f2SKenneth D. Merry MPR_SET_LUN(req->LUN, tm->cm_lun); 1437991554f2SKenneth D. Merry tm->cm_targ->logical_unit_resets++; 14386eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 14396eea4f46SScott Long "Sending logical unit reset to target %u lun %d\n", 14406eea4f46SScott Long target->tid, tm->cm_lun); 1441991554f2SKenneth D. Merry tm->cm_complete = mprsas_logical_unit_reset_complete; 1442a2c14879SStephen McConnell mprsas_prepare_for_tm(sc, tm, target, tm->cm_lun); 14436eea4f46SScott Long } else if (type == MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET) { 1444991554f2SKenneth D. Merry tm->cm_targ->target_resets++; 14456eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 14466eea4f46SScott Long "Sending target reset to target %u\n", target->tid); 1447991554f2SKenneth D. Merry tm->cm_complete = mprsas_target_reset_complete; 1448a2c14879SStephen McConnell mprsas_prepare_for_tm(sc, tm, target, CAM_LUN_WILDCARD); 1449991554f2SKenneth D. Merry } 1450991554f2SKenneth D. Merry else { 1451991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "unexpected reset type 0x%x\n", type); 1452991554f2SKenneth D. Merry return -1; 1453991554f2SKenneth D. Merry } 1454991554f2SKenneth D. Merry 1455991554f2SKenneth D. Merry if (target->encl_level_valid) { 14566eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 14576eea4f46SScott Long "At enclosure level %d, slot %d, connector name (%4s)\n", 14586eea4f46SScott Long target->encl_level, target->encl_slot, 14596eea4f46SScott Long target->connector_name); 1460991554f2SKenneth D. Merry } 1461991554f2SKenneth D. Merry 1462991554f2SKenneth D. Merry tm->cm_data = NULL; 1463991554f2SKenneth D. Merry tm->cm_complete_data = (void *)tm; 1464991554f2SKenneth D. Merry 146589d1c21fSKashyap D Desai callout_reset(&tm->cm_callout, timeout * hz, 1466991554f2SKenneth D. Merry mprsas_tm_timeout, tm); 1467991554f2SKenneth D. Merry 1468991554f2SKenneth D. Merry err = mpr_map_command(sc, tm); 1469991554f2SKenneth D. Merry if (err) 14706eea4f46SScott Long mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY, 1471a2c14879SStephen McConnell "error %d sending reset type %u\n", err, type); 1472991554f2SKenneth D. Merry 1473991554f2SKenneth D. Merry return err; 1474991554f2SKenneth D. Merry } 1475991554f2SKenneth D. Merry 1476991554f2SKenneth D. Merry static void 1477991554f2SKenneth D. Merry mprsas_abort_complete(struct mpr_softc *sc, struct mpr_command *tm) 1478991554f2SKenneth D. Merry { 1479991554f2SKenneth D. Merry struct mpr_command *cm; 1480991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 1481991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1482991554f2SKenneth D. Merry struct mprsas_target *targ; 1483991554f2SKenneth D. Merry 1484991554f2SKenneth D. Merry callout_stop(&tm->cm_callout); 1485991554f2SKenneth D. Merry 1486991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1487991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 1488991554f2SKenneth D. Merry targ = tm->cm_targ; 1489991554f2SKenneth D. Merry 1490991554f2SKenneth D. Merry /* 1491991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 1492991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 1493991554f2SKenneth D. Merry * task management commands don't have S/G lists. 1494991554f2SKenneth D. Merry */ 1495991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 14966eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_ERROR, 1497991554f2SKenneth D. Merry "cm_flags = %#x for abort %p TaskMID %u!\n", 1498991554f2SKenneth D. Merry tm->cm_flags, tm, le16toh(req->TaskMID)); 1499991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1500991554f2SKenneth D. Merry return; 1501991554f2SKenneth D. Merry } 1502991554f2SKenneth D. Merry 1503991554f2SKenneth D. Merry if (reply == NULL) { 15046eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 1505991554f2SKenneth D. Merry "NULL abort reply for tm %p TaskMID %u\n", 1506991554f2SKenneth D. Merry tm, le16toh(req->TaskMID)); 1507991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) { 1508991554f2SKenneth D. Merry /* this completion was due to a reset, just cleanup */ 15096eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing " 15106eea4f46SScott Long "reset, ignoring NULL abort reply\n"); 1511991554f2SKenneth D. Merry targ->tm = NULL; 1512991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 15136eea4f46SScott Long } else { 1514991554f2SKenneth D. Merry /* we should have gotten a reply. */ 15156eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on " 15166eea4f46SScott Long "abort attempt, resetting controller\n"); 1517991554f2SKenneth D. Merry mpr_reinit(sc); 1518991554f2SKenneth D. Merry } 1519991554f2SKenneth D. Merry return; 1520991554f2SKenneth D. Merry } 1521991554f2SKenneth D. Merry 15226eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 1523991554f2SKenneth D. Merry "abort TaskMID %u status 0x%x code 0x%x count %u\n", 1524991554f2SKenneth D. Merry le16toh(req->TaskMID), 1525991554f2SKenneth D. Merry le16toh(reply->IOCStatus), le32toh(reply->ResponseCode), 1526991554f2SKenneth D. Merry le32toh(reply->TerminationCount)); 1527991554f2SKenneth D. Merry 1528991554f2SKenneth D. Merry cm = TAILQ_FIRST(&tm->cm_targ->timedout_commands); 1529991554f2SKenneth D. Merry if (cm == NULL) { 15306eea4f46SScott Long /* 15316eea4f46SScott Long * if there are no more timedout commands, we're done with 1532991554f2SKenneth D. Merry * error recovery for this target. 1533991554f2SKenneth D. Merry */ 15346eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 15356eea4f46SScott Long "Finished abort recovery for target %u\n", targ->tid); 1536991554f2SKenneth D. Merry targ->tm = NULL; 1537991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 15386eea4f46SScott Long } else if (le16toh(req->TaskMID) != cm->cm_desc.Default.SMID) { 1539991554f2SKenneth D. Merry /* abort success, but we have more timedout commands to abort */ 15406eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 15416eea4f46SScott Long "Continuing abort recovery for target %u\n", targ->tid); 1542991554f2SKenneth D. Merry mprsas_send_abort(sc, tm, cm); 15436eea4f46SScott Long } else { 15446eea4f46SScott Long /* 15456eea4f46SScott Long * we didn't get a command completion, so the abort 1546991554f2SKenneth D. Merry * failed as far as we're concerned. escalate. 1547991554f2SKenneth D. Merry */ 15486eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 15496eea4f46SScott Long "Abort failed for target %u, sending logical unit reset\n", 15506eea4f46SScott Long targ->tid); 1551991554f2SKenneth D. Merry 1552991554f2SKenneth D. Merry mprsas_send_reset(sc, tm, 1553991554f2SKenneth D. Merry MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET); 1554991554f2SKenneth D. Merry } 1555991554f2SKenneth D. Merry } 1556991554f2SKenneth D. Merry 1557991554f2SKenneth D. Merry #define MPR_ABORT_TIMEOUT 5 1558991554f2SKenneth D. Merry 1559991554f2SKenneth D. Merry static int 1560991554f2SKenneth D. Merry mprsas_send_abort(struct mpr_softc *sc, struct mpr_command *tm, 1561991554f2SKenneth D. Merry struct mpr_command *cm) 1562991554f2SKenneth D. Merry { 1563991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1564991554f2SKenneth D. Merry struct mprsas_target *targ; 156589d1c21fSKashyap D Desai int err, timeout; 1566991554f2SKenneth D. Merry 1567991554f2SKenneth D. Merry targ = cm->cm_targ; 1568991554f2SKenneth D. Merry if (targ->handle == 0) { 15696eea4f46SScott Long mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY, 15706eea4f46SScott Long "%s null devhandle for target_id %d\n", 1571991554f2SKenneth D. Merry __func__, cm->cm_ccb->ccb_h.target_id); 1572991554f2SKenneth D. Merry return -1; 1573991554f2SKenneth D. Merry } 1574991554f2SKenneth D. Merry 1575855fe445SScott Long mprsas_log_command(cm, MPR_RECOVERY|MPR_INFO, 1576991554f2SKenneth D. Merry "Aborting command %p\n", cm); 1577991554f2SKenneth D. Merry 1578991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1579991554f2SKenneth D. Merry req->DevHandle = htole16(targ->handle); 1580991554f2SKenneth D. Merry req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK; 1581991554f2SKenneth D. Merry 1582991554f2SKenneth D. Merry /* XXX Need to handle invalid LUNs */ 1583991554f2SKenneth D. Merry MPR_SET_LUN(req->LUN, cm->cm_ccb->ccb_h.target_lun); 1584991554f2SKenneth D. Merry 1585991554f2SKenneth D. Merry req->TaskMID = htole16(cm->cm_desc.Default.SMID); 1586991554f2SKenneth D. Merry 1587991554f2SKenneth D. Merry tm->cm_data = NULL; 1588991554f2SKenneth D. Merry tm->cm_complete = mprsas_abort_complete; 1589991554f2SKenneth D. Merry tm->cm_complete_data = (void *)tm; 1590991554f2SKenneth D. Merry tm->cm_targ = cm->cm_targ; 1591991554f2SKenneth D. Merry tm->cm_lun = cm->cm_lun; 1592991554f2SKenneth D. Merry 159389d1c21fSKashyap D Desai if (!targ->is_nvme || sc->custom_nvme_tm_handling) 159489d1c21fSKashyap D Desai timeout = MPR_ABORT_TIMEOUT; 159589d1c21fSKashyap D Desai else 159689d1c21fSKashyap D Desai timeout = sc->nvme_abort_timeout; 159789d1c21fSKashyap D Desai 159889d1c21fSKashyap D Desai callout_reset(&tm->cm_callout, timeout * hz, 1599991554f2SKenneth D. Merry mprsas_tm_timeout, tm); 1600991554f2SKenneth D. Merry 1601991554f2SKenneth D. Merry targ->aborts++; 1602991554f2SKenneth D. Merry 1603a2c14879SStephen McConnell mprsas_prepare_for_tm(sc, tm, targ, tm->cm_lun); 1604a2c14879SStephen McConnell 1605991554f2SKenneth D. Merry err = mpr_map_command(sc, tm); 1606991554f2SKenneth D. Merry if (err) 16076eea4f46SScott Long mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY, 1608991554f2SKenneth D. Merry "error %d sending abort for cm %p SMID %u\n", 1609991554f2SKenneth D. Merry err, cm, req->TaskMID); 1610991554f2SKenneth D. Merry return err; 1611991554f2SKenneth D. Merry } 1612991554f2SKenneth D. Merry 1613991554f2SKenneth D. Merry static void 1614991554f2SKenneth D. Merry mprsas_scsiio_timeout(void *data) 1615991554f2SKenneth D. Merry { 16166eea4f46SScott Long sbintime_t elapsed, now; 16176eea4f46SScott Long union ccb *ccb; 1618991554f2SKenneth D. Merry struct mpr_softc *sc; 1619991554f2SKenneth D. Merry struct mpr_command *cm; 1620991554f2SKenneth D. Merry struct mprsas_target *targ; 1621991554f2SKenneth D. Merry 1622991554f2SKenneth D. Merry cm = (struct mpr_command *)data; 1623991554f2SKenneth D. Merry sc = cm->cm_sc; 16246eea4f46SScott Long ccb = cm->cm_ccb; 16256eea4f46SScott Long now = sbinuptime(); 1626991554f2SKenneth D. Merry 1627991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 1628991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 1629991554f2SKenneth D. Merry 16306eea4f46SScott Long mpr_dprint(sc, MPR_XINFO|MPR_RECOVERY, "Timeout checking cm %p\n", cm); 1631991554f2SKenneth D. Merry 1632991554f2SKenneth D. Merry /* 1633991554f2SKenneth D. Merry * Run the interrupt handler to make sure it's not pending. This 1634991554f2SKenneth D. Merry * isn't perfect because the command could have already completed 1635991554f2SKenneth D. Merry * and been re-used, though this is unlikely. 1636991554f2SKenneth D. Merry */ 1637991554f2SKenneth D. Merry mpr_intr_locked(sc); 16388fe7bf06SWarner Losh if (cm->cm_flags & MPR_CM_FLAGS_ON_RECOVERY) { 1639991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, 1640991554f2SKenneth D. Merry "SCSI command %p almost timed out\n", cm); 1641991554f2SKenneth D. Merry return; 1642991554f2SKenneth D. Merry } 1643991554f2SKenneth D. Merry 1644991554f2SKenneth D. Merry if (cm->cm_ccb == NULL) { 1645991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "command timeout with NULL ccb\n"); 1646991554f2SKenneth D. Merry return; 1647991554f2SKenneth D. Merry } 1648991554f2SKenneth D. Merry 1649991554f2SKenneth D. Merry targ = cm->cm_targ; 1650991554f2SKenneth D. Merry targ->timeouts++; 1651991554f2SKenneth D. Merry 16526eea4f46SScott Long elapsed = now - ccb->ccb_h.qos.sim_data; 16536eea4f46SScott Long mprsas_log_command(cm, MPR_INFO|MPR_RECOVERY, 16546eea4f46SScott Long "Command timeout on target %u(0x%04x), %d set, %d.%d elapsed\n", 16556eea4f46SScott Long targ->tid, targ->handle, ccb->ccb_h.timeout, 16566eea4f46SScott Long sbintime_getsec(elapsed), elapsed & 0xffffffff); 1657991554f2SKenneth D. Merry if (targ->encl_level_valid) { 16586eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 16596eea4f46SScott Long "At enclosure level %d, slot %d, connector name (%4s)\n", 16606eea4f46SScott Long targ->encl_level, targ->encl_slot, targ->connector_name); 1661991554f2SKenneth D. Merry } 1662991554f2SKenneth D. Merry 1663991554f2SKenneth D. Merry /* XXX first, check the firmware state, to see if it's still 1664991554f2SKenneth D. Merry * operational. if not, do a diag reset. 1665991554f2SKenneth D. Merry */ 1666a2c14879SStephen McConnell mprsas_set_ccbstatus(cm->cm_ccb, CAM_CMD_TIMEOUT); 16678fe7bf06SWarner Losh cm->cm_flags |= MPR_CM_FLAGS_ON_RECOVERY | MPR_CM_FLAGS_TIMEDOUT; 1668991554f2SKenneth D. Merry TAILQ_INSERT_TAIL(&targ->timedout_commands, cm, cm_recovery); 1669991554f2SKenneth D. Merry 1670991554f2SKenneth D. Merry if (targ->tm != NULL) { 1671991554f2SKenneth D. Merry /* target already in recovery, just queue up another 1672991554f2SKenneth D. Merry * timedout command to be processed later. 1673991554f2SKenneth D. Merry */ 16742bbaed4dSWarner Losh mpr_dprint(sc, MPR_RECOVERY, 16752bbaed4dSWarner Losh "queued timedout cm %p for processing by tm %p\n", 16762bbaed4dSWarner Losh cm, targ->tm); 16772bbaed4dSWarner Losh } else if ((targ->tm = mprsas_alloc_tm(sc)) != NULL) { 16786eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 16796eea4f46SScott Long "Sending abort to target %u for SMID %d\n", targ->tid, 16806eea4f46SScott Long cm->cm_desc.Default.SMID); 16816eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "timedout cm %p allocated tm %p\n", 16826eea4f46SScott Long cm, targ->tm); 16832bbaed4dSWarner Losh 16842bbaed4dSWarner Losh /* start recovery by aborting the first timedout command */ 1685991554f2SKenneth D. Merry mprsas_send_abort(sc, targ->tm, cm); 16862bbaed4dSWarner Losh } else { 1687991554f2SKenneth D. Merry /* XXX queue this target up for recovery once a TM becomes 1688991554f2SKenneth D. Merry * available. The firmware only has a limited number of 1689991554f2SKenneth D. Merry * HighPriority credits for the high priority requests used 1690991554f2SKenneth D. Merry * for task management, and we ran out. 1691991554f2SKenneth D. Merry * 1692991554f2SKenneth D. Merry * Isilon: don't worry about this for now, since we have 1693991554f2SKenneth D. Merry * more credits than disks in an enclosure, and limit 1694991554f2SKenneth D. Merry * ourselves to one TM per target for recovery. 1695991554f2SKenneth D. Merry */ 16966eea4f46SScott Long mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY, 16976eea4f46SScott Long "timedout cm %p failed to allocate a tm\n", cm); 1698991554f2SKenneth D. Merry } 1699991554f2SKenneth D. Merry } 1700991554f2SKenneth D. Merry 170167feec50SStephen McConnell /** 170267feec50SStephen McConnell * mprsas_build_nvme_unmap - Build Native NVMe DSM command equivalent 170367feec50SStephen McConnell * to SCSI Unmap. 170467feec50SStephen McConnell * Return 0 - for success, 170567feec50SStephen McConnell * 1 - to immediately return back the command with success status to CAM 170667feec50SStephen McConnell * negative value - to fallback to firmware path i.e. issue scsi unmap 170767feec50SStephen McConnell * to FW without any translation. 170867feec50SStephen McConnell */ 170967feec50SStephen McConnell static int 171067feec50SStephen McConnell mprsas_build_nvme_unmap(struct mpr_softc *sc, struct mpr_command *cm, 171167feec50SStephen McConnell union ccb *ccb, struct mprsas_target *targ) 171267feec50SStephen McConnell { 171367feec50SStephen McConnell Mpi26NVMeEncapsulatedRequest_t *req = NULL; 171467feec50SStephen McConnell struct ccb_scsiio *csio; 171567feec50SStephen McConnell struct unmap_parm_list *plist; 171667feec50SStephen McConnell struct nvme_dsm_range *nvme_dsm_ranges = NULL; 171767feec50SStephen McConnell struct nvme_command *c; 171867feec50SStephen McConnell int i, res; 171967feec50SStephen McConnell uint16_t ndesc, list_len, data_length; 172067feec50SStephen McConnell struct mpr_prp_page *prp_page_info; 172167feec50SStephen McConnell uint64_t nvme_dsm_ranges_dma_handle; 172267feec50SStephen McConnell 172367feec50SStephen McConnell csio = &ccb->csio; 172467feec50SStephen McConnell list_len = (scsiio_cdb_ptr(csio)[7] << 8 | scsiio_cdb_ptr(csio)[8]); 172567feec50SStephen McConnell if (!list_len) { 172667feec50SStephen McConnell mpr_dprint(sc, MPR_ERROR, "Parameter list length is Zero\n"); 172767feec50SStephen McConnell return -EINVAL; 172867feec50SStephen McConnell } 172967feec50SStephen McConnell 173067feec50SStephen McConnell plist = malloc(csio->dxfer_len, M_MPR, M_ZERO|M_NOWAIT); 173167feec50SStephen McConnell if (!plist) { 173267feec50SStephen McConnell mpr_dprint(sc, MPR_ERROR, "Unable to allocate memory to " 173367feec50SStephen McConnell "save UNMAP data\n"); 173467feec50SStephen McConnell return -ENOMEM; 173567feec50SStephen McConnell } 173667feec50SStephen McConnell 173767feec50SStephen McConnell /* Copy SCSI unmap data to a local buffer */ 173867feec50SStephen McConnell bcopy(csio->data_ptr, plist, csio->dxfer_len); 173967feec50SStephen McConnell 174067feec50SStephen McConnell /* return back the unmap command to CAM with success status, 174167feec50SStephen McConnell * if number of descripts is zero. 174267feec50SStephen McConnell */ 174367feec50SStephen McConnell ndesc = be16toh(plist->unmap_blk_desc_data_len) >> 4; 174467feec50SStephen McConnell if (!ndesc) { 174567feec50SStephen McConnell mpr_dprint(sc, MPR_XINFO, "Number of descriptors in " 174667feec50SStephen McConnell "UNMAP cmd is Zero\n"); 174767feec50SStephen McConnell res = 1; 174867feec50SStephen McConnell goto out; 174967feec50SStephen McConnell } 175067feec50SStephen McConnell 175167feec50SStephen McConnell data_length = ndesc * sizeof(struct nvme_dsm_range); 175267feec50SStephen McConnell if (data_length > targ->MDTS) { 175367feec50SStephen McConnell mpr_dprint(sc, MPR_ERROR, "data length: %d is greater than " 175467feec50SStephen McConnell "Device's MDTS: %d\n", data_length, targ->MDTS); 175567feec50SStephen McConnell res = -EINVAL; 175667feec50SStephen McConnell goto out; 175767feec50SStephen McConnell } 175867feec50SStephen McConnell 175967feec50SStephen McConnell prp_page_info = mpr_alloc_prp_page(sc); 176067feec50SStephen McConnell KASSERT(prp_page_info != NULL, ("%s: There is no PRP Page for " 176167feec50SStephen McConnell "UNMAP command.\n", __func__)); 176267feec50SStephen McConnell 176367feec50SStephen McConnell /* 176467feec50SStephen McConnell * Insert the allocated PRP page into the command's PRP page list. This 176567feec50SStephen McConnell * will be freed when the command is freed. 176667feec50SStephen McConnell */ 176767feec50SStephen McConnell TAILQ_INSERT_TAIL(&cm->cm_prp_page_list, prp_page_info, prp_page_link); 176867feec50SStephen McConnell 176967feec50SStephen McConnell nvme_dsm_ranges = (struct nvme_dsm_range *)prp_page_info->prp_page; 177067feec50SStephen McConnell nvme_dsm_ranges_dma_handle = prp_page_info->prp_page_busaddr; 177167feec50SStephen McConnell 177267feec50SStephen McConnell bzero(nvme_dsm_ranges, data_length); 177367feec50SStephen McConnell 177467feec50SStephen McConnell /* Convert SCSI unmap's descriptor data to NVMe DSM specific Range data 177567feec50SStephen McConnell * for each descriptors contained in SCSI UNMAP data. 177667feec50SStephen McConnell */ 177767feec50SStephen McConnell for (i = 0; i < ndesc; i++) { 177867feec50SStephen McConnell nvme_dsm_ranges[i].length = 177967feec50SStephen McConnell htole32(be32toh(plist->desc[i].nlb)); 178067feec50SStephen McConnell nvme_dsm_ranges[i].starting_lba = 178167feec50SStephen McConnell htole64(be64toh(plist->desc[i].slba)); 178267feec50SStephen McConnell nvme_dsm_ranges[i].attributes = 0; 178367feec50SStephen McConnell } 178467feec50SStephen McConnell 178567feec50SStephen McConnell /* Build MPI2.6's NVMe Encapsulated Request Message */ 178667feec50SStephen McConnell req = (Mpi26NVMeEncapsulatedRequest_t *)cm->cm_req; 178767feec50SStephen McConnell bzero(req, sizeof(*req)); 178867feec50SStephen McConnell req->DevHandle = htole16(targ->handle); 178967feec50SStephen McConnell req->Function = MPI2_FUNCTION_NVME_ENCAPSULATED; 179067feec50SStephen McConnell req->Flags = MPI26_NVME_FLAGS_WRITE; 179167feec50SStephen McConnell req->ErrorResponseBaseAddress.High = 179267feec50SStephen McConnell htole32((uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32)); 179367feec50SStephen McConnell req->ErrorResponseBaseAddress.Low = 179467feec50SStephen McConnell htole32(cm->cm_sense_busaddr); 179567feec50SStephen McConnell req->ErrorResponseAllocationLength = 179667feec50SStephen McConnell htole16(sizeof(struct nvme_completion)); 179767feec50SStephen McConnell req->EncapsulatedCommandLength = 179867feec50SStephen McConnell htole16(sizeof(struct nvme_command)); 179967feec50SStephen McConnell req->DataLength = htole32(data_length); 180067feec50SStephen McConnell 180167feec50SStephen McConnell /* Build NVMe DSM command */ 180267feec50SStephen McConnell c = (struct nvme_command *) req->NVMe_Command; 18039544e6dcSChuck Tuffli c->opc = NVME_OPC_DATASET_MANAGEMENT; 180467feec50SStephen McConnell c->nsid = htole32(csio->ccb_h.target_lun + 1); 180567feec50SStephen McConnell c->cdw10 = htole32(ndesc - 1); 180667feec50SStephen McConnell c->cdw11 = htole32(NVME_DSM_ATTR_DEALLOCATE); 180767feec50SStephen McConnell 180867feec50SStephen McConnell cm->cm_length = data_length; 180967feec50SStephen McConnell cm->cm_data = NULL; 181067feec50SStephen McConnell 181167feec50SStephen McConnell cm->cm_complete = mprsas_scsiio_complete; 181267feec50SStephen McConnell cm->cm_complete_data = ccb; 181367feec50SStephen McConnell cm->cm_targ = targ; 181467feec50SStephen McConnell cm->cm_lun = csio->ccb_h.target_lun; 181567feec50SStephen McConnell cm->cm_ccb = ccb; 181667feec50SStephen McConnell 181767feec50SStephen McConnell cm->cm_desc.Default.RequestFlags = 181867feec50SStephen McConnell MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED; 181967feec50SStephen McConnell 18206eea4f46SScott Long csio->ccb_h.qos.sim_data = sbinuptime(); 182167feec50SStephen McConnell callout_reset_sbt(&cm->cm_callout, SBT_1MS * ccb->ccb_h.timeout, 0, 182267feec50SStephen McConnell mprsas_scsiio_timeout, cm, 0); 182367feec50SStephen McConnell 182467feec50SStephen McConnell targ->issued++; 182567feec50SStephen McConnell targ->outstanding++; 182667feec50SStephen McConnell TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link); 182767feec50SStephen McConnell ccb->ccb_h.status |= CAM_SIM_QUEUED; 182867feec50SStephen McConnell 182967feec50SStephen McConnell mprsas_log_command(cm, MPR_XINFO, "%s cm %p ccb %p outstanding %u\n", 183067feec50SStephen McConnell __func__, cm, ccb, targ->outstanding); 183167feec50SStephen McConnell 183218982e8fSStephen McConnell mpr_build_nvme_prp(sc, cm, req, 183318982e8fSStephen McConnell (void *)(uintptr_t)nvme_dsm_ranges_dma_handle, 0, data_length); 183467feec50SStephen McConnell mpr_map_command(sc, cm); 183561f17c5fSScott Long res = 0; 183667feec50SStephen McConnell 183767feec50SStephen McConnell out: 183867feec50SStephen McConnell free(plist, M_MPR); 183961f17c5fSScott Long return (res); 184067feec50SStephen McConnell } 184167feec50SStephen McConnell 1842991554f2SKenneth D. Merry static void 1843991554f2SKenneth D. Merry mprsas_action_scsiio(struct mprsas_softc *sassc, union ccb *ccb) 1844991554f2SKenneth D. Merry { 1845991554f2SKenneth D. Merry MPI2_SCSI_IO_REQUEST *req; 1846991554f2SKenneth D. Merry struct ccb_scsiio *csio; 1847991554f2SKenneth D. Merry struct mpr_softc *sc; 1848991554f2SKenneth D. Merry struct mprsas_target *targ; 1849991554f2SKenneth D. Merry struct mprsas_lun *lun; 1850991554f2SKenneth D. Merry struct mpr_command *cm; 185167feec50SStephen McConnell uint8_t i, lba_byte, *ref_tag_addr, scsi_opcode; 1852991554f2SKenneth D. Merry uint16_t eedp_flags; 1853991554f2SKenneth D. Merry uint32_t mpi_control; 185467feec50SStephen McConnell int rc; 1855991554f2SKenneth D. Merry 1856991554f2SKenneth D. Merry sc = sassc->sc; 1857991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 1858991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 1859991554f2SKenneth D. Merry 1860991554f2SKenneth D. Merry csio = &ccb->csio; 1861a2c14879SStephen McConnell KASSERT(csio->ccb_h.target_id < sassc->maxtargets, 1862a2c14879SStephen McConnell ("Target %d out of bounds in XPT_SCSI_IO\n", 1863a2c14879SStephen McConnell csio->ccb_h.target_id)); 1864991554f2SKenneth D. Merry targ = &sassc->targets[csio->ccb_h.target_id]; 1865991554f2SKenneth D. Merry mpr_dprint(sc, MPR_TRACE, "ccb %p target flag %x\n", ccb, targ->flags); 1866991554f2SKenneth D. Merry if (targ->handle == 0x0) { 1867*e35816c1SWarner Losh if (targ->flags & MPRSAS_TARGET_INDIAGRESET) { 1868*e35816c1SWarner Losh mpr_dprint(sc, MPR_ERROR, 1869*e35816c1SWarner Losh "%s NULL handle for target %u in diag reset freezing queue\n", 1870*e35816c1SWarner Losh __func__, csio->ccb_h.target_id); 1871*e35816c1SWarner Losh ccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_DEV_QFRZN; 1872*e35816c1SWarner Losh xpt_freeze_devq(ccb->ccb_h.path, 1); 1873*e35816c1SWarner Losh xpt_done(ccb); 1874*e35816c1SWarner Losh return; 1875*e35816c1SWarner Losh } 1876991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s NULL handle for target %u\n", 1877991554f2SKenneth D. Merry __func__, csio->ccb_h.target_id); 1878a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1879991554f2SKenneth D. Merry xpt_done(ccb); 1880991554f2SKenneth D. Merry return; 1881991554f2SKenneth D. Merry } 1882991554f2SKenneth D. Merry if (targ->flags & MPR_TARGET_FLAGS_RAID_COMPONENT) { 1883a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s Raid component no SCSI IO " 1884991554f2SKenneth D. Merry "supported %u\n", __func__, csio->ccb_h.target_id); 1885a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1886991554f2SKenneth D. Merry xpt_done(ccb); 1887991554f2SKenneth D. Merry return; 1888991554f2SKenneth D. Merry } 1889991554f2SKenneth D. Merry /* 1890991554f2SKenneth D. Merry * Sometimes, it is possible to get a command that is not "In 1891991554f2SKenneth D. Merry * Progress" and was actually aborted by the upper layer. Check for 1892991554f2SKenneth D. Merry * this here and complete the command without error. 1893991554f2SKenneth D. Merry */ 1894a2c14879SStephen McConnell if (mprsas_get_ccbstatus(ccb) != CAM_REQ_INPROG) { 1895991554f2SKenneth D. Merry mpr_dprint(sc, MPR_TRACE, "%s Command is not in progress for " 1896991554f2SKenneth D. Merry "target %u\n", __func__, csio->ccb_h.target_id); 1897991554f2SKenneth D. Merry xpt_done(ccb); 1898991554f2SKenneth D. Merry return; 1899991554f2SKenneth D. Merry } 1900991554f2SKenneth D. Merry /* 1901991554f2SKenneth D. Merry * If devinfo is 0 this will be a volume. In that case don't tell CAM 1902991554f2SKenneth D. Merry * that the volume has timed out. We want volumes to be enumerated 19034c1cdd4aSWarner Losh * until they are deleted/removed, not just failed. In either event, 19044c1cdd4aSWarner Losh * we're removing the target due to a firmware event telling us 19054c1cdd4aSWarner Losh * the device is now gone (as opposed to some transient event). Since 19064c1cdd4aSWarner Losh * we're opting to remove failed devices from the OS's view, we need 19074c1cdd4aSWarner Losh * to propagate that status up the stack. 1908991554f2SKenneth D. Merry */ 1909991554f2SKenneth D. Merry if (targ->flags & MPRSAS_TARGET_INREMOVAL) { 1910991554f2SKenneth D. Merry if (targ->devinfo == 0) 1911a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1912991554f2SKenneth D. Merry else 19134c1cdd4aSWarner Losh mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1914991554f2SKenneth D. Merry xpt_done(ccb); 1915991554f2SKenneth D. Merry return; 1916991554f2SKenneth D. Merry } 1917991554f2SKenneth D. Merry 1918991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_SHUTDOWN) != 0) { 1919a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s shutting down\n", __func__); 1920a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1921a2c14879SStephen McConnell xpt_done(ccb); 1922a2c14879SStephen McConnell return; 1923a2c14879SStephen McConnell } 1924a2c14879SStephen McConnell 1925a2c14879SStephen McConnell /* 19269781c28cSAlexander Motin * If target has a reset in progress, the devq should be frozen. 19279781c28cSAlexander Motin * Geting here we likely hit a race, so just requeue. 1928a2c14879SStephen McConnell */ 1929a2c14879SStephen McConnell if (targ->flags & MPRSAS_TARGET_INRESET) { 19309781c28cSAlexander Motin ccb->ccb_h.status = CAM_REQUEUE_REQ | CAM_DEV_QFRZN; 1931a8837c77SWarner Losh mpr_dprint(sc, MPR_XINFO | MPR_RECOVERY, 1932a8837c77SWarner Losh "%s: Freezing devq for target ID %d\n", 1933a8837c77SWarner Losh __func__, targ->tid); 1934a2c14879SStephen McConnell xpt_freeze_devq(ccb->ccb_h.path, 1); 1935991554f2SKenneth D. Merry xpt_done(ccb); 1936991554f2SKenneth D. Merry return; 1937991554f2SKenneth D. Merry } 1938991554f2SKenneth D. Merry 1939991554f2SKenneth D. Merry cm = mpr_alloc_command(sc); 1940991554f2SKenneth D. Merry if (cm == NULL || (sc->mpr_flags & MPR_FLAGS_DIAGRESET)) { 1941991554f2SKenneth D. Merry if (cm != NULL) { 1942991554f2SKenneth D. Merry mpr_free_command(sc, cm); 1943991554f2SKenneth D. Merry } 1944991554f2SKenneth D. Merry if ((sassc->flags & MPRSAS_QUEUE_FROZEN) == 0) { 1945991554f2SKenneth D. Merry xpt_freeze_simq(sassc->sim, 1); 1946991554f2SKenneth D. Merry sassc->flags |= MPRSAS_QUEUE_FROZEN; 1947991554f2SKenneth D. Merry } 1948991554f2SKenneth D. Merry ccb->ccb_h.status &= ~CAM_SIM_QUEUED; 1949991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_REQUEUE_REQ; 1950991554f2SKenneth D. Merry xpt_done(ccb); 1951991554f2SKenneth D. Merry return; 1952991554f2SKenneth D. Merry } 1953991554f2SKenneth D. Merry 195467feec50SStephen McConnell /* For NVME device's issue UNMAP command directly to NVME drives by 195567feec50SStephen McConnell * constructing equivalent native NVMe DataSetManagement command. 195667feec50SStephen McConnell */ 195767feec50SStephen McConnell scsi_opcode = scsiio_cdb_ptr(csio)[0]; 195867feec50SStephen McConnell if (scsi_opcode == UNMAP && 195967feec50SStephen McConnell targ->is_nvme && 196067feec50SStephen McConnell (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) { 196167feec50SStephen McConnell rc = mprsas_build_nvme_unmap(sc, cm, ccb, targ); 196267feec50SStephen McConnell if (rc == 1) { /* return command to CAM with success status */ 196367feec50SStephen McConnell mpr_free_command(sc, cm); 196467feec50SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 196567feec50SStephen McConnell xpt_done(ccb); 196667feec50SStephen McConnell return; 196767feec50SStephen McConnell } else if (!rc) /* Issued NVMe Encapsulated Request Message */ 196867feec50SStephen McConnell return; 196967feec50SStephen McConnell } 197067feec50SStephen McConnell 1971991554f2SKenneth D. Merry req = (MPI2_SCSI_IO_REQUEST *)cm->cm_req; 1972991554f2SKenneth D. Merry bzero(req, sizeof(*req)); 1973991554f2SKenneth D. Merry req->DevHandle = htole16(targ->handle); 1974991554f2SKenneth D. Merry req->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; 1975991554f2SKenneth D. Merry req->MsgFlags = 0; 1976991554f2SKenneth D. Merry req->SenseBufferLowAddress = htole32(cm->cm_sense_busaddr); 1977991554f2SKenneth D. Merry req->SenseBufferLength = MPR_SENSE_LEN; 1978991554f2SKenneth D. Merry req->SGLFlags = 0; 1979991554f2SKenneth D. Merry req->ChainOffset = 0; 1980991554f2SKenneth D. Merry req->SGLOffset0 = 24; /* 32bit word offset to the SGL */ 1981991554f2SKenneth D. Merry req->SGLOffset1= 0; 1982991554f2SKenneth D. Merry req->SGLOffset2= 0; 1983991554f2SKenneth D. Merry req->SGLOffset3= 0; 1984991554f2SKenneth D. Merry req->SkipCount = 0; 1985991554f2SKenneth D. Merry req->DataLength = htole32(csio->dxfer_len); 1986991554f2SKenneth D. Merry req->BidirectionalDataLength = 0; 1987991554f2SKenneth D. Merry req->IoFlags = htole16(csio->cdb_len); 1988991554f2SKenneth D. Merry req->EEDPFlags = 0; 1989991554f2SKenneth D. Merry 1990991554f2SKenneth D. Merry /* Note: BiDirectional transfers are not supported */ 1991991554f2SKenneth D. Merry switch (csio->ccb_h.flags & CAM_DIR_MASK) { 1992991554f2SKenneth D. Merry case CAM_DIR_IN: 1993991554f2SKenneth D. Merry mpi_control = MPI2_SCSIIO_CONTROL_READ; 1994991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_DATAIN; 1995991554f2SKenneth D. Merry break; 1996991554f2SKenneth D. Merry case CAM_DIR_OUT: 1997991554f2SKenneth D. Merry mpi_control = MPI2_SCSIIO_CONTROL_WRITE; 1998991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_DATAOUT; 1999991554f2SKenneth D. Merry break; 2000991554f2SKenneth D. Merry case CAM_DIR_NONE: 2001991554f2SKenneth D. Merry default: 2002991554f2SKenneth D. Merry mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; 2003991554f2SKenneth D. Merry break; 2004991554f2SKenneth D. Merry } 2005991554f2SKenneth D. Merry 2006991554f2SKenneth D. Merry if (csio->cdb_len == 32) 2007991554f2SKenneth D. Merry mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT; 2008991554f2SKenneth D. Merry /* 2009991554f2SKenneth D. Merry * It looks like the hardware doesn't require an explicit tag 2010991554f2SKenneth D. Merry * number for each transaction. SAM Task Management not supported 2011991554f2SKenneth D. Merry * at the moment. 2012991554f2SKenneth D. Merry */ 2013991554f2SKenneth D. Merry switch (csio->tag_action) { 2014991554f2SKenneth D. Merry case MSG_HEAD_OF_Q_TAG: 2015991554f2SKenneth D. Merry mpi_control |= MPI2_SCSIIO_CONTROL_HEADOFQ; 2016991554f2SKenneth D. Merry break; 2017991554f2SKenneth D. Merry case MSG_ORDERED_Q_TAG: 2018991554f2SKenneth D. Merry mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; 2019991554f2SKenneth D. Merry break; 2020991554f2SKenneth D. Merry case MSG_ACA_TASK: 2021991554f2SKenneth D. Merry mpi_control |= MPI2_SCSIIO_CONTROL_ACAQ; 2022991554f2SKenneth D. Merry break; 2023991554f2SKenneth D. Merry case CAM_TAG_ACTION_NONE: 2024991554f2SKenneth D. Merry case MSG_SIMPLE_Q_TAG: 2025991554f2SKenneth D. Merry default: 2026991554f2SKenneth D. Merry mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; 2027991554f2SKenneth D. Merry break; 2028991554f2SKenneth D. Merry } 202988364968SAlexander Motin mpi_control |= (csio->priority << MPI2_SCSIIO_CONTROL_CMDPRI_SHIFT) & 203088364968SAlexander Motin MPI2_SCSIIO_CONTROL_CMDPRI_MASK; 2031991554f2SKenneth D. Merry mpi_control |= sc->mapping_table[csio->ccb_h.target_id].TLR_bits; 2032991554f2SKenneth D. Merry req->Control = htole32(mpi_control); 2033991554f2SKenneth D. Merry 2034991554f2SKenneth D. Merry if (MPR_SET_LUN(req->LUN, csio->ccb_h.target_lun) != 0) { 2035991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2036a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_LUN_INVALID); 2037991554f2SKenneth D. Merry xpt_done(ccb); 2038991554f2SKenneth D. Merry return; 2039991554f2SKenneth D. Merry } 2040991554f2SKenneth D. Merry 2041991554f2SKenneth D. Merry if (csio->ccb_h.flags & CAM_CDB_POINTER) 2042991554f2SKenneth D. Merry bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len); 2043fa699bb2SAlan Somers else { 2044fa699bb2SAlan Somers KASSERT(csio->cdb_len <= IOCDBLEN, 204567feec50SStephen McConnell ("cdb_len %d is greater than IOCDBLEN but CAM_CDB_POINTER " 204667feec50SStephen McConnell "is not set", csio->cdb_len)); 2047991554f2SKenneth D. Merry bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len); 2048fa699bb2SAlan Somers } 2049991554f2SKenneth D. Merry req->IoFlags = htole16(csio->cdb_len); 2050991554f2SKenneth D. Merry 2051991554f2SKenneth D. Merry /* 2052991554f2SKenneth D. Merry * Check if EEDP is supported and enabled. If it is then check if the 2053991554f2SKenneth D. Merry * SCSI opcode could be using EEDP. If so, make sure the LUN exists and 2054991554f2SKenneth D. Merry * is formatted for EEDP support. If all of this is true, set CDB up 2055991554f2SKenneth D. Merry * for EEDP transfer. 2056991554f2SKenneth D. Merry */ 2057991554f2SKenneth D. Merry eedp_flags = op_code_prot[req->CDB.CDB32[0]]; 2058991554f2SKenneth D. Merry if (sc->eedp_enabled && eedp_flags) { 2059991554f2SKenneth D. Merry SLIST_FOREACH(lun, &targ->luns, lun_link) { 2060991554f2SKenneth D. Merry if (lun->lun_id == csio->ccb_h.target_lun) { 2061991554f2SKenneth D. Merry break; 2062991554f2SKenneth D. Merry } 2063991554f2SKenneth D. Merry } 2064991554f2SKenneth D. Merry 2065991554f2SKenneth D. Merry if ((lun != NULL) && (lun->eedp_formatted)) { 206671900a79SAlfredo Dal'Ava Junior req->EEDPBlockSize = htole32(lun->eedp_block_size); 2067991554f2SKenneth D. Merry eedp_flags |= (MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | 2068991554f2SKenneth D. Merry MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | 2069991554f2SKenneth D. Merry MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD); 207067feec50SStephen McConnell if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) { 207167feec50SStephen McConnell eedp_flags |= 207267feec50SStephen McConnell MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE; 207367feec50SStephen McConnell } 2074991554f2SKenneth D. Merry req->EEDPFlags = htole16(eedp_flags); 2075991554f2SKenneth D. Merry 2076991554f2SKenneth D. Merry /* 2077991554f2SKenneth D. Merry * If CDB less than 32, fill in Primary Ref Tag with 2078991554f2SKenneth D. Merry * low 4 bytes of LBA. If CDB is 32, tag stuff is 2079991554f2SKenneth D. Merry * already there. Also, set protection bit. FreeBSD 2080991554f2SKenneth D. Merry * currently does not support CDBs bigger than 16, but 2081991554f2SKenneth D. Merry * the code doesn't hurt, and will be here for the 2082991554f2SKenneth D. Merry * future. 2083991554f2SKenneth D. Merry */ 2084991554f2SKenneth D. Merry if (csio->cdb_len != 32) { 2085991554f2SKenneth D. Merry lba_byte = (csio->cdb_len == 16) ? 6 : 2; 2086991554f2SKenneth D. Merry ref_tag_addr = (uint8_t *)&req->CDB.EEDP32. 2087991554f2SKenneth D. Merry PrimaryReferenceTag; 2088991554f2SKenneth D. Merry for (i = 0; i < 4; i++) { 2089991554f2SKenneth D. Merry *ref_tag_addr = 2090991554f2SKenneth D. Merry req->CDB.CDB32[lba_byte + i]; 2091991554f2SKenneth D. Merry ref_tag_addr++; 2092991554f2SKenneth D. Merry } 2093991554f2SKenneth D. Merry req->CDB.EEDP32.PrimaryReferenceTag = 2094991554f2SKenneth D. Merry htole32(req-> 2095991554f2SKenneth D. Merry CDB.EEDP32.PrimaryReferenceTag); 2096991554f2SKenneth D. Merry req->CDB.EEDP32.PrimaryApplicationTagMask = 2097991554f2SKenneth D. Merry 0xFFFF; 20988881681bSKenneth D. Merry req->CDB.CDB32[1] = 20998881681bSKenneth D. Merry (req->CDB.CDB32[1] & 0x1F) | 0x20; 2100991554f2SKenneth D. Merry } else { 2101991554f2SKenneth D. Merry eedp_flags |= 2102991554f2SKenneth D. Merry MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG; 2103991554f2SKenneth D. Merry req->EEDPFlags = htole16(eedp_flags); 2104991554f2SKenneth D. Merry req->CDB.CDB32[10] = (req->CDB.CDB32[10] & 2105991554f2SKenneth D. Merry 0x1F) | 0x20; 2106991554f2SKenneth D. Merry } 2107991554f2SKenneth D. Merry } 2108991554f2SKenneth D. Merry } 2109991554f2SKenneth D. Merry 2110991554f2SKenneth D. Merry cm->cm_length = csio->dxfer_len; 2111991554f2SKenneth D. Merry if (cm->cm_length != 0) { 2112991554f2SKenneth D. Merry cm->cm_data = ccb; 2113991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_USE_CCB; 2114991554f2SKenneth D. Merry } else { 2115991554f2SKenneth D. Merry cm->cm_data = NULL; 2116991554f2SKenneth D. Merry } 2117991554f2SKenneth D. Merry cm->cm_sge = &req->SGL; 2118991554f2SKenneth D. Merry cm->cm_sglsize = (32 - 24) * 4; 2119991554f2SKenneth D. Merry cm->cm_complete = mprsas_scsiio_complete; 2120991554f2SKenneth D. Merry cm->cm_complete_data = ccb; 2121991554f2SKenneth D. Merry cm->cm_targ = targ; 2122991554f2SKenneth D. Merry cm->cm_lun = csio->ccb_h.target_lun; 2123991554f2SKenneth D. Merry cm->cm_ccb = ccb; 2124991554f2SKenneth D. Merry /* 2125991554f2SKenneth D. Merry * If using FP desc type, need to set a bit in IoFlags (SCSI IO is 0) 2126991554f2SKenneth D. Merry * and set descriptor type. 2127991554f2SKenneth D. Merry */ 2128991554f2SKenneth D. Merry if (targ->scsi_req_desc_type == 2129991554f2SKenneth D. Merry MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO) { 2130991554f2SKenneth D. Merry req->IoFlags |= MPI25_SCSIIO_IOFLAGS_FAST_PATH; 2131991554f2SKenneth D. Merry cm->cm_desc.FastPathSCSIIO.RequestFlags = 2132991554f2SKenneth D. Merry MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; 213367feec50SStephen McConnell if (!sc->atomic_desc_capable) { 213467feec50SStephen McConnell cm->cm_desc.FastPathSCSIIO.DevHandle = 213567feec50SStephen McConnell htole16(targ->handle); 213667feec50SStephen McConnell } 2137991554f2SKenneth D. Merry } else { 2138991554f2SKenneth D. Merry cm->cm_desc.SCSIIO.RequestFlags = 2139991554f2SKenneth D. Merry MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; 214067feec50SStephen McConnell if (!sc->atomic_desc_capable) 2141991554f2SKenneth D. Merry cm->cm_desc.SCSIIO.DevHandle = htole16(targ->handle); 2142991554f2SKenneth D. Merry } 2143991554f2SKenneth D. Merry 21446eea4f46SScott Long csio->ccb_h.qos.sim_data = sbinuptime(); 214585c9dd9dSSteven Hartland callout_reset_sbt(&cm->cm_callout, SBT_1MS * ccb->ccb_h.timeout, 0, 214685c9dd9dSSteven Hartland mprsas_scsiio_timeout, cm, 0); 2147991554f2SKenneth D. Merry 2148991554f2SKenneth D. Merry targ->issued++; 2149991554f2SKenneth D. Merry targ->outstanding++; 2150991554f2SKenneth D. Merry TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link); 2151991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_SIM_QUEUED; 2152991554f2SKenneth D. Merry 2153991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, "%s cm %p ccb %p outstanding %u\n", 2154991554f2SKenneth D. Merry __func__, cm, ccb, targ->outstanding); 2155991554f2SKenneth D. Merry 2156991554f2SKenneth D. Merry mpr_map_command(sc, cm); 2157991554f2SKenneth D. Merry return; 2158991554f2SKenneth D. Merry } 2159991554f2SKenneth D. Merry 2160991554f2SKenneth D. Merry /** 2161991554f2SKenneth D. Merry * mpr_sc_failed_io_info - translated non-succesfull SCSI_IO request 2162991554f2SKenneth D. Merry */ 2163991554f2SKenneth D. Merry static void 2164991554f2SKenneth D. Merry mpr_sc_failed_io_info(struct mpr_softc *sc, struct ccb_scsiio *csio, 2165991554f2SKenneth D. Merry Mpi2SCSIIOReply_t *mpi_reply, struct mprsas_target *targ) 2166991554f2SKenneth D. Merry { 2167991554f2SKenneth D. Merry u32 response_info; 2168991554f2SKenneth D. Merry u8 *response_bytes; 2169991554f2SKenneth D. Merry u16 ioc_status = le16toh(mpi_reply->IOCStatus) & 2170991554f2SKenneth D. Merry MPI2_IOCSTATUS_MASK; 2171991554f2SKenneth D. Merry u8 scsi_state = mpi_reply->SCSIState; 2172991554f2SKenneth D. Merry u8 scsi_status = mpi_reply->SCSIStatus; 2173991554f2SKenneth D. Merry char *desc_ioc_state = NULL; 2174991554f2SKenneth D. Merry char *desc_scsi_status = NULL; 2175991554f2SKenneth D. Merry u32 log_info = le32toh(mpi_reply->IOCLogInfo); 2176991554f2SKenneth D. Merry 2177991554f2SKenneth D. Merry if (log_info == 0x31170000) 2178991554f2SKenneth D. Merry return; 2179991554f2SKenneth D. Merry 21802bf620cbSScott Long desc_ioc_state = mpr_describe_table(mpr_iocstatus_string, 21812bf620cbSScott Long ioc_status); 21822bf620cbSScott Long desc_scsi_status = mpr_describe_table(mpr_scsi_status_string, 21832bf620cbSScott Long scsi_status); 2184991554f2SKenneth D. Merry 2185991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "\thandle(0x%04x), ioc_status(%s)(0x%04x)\n", 2186991554f2SKenneth D. Merry le16toh(mpi_reply->DevHandle), desc_ioc_state, ioc_status); 2187991554f2SKenneth D. Merry if (targ->encl_level_valid) { 2188991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "At enclosure level %d, slot %d, " 2189991554f2SKenneth D. Merry "connector name (%4s)\n", targ->encl_level, targ->encl_slot, 2190991554f2SKenneth D. Merry targ->connector_name); 2191991554f2SKenneth D. Merry } 21922bf620cbSScott Long 21932bf620cbSScott Long /* 21942bf620cbSScott Long * We can add more detail about underflow data here 2195991554f2SKenneth D. Merry * TO-DO 21962bf620cbSScott Long */ 2197991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "\tscsi_status(%s)(0x%02x), " 21982bf620cbSScott Long "scsi_state %b\n", desc_scsi_status, scsi_status, 21992bf620cbSScott Long scsi_state, "\20" "\1AutosenseValid" "\2AutosenseFailed" 22002bf620cbSScott Long "\3NoScsiStatus" "\4Terminated" "\5Response InfoValid"); 2201991554f2SKenneth D. Merry 2202991554f2SKenneth D. Merry if (sc->mpr_debug & MPR_XINFO && 2203991554f2SKenneth D. Merry scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { 2204991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "-> Sense Buffer Data : Start :\n"); 2205991554f2SKenneth D. Merry scsi_sense_print(csio); 2206991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "-> Sense Buffer Data : End :\n"); 2207991554f2SKenneth D. Merry } 2208991554f2SKenneth D. Merry 2209991554f2SKenneth D. Merry if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { 2210991554f2SKenneth D. Merry response_info = le32toh(mpi_reply->ResponseInfo); 2211991554f2SKenneth D. Merry response_bytes = (u8 *)&response_info; 22122bf620cbSScott Long mpr_dprint(sc, MPR_XINFO, "response code(0x%01x): %s\n", 22132bf620cbSScott Long response_bytes[0], 22142bf620cbSScott Long mpr_describe_table(mpr_scsi_taskmgmt_string, 22152bf620cbSScott Long response_bytes[0])); 2216991554f2SKenneth D. Merry } 2217991554f2SKenneth D. Merry } 2218991554f2SKenneth D. Merry 221967feec50SStephen McConnell /** mprsas_nvme_trans_status_code 222067feec50SStephen McConnell * 222167feec50SStephen McConnell * Convert Native NVMe command error status to 222267feec50SStephen McConnell * equivalent SCSI error status. 222367feec50SStephen McConnell * 222467feec50SStephen McConnell * Returns appropriate scsi_status 222567feec50SStephen McConnell */ 222667feec50SStephen McConnell static u8 22270d787e9bSWojciech Macek mprsas_nvme_trans_status_code(uint16_t nvme_status, 222867feec50SStephen McConnell struct mpr_command *cm) 222967feec50SStephen McConnell { 223067feec50SStephen McConnell u8 status = MPI2_SCSI_STATUS_GOOD; 223167feec50SStephen McConnell int skey, asc, ascq; 223267feec50SStephen McConnell union ccb *ccb = cm->cm_complete_data; 223367feec50SStephen McConnell int returned_sense_len; 22340d787e9bSWojciech Macek uint8_t sct, sc; 22350d787e9bSWojciech Macek 22360d787e9bSWojciech Macek sct = NVME_STATUS_GET_SCT(nvme_status); 22370d787e9bSWojciech Macek sc = NVME_STATUS_GET_SC(nvme_status); 223867feec50SStephen McConnell 223967feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 224067feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 224167feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 224267feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 224367feec50SStephen McConnell 22440d787e9bSWojciech Macek switch (sct) { 224567feec50SStephen McConnell case NVME_SCT_GENERIC: 22460d787e9bSWojciech Macek switch (sc) { 224767feec50SStephen McConnell case NVME_SC_SUCCESS: 224867feec50SStephen McConnell status = MPI2_SCSI_STATUS_GOOD; 224967feec50SStephen McConnell skey = SSD_KEY_NO_SENSE; 225067feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 225167feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 225267feec50SStephen McConnell break; 225367feec50SStephen McConnell case NVME_SC_INVALID_OPCODE: 225467feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 225567feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 225667feec50SStephen McConnell asc = SCSI_ASC_ILLEGAL_COMMAND; 225767feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 225867feec50SStephen McConnell break; 225967feec50SStephen McConnell case NVME_SC_INVALID_FIELD: 226067feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 226167feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 226267feec50SStephen McConnell asc = SCSI_ASC_INVALID_CDB; 226367feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 226467feec50SStephen McConnell break; 226567feec50SStephen McConnell case NVME_SC_DATA_TRANSFER_ERROR: 226667feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 226767feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 226867feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 226967feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 227067feec50SStephen McConnell break; 227167feec50SStephen McConnell case NVME_SC_ABORTED_POWER_LOSS: 227267feec50SStephen McConnell status = MPI2_SCSI_STATUS_TASK_ABORTED; 227367feec50SStephen McConnell skey = SSD_KEY_ABORTED_COMMAND; 227467feec50SStephen McConnell asc = SCSI_ASC_WARNING; 227567feec50SStephen McConnell ascq = SCSI_ASCQ_POWER_LOSS_EXPECTED; 227667feec50SStephen McConnell break; 227767feec50SStephen McConnell case NVME_SC_INTERNAL_DEVICE_ERROR: 227867feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 227967feec50SStephen McConnell skey = SSD_KEY_HARDWARE_ERROR; 228067feec50SStephen McConnell asc = SCSI_ASC_INTERNAL_TARGET_FAILURE; 228167feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 228267feec50SStephen McConnell break; 228367feec50SStephen McConnell case NVME_SC_ABORTED_BY_REQUEST: 228467feec50SStephen McConnell case NVME_SC_ABORTED_SQ_DELETION: 228567feec50SStephen McConnell case NVME_SC_ABORTED_FAILED_FUSED: 228667feec50SStephen McConnell case NVME_SC_ABORTED_MISSING_FUSED: 228767feec50SStephen McConnell status = MPI2_SCSI_STATUS_TASK_ABORTED; 228867feec50SStephen McConnell skey = SSD_KEY_ABORTED_COMMAND; 228967feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 229067feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 229167feec50SStephen McConnell break; 229267feec50SStephen McConnell case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: 229367feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 229467feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 229567feec50SStephen McConnell asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID; 229667feec50SStephen McConnell ascq = SCSI_ASCQ_INVALID_LUN_ID; 229767feec50SStephen McConnell break; 229867feec50SStephen McConnell case NVME_SC_LBA_OUT_OF_RANGE: 229967feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 230067feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 230167feec50SStephen McConnell asc = SCSI_ASC_ILLEGAL_BLOCK; 230267feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 230367feec50SStephen McConnell break; 230467feec50SStephen McConnell case NVME_SC_CAPACITY_EXCEEDED: 230567feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 230667feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 230767feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 230867feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 230967feec50SStephen McConnell break; 231067feec50SStephen McConnell case NVME_SC_NAMESPACE_NOT_READY: 231167feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 231267feec50SStephen McConnell skey = SSD_KEY_NOT_READY; 231367feec50SStephen McConnell asc = SCSI_ASC_LUN_NOT_READY; 231467feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 231567feec50SStephen McConnell break; 231667feec50SStephen McConnell } 231767feec50SStephen McConnell break; 231867feec50SStephen McConnell case NVME_SCT_COMMAND_SPECIFIC: 23190d787e9bSWojciech Macek switch (sc) { 232067feec50SStephen McConnell case NVME_SC_INVALID_FORMAT: 232167feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 232267feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 232367feec50SStephen McConnell asc = SCSI_ASC_FORMAT_COMMAND_FAILED; 232467feec50SStephen McConnell ascq = SCSI_ASCQ_FORMAT_COMMAND_FAILED; 232567feec50SStephen McConnell break; 232667feec50SStephen McConnell case NVME_SC_CONFLICTING_ATTRIBUTES: 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 } 233367feec50SStephen McConnell break; 233467feec50SStephen McConnell case NVME_SCT_MEDIA_ERROR: 23350d787e9bSWojciech Macek switch (sc) { 233667feec50SStephen McConnell case NVME_SC_WRITE_FAULTS: 233767feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 233867feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 233967feec50SStephen McConnell asc = SCSI_ASC_PERIPHERAL_DEV_WRITE_FAULT; 234067feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 234167feec50SStephen McConnell break; 234267feec50SStephen McConnell case NVME_SC_UNRECOVERED_READ_ERROR: 234367feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 234467feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 234567feec50SStephen McConnell asc = SCSI_ASC_UNRECOVERED_READ_ERROR; 234667feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 234767feec50SStephen McConnell break; 234867feec50SStephen McConnell case NVME_SC_GUARD_CHECK_ERROR: 234967feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 235067feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 235167feec50SStephen McConnell asc = SCSI_ASC_LOG_BLOCK_GUARD_CHECK_FAILED; 235267feec50SStephen McConnell ascq = SCSI_ASCQ_LOG_BLOCK_GUARD_CHECK_FAILED; 235367feec50SStephen McConnell break; 235467feec50SStephen McConnell case NVME_SC_APPLICATION_TAG_CHECK_ERROR: 235567feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 235667feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 235767feec50SStephen McConnell asc = SCSI_ASC_LOG_BLOCK_APPTAG_CHECK_FAILED; 235867feec50SStephen McConnell ascq = SCSI_ASCQ_LOG_BLOCK_APPTAG_CHECK_FAILED; 235967feec50SStephen McConnell break; 236067feec50SStephen McConnell case NVME_SC_REFERENCE_TAG_CHECK_ERROR: 236167feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 236267feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 236367feec50SStephen McConnell asc = SCSI_ASC_LOG_BLOCK_REFTAG_CHECK_FAILED; 236467feec50SStephen McConnell ascq = SCSI_ASCQ_LOG_BLOCK_REFTAG_CHECK_FAILED; 236567feec50SStephen McConnell break; 236667feec50SStephen McConnell case NVME_SC_COMPARE_FAILURE: 236767feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 236867feec50SStephen McConnell skey = SSD_KEY_MISCOMPARE; 236967feec50SStephen McConnell asc = SCSI_ASC_MISCOMPARE_DURING_VERIFY; 237067feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 237167feec50SStephen McConnell break; 237267feec50SStephen McConnell case NVME_SC_ACCESS_DENIED: 237367feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 237467feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 237567feec50SStephen McConnell asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID; 237667feec50SStephen McConnell ascq = SCSI_ASCQ_INVALID_LUN_ID; 237767feec50SStephen McConnell break; 237867feec50SStephen McConnell } 237967feec50SStephen McConnell break; 238067feec50SStephen McConnell } 238167feec50SStephen McConnell 238267feec50SStephen McConnell returned_sense_len = sizeof(struct scsi_sense_data); 238367feec50SStephen McConnell if (returned_sense_len < ccb->csio.sense_len) 238467feec50SStephen McConnell ccb->csio.sense_resid = ccb->csio.sense_len - 238567feec50SStephen McConnell returned_sense_len; 238667feec50SStephen McConnell else 238767feec50SStephen McConnell ccb->csio.sense_resid = 0; 238867feec50SStephen McConnell 238967feec50SStephen McConnell scsi_set_sense_data(&ccb->csio.sense_data, SSD_TYPE_FIXED, 239067feec50SStephen McConnell 1, skey, asc, ascq, SSD_ELEM_NONE); 239167feec50SStephen McConnell ccb->ccb_h.status |= CAM_AUTOSNS_VALID; 239267feec50SStephen McConnell 239367feec50SStephen McConnell return status; 239467feec50SStephen McConnell } 239567feec50SStephen McConnell 239667feec50SStephen McConnell /** mprsas_complete_nvme_unmap 239767feec50SStephen McConnell * 239867feec50SStephen McConnell * Complete native NVMe command issued using NVMe Encapsulated 239967feec50SStephen McConnell * Request Message. 240067feec50SStephen McConnell */ 240167feec50SStephen McConnell static u8 240267feec50SStephen McConnell mprsas_complete_nvme_unmap(struct mpr_softc *sc, struct mpr_command *cm) 240367feec50SStephen McConnell { 240467feec50SStephen McConnell Mpi26NVMeEncapsulatedErrorReply_t *mpi_reply; 240567feec50SStephen McConnell struct nvme_completion *nvme_completion = NULL; 240667feec50SStephen McConnell u8 scsi_status = MPI2_SCSI_STATUS_GOOD; 240767feec50SStephen McConnell 240867feec50SStephen McConnell mpi_reply =(Mpi26NVMeEncapsulatedErrorReply_t *)cm->cm_reply; 240967feec50SStephen McConnell if (le16toh(mpi_reply->ErrorResponseCount)){ 241067feec50SStephen McConnell nvme_completion = (struct nvme_completion *)cm->cm_sense; 241167feec50SStephen McConnell scsi_status = mprsas_nvme_trans_status_code( 241267feec50SStephen McConnell nvme_completion->status, cm); 241367feec50SStephen McConnell } 241467feec50SStephen McConnell return scsi_status; 241567feec50SStephen McConnell } 241667feec50SStephen McConnell 2417991554f2SKenneth D. Merry static void 2418991554f2SKenneth D. Merry mprsas_scsiio_complete(struct mpr_softc *sc, struct mpr_command *cm) 2419991554f2SKenneth D. Merry { 2420991554f2SKenneth D. Merry MPI2_SCSI_IO_REPLY *rep; 2421991554f2SKenneth D. Merry union ccb *ccb; 2422991554f2SKenneth D. Merry struct ccb_scsiio *csio; 2423991554f2SKenneth D. Merry struct mprsas_softc *sassc; 2424991554f2SKenneth D. Merry struct scsi_vpd_supported_page_list *vpd_list = NULL; 242567feec50SStephen McConnell u8 *TLR_bits, TLR_on, *scsi_cdb; 2426991554f2SKenneth D. Merry int dir = 0, i; 2427991554f2SKenneth D. Merry u16 alloc_len; 2428a2c14879SStephen McConnell struct mprsas_target *target; 2429a2c14879SStephen McConnell target_id_t target_id; 2430991554f2SKenneth D. Merry 2431991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 2432991554f2SKenneth D. Merry 2433991554f2SKenneth D. Merry callout_stop(&cm->cm_callout); 2434991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 2435991554f2SKenneth D. Merry 2436991554f2SKenneth D. Merry sassc = sc->sassc; 2437991554f2SKenneth D. Merry ccb = cm->cm_complete_data; 2438991554f2SKenneth D. Merry csio = &ccb->csio; 2439a2c14879SStephen McConnell target_id = csio->ccb_h.target_id; 2440991554f2SKenneth D. Merry rep = (MPI2_SCSI_IO_REPLY *)cm->cm_reply; 244171900a79SAlfredo Dal'Ava Junior mpr_dprint(sc, MPR_TRACE, 244271900a79SAlfredo Dal'Ava Junior "cm %p SMID %u ccb %p reply %p outstanding %u csio->scsi_status 0x%x," 244371900a79SAlfredo Dal'Ava Junior "csio->dxfer_len 0x%x, csio->msg_le 0x%xn\n", cm, 244471900a79SAlfredo Dal'Ava Junior cm->cm_desc.Default.SMID, cm->cm_ccb, cm->cm_reply, 244571900a79SAlfredo Dal'Ava Junior cm->cm_targ->outstanding, csio->scsi_status, 244671900a79SAlfredo Dal'Ava Junior csio->dxfer_len, csio->msg_len); 2447991554f2SKenneth D. Merry /* 2448991554f2SKenneth D. Merry * XXX KDM if the chain allocation fails, does it matter if we do 2449991554f2SKenneth D. Merry * the sync and unload here? It is simpler to do it in every case, 2450991554f2SKenneth D. Merry * assuming it doesn't cause problems. 2451991554f2SKenneth D. Merry */ 2452991554f2SKenneth D. Merry if (cm->cm_data != NULL) { 2453991554f2SKenneth D. Merry if (cm->cm_flags & MPR_CM_FLAGS_DATAIN) 2454991554f2SKenneth D. Merry dir = BUS_DMASYNC_POSTREAD; 2455991554f2SKenneth D. Merry else if (cm->cm_flags & MPR_CM_FLAGS_DATAOUT) 2456991554f2SKenneth D. Merry dir = BUS_DMASYNC_POSTWRITE; 2457991554f2SKenneth D. Merry bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir); 2458991554f2SKenneth D. Merry bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); 2459991554f2SKenneth D. Merry } 2460991554f2SKenneth D. Merry 2461991554f2SKenneth D. Merry cm->cm_targ->completed++; 2462991554f2SKenneth D. Merry cm->cm_targ->outstanding--; 2463991554f2SKenneth D. Merry TAILQ_REMOVE(&cm->cm_targ->commands, cm, cm_link); 2464991554f2SKenneth D. Merry ccb->ccb_h.status &= ~(CAM_STATUS_MASK | CAM_SIM_QUEUED); 2465991554f2SKenneth D. Merry 24668fe7bf06SWarner Losh if (cm->cm_flags & MPR_CM_FLAGS_ON_RECOVERY) { 2467991554f2SKenneth D. Merry TAILQ_REMOVE(&cm->cm_targ->timedout_commands, cm, cm_recovery); 24688fe7bf06SWarner Losh KASSERT(cm->cm_state == MPR_CM_STATE_BUSY, 2469175ad3d0SKenneth D. Merry ("Not busy for CM_FLAGS_TIMEDOUT: %u\n", cm->cm_state)); 24708fe7bf06SWarner Losh cm->cm_flags &= ~MPR_CM_FLAGS_ON_RECOVERY; 2471991554f2SKenneth D. Merry if (cm->cm_reply != NULL) 2472991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2473991554f2SKenneth D. Merry "completed timedout cm %p ccb %p during recovery " 2474991554f2SKenneth D. Merry "ioc %x scsi %x state %x xfer %u\n", cm, cm->cm_ccb, 2475991554f2SKenneth D. Merry le16toh(rep->IOCStatus), rep->SCSIStatus, 2476991554f2SKenneth D. Merry rep->SCSIState, le32toh(rep->TransferCount)); 2477991554f2SKenneth D. Merry else 2478991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2479991554f2SKenneth D. Merry "completed timedout cm %p ccb %p during recovery\n", 2480991554f2SKenneth D. Merry cm, cm->cm_ccb); 2481991554f2SKenneth D. Merry } else if (cm->cm_targ->tm != NULL) { 2482991554f2SKenneth D. Merry if (cm->cm_reply != NULL) 2483991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2484991554f2SKenneth D. Merry "completed cm %p ccb %p during recovery " 2485991554f2SKenneth D. Merry "ioc %x scsi %x state %x xfer %u\n", 2486991554f2SKenneth D. Merry cm, cm->cm_ccb, le16toh(rep->IOCStatus), 2487991554f2SKenneth D. Merry rep->SCSIStatus, rep->SCSIState, 2488991554f2SKenneth D. Merry le32toh(rep->TransferCount)); 2489991554f2SKenneth D. Merry else 2490991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2491991554f2SKenneth D. Merry "completed cm %p ccb %p during recovery\n", 2492991554f2SKenneth D. Merry cm, cm->cm_ccb); 2493991554f2SKenneth D. Merry } else if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) { 2494991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2495991554f2SKenneth D. Merry "reset completed cm %p ccb %p\n", cm, cm->cm_ccb); 2496991554f2SKenneth D. Merry } 2497991554f2SKenneth D. Merry 2498991554f2SKenneth D. Merry if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 2499991554f2SKenneth D. Merry /* 2500991554f2SKenneth D. Merry * We ran into an error after we tried to map the command, 2501991554f2SKenneth D. Merry * so we're getting a callback without queueing the command 2502991554f2SKenneth D. Merry * to the hardware. So we set the status here, and it will 2503991554f2SKenneth D. Merry * be retained below. We'll go through the "fast path", 2504991554f2SKenneth D. Merry * because there can be no reply when we haven't actually 2505991554f2SKenneth D. Merry * gone out to the hardware. 2506991554f2SKenneth D. Merry */ 2507a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQUEUE_REQ); 2508991554f2SKenneth D. Merry 2509991554f2SKenneth D. Merry /* 2510991554f2SKenneth D. Merry * Currently the only error included in the mask is 2511991554f2SKenneth D. Merry * MPR_CM_FLAGS_CHAIN_FAILED, which means we're out of 2512991554f2SKenneth D. Merry * chain frames. We need to freeze the queue until we get 2513991554f2SKenneth D. Merry * a command that completed without this error, which will 2514991554f2SKenneth D. Merry * hopefully have some chain frames attached that we can 2515991554f2SKenneth D. Merry * use. If we wanted to get smarter about it, we would 2516991554f2SKenneth D. Merry * only unfreeze the queue in this condition when we're 2517991554f2SKenneth D. Merry * sure that we're getting some chain frames back. That's 2518991554f2SKenneth D. Merry * probably unnecessary. 2519991554f2SKenneth D. Merry */ 2520991554f2SKenneth D. Merry if ((sassc->flags & MPRSAS_QUEUE_FROZEN) == 0) { 2521991554f2SKenneth D. Merry xpt_freeze_simq(sassc->sim, 1); 2522991554f2SKenneth D. Merry sassc->flags |= MPRSAS_QUEUE_FROZEN; 2523a8837c77SWarner Losh mpr_dprint(sc, MPR_XINFO | MPR_RECOVERY, 2524a8837c77SWarner Losh "Error sending command, freezing SIM queue\n"); 2525991554f2SKenneth D. Merry } 2526991554f2SKenneth D. Merry } 2527991554f2SKenneth D. Merry 2528991554f2SKenneth D. Merry /* 252967feec50SStephen McConnell * Point to the SCSI CDB, which is dependent on the CAM_CDB_POINTER 253067feec50SStephen McConnell * flag, and use it in a few places in the rest of this function for 253167feec50SStephen McConnell * convenience. Use the macro if available. 253267feec50SStephen McConnell */ 253367feec50SStephen McConnell scsi_cdb = scsiio_cdb_ptr(csio); 253467feec50SStephen McConnell 253567feec50SStephen McConnell /* 2536991554f2SKenneth D. Merry * If this is a Start Stop Unit command and it was issued by the driver 2537991554f2SKenneth D. Merry * during shutdown, decrement the refcount to account for all of the 2538991554f2SKenneth D. Merry * commands that were sent. All SSU commands should be completed before 2539991554f2SKenneth D. Merry * shutdown completes, meaning SSU_refcount will be 0 after SSU_started 2540991554f2SKenneth D. Merry * is TRUE. 2541991554f2SKenneth D. Merry */ 254267feec50SStephen McConnell if (sc->SSU_started && (scsi_cdb[0] == START_STOP_UNIT)) { 2543991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INFO, "Decrementing SSU count.\n"); 2544991554f2SKenneth D. Merry sc->SSU_refcount--; 2545991554f2SKenneth D. Merry } 2546991554f2SKenneth D. Merry 2547991554f2SKenneth D. Merry /* Take the fast path to completion */ 2548991554f2SKenneth D. Merry if (cm->cm_reply == NULL) { 2549a2c14879SStephen McConnell if (mprsas_get_ccbstatus(ccb) == CAM_REQ_INPROG) { 2550991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) 2551a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SCSI_BUS_RESET); 2552991554f2SKenneth D. Merry else { 2553a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 2554a2c14879SStephen McConnell csio->scsi_status = SCSI_STATUS_OK; 2555991554f2SKenneth D. Merry } 2556991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_QUEUE_FROZEN) { 2557991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_RELEASE_SIMQ; 2558991554f2SKenneth D. Merry sassc->flags &= ~MPRSAS_QUEUE_FROZEN; 2559a8837c77SWarner Losh mpr_dprint(sc, MPR_XINFO | MPR_RECOVERY, 2560991554f2SKenneth D. Merry "Unfreezing SIM queue\n"); 2561991554f2SKenneth D. Merry } 2562991554f2SKenneth D. Merry } 2563991554f2SKenneth D. Merry 2564991554f2SKenneth D. Merry /* 2565991554f2SKenneth D. Merry * There are two scenarios where the status won't be 2566991554f2SKenneth D. Merry * CAM_REQ_CMP. The first is if MPR_CM_FLAGS_ERROR_MASK is 2567991554f2SKenneth D. Merry * set, the second is in the MPR_FLAGS_DIAGRESET above. 2568991554f2SKenneth D. Merry */ 2569a2c14879SStephen McConnell if (mprsas_get_ccbstatus(ccb) != CAM_REQ_CMP) { 2570991554f2SKenneth D. Merry /* 2571991554f2SKenneth D. Merry * Freeze the dev queue so that commands are 2572a2c14879SStephen McConnell * executed in the correct order after error 2573991554f2SKenneth D. Merry * recovery. 2574991554f2SKenneth D. Merry */ 2575991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_DEV_QFRZN; 2576991554f2SKenneth D. Merry xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1); 2577991554f2SKenneth D. Merry } 2578991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2579991554f2SKenneth D. Merry xpt_done(ccb); 2580991554f2SKenneth D. Merry return; 2581991554f2SKenneth D. Merry } 2582991554f2SKenneth D. Merry 258367feec50SStephen McConnell target = &sassc->targets[target_id]; 258467feec50SStephen McConnell if (scsi_cdb[0] == UNMAP && 258567feec50SStephen McConnell target->is_nvme && 258667feec50SStephen McConnell (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) { 258767feec50SStephen McConnell rep->SCSIStatus = mprsas_complete_nvme_unmap(sc, cm); 258867feec50SStephen McConnell csio->scsi_status = rep->SCSIStatus; 258967feec50SStephen McConnell } 259067feec50SStephen McConnell 2591991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, 2592991554f2SKenneth D. Merry "ioc %x scsi %x state %x xfer %u\n", 2593991554f2SKenneth D. Merry le16toh(rep->IOCStatus), rep->SCSIStatus, rep->SCSIState, 2594991554f2SKenneth D. Merry le32toh(rep->TransferCount)); 2595991554f2SKenneth D. Merry 2596991554f2SKenneth D. Merry switch (le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK) { 2597991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: 2598991554f2SKenneth D. Merry csio->resid = cm->cm_length - le32toh(rep->TransferCount); 2599991554f2SKenneth D. Merry /* FALLTHROUGH */ 2600991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SUCCESS: 2601991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: 2602991554f2SKenneth D. Merry if ((le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK) == 2603991554f2SKenneth D. Merry MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR) 2604991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, "recovered error\n"); 2605991554f2SKenneth D. Merry 2606991554f2SKenneth D. Merry /* Completion failed at the transport level. */ 2607991554f2SKenneth D. Merry if (rep->SCSIState & (MPI2_SCSI_STATE_NO_SCSI_STATUS | 2608991554f2SKenneth D. Merry MPI2_SCSI_STATE_TERMINATED)) { 2609a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 2610991554f2SKenneth D. Merry break; 2611991554f2SKenneth D. Merry } 2612991554f2SKenneth D. Merry 2613991554f2SKenneth D. Merry /* In a modern packetized environment, an autosense failure 2614991554f2SKenneth D. Merry * implies that there's not much else that can be done to 2615991554f2SKenneth D. Merry * recover the command. 2616991554f2SKenneth D. Merry */ 2617991554f2SKenneth D. Merry if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_FAILED) { 2618a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_AUTOSENSE_FAIL); 2619991554f2SKenneth D. Merry break; 2620991554f2SKenneth D. Merry } 2621991554f2SKenneth D. Merry 2622991554f2SKenneth D. Merry /* 2623991554f2SKenneth D. Merry * CAM doesn't care about SAS Response Info data, but if this is 2624991554f2SKenneth D. Merry * the state check if TLR should be done. If not, clear the 2625991554f2SKenneth D. Merry * TLR_bits for the target. 2626991554f2SKenneth D. Merry */ 2627991554f2SKenneth D. Merry if ((rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) && 2628991554f2SKenneth D. Merry ((le32toh(rep->ResponseInfo) & MPI2_SCSI_RI_MASK_REASONCODE) 2629991554f2SKenneth D. Merry == MPR_SCSI_RI_INVALID_FRAME)) { 2630a2c14879SStephen McConnell sc->mapping_table[target_id].TLR_bits = 2631991554f2SKenneth D. Merry (u8)MPI2_SCSIIO_CONTROL_NO_TLR; 2632991554f2SKenneth D. Merry } 2633991554f2SKenneth D. Merry 2634991554f2SKenneth D. Merry /* 2635991554f2SKenneth D. Merry * Intentionally override the normal SCSI status reporting 2636991554f2SKenneth D. Merry * for these two cases. These are likely to happen in a 2637991554f2SKenneth D. Merry * multi-initiator environment, and we want to make sure that 2638991554f2SKenneth D. Merry * CAM retries these commands rather than fail them. 2639991554f2SKenneth D. Merry */ 2640991554f2SKenneth D. Merry if ((rep->SCSIStatus == MPI2_SCSI_STATUS_COMMAND_TERMINATED) || 2641991554f2SKenneth D. Merry (rep->SCSIStatus == MPI2_SCSI_STATUS_TASK_ABORTED)) { 2642a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_ABORTED); 2643991554f2SKenneth D. Merry break; 2644991554f2SKenneth D. Merry } 2645991554f2SKenneth D. Merry 2646991554f2SKenneth D. Merry /* Handle normal status and sense */ 2647991554f2SKenneth D. Merry csio->scsi_status = rep->SCSIStatus; 2648991554f2SKenneth D. Merry if (rep->SCSIStatus == MPI2_SCSI_STATUS_GOOD) 2649a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 2650991554f2SKenneth D. Merry else 2651a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SCSI_STATUS_ERROR); 2652991554f2SKenneth D. Merry 2653991554f2SKenneth D. Merry if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) { 2654991554f2SKenneth D. Merry int sense_len, returned_sense_len; 2655991554f2SKenneth D. Merry 2656991554f2SKenneth D. Merry returned_sense_len = min(le32toh(rep->SenseCount), 2657991554f2SKenneth D. Merry sizeof(struct scsi_sense_data)); 2658991554f2SKenneth D. Merry if (returned_sense_len < csio->sense_len) 2659991554f2SKenneth D. Merry csio->sense_resid = csio->sense_len - 2660991554f2SKenneth D. Merry returned_sense_len; 2661991554f2SKenneth D. Merry else 2662991554f2SKenneth D. Merry csio->sense_resid = 0; 2663991554f2SKenneth D. Merry 2664991554f2SKenneth D. Merry sense_len = min(returned_sense_len, 2665991554f2SKenneth D. Merry csio->sense_len - csio->sense_resid); 2666991554f2SKenneth D. Merry bzero(&csio->sense_data, sizeof(csio->sense_data)); 2667991554f2SKenneth D. Merry bcopy(cm->cm_sense, &csio->sense_data, sense_len); 2668991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_AUTOSNS_VALID; 2669991554f2SKenneth D. Merry } 2670991554f2SKenneth D. Merry 2671991554f2SKenneth D. Merry /* 2672991554f2SKenneth D. Merry * Check if this is an INQUIRY command. If it's a VPD inquiry, 2673991554f2SKenneth D. Merry * and it's page code 0 (Supported Page List), and there is 2674991554f2SKenneth D. Merry * inquiry data, and this is for a sequential access device, and 2675991554f2SKenneth D. Merry * the device is an SSP target, and TLR is supported by the 2676991554f2SKenneth D. Merry * controller, turn the TLR_bits value ON if page 0x90 is 2677991554f2SKenneth D. Merry * supported. 2678991554f2SKenneth D. Merry */ 267967feec50SStephen McConnell if ((scsi_cdb[0] == INQUIRY) && 268067feec50SStephen McConnell (scsi_cdb[1] & SI_EVPD) && 268167feec50SStephen McConnell (scsi_cdb[2] == SVPD_SUPPORTED_PAGE_LIST) && 2682991554f2SKenneth D. Merry ((csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) && 2683d2b4e18bSKenneth D. Merry (csio->data_ptr != NULL) && 2684d2b4e18bSKenneth D. Merry ((csio->data_ptr[0] & 0x1f) == T_SEQUENTIAL) && 2685d2b4e18bSKenneth D. Merry (sc->control_TLR) && 2686a2c14879SStephen McConnell (sc->mapping_table[target_id].device_info & 2687991554f2SKenneth D. Merry MPI2_SAS_DEVICE_INFO_SSP_TARGET)) { 2688991554f2SKenneth D. Merry vpd_list = (struct scsi_vpd_supported_page_list *) 2689991554f2SKenneth D. Merry csio->data_ptr; 2690a2c14879SStephen McConnell TLR_bits = &sc->mapping_table[target_id].TLR_bits; 2691991554f2SKenneth D. Merry *TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR; 2692991554f2SKenneth D. Merry TLR_on = (u8)MPI2_SCSIIO_CONTROL_TLR_ON; 269367feec50SStephen McConnell alloc_len = ((u16)scsi_cdb[3] << 8) + scsi_cdb[4]; 2694d2b4e18bSKenneth D. Merry alloc_len -= csio->resid; 2695991554f2SKenneth D. Merry for (i = 0; i < MIN(vpd_list->length, alloc_len); i++) { 2696991554f2SKenneth D. Merry if (vpd_list->list[i] == 0x90) { 2697991554f2SKenneth D. Merry *TLR_bits = TLR_on; 2698991554f2SKenneth D. Merry break; 2699991554f2SKenneth D. Merry } 2700991554f2SKenneth D. Merry } 2701991554f2SKenneth D. Merry } 2702a2c14879SStephen McConnell 2703a2c14879SStephen McConnell /* 2704a2c14879SStephen McConnell * If this is a SATA direct-access end device, mark it so that 2705a2c14879SStephen McConnell * a SCSI StartStopUnit command will be sent to it when the 2706a2c14879SStephen McConnell * driver is being shutdown. 2707a2c14879SStephen McConnell */ 270867feec50SStephen McConnell if ((scsi_cdb[0] == INQUIRY) && 2709fa699bb2SAlan Somers (csio->data_ptr != NULL) && 2710a2c14879SStephen McConnell ((csio->data_ptr[0] & 0x1f) == T_DIRECT) && 2711a2c14879SStephen McConnell (sc->mapping_table[target_id].device_info & 2712a2c14879SStephen McConnell MPI2_SAS_DEVICE_INFO_SATA_DEVICE) && 2713a2c14879SStephen McConnell ((sc->mapping_table[target_id].device_info & 2714a2c14879SStephen McConnell MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) == 2715a2c14879SStephen McConnell MPI2_SAS_DEVICE_INFO_END_DEVICE)) { 2716a2c14879SStephen McConnell target = &sassc->targets[target_id]; 2717a2c14879SStephen McConnell target->supports_SSU = TRUE; 2718a2c14879SStephen McConnell mpr_dprint(sc, MPR_XINFO, "Target %d supports SSU\n", 2719a2c14879SStephen McConnell target_id); 2720a2c14879SStephen McConnell } 2721991554f2SKenneth D. Merry break; 2722991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: 2723991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: 2724991554f2SKenneth D. Merry /* 2725991554f2SKenneth D. Merry * If devinfo is 0 this will be a volume. In that case don't 2726991554f2SKenneth D. Merry * tell CAM that the volume is not there. We want volumes to 2727991554f2SKenneth D. Merry * be enumerated until they are deleted/removed, not just 2728991554f2SKenneth D. Merry * failed. 2729991554f2SKenneth D. Merry */ 2730991554f2SKenneth D. Merry if (cm->cm_targ->devinfo == 0) 2731a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 2732991554f2SKenneth D. Merry else 2733a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 2734991554f2SKenneth D. Merry break; 2735991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_SGL: 2736991554f2SKenneth D. Merry mpr_print_scsiio_cmd(sc, cm); 2737a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_UNREC_HBA_ERROR); 2738991554f2SKenneth D. Merry break; 2739991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: 2740991554f2SKenneth D. Merry /* 2741991554f2SKenneth D. Merry * This is one of the responses that comes back when an I/O 2742991554f2SKenneth D. Merry * has been aborted. If it is because of a timeout that we 2743991554f2SKenneth D. Merry * initiated, just set the status to CAM_CMD_TIMEOUT. 2744991554f2SKenneth D. Merry * Otherwise set it to CAM_REQ_ABORTED. The effect on the 2745991554f2SKenneth D. Merry * command is the same (it gets retried, subject to the 2746991554f2SKenneth D. Merry * retry counter), the only difference is what gets printed 2747991554f2SKenneth D. Merry * on the console. 2748991554f2SKenneth D. Merry */ 27498fe7bf06SWarner Losh if (cm->cm_flags & MPR_CM_FLAGS_TIMEDOUT) 2750a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_CMD_TIMEOUT); 2751991554f2SKenneth D. Merry else 2752a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_ABORTED); 2753991554f2SKenneth D. Merry break; 2754991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: 2755991554f2SKenneth D. Merry /* resid is ignored for this condition */ 2756991554f2SKenneth D. Merry csio->resid = 0; 2757a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DATA_RUN_ERR); 2758991554f2SKenneth D. Merry break; 2759991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: 2760991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: 2761991554f2SKenneth D. Merry /* 27626adfa7edSAlan Somers * These can sometimes be transient transport-related 27636adfa7edSAlan Somers * errors, and sometimes persistent drive-related errors. 27646adfa7edSAlan Somers * We used to retry these without decrementing the retry 27656adfa7edSAlan Somers * count by returning CAM_REQUEUE_REQ. Unfortunately, if 27666adfa7edSAlan Somers * we hit a persistent drive problem that returns one of 27676adfa7edSAlan Somers * these error codes, we would retry indefinitely. So, 27686adfa7edSAlan Somers * return CAM_REQ_CMP_ERROR so that we decrement the retry 27696adfa7edSAlan Somers * count and avoid infinite retries. We're taking the 27706adfa7edSAlan Somers * potential risk of flagging false failures in the event 27716adfa7edSAlan Somers * of a topology-related error (e.g. a SAS expander problem 27726adfa7edSAlan Somers * causes a command addressed to a drive to fail), but 27734c1cdd4aSWarner Losh * avoiding getting into an infinite retry loop. However, 27744c1cdd4aSWarner Losh * if we get them while were moving a device, we should 27754c1cdd4aSWarner Losh * fail the request as 'not there' because the device 27764c1cdd4aSWarner Losh * is effectively gone. 2777991554f2SKenneth D. Merry */ 27784c1cdd4aSWarner Losh if (cm->cm_targ->flags & MPRSAS_TARGET_INREMOVAL) 27794c1cdd4aSWarner Losh mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 27804c1cdd4aSWarner Losh else 27816adfa7edSAlan Somers mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 27826eea4f46SScott Long mpr_dprint(sc, MPR_INFO, 27834c1cdd4aSWarner Losh "Controller reported %s tgt %u SMID %u loginfo %x%s\n", 27842bf620cbSScott Long mpr_describe_table(mpr_iocstatus_string, 27852bf620cbSScott Long le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK), 27862bf620cbSScott Long target_id, cm->cm_desc.Default.SMID, 27874c1cdd4aSWarner Losh le32toh(rep->IOCLogInfo), 27884c1cdd4aSWarner Losh (cm->cm_targ->flags & MPRSAS_TARGET_INREMOVAL) ? " departing" : ""); 27896eea4f46SScott Long mpr_dprint(sc, MPR_XINFO, 27906eea4f46SScott Long "SCSIStatus %x SCSIState %x xfercount %u\n", 2791694cb8b8SScott Long rep->SCSIStatus, rep->SCSIState, 2792991554f2SKenneth D. Merry le32toh(rep->TransferCount)); 2793991554f2SKenneth D. Merry break; 2794991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_FUNCTION: 2795991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INTERNAL_ERROR: 2796991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_VPID: 2797991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_FIELD: 2798991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_STATE: 2799991554f2SKenneth D. Merry case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: 2800991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: 2801991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: 2802991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: 2803991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: 2804991554f2SKenneth D. Merry default: 2805991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, 2806694cb8b8SScott Long "completed ioc %x loginfo %x scsi %x state %x xfer %u\n", 2807694cb8b8SScott Long le16toh(rep->IOCStatus), le32toh(rep->IOCLogInfo), 2808694cb8b8SScott Long rep->SCSIStatus, rep->SCSIState, 2809991554f2SKenneth D. Merry le32toh(rep->TransferCount)); 2810991554f2SKenneth D. Merry csio->resid = cm->cm_length; 281167feec50SStephen McConnell 281267feec50SStephen McConnell if (scsi_cdb[0] == UNMAP && 281367feec50SStephen McConnell target->is_nvme && 281467feec50SStephen McConnell (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) 281567feec50SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 281667feec50SStephen McConnell else 2817a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 281867feec50SStephen McConnell 2819991554f2SKenneth D. Merry break; 2820991554f2SKenneth D. Merry } 2821991554f2SKenneth D. Merry 2822991554f2SKenneth D. Merry mpr_sc_failed_io_info(sc, csio, rep, cm->cm_targ); 2823991554f2SKenneth D. Merry 2824991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_QUEUE_FROZEN) { 2825991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_RELEASE_SIMQ; 2826991554f2SKenneth D. Merry sassc->flags &= ~MPRSAS_QUEUE_FROZEN; 2827a8837c77SWarner Losh mpr_dprint(sc, MPR_INFO, "Command completed, unfreezing SIM " 2828991554f2SKenneth D. Merry "queue\n"); 2829991554f2SKenneth D. Merry } 2830991554f2SKenneth D. Merry 2831a2c14879SStephen McConnell if (mprsas_get_ccbstatus(ccb) != CAM_REQ_CMP) { 2832991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_DEV_QFRZN; 2833991554f2SKenneth D. Merry xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1); 2834991554f2SKenneth D. Merry } 2835991554f2SKenneth D. Merry 28364c1cdd4aSWarner Losh /* 28374c1cdd4aSWarner Losh * Check to see if we're removing the device. If so, and this is the 28384c1cdd4aSWarner Losh * last command on the queue, proceed with the deferred removal of the 28394c1cdd4aSWarner Losh * device. Note, for removing a volume, this won't trigger because 28404c1cdd4aSWarner Losh * pending_remove_tm will be NULL. 28414c1cdd4aSWarner Losh */ 28424c1cdd4aSWarner Losh if (cm->cm_targ->flags & MPRSAS_TARGET_INREMOVAL) { 28434c1cdd4aSWarner Losh if (TAILQ_FIRST(&cm->cm_targ->commands) == NULL && 28444c1cdd4aSWarner Losh cm->cm_targ->pending_remove_tm != NULL) { 28454c1cdd4aSWarner Losh mpr_dprint(sc, MPR_INFO, "Last pending command complete: starting remove_device\n"); 28464c1cdd4aSWarner Losh mpr_map_command(sc, cm->cm_targ->pending_remove_tm); 28474c1cdd4aSWarner Losh cm->cm_targ->pending_remove_tm = NULL; 28484c1cdd4aSWarner Losh } 28494c1cdd4aSWarner Losh } 28504c1cdd4aSWarner Losh 2851991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2852991554f2SKenneth D. Merry xpt_done(ccb); 2853991554f2SKenneth D. Merry } 2854991554f2SKenneth D. Merry 2855991554f2SKenneth D. Merry static void 2856991554f2SKenneth D. Merry mprsas_smpio_complete(struct mpr_softc *sc, struct mpr_command *cm) 2857991554f2SKenneth D. Merry { 2858991554f2SKenneth D. Merry MPI2_SMP_PASSTHROUGH_REPLY *rpl; 2859991554f2SKenneth D. Merry MPI2_SMP_PASSTHROUGH_REQUEST *req; 2860991554f2SKenneth D. Merry uint64_t sasaddr; 2861991554f2SKenneth D. Merry union ccb *ccb; 2862991554f2SKenneth D. Merry 2863991554f2SKenneth D. Merry ccb = cm->cm_complete_data; 2864991554f2SKenneth D. Merry 2865991554f2SKenneth D. Merry /* 2866991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 2867991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and SMP 2868991554f2SKenneth D. Merry * commands require two S/G elements only. That should be handled 2869991554f2SKenneth D. Merry * in the standard request size. 2870991554f2SKenneth D. Merry */ 2871991554f2SKenneth D. Merry if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 2872a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x on SMP " 2873a2c14879SStephen McConnell "request!\n", __func__, cm->cm_flags); 2874a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 2875991554f2SKenneth D. Merry goto bailout; 2876991554f2SKenneth D. Merry } 2877991554f2SKenneth D. Merry 2878991554f2SKenneth D. Merry rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply; 2879991554f2SKenneth D. Merry if (rpl == NULL) { 2880991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: NULL cm_reply!\n", __func__); 2881a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 2882991554f2SKenneth D. Merry goto bailout; 2883991554f2SKenneth D. Merry } 2884991554f2SKenneth D. Merry 2885991554f2SKenneth D. Merry req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; 2886991554f2SKenneth D. Merry sasaddr = le32toh(req->SASAddress.Low); 2887991554f2SKenneth D. Merry sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32; 2888991554f2SKenneth D. Merry 2889991554f2SKenneth D. Merry if ((le16toh(rpl->IOCStatus) & MPI2_IOCSTATUS_MASK) != 2890991554f2SKenneth D. Merry MPI2_IOCSTATUS_SUCCESS || 2891991554f2SKenneth D. Merry rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) { 2892991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s: IOCStatus %04x SASStatus %02x\n", 2893991554f2SKenneth D. Merry __func__, le16toh(rpl->IOCStatus), rpl->SASStatus); 2894a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 2895991554f2SKenneth D. Merry goto bailout; 2896991554f2SKenneth D. Merry } 2897991554f2SKenneth D. Merry 2898a2c14879SStephen McConnell mpr_dprint(sc, MPR_XINFO, "%s: SMP request to SAS address %#jx " 2899a2c14879SStephen McConnell "completed successfully\n", __func__, (uintmax_t)sasaddr); 2900991554f2SKenneth D. Merry 2901991554f2SKenneth D. Merry if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED) 2902a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 2903991554f2SKenneth D. Merry else 2904a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SMP_STATUS_ERROR); 2905991554f2SKenneth D. Merry 2906991554f2SKenneth D. Merry bailout: 2907991554f2SKenneth D. Merry /* 2908991554f2SKenneth D. Merry * We sync in both directions because we had DMAs in the S/G list 2909991554f2SKenneth D. Merry * in both directions. 2910991554f2SKenneth D. Merry */ 2911991554f2SKenneth D. Merry bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, 2912991554f2SKenneth D. Merry BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 2913991554f2SKenneth D. Merry bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); 2914991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2915991554f2SKenneth D. Merry xpt_done(ccb); 2916991554f2SKenneth D. Merry } 2917991554f2SKenneth D. Merry 2918991554f2SKenneth D. Merry static void 29197a2a6a1aSStephen McConnell mprsas_send_smpcmd(struct mprsas_softc *sassc, union ccb *ccb, uint64_t sasaddr) 2920991554f2SKenneth D. Merry { 2921991554f2SKenneth D. Merry struct mpr_command *cm; 2922991554f2SKenneth D. Merry uint8_t *request, *response; 2923991554f2SKenneth D. Merry MPI2_SMP_PASSTHROUGH_REQUEST *req; 2924991554f2SKenneth D. Merry struct mpr_softc *sc; 2925991554f2SKenneth D. Merry int error; 2926991554f2SKenneth D. Merry 2927991554f2SKenneth D. Merry sc = sassc->sc; 2928991554f2SKenneth D. Merry error = 0; 2929991554f2SKenneth D. Merry 2930991554f2SKenneth D. Merry switch (ccb->ccb_h.flags & CAM_DATA_MASK) { 2931991554f2SKenneth D. Merry case CAM_DATA_PADDR: 2932991554f2SKenneth D. Merry case CAM_DATA_SG_PADDR: 2933991554f2SKenneth D. Merry /* 2934991554f2SKenneth D. Merry * XXX We don't yet support physical addresses here. 2935991554f2SKenneth D. Merry */ 2936991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: physical addresses not " 2937991554f2SKenneth D. Merry "supported\n", __func__); 2938a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID); 2939991554f2SKenneth D. Merry xpt_done(ccb); 2940991554f2SKenneth D. Merry return; 2941991554f2SKenneth D. Merry case CAM_DATA_SG: 2942991554f2SKenneth D. Merry /* 2943991554f2SKenneth D. Merry * The chip does not support more than one buffer for the 2944991554f2SKenneth D. Merry * request or response. 2945991554f2SKenneth D. Merry */ 2946991554f2SKenneth D. Merry if ((ccb->smpio.smp_request_sglist_cnt > 1) 2947991554f2SKenneth D. Merry || (ccb->smpio.smp_response_sglist_cnt > 1)) { 29487a2a6a1aSStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: multiple request or " 29497a2a6a1aSStephen McConnell "response buffer segments not supported for SMP\n", 29507a2a6a1aSStephen McConnell __func__); 2951a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID); 2952991554f2SKenneth D. Merry xpt_done(ccb); 2953991554f2SKenneth D. Merry return; 2954991554f2SKenneth D. Merry } 2955991554f2SKenneth D. Merry 2956991554f2SKenneth D. Merry /* 2957991554f2SKenneth D. Merry * The CAM_SCATTER_VALID flag was originally implemented 2958991554f2SKenneth D. Merry * for the XPT_SCSI_IO CCB, which only has one data pointer. 2959991554f2SKenneth D. Merry * We have two. So, just take that flag to mean that we 2960991554f2SKenneth D. Merry * might have S/G lists, and look at the S/G segment count 2961991554f2SKenneth D. Merry * to figure out whether that is the case for each individual 2962991554f2SKenneth D. Merry * buffer. 2963991554f2SKenneth D. Merry */ 2964991554f2SKenneth D. Merry if (ccb->smpio.smp_request_sglist_cnt != 0) { 2965991554f2SKenneth D. Merry bus_dma_segment_t *req_sg; 2966991554f2SKenneth D. Merry 2967991554f2SKenneth D. Merry req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request; 2968991554f2SKenneth D. Merry request = (uint8_t *)(uintptr_t)req_sg[0].ds_addr; 2969991554f2SKenneth D. Merry } else 2970991554f2SKenneth D. Merry request = ccb->smpio.smp_request; 2971991554f2SKenneth D. Merry 2972991554f2SKenneth D. Merry if (ccb->smpio.smp_response_sglist_cnt != 0) { 2973991554f2SKenneth D. Merry bus_dma_segment_t *rsp_sg; 2974991554f2SKenneth D. Merry 2975991554f2SKenneth D. Merry rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response; 2976991554f2SKenneth D. Merry response = (uint8_t *)(uintptr_t)rsp_sg[0].ds_addr; 2977991554f2SKenneth D. Merry } else 2978991554f2SKenneth D. Merry response = ccb->smpio.smp_response; 2979991554f2SKenneth D. Merry break; 2980991554f2SKenneth D. Merry case CAM_DATA_VADDR: 2981991554f2SKenneth D. Merry request = ccb->smpio.smp_request; 2982991554f2SKenneth D. Merry response = ccb->smpio.smp_response; 2983991554f2SKenneth D. Merry break; 2984991554f2SKenneth D. Merry default: 2985a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID); 2986991554f2SKenneth D. Merry xpt_done(ccb); 2987991554f2SKenneth D. Merry return; 2988991554f2SKenneth D. Merry } 2989991554f2SKenneth D. Merry 2990991554f2SKenneth D. Merry cm = mpr_alloc_command(sc); 2991991554f2SKenneth D. Merry if (cm == NULL) { 29927a2a6a1aSStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: cannot allocate command\n", 29937a2a6a1aSStephen McConnell __func__); 2994a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL); 2995991554f2SKenneth D. Merry xpt_done(ccb); 2996991554f2SKenneth D. Merry return; 2997991554f2SKenneth D. Merry } 2998991554f2SKenneth D. Merry 2999991554f2SKenneth D. Merry req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; 3000991554f2SKenneth D. Merry bzero(req, sizeof(*req)); 3001991554f2SKenneth D. Merry req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; 3002991554f2SKenneth D. Merry 3003991554f2SKenneth D. Merry /* Allow the chip to use any route to this SAS address. */ 3004991554f2SKenneth D. Merry req->PhysicalPort = 0xff; 3005991554f2SKenneth D. Merry 3006991554f2SKenneth D. Merry req->RequestDataLength = htole16(ccb->smpio.smp_request_len); 3007991554f2SKenneth D. Merry req->SGLFlags = 3008991554f2SKenneth D. Merry MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI; 3009991554f2SKenneth D. Merry 3010991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s: sending SMP request to SAS address " 3011991554f2SKenneth D. Merry "%#jx\n", __func__, (uintmax_t)sasaddr); 3012991554f2SKenneth D. Merry 3013991554f2SKenneth D. Merry mpr_init_sge(cm, req, &req->SGL); 3014991554f2SKenneth D. Merry 3015991554f2SKenneth D. Merry /* 3016991554f2SKenneth D. Merry * Set up a uio to pass into mpr_map_command(). This allows us to 3017991554f2SKenneth D. Merry * do one map command, and one busdma call in there. 3018991554f2SKenneth D. Merry */ 3019991554f2SKenneth D. Merry cm->cm_uio.uio_iov = cm->cm_iovec; 3020991554f2SKenneth D. Merry cm->cm_uio.uio_iovcnt = 2; 3021991554f2SKenneth D. Merry cm->cm_uio.uio_segflg = UIO_SYSSPACE; 3022991554f2SKenneth D. Merry 3023991554f2SKenneth D. Merry /* 3024991554f2SKenneth D. Merry * The read/write flag isn't used by busdma, but set it just in 3025991554f2SKenneth D. Merry * case. This isn't exactly accurate, either, since we're going in 3026991554f2SKenneth D. Merry * both directions. 3027991554f2SKenneth D. Merry */ 3028991554f2SKenneth D. Merry cm->cm_uio.uio_rw = UIO_WRITE; 3029991554f2SKenneth D. Merry 3030991554f2SKenneth D. Merry cm->cm_iovec[0].iov_base = request; 3031991554f2SKenneth D. Merry cm->cm_iovec[0].iov_len = le16toh(req->RequestDataLength); 3032991554f2SKenneth D. Merry cm->cm_iovec[1].iov_base = response; 3033991554f2SKenneth D. Merry cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len; 3034991554f2SKenneth D. Merry 3035991554f2SKenneth D. Merry cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len + 3036991554f2SKenneth D. Merry cm->cm_iovec[1].iov_len; 3037991554f2SKenneth D. Merry 3038991554f2SKenneth D. Merry /* 3039991554f2SKenneth D. Merry * Trigger a warning message in mpr_data_cb() for the user if we 3040991554f2SKenneth D. Merry * wind up exceeding two S/G segments. The chip expects one 3041991554f2SKenneth D. Merry * segment for the request and another for the response. 3042991554f2SKenneth D. Merry */ 3043991554f2SKenneth D. Merry cm->cm_max_segs = 2; 3044991554f2SKenneth D. Merry 3045991554f2SKenneth D. Merry cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 3046991554f2SKenneth D. Merry cm->cm_complete = mprsas_smpio_complete; 3047991554f2SKenneth D. Merry cm->cm_complete_data = ccb; 3048991554f2SKenneth D. Merry 3049991554f2SKenneth D. Merry /* 3050991554f2SKenneth D. Merry * Tell the mapping code that we're using a uio, and that this is 3051991554f2SKenneth D. Merry * an SMP passthrough request. There is a little special-case 3052991554f2SKenneth D. Merry * logic there (in mpr_data_cb()) to handle the bidirectional 3053991554f2SKenneth D. Merry * transfer. 3054991554f2SKenneth D. Merry */ 3055991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_USE_UIO | MPR_CM_FLAGS_SMP_PASS | 3056991554f2SKenneth D. Merry MPR_CM_FLAGS_DATAIN | MPR_CM_FLAGS_DATAOUT; 3057991554f2SKenneth D. Merry 3058991554f2SKenneth D. Merry /* The chip data format is little endian. */ 3059991554f2SKenneth D. Merry req->SASAddress.High = htole32(sasaddr >> 32); 3060991554f2SKenneth D. Merry req->SASAddress.Low = htole32(sasaddr); 3061991554f2SKenneth D. Merry 3062991554f2SKenneth D. Merry /* 3063991554f2SKenneth D. Merry * XXX Note that we don't have a timeout/abort mechanism here. 3064991554f2SKenneth D. Merry * From the manual, it looks like task management requests only 3065991554f2SKenneth D. Merry * work for SCSI IO and SATA passthrough requests. We may need to 3066991554f2SKenneth D. Merry * have a mechanism to retry requests in the event of a chip reset 3067991554f2SKenneth D. Merry * at least. Hopefully the chip will insure that any errors short 3068991554f2SKenneth D. Merry * of that are relayed back to the driver. 3069991554f2SKenneth D. Merry */ 3070991554f2SKenneth D. Merry error = mpr_map_command(sc, cm); 3071991554f2SKenneth D. Merry if ((error != 0) && (error != EINPROGRESS)) { 3072991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: error %d returned from " 3073991554f2SKenneth D. Merry "mpr_map_command()\n", __func__, error); 3074991554f2SKenneth D. Merry goto bailout_error; 3075991554f2SKenneth D. Merry } 3076991554f2SKenneth D. Merry 3077991554f2SKenneth D. Merry return; 3078991554f2SKenneth D. Merry 3079991554f2SKenneth D. Merry bailout_error: 3080991554f2SKenneth D. Merry mpr_free_command(sc, cm); 3081a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL); 3082991554f2SKenneth D. Merry xpt_done(ccb); 3083991554f2SKenneth D. Merry return; 3084991554f2SKenneth D. Merry } 3085991554f2SKenneth D. Merry 3086991554f2SKenneth D. Merry static void 3087991554f2SKenneth D. Merry mprsas_action_smpio(struct mprsas_softc *sassc, union ccb *ccb) 3088991554f2SKenneth D. Merry { 3089991554f2SKenneth D. Merry struct mpr_softc *sc; 3090991554f2SKenneth D. Merry struct mprsas_target *targ; 3091991554f2SKenneth D. Merry uint64_t sasaddr = 0; 3092991554f2SKenneth D. Merry 3093991554f2SKenneth D. Merry sc = sassc->sc; 3094991554f2SKenneth D. Merry 3095991554f2SKenneth D. Merry /* 3096991554f2SKenneth D. Merry * Make sure the target exists. 3097991554f2SKenneth D. Merry */ 3098991554f2SKenneth D. Merry KASSERT(ccb->ccb_h.target_id < sassc->maxtargets, 3099991554f2SKenneth D. Merry ("Target %d out of bounds in XPT_SMP_IO\n", ccb->ccb_h.target_id)); 3100991554f2SKenneth D. Merry targ = &sassc->targets[ccb->ccb_h.target_id]; 3101991554f2SKenneth D. Merry if (targ->handle == 0x0) { 3102991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: target %d does not exist!\n", 3103991554f2SKenneth D. Merry __func__, ccb->ccb_h.target_id); 3104a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SEL_TIMEOUT); 3105991554f2SKenneth D. Merry xpt_done(ccb); 3106991554f2SKenneth D. Merry return; 3107991554f2SKenneth D. Merry } 3108991554f2SKenneth D. Merry 3109991554f2SKenneth D. Merry /* 3110991554f2SKenneth D. Merry * If this device has an embedded SMP target, we'll talk to it 3111991554f2SKenneth D. Merry * directly. 3112991554f2SKenneth D. Merry * figure out what the expander's address is. 3113991554f2SKenneth D. Merry */ 3114991554f2SKenneth D. Merry if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0) 3115991554f2SKenneth D. Merry sasaddr = targ->sasaddr; 3116991554f2SKenneth D. Merry 3117991554f2SKenneth D. Merry /* 3118991554f2SKenneth D. Merry * If we don't have a SAS address for the expander yet, try 3119991554f2SKenneth D. Merry * grabbing it from the page 0x83 information cached in the 3120991554f2SKenneth D. Merry * transport layer for this target. LSI expanders report the 3121991554f2SKenneth D. Merry * expander SAS address as the port-associated SAS address in 3122991554f2SKenneth D. Merry * Inquiry VPD page 0x83. Maxim expanders don't report it in page 3123991554f2SKenneth D. Merry * 0x83. 3124991554f2SKenneth D. Merry * 3125991554f2SKenneth D. Merry * XXX KDM disable this for now, but leave it commented out so that 3126991554f2SKenneth D. Merry * it is obvious that this is another possible way to get the SAS 3127991554f2SKenneth D. Merry * address. 3128991554f2SKenneth D. Merry * 3129991554f2SKenneth D. Merry * The parent handle method below is a little more reliable, and 3130991554f2SKenneth D. Merry * the other benefit is that it works for devices other than SES 3131991554f2SKenneth D. Merry * devices. So you can send a SMP request to a da(4) device and it 3132991554f2SKenneth D. Merry * will get routed to the expander that device is attached to. 3133991554f2SKenneth D. Merry * (Assuming the da(4) device doesn't contain an SMP target...) 3134991554f2SKenneth D. Merry */ 3135991554f2SKenneth D. Merry #if 0 3136991554f2SKenneth D. Merry if (sasaddr == 0) 3137991554f2SKenneth D. Merry sasaddr = xpt_path_sas_addr(ccb->ccb_h.path); 3138991554f2SKenneth D. Merry #endif 3139991554f2SKenneth D. Merry 3140991554f2SKenneth D. Merry /* 3141991554f2SKenneth D. Merry * If we still don't have a SAS address for the expander, look for 3142991554f2SKenneth D. Merry * the parent device of this device, which is probably the expander. 3143991554f2SKenneth D. Merry */ 3144991554f2SKenneth D. Merry if (sasaddr == 0) { 3145991554f2SKenneth D. Merry #ifdef OLD_MPR_PROBE 3146991554f2SKenneth D. Merry struct mprsas_target *parent_target; 3147991554f2SKenneth D. Merry #endif 3148991554f2SKenneth D. Merry 3149991554f2SKenneth D. Merry if (targ->parent_handle == 0x0) { 3150991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d does not have " 3151991554f2SKenneth D. Merry "a valid parent handle!\n", __func__, targ->handle); 3152a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3153991554f2SKenneth D. Merry goto bailout; 3154991554f2SKenneth D. Merry } 3155991554f2SKenneth D. Merry #ifdef OLD_MPR_PROBE 3156991554f2SKenneth D. Merry parent_target = mprsas_find_target_by_handle(sassc, 0, 3157991554f2SKenneth D. Merry targ->parent_handle); 3158991554f2SKenneth D. Merry 3159991554f2SKenneth D. Merry if (parent_target == NULL) { 3160991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d does not have " 3161991554f2SKenneth D. Merry "a valid parent target!\n", __func__, targ->handle); 3162a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3163991554f2SKenneth D. Merry goto bailout; 3164991554f2SKenneth D. Merry } 3165991554f2SKenneth D. Merry 3166991554f2SKenneth D. Merry if ((parent_target->devinfo & 3167991554f2SKenneth D. Merry MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) { 3168991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent %d " 3169991554f2SKenneth D. Merry "does not have an SMP target!\n", __func__, 3170991554f2SKenneth D. Merry targ->handle, parent_target->handle); 3171a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3172991554f2SKenneth D. Merry goto bailout; 3173991554f2SKenneth D. Merry } 3174991554f2SKenneth D. Merry 3175991554f2SKenneth D. Merry sasaddr = parent_target->sasaddr; 3176991554f2SKenneth D. Merry #else /* OLD_MPR_PROBE */ 3177991554f2SKenneth D. Merry if ((targ->parent_devinfo & 3178991554f2SKenneth D. Merry MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) { 3179991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent %d " 3180991554f2SKenneth D. Merry "does not have an SMP target!\n", __func__, 3181991554f2SKenneth D. Merry targ->handle, targ->parent_handle); 3182a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3183991554f2SKenneth D. Merry goto bailout; 3184991554f2SKenneth D. Merry } 3185991554f2SKenneth D. Merry if (targ->parent_sasaddr == 0x0) { 3186991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent handle " 3187991554f2SKenneth D. Merry "%d does not have a valid SAS address!\n", __func__, 3188991554f2SKenneth D. Merry targ->handle, targ->parent_handle); 3189a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3190991554f2SKenneth D. Merry goto bailout; 3191991554f2SKenneth D. Merry } 3192991554f2SKenneth D. Merry 3193991554f2SKenneth D. Merry sasaddr = targ->parent_sasaddr; 3194991554f2SKenneth D. Merry #endif /* OLD_MPR_PROBE */ 3195991554f2SKenneth D. Merry } 3196991554f2SKenneth D. Merry 3197991554f2SKenneth D. Merry if (sasaddr == 0) { 3198991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INFO, "%s: unable to find SAS address for " 3199991554f2SKenneth D. Merry "handle %d\n", __func__, targ->handle); 3200a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3201991554f2SKenneth D. Merry goto bailout; 3202991554f2SKenneth D. Merry } 3203991554f2SKenneth D. Merry mprsas_send_smpcmd(sassc, ccb, sasaddr); 3204991554f2SKenneth D. Merry 3205991554f2SKenneth D. Merry return; 3206991554f2SKenneth D. Merry 3207991554f2SKenneth D. Merry bailout: 3208991554f2SKenneth D. Merry xpt_done(ccb); 3209991554f2SKenneth D. Merry 3210991554f2SKenneth D. Merry } 3211991554f2SKenneth D. Merry 3212991554f2SKenneth D. Merry static void 3213991554f2SKenneth D. Merry mprsas_action_resetdev(struct mprsas_softc *sassc, union ccb *ccb) 3214991554f2SKenneth D. Merry { 3215991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 3216991554f2SKenneth D. Merry struct mpr_softc *sc; 3217991554f2SKenneth D. Merry struct mpr_command *tm; 3218991554f2SKenneth D. Merry struct mprsas_target *targ; 3219991554f2SKenneth D. Merry 3220991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 3221991554f2SKenneth D. Merry mtx_assert(&sassc->sc->mpr_mtx, MA_OWNED); 3222991554f2SKenneth D. Merry 32237a2a6a1aSStephen McConnell KASSERT(ccb->ccb_h.target_id < sassc->maxtargets, ("Target %d out of " 32247a2a6a1aSStephen McConnell "bounds in XPT_RESET_DEV\n", ccb->ccb_h.target_id)); 3225991554f2SKenneth D. Merry sc = sassc->sc; 32263921a9f7SScott Long tm = mprsas_alloc_tm(sc); 3227991554f2SKenneth D. Merry if (tm == NULL) { 32287a2a6a1aSStephen McConnell mpr_dprint(sc, MPR_ERROR, "command alloc failure in " 32297a2a6a1aSStephen McConnell "mprsas_action_resetdev\n"); 3230a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL); 3231991554f2SKenneth D. Merry xpt_done(ccb); 3232991554f2SKenneth D. Merry return; 3233991554f2SKenneth D. Merry } 3234991554f2SKenneth D. Merry 3235991554f2SKenneth D. Merry targ = &sassc->targets[ccb->ccb_h.target_id]; 3236991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 3237991554f2SKenneth D. Merry req->DevHandle = htole16(targ->handle); 3238991554f2SKenneth D. Merry req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; 3239991554f2SKenneth D. Merry 324089d1c21fSKashyap D Desai if (!targ->is_nvme || sc->custom_nvme_tm_handling) { 3241991554f2SKenneth D. Merry /* SAS Hard Link Reset / SATA Link Reset */ 3242991554f2SKenneth D. Merry req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; 324389d1c21fSKashyap D Desai } else { 324489d1c21fSKashyap D Desai /* PCIe Protocol Level Reset*/ 324589d1c21fSKashyap D Desai req->MsgFlags = 324689d1c21fSKashyap D Desai MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; 324789d1c21fSKashyap D Desai } 3248991554f2SKenneth D. Merry 3249991554f2SKenneth D. Merry tm->cm_data = NULL; 3250991554f2SKenneth D. Merry tm->cm_complete = mprsas_resetdev_complete; 3251991554f2SKenneth D. Merry tm->cm_complete_data = ccb; 3252a2c14879SStephen McConnell 3253a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n", 3254a2c14879SStephen McConnell __func__, targ->tid); 3255991554f2SKenneth D. Merry tm->cm_targ = targ; 3256a2c14879SStephen McConnell 325746b9415fSScott Long mprsas_prepare_for_tm(sc, tm, targ, CAM_LUN_WILDCARD); 3258991554f2SKenneth D. Merry mpr_map_command(sc, tm); 3259991554f2SKenneth D. Merry } 3260991554f2SKenneth D. Merry 3261991554f2SKenneth D. Merry static void 3262991554f2SKenneth D. Merry mprsas_resetdev_complete(struct mpr_softc *sc, struct mpr_command *tm) 3263991554f2SKenneth D. Merry { 3264991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *resp; 3265991554f2SKenneth D. Merry union ccb *ccb; 3266991554f2SKenneth D. Merry 3267991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 3268991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 3269991554f2SKenneth D. Merry 3270991554f2SKenneth D. Merry resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 3271991554f2SKenneth D. Merry ccb = tm->cm_complete_data; 3272991554f2SKenneth D. Merry 3273991554f2SKenneth D. Merry /* 3274991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 3275991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 3276991554f2SKenneth D. Merry * task management commands don't have S/G lists. 3277991554f2SKenneth D. Merry */ 3278991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 3279991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 3280991554f2SKenneth D. Merry 3281991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 3282991554f2SKenneth D. Merry 3283991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for reset of " 3284991554f2SKenneth D. Merry "handle %#04x! This should not happen!\n", __func__, 3285991554f2SKenneth D. Merry tm->cm_flags, req->DevHandle); 3286a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 3287991554f2SKenneth D. Merry goto bailout; 3288991554f2SKenneth D. Merry } 3289991554f2SKenneth D. Merry 32907a2a6a1aSStephen McConnell mpr_dprint(sc, MPR_XINFO, "%s: IOCStatus = 0x%x ResponseCode = 0x%x\n", 32917a2a6a1aSStephen McConnell __func__, le16toh(resp->IOCStatus), le32toh(resp->ResponseCode)); 3292991554f2SKenneth D. Merry 3293991554f2SKenneth D. Merry if (le32toh(resp->ResponseCode) == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) { 3294a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 3295991554f2SKenneth D. Merry mprsas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid, 3296991554f2SKenneth D. Merry CAM_LUN_WILDCARD); 3297991554f2SKenneth D. Merry } 3298991554f2SKenneth D. Merry else 3299a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 3300991554f2SKenneth D. Merry 3301991554f2SKenneth D. Merry bailout: 3302991554f2SKenneth D. Merry 3303991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 3304991554f2SKenneth D. Merry xpt_done(ccb); 3305991554f2SKenneth D. Merry } 3306991554f2SKenneth D. Merry 3307991554f2SKenneth D. Merry static void 3308991554f2SKenneth D. Merry mprsas_poll(struct cam_sim *sim) 3309991554f2SKenneth D. Merry { 3310991554f2SKenneth D. Merry struct mprsas_softc *sassc; 3311991554f2SKenneth D. Merry 3312991554f2SKenneth D. Merry sassc = cam_sim_softc(sim); 3313991554f2SKenneth D. Merry 3314991554f2SKenneth D. Merry if (sassc->sc->mpr_debug & MPR_TRACE) { 3315991554f2SKenneth D. Merry /* frequent debug messages during a panic just slow 3316991554f2SKenneth D. Merry * everything down too much. 3317991554f2SKenneth D. Merry */ 3318a2c14879SStephen McConnell mpr_dprint(sassc->sc, MPR_XINFO, "%s clearing MPR_TRACE\n", 3319a2c14879SStephen McConnell __func__); 3320991554f2SKenneth D. Merry sassc->sc->mpr_debug &= ~MPR_TRACE; 3321991554f2SKenneth D. Merry } 3322991554f2SKenneth D. Merry 3323991554f2SKenneth D. Merry mpr_intr_locked(sassc->sc); 3324991554f2SKenneth D. Merry } 3325991554f2SKenneth D. Merry 3326991554f2SKenneth D. Merry static void 3327991554f2SKenneth D. Merry mprsas_async(void *callback_arg, uint32_t code, struct cam_path *path, 3328991554f2SKenneth D. Merry void *arg) 3329991554f2SKenneth D. Merry { 3330991554f2SKenneth D. Merry struct mpr_softc *sc; 3331991554f2SKenneth D. Merry 3332991554f2SKenneth D. Merry sc = (struct mpr_softc *)callback_arg; 3333991554f2SKenneth D. Merry 333402d81940SAlexander Motin mpr_lock(sc); 3335991554f2SKenneth D. Merry switch (code) { 3336991554f2SKenneth D. Merry case AC_ADVINFO_CHANGED: { 3337991554f2SKenneth D. Merry struct mprsas_target *target; 3338991554f2SKenneth D. Merry struct mprsas_softc *sassc; 3339991554f2SKenneth D. Merry struct scsi_read_capacity_data_long rcap_buf; 3340991554f2SKenneth D. Merry struct ccb_dev_advinfo cdai; 3341991554f2SKenneth D. Merry struct mprsas_lun *lun; 3342991554f2SKenneth D. Merry lun_id_t lunid; 3343991554f2SKenneth D. Merry int found_lun; 3344991554f2SKenneth D. Merry uintptr_t buftype; 3345991554f2SKenneth D. Merry 3346991554f2SKenneth D. Merry buftype = (uintptr_t)arg; 3347991554f2SKenneth D. Merry 3348991554f2SKenneth D. Merry found_lun = 0; 3349991554f2SKenneth D. Merry sassc = sc->sassc; 3350991554f2SKenneth D. Merry 3351991554f2SKenneth D. Merry /* 3352991554f2SKenneth D. Merry * We're only interested in read capacity data changes. 3353991554f2SKenneth D. Merry */ 3354991554f2SKenneth D. Merry if (buftype != CDAI_TYPE_RCAPLONG) 3355991554f2SKenneth D. Merry break; 3356991554f2SKenneth D. Merry 3357991554f2SKenneth D. Merry /* 3358991554f2SKenneth D. Merry * We should have a handle for this, but check to make sure. 3359991554f2SKenneth D. Merry */ 3360991554f2SKenneth D. Merry KASSERT(xpt_path_target_id(path) < sassc->maxtargets, 3361991554f2SKenneth D. Merry ("Target %d out of bounds in mprsas_async\n", 3362991554f2SKenneth D. Merry xpt_path_target_id(path))); 3363991554f2SKenneth D. Merry target = &sassc->targets[xpt_path_target_id(path)]; 3364991554f2SKenneth D. Merry if (target->handle == 0) 3365991554f2SKenneth D. Merry break; 3366991554f2SKenneth D. Merry 3367991554f2SKenneth D. Merry lunid = xpt_path_lun_id(path); 3368991554f2SKenneth D. Merry 3369991554f2SKenneth D. Merry SLIST_FOREACH(lun, &target->luns, lun_link) { 3370991554f2SKenneth D. Merry if (lun->lun_id == lunid) { 3371991554f2SKenneth D. Merry found_lun = 1; 3372991554f2SKenneth D. Merry break; 3373991554f2SKenneth D. Merry } 3374991554f2SKenneth D. Merry } 3375991554f2SKenneth D. Merry 3376991554f2SKenneth D. Merry if (found_lun == 0) { 3377991554f2SKenneth D. Merry lun = malloc(sizeof(struct mprsas_lun), M_MPR, 3378991554f2SKenneth D. Merry M_NOWAIT | M_ZERO); 3379991554f2SKenneth D. Merry if (lun == NULL) { 3380991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "Unable to alloc " 3381991554f2SKenneth D. Merry "LUN for EEDP support.\n"); 3382991554f2SKenneth D. Merry break; 3383991554f2SKenneth D. Merry } 3384991554f2SKenneth D. Merry lun->lun_id = lunid; 3385991554f2SKenneth D. Merry SLIST_INSERT_HEAD(&target->luns, lun, lun_link); 3386991554f2SKenneth D. Merry } 3387991554f2SKenneth D. Merry 3388991554f2SKenneth D. Merry bzero(&rcap_buf, sizeof(rcap_buf)); 33897608b98cSEdward Tomasz Napierala bzero(&cdai, sizeof(cdai)); 3390991554f2SKenneth D. Merry xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); 3391991554f2SKenneth D. Merry cdai.ccb_h.func_code = XPT_DEV_ADVINFO; 3392991554f2SKenneth D. Merry cdai.ccb_h.flags = CAM_DIR_IN; 3393991554f2SKenneth D. Merry cdai.buftype = CDAI_TYPE_RCAPLONG; 3394e8577fb4SKenneth D. Merry cdai.flags = CDAI_FLAG_NONE; 3395991554f2SKenneth D. Merry cdai.bufsiz = sizeof(rcap_buf); 3396991554f2SKenneth D. Merry cdai.buf = (uint8_t *)&rcap_buf; 3397991554f2SKenneth D. Merry xpt_action((union ccb *)&cdai); 3398991554f2SKenneth D. Merry if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) 3399991554f2SKenneth D. Merry cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); 3400991554f2SKenneth D. Merry 3401a2c14879SStephen McConnell if ((mprsas_get_ccbstatus((union ccb *)&cdai) == CAM_REQ_CMP) 3402991554f2SKenneth D. Merry && (rcap_buf.prot & SRC16_PROT_EN)) { 34038881681bSKenneth D. Merry switch (rcap_buf.prot & SRC16_P_TYPE) { 34048881681bSKenneth D. Merry case SRC16_PTYPE_1: 34058881681bSKenneth D. Merry case SRC16_PTYPE_3: 3406991554f2SKenneth D. Merry lun->eedp_formatted = TRUE; 34078881681bSKenneth D. Merry lun->eedp_block_size = 34088881681bSKenneth D. Merry scsi_4btoul(rcap_buf.length); 34098881681bSKenneth D. Merry break; 34108881681bSKenneth D. Merry case SRC16_PTYPE_2: 34118881681bSKenneth D. Merry default: 34128881681bSKenneth D. Merry lun->eedp_formatted = FALSE; 34138881681bSKenneth D. Merry lun->eedp_block_size = 0; 34148881681bSKenneth D. Merry break; 34158881681bSKenneth D. Merry } 3416991554f2SKenneth D. Merry } else { 3417991554f2SKenneth D. Merry lun->eedp_formatted = FALSE; 3418991554f2SKenneth D. Merry lun->eedp_block_size = 0; 3419991554f2SKenneth D. Merry } 3420991554f2SKenneth D. Merry break; 3421991554f2SKenneth D. Merry } 3422991554f2SKenneth D. Merry default: 3423991554f2SKenneth D. Merry break; 3424991554f2SKenneth D. Merry } 342502d81940SAlexander Motin mpr_unlock(sc); 3426991554f2SKenneth D. Merry } 3427991554f2SKenneth D. Merry 3428a2c14879SStephen McConnell /* 34299781c28cSAlexander Motin * Freeze the devq and set the INRESET flag so that no I/O will be sent to 34309781c28cSAlexander Motin * the target until the reset has completed. The CCB holds the path which 34319781c28cSAlexander Motin * is used to release the devq. The devq is released and the CCB is freed 3432a2c14879SStephen McConnell * when the TM completes. 3433a8837c77SWarner Losh * We only need to do this when we're entering reset, not at each time we 3434a8837c77SWarner Losh * need to send an abort (which will happen if multiple commands timeout 3435a8837c77SWarner Losh * while we're sending the abort). We do not release the queue for each 3436a8837c77SWarner Losh * command we complete (just at the end when we free the tm), so freezing 3437a8837c77SWarner Losh * it each time doesn't make sense. 3438a2c14879SStephen McConnell */ 3439b7f1ee79SScott Long void 3440b7f1ee79SScott Long mprsas_prepare_for_tm(struct mpr_softc *sc, struct mpr_command *tm, 3441b7f1ee79SScott Long struct mprsas_target *target, lun_id_t lun_id) 3442b7f1ee79SScott Long { 3443b7f1ee79SScott Long union ccb *ccb; 3444b7f1ee79SScott Long path_id_t path_id; 3445b7f1ee79SScott Long 3446a2c14879SStephen McConnell ccb = xpt_alloc_ccb_nowait(); 3447a2c14879SStephen McConnell if (ccb) { 3448a2c14879SStephen McConnell path_id = cam_sim_path(sc->sassc->sim); 3449a2c14879SStephen McConnell if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, path_id, 3450a2c14879SStephen McConnell target->tid, lun_id) != CAM_REQ_CMP) { 3451a2c14879SStephen McConnell xpt_free_ccb(ccb); 3452a2c14879SStephen McConnell } else { 3453a8837c77SWarner Losh tm->cm_ccb = ccb; 3454a8837c77SWarner Losh tm->cm_targ = target; 3455a8837c77SWarner Losh if ((target->flags & MPRSAS_TARGET_INRESET) == 0) { 3456a8837c77SWarner Losh mpr_dprint(sc, MPR_XINFO | MPR_RECOVERY, 34579781c28cSAlexander Motin "%s: Freezing devq for target ID %d\n", 34589781c28cSAlexander Motin __func__, target->tid); 34599781c28cSAlexander Motin xpt_freeze_devq(ccb->ccb_h.path, 1); 3460a2c14879SStephen McConnell target->flags |= MPRSAS_TARGET_INRESET; 3461a2c14879SStephen McConnell } 3462a2c14879SStephen McConnell } 3463a2c14879SStephen McConnell } 3464a8837c77SWarner Losh } 3465a2c14879SStephen McConnell 3466991554f2SKenneth D. Merry int 3467991554f2SKenneth D. Merry mprsas_startup(struct mpr_softc *sc) 3468991554f2SKenneth D. Merry { 3469991554f2SKenneth D. Merry /* 3470991554f2SKenneth D. Merry * Send the port enable message and set the wait_for_port_enable flag. 3471991554f2SKenneth D. Merry * This flag helps to keep the simq frozen until all discovery events 3472991554f2SKenneth D. Merry * are processed. 3473991554f2SKenneth D. Merry */ 3474991554f2SKenneth D. Merry sc->wait_for_port_enable = 1; 3475991554f2SKenneth D. Merry mprsas_send_portenable(sc); 3476991554f2SKenneth D. Merry return (0); 3477991554f2SKenneth D. Merry } 3478991554f2SKenneth D. Merry 3479991554f2SKenneth D. Merry static int 3480991554f2SKenneth D. Merry mprsas_send_portenable(struct mpr_softc *sc) 3481991554f2SKenneth D. Merry { 3482991554f2SKenneth D. Merry MPI2_PORT_ENABLE_REQUEST *request; 3483991554f2SKenneth D. Merry struct mpr_command *cm; 3484991554f2SKenneth D. Merry 3485991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 3486991554f2SKenneth D. Merry 3487991554f2SKenneth D. Merry if ((cm = mpr_alloc_command(sc)) == NULL) 3488991554f2SKenneth D. Merry return (EBUSY); 3489991554f2SKenneth D. Merry request = (MPI2_PORT_ENABLE_REQUEST *)cm->cm_req; 3490991554f2SKenneth D. Merry request->Function = MPI2_FUNCTION_PORT_ENABLE; 3491991554f2SKenneth D. Merry request->MsgFlags = 0; 3492991554f2SKenneth D. Merry request->VP_ID = 0; 3493991554f2SKenneth D. Merry cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 3494991554f2SKenneth D. Merry cm->cm_complete = mprsas_portenable_complete; 3495991554f2SKenneth D. Merry cm->cm_data = NULL; 3496991554f2SKenneth D. Merry cm->cm_sge = NULL; 3497991554f2SKenneth D. Merry 3498991554f2SKenneth D. Merry mpr_map_command(sc, cm); 3499991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, 3500991554f2SKenneth D. Merry "mpr_send_portenable finished cm %p req %p complete %p\n", 3501991554f2SKenneth D. Merry cm, cm->cm_req, cm->cm_complete); 3502991554f2SKenneth D. Merry return (0); 3503991554f2SKenneth D. Merry } 3504991554f2SKenneth D. Merry 3505991554f2SKenneth D. Merry static void 3506991554f2SKenneth D. Merry mprsas_portenable_complete(struct mpr_softc *sc, struct mpr_command *cm) 3507991554f2SKenneth D. Merry { 3508991554f2SKenneth D. Merry MPI2_PORT_ENABLE_REPLY *reply; 3509991554f2SKenneth D. Merry struct mprsas_softc *sassc; 3510991554f2SKenneth D. Merry 3511991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 3512991554f2SKenneth D. Merry sassc = sc->sassc; 3513991554f2SKenneth D. Merry 3514991554f2SKenneth D. Merry /* 3515991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 3516991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 3517991554f2SKenneth D. Merry * port enable commands don't have S/G lists. 3518991554f2SKenneth D. Merry */ 3519991554f2SKenneth D. Merry if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 3520991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for port enable! " 3521991554f2SKenneth D. Merry "This should not happen!\n", __func__, cm->cm_flags); 3522991554f2SKenneth D. Merry } 3523991554f2SKenneth D. Merry 3524991554f2SKenneth D. Merry reply = (MPI2_PORT_ENABLE_REPLY *)cm->cm_reply; 3525991554f2SKenneth D. Merry if (reply == NULL) 3526991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "Portenable NULL reply\n"); 352771900a79SAlfredo Dal'Ava Junior else if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != 3528991554f2SKenneth D. Merry MPI2_IOCSTATUS_SUCCESS) 3529991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "Portenable failed\n"); 3530991554f2SKenneth D. Merry 3531991554f2SKenneth D. Merry mpr_free_command(sc, cm); 3532991554f2SKenneth D. Merry /* 3533991554f2SKenneth D. Merry * Done waiting for port enable to complete. Decrement the refcount. 3534991554f2SKenneth D. Merry * If refcount is 0, discovery is complete and a rescan of the bus can 3535991554f2SKenneth D. Merry * take place. 3536991554f2SKenneth D. Merry */ 3537991554f2SKenneth D. Merry sc->wait_for_port_enable = 0; 3538991554f2SKenneth D. Merry sc->port_enable_complete = 1; 3539991554f2SKenneth D. Merry wakeup(&sc->port_enable_complete); 3540991554f2SKenneth D. Merry mprsas_startup_decrement(sassc); 3541991554f2SKenneth D. Merry } 3542991554f2SKenneth D. Merry 3543991554f2SKenneth D. Merry int 3544991554f2SKenneth D. Merry mprsas_check_id(struct mprsas_softc *sassc, int id) 3545991554f2SKenneth D. Merry { 3546991554f2SKenneth D. Merry struct mpr_softc *sc = sassc->sc; 3547991554f2SKenneth D. Merry char *ids; 3548991554f2SKenneth D. Merry char *name; 3549991554f2SKenneth D. Merry 3550991554f2SKenneth D. Merry ids = &sc->exclude_ids[0]; 3551991554f2SKenneth D. Merry while((name = strsep(&ids, ",")) != NULL) { 3552991554f2SKenneth D. Merry if (name[0] == '\0') 3553991554f2SKenneth D. Merry continue; 3554991554f2SKenneth D. Merry if (strtol(name, NULL, 0) == (long)id) 3555991554f2SKenneth D. Merry return (1); 3556991554f2SKenneth D. Merry } 3557991554f2SKenneth D. Merry 3558991554f2SKenneth D. Merry return (0); 3559991554f2SKenneth D. Merry } 3560a2c14879SStephen McConnell 3561a2c14879SStephen McConnell void 3562a2c14879SStephen McConnell mprsas_realloc_targets(struct mpr_softc *sc, int maxtargets) 3563a2c14879SStephen McConnell { 3564a2c14879SStephen McConnell struct mprsas_softc *sassc; 3565a2c14879SStephen McConnell struct mprsas_lun *lun, *lun_tmp; 3566a2c14879SStephen McConnell struct mprsas_target *targ; 3567a2c14879SStephen McConnell int i; 3568a2c14879SStephen McConnell 3569a2c14879SStephen McConnell sassc = sc->sassc; 3570a2c14879SStephen McConnell /* 3571a2c14879SStephen McConnell * The number of targets is based on IOC Facts, so free all of 3572a2c14879SStephen McConnell * the allocated LUNs for each target and then the target buffer 3573a2c14879SStephen McConnell * itself. 3574a2c14879SStephen McConnell */ 3575a2c14879SStephen McConnell for (i=0; i< maxtargets; i++) { 3576a2c14879SStephen McConnell targ = &sassc->targets[i]; 3577a2c14879SStephen McConnell SLIST_FOREACH_SAFE(lun, &targ->luns, lun_link, lun_tmp) { 3578a2c14879SStephen McConnell free(lun, M_MPR); 3579a2c14879SStephen McConnell } 3580a2c14879SStephen McConnell } 3581a2c14879SStephen McConnell free(sassc->targets, M_MPR); 3582a2c14879SStephen McConnell 3583a2c14879SStephen McConnell sassc->targets = malloc(sizeof(struct mprsas_target) * maxtargets, 3584a2c14879SStephen McConnell M_MPR, M_WAITOK|M_ZERO); 3585a2c14879SStephen McConnell } 3586