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 5*46b23587SKashyap 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 * 29*46b23587SKashyap 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 #if __FreeBSD_version >= 900026 73991554f2SKenneth D. Merry #include <cam/scsi/smp_all.h> 74991554f2SKenneth D. Merry #endif 75991554f2SKenneth D. Merry 7667feec50SStephen McConnell #include <dev/nvme/nvme.h> 7767feec50SStephen McConnell 78991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_type.h> 79991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2.h> 80991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_ioc.h> 81991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_sas.h> 8267feec50SStephen McConnell #include <dev/mpr/mpi/mpi2_pci.h> 83991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_cnfg.h> 84991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_init.h> 85991554f2SKenneth D. Merry #include <dev/mpr/mpi/mpi2_tool.h> 86991554f2SKenneth D. Merry #include <dev/mpr/mpr_ioctl.h> 87991554f2SKenneth D. Merry #include <dev/mpr/mprvar.h> 88991554f2SKenneth D. Merry #include <dev/mpr/mpr_table.h> 89991554f2SKenneth D. Merry #include <dev/mpr/mpr_sas.h> 90991554f2SKenneth D. Merry 91991554f2SKenneth D. Merry #define MPRSAS_DISCOVERY_TIMEOUT 20 92991554f2SKenneth D. Merry #define MPRSAS_MAX_DISCOVERY_TIMEOUTS 10 /* 200 seconds */ 93991554f2SKenneth D. Merry 94991554f2SKenneth D. Merry /* 95991554f2SKenneth D. Merry * static array to check SCSI OpCode for EEDP protection bits 96991554f2SKenneth D. Merry */ 97991554f2SKenneth D. Merry #define PRO_R MPI2_SCSIIO_EEDPFLAGS_CHECK_REMOVE_OP 98991554f2SKenneth D. Merry #define PRO_W MPI2_SCSIIO_EEDPFLAGS_INSERT_OP 99991554f2SKenneth D. Merry #define PRO_V MPI2_SCSIIO_EEDPFLAGS_INSERT_OP 100991554f2SKenneth D. Merry static uint8_t op_code_prot[256] = { 101991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 102991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 103991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V, 104991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 105991554f2SKenneth D. Merry 0, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 106991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 107991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 108991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 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, PRO_W, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, PRO_R, 0, PRO_W, 0, 0, 0, PRO_W, PRO_V, 112991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 113991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 114991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 115991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 116991554f2SKenneth D. Merry 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 117991554f2SKenneth D. Merry }; 118991554f2SKenneth D. Merry 119991554f2SKenneth D. Merry MALLOC_DEFINE(M_MPRSAS, "MPRSAS", "MPR SAS memory"); 120991554f2SKenneth D. Merry 121991554f2SKenneth D. Merry static void mprsas_remove_device(struct mpr_softc *, struct mpr_command *); 122991554f2SKenneth D. Merry static void mprsas_remove_complete(struct mpr_softc *, struct mpr_command *); 123991554f2SKenneth D. Merry static void mprsas_action(struct cam_sim *sim, union ccb *ccb); 124991554f2SKenneth D. Merry static void mprsas_poll(struct cam_sim *sim); 125991554f2SKenneth D. Merry static void mprsas_scsiio_timeout(void *data); 1267a2a6a1aSStephen McConnell static void mprsas_abort_complete(struct mpr_softc *sc, struct mpr_command *cm); 127991554f2SKenneth D. Merry static void mprsas_action_scsiio(struct mprsas_softc *, union ccb *); 128991554f2SKenneth D. Merry static void mprsas_scsiio_complete(struct mpr_softc *, struct mpr_command *); 129991554f2SKenneth D. Merry static void mprsas_action_resetdev(struct mprsas_softc *, union ccb *); 1307a2a6a1aSStephen McConnell static void mprsas_resetdev_complete(struct mpr_softc *, struct mpr_command *); 131991554f2SKenneth D. Merry static int mprsas_send_abort(struct mpr_softc *sc, struct mpr_command *tm, 132991554f2SKenneth D. Merry struct mpr_command *cm); 133991554f2SKenneth D. Merry static void mprsas_async(void *callback_arg, uint32_t code, 134991554f2SKenneth D. Merry struct cam_path *path, void *arg); 135991554f2SKenneth D. Merry #if (__FreeBSD_version < 901503) || \ 136991554f2SKenneth D. Merry ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000006)) 137991554f2SKenneth D. Merry static void mprsas_check_eedp(struct mpr_softc *sc, struct cam_path *path, 138991554f2SKenneth D. Merry struct ccb_getdev *cgd); 139991554f2SKenneth D. Merry static void mprsas_read_cap_done(struct cam_periph *periph, 140991554f2SKenneth D. Merry union ccb *done_ccb); 141991554f2SKenneth D. Merry #endif 142991554f2SKenneth D. Merry static int mprsas_send_portenable(struct mpr_softc *sc); 143991554f2SKenneth D. Merry static void mprsas_portenable_complete(struct mpr_softc *sc, 144991554f2SKenneth D. Merry struct mpr_command *cm); 145991554f2SKenneth D. Merry 146991554f2SKenneth D. Merry #if __FreeBSD_version >= 900026 1477a2a6a1aSStephen McConnell static void mprsas_smpio_complete(struct mpr_softc *sc, struct mpr_command *cm); 1487a2a6a1aSStephen McConnell static void mprsas_send_smpcmd(struct mprsas_softc *sassc, union ccb *ccb, 1497a2a6a1aSStephen McConnell uint64_t sasaddr); 150a2c14879SStephen McConnell static void mprsas_action_smpio(struct mprsas_softc *sassc, union ccb *ccb); 151a2c14879SStephen McConnell #endif //FreeBSD_version >= 900026 152991554f2SKenneth D. Merry 153991554f2SKenneth D. Merry struct mprsas_target * 154991554f2SKenneth D. Merry mprsas_find_target_by_handle(struct mprsas_softc *sassc, int start, 155991554f2SKenneth D. Merry uint16_t handle) 156991554f2SKenneth D. Merry { 157991554f2SKenneth D. Merry struct mprsas_target *target; 158991554f2SKenneth D. Merry int i; 159991554f2SKenneth D. Merry 160991554f2SKenneth D. Merry for (i = start; i < sassc->maxtargets; i++) { 161991554f2SKenneth D. Merry target = &sassc->targets[i]; 162991554f2SKenneth D. Merry if (target->handle == handle) 163991554f2SKenneth D. Merry return (target); 164991554f2SKenneth D. Merry } 165991554f2SKenneth D. Merry 166991554f2SKenneth D. Merry return (NULL); 167991554f2SKenneth D. Merry } 168991554f2SKenneth D. Merry 169991554f2SKenneth D. Merry /* we need to freeze the simq during attach and diag reset, to avoid failing 170991554f2SKenneth D. Merry * commands before device handles have been found by discovery. Since 171991554f2SKenneth D. Merry * discovery involves reading config pages and possibly sending commands, 172991554f2SKenneth D. Merry * discovery actions may continue even after we receive the end of discovery 173991554f2SKenneth D. Merry * event, so refcount discovery actions instead of assuming we can unfreeze 174991554f2SKenneth D. Merry * the simq when we get the event. 175991554f2SKenneth D. Merry */ 176991554f2SKenneth D. Merry void 177991554f2SKenneth D. Merry mprsas_startup_increment(struct mprsas_softc *sassc) 178991554f2SKenneth D. Merry { 179991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 180991554f2SKenneth D. Merry 181991554f2SKenneth D. Merry if ((sassc->flags & MPRSAS_IN_STARTUP) != 0) { 182991554f2SKenneth D. Merry if (sassc->startup_refcount++ == 0) { 183991554f2SKenneth D. Merry /* just starting, freeze the simq */ 184991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INIT, 185991554f2SKenneth D. Merry "%s freezing simq\n", __func__); 186a371d6f9SKenneth D. Merry #if (__FreeBSD_version >= 1000039) || \ 187a371d6f9SKenneth D. Merry ((__FreeBSD_version < 1000000) && (__FreeBSD_version >= 902502)) 188991554f2SKenneth D. Merry xpt_hold_boot(); 189991554f2SKenneth D. Merry #endif 190991554f2SKenneth D. Merry xpt_freeze_simq(sassc->sim, 1); 191991554f2SKenneth D. Merry } 192991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INIT, "%s refcount %u\n", __func__, 193991554f2SKenneth D. Merry sassc->startup_refcount); 194991554f2SKenneth D. Merry } 195991554f2SKenneth D. Merry } 196991554f2SKenneth D. Merry 197991554f2SKenneth D. Merry void 198991554f2SKenneth D. Merry mprsas_release_simq_reinit(struct mprsas_softc *sassc) 199991554f2SKenneth D. Merry { 200991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_QUEUE_FROZEN) { 201991554f2SKenneth D. Merry sassc->flags &= ~MPRSAS_QUEUE_FROZEN; 202991554f2SKenneth D. Merry xpt_release_simq(sassc->sim, 1); 203991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INFO, "Unfreezing SIM queue\n"); 204991554f2SKenneth D. Merry } 205991554f2SKenneth D. Merry } 206991554f2SKenneth D. Merry 207991554f2SKenneth D. Merry void 208991554f2SKenneth D. Merry mprsas_startup_decrement(struct mprsas_softc *sassc) 209991554f2SKenneth D. Merry { 210991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 211991554f2SKenneth D. Merry 212991554f2SKenneth D. Merry if ((sassc->flags & MPRSAS_IN_STARTUP) != 0) { 213991554f2SKenneth D. Merry if (--sassc->startup_refcount == 0) { 214991554f2SKenneth D. Merry /* finished all discovery-related actions, release 215991554f2SKenneth D. Merry * the simq and rescan for the latest topology. 216991554f2SKenneth D. Merry */ 217991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INIT, 218991554f2SKenneth D. Merry "%s releasing simq\n", __func__); 219991554f2SKenneth D. Merry sassc->flags &= ~MPRSAS_IN_STARTUP; 220991554f2SKenneth D. Merry xpt_release_simq(sassc->sim, 1); 221a371d6f9SKenneth D. Merry #if (__FreeBSD_version >= 1000039) || \ 222a371d6f9SKenneth D. Merry ((__FreeBSD_version < 1000000) && (__FreeBSD_version >= 902502)) 223991554f2SKenneth D. Merry xpt_release_boot(); 224991554f2SKenneth D. Merry #else 225991554f2SKenneth D. Merry mprsas_rescan_target(sassc->sc, NULL); 226991554f2SKenneth D. Merry #endif 227991554f2SKenneth D. Merry } 228991554f2SKenneth D. Merry mpr_dprint(sassc->sc, MPR_INIT, "%s refcount %u\n", __func__, 229991554f2SKenneth D. Merry sassc->startup_refcount); 230991554f2SKenneth D. Merry } 231991554f2SKenneth D. Merry } 232991554f2SKenneth D. Merry 233b7f1ee79SScott Long /* 234b7f1ee79SScott Long * The firmware requires us to stop sending commands when we're doing task 235b7f1ee79SScott Long * management. 236991554f2SKenneth D. Merry * use. 237b7f1ee79SScott Long * XXX The logic for serializing the device has been made lazy and moved to 238b7f1ee79SScott Long * mprsas_prepare_for_tm(). 239991554f2SKenneth D. Merry */ 240991554f2SKenneth D. Merry struct mpr_command * 241991554f2SKenneth D. Merry mprsas_alloc_tm(struct mpr_softc *sc) 242991554f2SKenneth D. Merry { 24346b9415fSScott Long MPI2_SCSI_TASK_MANAGE_REQUEST *req; 244991554f2SKenneth D. Merry struct mpr_command *tm; 245991554f2SKenneth D. Merry 246991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 247991554f2SKenneth D. Merry tm = mpr_alloc_high_priority_command(sc); 24846b9415fSScott Long if (tm == NULL) 24946b9415fSScott Long return (NULL); 25046b9415fSScott Long 25146b9415fSScott Long req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 25246b9415fSScott Long req->Function = MPI2_FUNCTION_SCSI_TASK_MGMT; 253991554f2SKenneth D. Merry return tm; 254991554f2SKenneth D. Merry } 255991554f2SKenneth D. Merry 256991554f2SKenneth D. Merry void 257991554f2SKenneth D. Merry mprsas_free_tm(struct mpr_softc *sc, struct mpr_command *tm) 258991554f2SKenneth D. Merry { 25988619392SStephen McConnell int target_id = 0xFFFFFFFF; 26088619392SStephen McConnell 261a2c14879SStephen McConnell MPR_FUNCTRACE(sc); 262991554f2SKenneth D. Merry if (tm == NULL) 263991554f2SKenneth D. Merry return; 264991554f2SKenneth D. Merry 265a2c14879SStephen McConnell /* 266a2c14879SStephen McConnell * For TM's the devq is frozen for the device. Unfreeze it here and 267a2c14879SStephen McConnell * free the resources used for freezing the devq. Must clear the 268a2c14879SStephen McConnell * INRESET flag as well or scsi I/O will not work. 269991554f2SKenneth D. Merry */ 270a2c14879SStephen McConnell if (tm->cm_targ != NULL) { 271a2c14879SStephen McConnell tm->cm_targ->flags &= ~MPRSAS_TARGET_INRESET; 27288619392SStephen McConnell target_id = tm->cm_targ->tid; 273991554f2SKenneth D. Merry } 274a2c14879SStephen McConnell if (tm->cm_ccb) { 275a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "Unfreezing devq for target ID %d\n", 27688619392SStephen McConnell target_id); 277a2c14879SStephen McConnell xpt_release_devq(tm->cm_ccb->ccb_h.path, 1, TRUE); 278a2c14879SStephen McConnell xpt_free_path(tm->cm_ccb->ccb_h.path); 279a2c14879SStephen McConnell xpt_free_ccb(tm->cm_ccb); 280a2c14879SStephen McConnell } 281991554f2SKenneth D. Merry 282991554f2SKenneth D. Merry mpr_free_high_priority_command(sc, tm); 283991554f2SKenneth D. Merry } 284991554f2SKenneth D. Merry 285991554f2SKenneth D. Merry void 286991554f2SKenneth D. Merry mprsas_rescan_target(struct mpr_softc *sc, struct mprsas_target *targ) 287991554f2SKenneth D. Merry { 288991554f2SKenneth D. Merry struct mprsas_softc *sassc = sc->sassc; 289991554f2SKenneth D. Merry path_id_t pathid; 290991554f2SKenneth D. Merry target_id_t targetid; 291991554f2SKenneth D. Merry union ccb *ccb; 292991554f2SKenneth D. Merry 293991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 294991554f2SKenneth D. Merry pathid = cam_sim_path(sassc->sim); 295991554f2SKenneth D. Merry if (targ == NULL) 296991554f2SKenneth D. Merry targetid = CAM_TARGET_WILDCARD; 297991554f2SKenneth D. Merry else 298991554f2SKenneth D. Merry targetid = targ - sassc->targets; 299991554f2SKenneth D. Merry 300991554f2SKenneth D. Merry /* 301991554f2SKenneth D. Merry * Allocate a CCB and schedule a rescan. 302991554f2SKenneth D. Merry */ 303991554f2SKenneth D. Merry ccb = xpt_alloc_ccb_nowait(); 304991554f2SKenneth D. Merry if (ccb == NULL) { 305991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "unable to alloc CCB for rescan\n"); 306991554f2SKenneth D. Merry return; 307991554f2SKenneth D. Merry } 308991554f2SKenneth D. Merry 309a2c14879SStephen McConnell if (xpt_create_path(&ccb->ccb_h.path, NULL, pathid, targetid, 310a2c14879SStephen McConnell CAM_LUN_WILDCARD) != CAM_REQ_CMP) { 311991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "unable to create path for rescan\n"); 312991554f2SKenneth D. Merry xpt_free_ccb(ccb); 313991554f2SKenneth D. Merry return; 314991554f2SKenneth D. Merry } 315991554f2SKenneth D. Merry 316991554f2SKenneth D. Merry if (targetid == CAM_TARGET_WILDCARD) 317991554f2SKenneth D. Merry ccb->ccb_h.func_code = XPT_SCAN_BUS; 318991554f2SKenneth D. Merry else 319991554f2SKenneth D. Merry ccb->ccb_h.func_code = XPT_SCAN_TGT; 320991554f2SKenneth D. Merry 321991554f2SKenneth D. Merry mpr_dprint(sc, MPR_TRACE, "%s targetid %u\n", __func__, targetid); 322991554f2SKenneth D. Merry xpt_rescan(ccb); 323991554f2SKenneth D. Merry } 324991554f2SKenneth D. Merry 325991554f2SKenneth D. Merry static void 326991554f2SKenneth D. Merry mprsas_log_command(struct mpr_command *cm, u_int level, const char *fmt, ...) 327991554f2SKenneth D. Merry { 328991554f2SKenneth D. Merry struct sbuf sb; 329991554f2SKenneth D. Merry va_list ap; 330991554f2SKenneth D. Merry char str[192]; 331991554f2SKenneth D. Merry char path_str[64]; 332991554f2SKenneth D. Merry 333991554f2SKenneth D. Merry if (cm == NULL) 334991554f2SKenneth D. Merry return; 335991554f2SKenneth D. Merry 336991554f2SKenneth D. Merry /* No need to be in here if debugging isn't enabled */ 337991554f2SKenneth D. Merry if ((cm->cm_sc->mpr_debug & level) == 0) 338991554f2SKenneth D. Merry return; 339991554f2SKenneth D. Merry 340991554f2SKenneth D. Merry sbuf_new(&sb, str, sizeof(str), 0); 341991554f2SKenneth D. Merry 342991554f2SKenneth D. Merry va_start(ap, fmt); 343991554f2SKenneth D. Merry 344991554f2SKenneth D. Merry if (cm->cm_ccb != NULL) { 345991554f2SKenneth D. Merry xpt_path_string(cm->cm_ccb->csio.ccb_h.path, path_str, 346991554f2SKenneth D. Merry sizeof(path_str)); 347991554f2SKenneth D. Merry sbuf_cat(&sb, path_str); 348991554f2SKenneth D. Merry if (cm->cm_ccb->ccb_h.func_code == XPT_SCSI_IO) { 349991554f2SKenneth D. Merry scsi_command_string(&cm->cm_ccb->csio, &sb); 350991554f2SKenneth D. Merry sbuf_printf(&sb, "length %d ", 351991554f2SKenneth D. Merry cm->cm_ccb->csio.dxfer_len); 352991554f2SKenneth D. Merry } 353991554f2SKenneth D. Merry } else { 354991554f2SKenneth D. Merry sbuf_printf(&sb, "(noperiph:%s%d:%u:%u:%u): ", 355991554f2SKenneth D. Merry cam_sim_name(cm->cm_sc->sassc->sim), 356991554f2SKenneth D. Merry cam_sim_unit(cm->cm_sc->sassc->sim), 357991554f2SKenneth D. Merry cam_sim_bus(cm->cm_sc->sassc->sim), 358991554f2SKenneth D. Merry cm->cm_targ ? cm->cm_targ->tid : 0xFFFFFFFF, 359991554f2SKenneth D. Merry cm->cm_lun); 360991554f2SKenneth D. Merry } 361991554f2SKenneth D. Merry 362991554f2SKenneth D. Merry sbuf_printf(&sb, "SMID %u ", cm->cm_desc.Default.SMID); 363991554f2SKenneth D. Merry sbuf_vprintf(&sb, fmt, ap); 364991554f2SKenneth D. Merry sbuf_finish(&sb); 365c11c484fSScott Long mpr_print_field(cm->cm_sc, "%s", sbuf_data(&sb)); 366991554f2SKenneth D. Merry 367991554f2SKenneth D. Merry va_end(ap); 368991554f2SKenneth D. Merry } 369991554f2SKenneth D. Merry 370991554f2SKenneth D. Merry static void 371991554f2SKenneth D. Merry mprsas_remove_volume(struct mpr_softc *sc, struct mpr_command *tm) 372991554f2SKenneth D. Merry { 373991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 374991554f2SKenneth D. Merry struct mprsas_target *targ; 375991554f2SKenneth D. Merry uint16_t handle; 376991554f2SKenneth D. Merry 377991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 378991554f2SKenneth D. Merry 379991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 380991554f2SKenneth D. Merry handle = (uint16_t)(uintptr_t)tm->cm_complete_data; 381991554f2SKenneth D. Merry targ = tm->cm_targ; 382991554f2SKenneth D. Merry 383991554f2SKenneth D. Merry if (reply == NULL) { 384991554f2SKenneth D. Merry /* XXX retry the remove after the diag reset completes? */ 385991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "%s NULL reply resetting device " 386991554f2SKenneth D. Merry "0x%04x\n", __func__, handle); 387991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 388991554f2SKenneth D. Merry return; 389991554f2SKenneth D. Merry } 390991554f2SKenneth D. Merry 391d3f6eabfSStephen McConnell if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != 392d3f6eabfSStephen McConnell MPI2_IOCSTATUS_SUCCESS) { 39358581c13SStephen McConnell mpr_dprint(sc, MPR_ERROR, "IOCStatus = 0x%x while resetting " 394d3f6eabfSStephen McConnell "device 0x%x\n", le16toh(reply->IOCStatus), handle); 395991554f2SKenneth D. Merry } 396991554f2SKenneth D. Merry 397991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "Reset aborted %u commands\n", 398d3f6eabfSStephen McConnell le32toh(reply->TerminationCount)); 399991554f2SKenneth D. Merry mpr_free_reply(sc, tm->cm_reply_data); 400991554f2SKenneth D. Merry tm->cm_reply = NULL; /* Ensures the reply won't get re-freed */ 401991554f2SKenneth D. Merry 402991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "clearing target %u handle 0x%04x\n", 403991554f2SKenneth D. Merry targ->tid, handle); 404991554f2SKenneth D. Merry 405991554f2SKenneth D. Merry /* 406991554f2SKenneth D. Merry * Don't clear target if remove fails because things will get confusing. 407991554f2SKenneth D. Merry * Leave the devname and sasaddr intact so that we know to avoid reusing 408991554f2SKenneth D. Merry * this target id if possible, and so we can assign the same target id 409991554f2SKenneth D. Merry * to this device if it comes back in the future. 410991554f2SKenneth D. Merry */ 411d3f6eabfSStephen McConnell if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) == 412d3f6eabfSStephen McConnell MPI2_IOCSTATUS_SUCCESS) { 413991554f2SKenneth D. Merry targ = tm->cm_targ; 414991554f2SKenneth D. Merry targ->handle = 0x0; 415991554f2SKenneth D. Merry targ->encl_handle = 0x0; 416991554f2SKenneth D. Merry targ->encl_level_valid = 0x0; 417991554f2SKenneth D. Merry targ->encl_level = 0x0; 418991554f2SKenneth D. Merry targ->connector_name[0] = ' '; 419991554f2SKenneth D. Merry targ->connector_name[1] = ' '; 420991554f2SKenneth D. Merry targ->connector_name[2] = ' '; 421991554f2SKenneth D. Merry targ->connector_name[3] = ' '; 422991554f2SKenneth D. Merry targ->encl_slot = 0x0; 423991554f2SKenneth D. Merry targ->exp_dev_handle = 0x0; 424991554f2SKenneth D. Merry targ->phy_num = 0x0; 425991554f2SKenneth D. Merry targ->linkrate = 0x0; 426991554f2SKenneth D. Merry targ->devinfo = 0x0; 427991554f2SKenneth D. Merry targ->flags = 0x0; 428991554f2SKenneth D. Merry targ->scsi_req_desc_type = 0; 429991554f2SKenneth D. Merry } 430991554f2SKenneth D. Merry 431991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 432991554f2SKenneth D. Merry } 433991554f2SKenneth D. Merry 434991554f2SKenneth D. Merry 435991554f2SKenneth D. Merry /* 436991554f2SKenneth D. Merry * No Need to call "MPI2_SAS_OP_REMOVE_DEVICE" For Volume removal. 437991554f2SKenneth D. Merry * Otherwise Volume Delete is same as Bare Drive Removal. 438991554f2SKenneth D. Merry */ 439991554f2SKenneth D. Merry void 440991554f2SKenneth D. Merry mprsas_prepare_volume_remove(struct mprsas_softc *sassc, uint16_t handle) 441991554f2SKenneth D. Merry { 442991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 443991554f2SKenneth D. Merry struct mpr_softc *sc; 444991554f2SKenneth D. Merry struct mpr_command *cm; 445991554f2SKenneth D. Merry struct mprsas_target *targ = NULL; 446991554f2SKenneth D. Merry 447991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 448991554f2SKenneth D. Merry sc = sassc->sc; 449991554f2SKenneth D. Merry 450991554f2SKenneth D. Merry targ = mprsas_find_target_by_handle(sassc, 0, handle); 451991554f2SKenneth D. Merry if (targ == NULL) { 452991554f2SKenneth D. Merry /* FIXME: what is the action? */ 453991554f2SKenneth D. Merry /* We don't know about this device? */ 454991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, 455991554f2SKenneth D. Merry "%s %d : invalid handle 0x%x \n", __func__,__LINE__, handle); 456991554f2SKenneth D. Merry return; 457991554f2SKenneth D. Merry } 458991554f2SKenneth D. Merry 459991554f2SKenneth D. Merry targ->flags |= MPRSAS_TARGET_INREMOVAL; 460991554f2SKenneth D. Merry 461991554f2SKenneth D. Merry cm = mprsas_alloc_tm(sc); 462991554f2SKenneth D. Merry if (cm == NULL) { 463991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, 464991554f2SKenneth D. Merry "%s: command alloc failure\n", __func__); 465991554f2SKenneth D. Merry return; 466991554f2SKenneth D. Merry } 467991554f2SKenneth D. Merry 468991554f2SKenneth D. Merry mprsas_rescan_target(sc, targ); 469991554f2SKenneth D. Merry 470991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)cm->cm_req; 471991554f2SKenneth D. Merry req->DevHandle = targ->handle; 472991554f2SKenneth D. Merry req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; 473991554f2SKenneth D. Merry 47489d1c21fSKashyap D Desai if (!targ->is_nvme || sc->custom_nvme_tm_handling) { 475991554f2SKenneth D. Merry /* SAS Hard Link Reset / SATA Link Reset */ 476991554f2SKenneth D. Merry req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; 47789d1c21fSKashyap D Desai } else { 47889d1c21fSKashyap D Desai /* PCIe Protocol Level Reset*/ 47989d1c21fSKashyap D Desai req->MsgFlags = 48089d1c21fSKashyap D Desai MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; 48189d1c21fSKashyap D Desai } 482991554f2SKenneth D. Merry 483991554f2SKenneth D. Merry cm->cm_targ = targ; 484991554f2SKenneth D. Merry cm->cm_data = NULL; 485991554f2SKenneth D. Merry cm->cm_complete = mprsas_remove_volume; 486991554f2SKenneth D. Merry cm->cm_complete_data = (void *)(uintptr_t)handle; 487a2c14879SStephen McConnell 488a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n", 489a2c14879SStephen McConnell __func__, targ->tid); 490a2c14879SStephen McConnell mprsas_prepare_for_tm(sc, cm, targ, CAM_LUN_WILDCARD); 491a2c14879SStephen McConnell 492991554f2SKenneth D. Merry mpr_map_command(sc, cm); 493991554f2SKenneth D. Merry } 494991554f2SKenneth D. Merry 495991554f2SKenneth D. Merry /* 49667feec50SStephen McConnell * The firmware performs debounce on the link to avoid transient link errors 49767feec50SStephen McConnell * and false removals. When it does decide that link has been lost and a 49867feec50SStephen McConnell * device needs to go away, it expects that the host will perform a target reset 49967feec50SStephen McConnell * and then an op remove. The reset has the side-effect of aborting any 50067feec50SStephen McConnell * outstanding requests for the device, which is required for the op-remove to 50167feec50SStephen McConnell * succeed. It's not clear if the host should check for the device coming back 50267feec50SStephen McConnell * alive after the reset. 503991554f2SKenneth D. Merry */ 504991554f2SKenneth D. Merry void 505991554f2SKenneth D. Merry mprsas_prepare_remove(struct mprsas_softc *sassc, uint16_t handle) 506991554f2SKenneth D. Merry { 507991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 508991554f2SKenneth D. Merry struct mpr_softc *sc; 50946b9415fSScott Long struct mpr_command *tm; 510991554f2SKenneth D. Merry struct mprsas_target *targ = NULL; 511991554f2SKenneth D. Merry 512991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 513991554f2SKenneth D. Merry 514991554f2SKenneth D. Merry sc = sassc->sc; 515991554f2SKenneth D. Merry 516991554f2SKenneth D. Merry targ = mprsas_find_target_by_handle(sassc, 0, handle); 517991554f2SKenneth D. Merry if (targ == NULL) { 518991554f2SKenneth D. Merry /* FIXME: what is the action? */ 519991554f2SKenneth D. Merry /* We don't know about this device? */ 520991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s : invalid handle 0x%x \n", 521991554f2SKenneth D. Merry __func__, handle); 522991554f2SKenneth D. Merry return; 523991554f2SKenneth D. Merry } 524991554f2SKenneth D. Merry 525991554f2SKenneth D. Merry targ->flags |= MPRSAS_TARGET_INREMOVAL; 526991554f2SKenneth D. Merry 52746b9415fSScott Long tm = mprsas_alloc_tm(sc); 52846b9415fSScott Long if (tm == NULL) { 529991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: command alloc failure\n", 530991554f2SKenneth D. Merry __func__); 531991554f2SKenneth D. Merry return; 532991554f2SKenneth D. Merry } 533991554f2SKenneth D. Merry 534991554f2SKenneth D. Merry mprsas_rescan_target(sc, targ); 535991554f2SKenneth D. Merry 53646b9415fSScott Long req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 537991554f2SKenneth D. Merry memset(req, 0, sizeof(*req)); 538991554f2SKenneth D. Merry req->DevHandle = htole16(targ->handle); 539991554f2SKenneth D. Merry req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; 540991554f2SKenneth D. Merry 541991554f2SKenneth D. Merry /* SAS Hard Link Reset / SATA Link Reset */ 542991554f2SKenneth D. Merry req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; 543991554f2SKenneth D. Merry 54446b9415fSScott Long tm->cm_targ = targ; 54546b9415fSScott Long tm->cm_data = NULL; 54646b9415fSScott Long tm->cm_complete = mprsas_remove_device; 54746b9415fSScott Long tm->cm_complete_data = (void *)(uintptr_t)handle; 548a2c14879SStephen McConnell 549a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n", 550a2c14879SStephen McConnell __func__, targ->tid); 55146b9415fSScott Long mprsas_prepare_for_tm(sc, tm, targ, CAM_LUN_WILDCARD); 552a2c14879SStephen McConnell 55346b9415fSScott Long mpr_map_command(sc, tm); 554991554f2SKenneth D. Merry } 555991554f2SKenneth D. Merry 556991554f2SKenneth D. Merry static void 557991554f2SKenneth D. Merry mprsas_remove_device(struct mpr_softc *sc, struct mpr_command *tm) 558991554f2SKenneth D. Merry { 559991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 560991554f2SKenneth D. Merry MPI2_SAS_IOUNIT_CONTROL_REQUEST *req; 561991554f2SKenneth D. Merry struct mprsas_target *targ; 562991554f2SKenneth D. Merry struct mpr_command *next_cm; 563991554f2SKenneth D. Merry uint16_t handle; 564991554f2SKenneth D. Merry 565991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 566991554f2SKenneth D. Merry 567991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 568991554f2SKenneth D. Merry handle = (uint16_t)(uintptr_t)tm->cm_complete_data; 569991554f2SKenneth D. Merry targ = tm->cm_targ; 570991554f2SKenneth D. Merry 571991554f2SKenneth D. Merry /* 572991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 573991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 574991554f2SKenneth D. Merry * task management commands don't have S/G lists. 575991554f2SKenneth D. Merry */ 576991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 577991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for remove of " 578991554f2SKenneth D. Merry "handle %#04x! This should not happen!\n", __func__, 579991554f2SKenneth D. Merry tm->cm_flags, handle); 580991554f2SKenneth D. Merry } 581991554f2SKenneth D. Merry 582991554f2SKenneth D. Merry if (reply == NULL) { 583991554f2SKenneth D. Merry /* XXX retry the remove after the diag reset completes? */ 584991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "%s NULL reply resetting device " 585991554f2SKenneth D. Merry "0x%04x\n", __func__, handle); 586991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 587991554f2SKenneth D. Merry return; 588991554f2SKenneth D. Merry } 589991554f2SKenneth D. Merry 590d3f6eabfSStephen McConnell if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) != 591d3f6eabfSStephen McConnell MPI2_IOCSTATUS_SUCCESS) { 59258581c13SStephen McConnell mpr_dprint(sc, MPR_ERROR, "IOCStatus = 0x%x while resetting " 593991554f2SKenneth D. Merry "device 0x%x\n", le16toh(reply->IOCStatus), handle); 594991554f2SKenneth D. Merry } 595991554f2SKenneth D. Merry 596991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "Reset aborted %u commands\n", 597991554f2SKenneth D. Merry le32toh(reply->TerminationCount)); 598991554f2SKenneth D. Merry mpr_free_reply(sc, tm->cm_reply_data); 599991554f2SKenneth D. Merry tm->cm_reply = NULL; /* Ensures the reply won't get re-freed */ 600991554f2SKenneth D. Merry 601991554f2SKenneth D. Merry /* Reuse the existing command */ 602991554f2SKenneth D. Merry req = (MPI2_SAS_IOUNIT_CONTROL_REQUEST *)tm->cm_req; 603991554f2SKenneth D. Merry memset(req, 0, sizeof(*req)); 604991554f2SKenneth D. Merry req->Function = MPI2_FUNCTION_SAS_IO_UNIT_CONTROL; 605991554f2SKenneth D. Merry req->Operation = MPI2_SAS_OP_REMOVE_DEVICE; 606991554f2SKenneth D. Merry req->DevHandle = htole16(handle); 607991554f2SKenneth D. Merry tm->cm_data = NULL; 608991554f2SKenneth D. Merry tm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 609991554f2SKenneth D. Merry tm->cm_complete = mprsas_remove_complete; 610991554f2SKenneth D. Merry tm->cm_complete_data = (void *)(uintptr_t)handle; 611991554f2SKenneth D. Merry 612991554f2SKenneth D. Merry mpr_map_command(sc, tm); 613991554f2SKenneth D. Merry 614a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "clearing target %u handle 0x%04x\n", 615991554f2SKenneth D. Merry targ->tid, handle); 616991554f2SKenneth D. Merry if (targ->encl_level_valid) { 617a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "At enclosure level %d, slot %d, " 618991554f2SKenneth D. Merry "connector name (%4s)\n", targ->encl_level, targ->encl_slot, 619991554f2SKenneth D. Merry targ->connector_name); 620991554f2SKenneth D. Merry } 621991554f2SKenneth D. Merry TAILQ_FOREACH_SAFE(tm, &targ->commands, cm_link, next_cm) { 622991554f2SKenneth D. Merry union ccb *ccb; 623991554f2SKenneth D. Merry 624991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "Completing missed command %p\n", tm); 625991554f2SKenneth D. Merry ccb = tm->cm_complete_data; 626a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 627991554f2SKenneth D. Merry mprsas_scsiio_complete(sc, tm); 628991554f2SKenneth D. Merry } 629991554f2SKenneth D. Merry } 630991554f2SKenneth D. Merry 631991554f2SKenneth D. Merry static void 632991554f2SKenneth D. Merry mprsas_remove_complete(struct mpr_softc *sc, struct mpr_command *tm) 633991554f2SKenneth D. Merry { 634991554f2SKenneth D. Merry MPI2_SAS_IOUNIT_CONTROL_REPLY *reply; 635991554f2SKenneth D. Merry uint16_t handle; 636991554f2SKenneth D. Merry struct mprsas_target *targ; 637991554f2SKenneth D. Merry struct mprsas_lun *lun; 638991554f2SKenneth D. Merry 639991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 640991554f2SKenneth D. Merry 641991554f2SKenneth D. Merry reply = (MPI2_SAS_IOUNIT_CONTROL_REPLY *)tm->cm_reply; 642991554f2SKenneth D. Merry handle = (uint16_t)(uintptr_t)tm->cm_complete_data; 643991554f2SKenneth D. Merry 644991554f2SKenneth D. Merry /* 645991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 646991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 647991554f2SKenneth D. Merry * task management commands don't have S/G lists. 648991554f2SKenneth D. Merry */ 649991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 650991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s: cm_flags = %#x for remove of " 651991554f2SKenneth D. Merry "handle %#04x! This should not happen!\n", __func__, 652991554f2SKenneth D. Merry tm->cm_flags, handle); 653991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 654991554f2SKenneth D. Merry return; 655991554f2SKenneth D. Merry } 656991554f2SKenneth D. Merry 657991554f2SKenneth D. Merry if (reply == NULL) { 658991554f2SKenneth D. Merry /* most likely a chip reset */ 659991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "%s NULL reply removing device " 660991554f2SKenneth D. Merry "0x%04x\n", __func__, handle); 661991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 662991554f2SKenneth D. Merry return; 663991554f2SKenneth D. Merry } 664991554f2SKenneth D. Merry 665991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s on handle 0x%04x, IOCStatus= 0x%x\n", 666991554f2SKenneth D. Merry __func__, handle, le16toh(reply->IOCStatus)); 667991554f2SKenneth D. Merry 668991554f2SKenneth D. Merry /* 669991554f2SKenneth D. Merry * Don't clear target if remove fails because things will get confusing. 670991554f2SKenneth D. Merry * Leave the devname and sasaddr intact so that we know to avoid reusing 671991554f2SKenneth D. Merry * this target id if possible, and so we can assign the same target id 672991554f2SKenneth D. Merry * to this device if it comes back in the future. 673991554f2SKenneth D. Merry */ 674d3f6eabfSStephen McConnell if ((le16toh(reply->IOCStatus) & MPI2_IOCSTATUS_MASK) == 675d3f6eabfSStephen McConnell MPI2_IOCSTATUS_SUCCESS) { 676991554f2SKenneth D. Merry targ = tm->cm_targ; 677991554f2SKenneth D. Merry targ->handle = 0x0; 678991554f2SKenneth D. Merry targ->encl_handle = 0x0; 679991554f2SKenneth D. Merry targ->encl_level_valid = 0x0; 680991554f2SKenneth D. Merry targ->encl_level = 0x0; 681991554f2SKenneth D. Merry targ->connector_name[0] = ' '; 682991554f2SKenneth D. Merry targ->connector_name[1] = ' '; 683991554f2SKenneth D. Merry targ->connector_name[2] = ' '; 684991554f2SKenneth D. Merry targ->connector_name[3] = ' '; 685991554f2SKenneth D. Merry targ->encl_slot = 0x0; 686991554f2SKenneth D. Merry targ->exp_dev_handle = 0x0; 687991554f2SKenneth D. Merry targ->phy_num = 0x0; 688991554f2SKenneth D. Merry targ->linkrate = 0x0; 689991554f2SKenneth D. Merry targ->devinfo = 0x0; 690991554f2SKenneth D. Merry targ->flags = 0x0; 691991554f2SKenneth D. Merry targ->scsi_req_desc_type = 0; 692991554f2SKenneth D. Merry 693991554f2SKenneth D. Merry while (!SLIST_EMPTY(&targ->luns)) { 694991554f2SKenneth D. Merry lun = SLIST_FIRST(&targ->luns); 695991554f2SKenneth D. Merry SLIST_REMOVE_HEAD(&targ->luns, lun_link); 696991554f2SKenneth D. Merry free(lun, M_MPR); 697991554f2SKenneth D. Merry } 698991554f2SKenneth D. Merry } 699991554f2SKenneth D. Merry 700991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 701991554f2SKenneth D. Merry } 702991554f2SKenneth D. Merry 703991554f2SKenneth D. Merry static int 704991554f2SKenneth D. Merry mprsas_register_events(struct mpr_softc *sc) 705991554f2SKenneth D. Merry { 706991554f2SKenneth D. Merry uint8_t events[16]; 707991554f2SKenneth D. Merry 708991554f2SKenneth D. Merry bzero(events, 16); 709991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_DEVICE_STATUS_CHANGE); 710991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_DISCOVERY); 711991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_BROADCAST_PRIMITIVE); 712991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_INIT_DEVICE_STATUS_CHANGE); 713991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_INIT_TABLE_OVERFLOW); 714991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_TOPOLOGY_CHANGE_LIST); 715991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_SAS_ENCL_DEVICE_STATUS_CHANGE); 716991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_IR_CONFIGURATION_CHANGE_LIST); 717991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_IR_VOLUME); 718991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_IR_PHYSICAL_DISK); 719991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_IR_OPERATION_STATUS); 720991554f2SKenneth D. Merry setbit(events, MPI2_EVENT_TEMP_THRESHOLD); 7215f5baf0eSAlexander Motin setbit(events, MPI2_EVENT_SAS_DEVICE_DISCOVERY_ERROR); 72267feec50SStephen McConnell if (sc->facts->MsgVersion >= MPI2_VERSION_02_06) { 7232bbc5fcbSStephen McConnell setbit(events, MPI2_EVENT_ACTIVE_CABLE_EXCEPTION); 72467feec50SStephen McConnell if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) { 72567feec50SStephen McConnell setbit(events, MPI2_EVENT_PCIE_DEVICE_STATUS_CHANGE); 72667feec50SStephen McConnell setbit(events, MPI2_EVENT_PCIE_ENUMERATION); 72767feec50SStephen McConnell setbit(events, MPI2_EVENT_PCIE_TOPOLOGY_CHANGE_LIST); 72867feec50SStephen McConnell } 72967feec50SStephen McConnell } 730991554f2SKenneth D. Merry 731991554f2SKenneth D. Merry mpr_register_events(sc, events, mprsas_evt_handler, NULL, 732991554f2SKenneth D. Merry &sc->sassc->mprsas_eh); 733991554f2SKenneth D. Merry 734991554f2SKenneth D. Merry return (0); 735991554f2SKenneth D. Merry } 736991554f2SKenneth D. Merry 737991554f2SKenneth D. Merry int 738991554f2SKenneth D. Merry mpr_attach_sas(struct mpr_softc *sc) 739991554f2SKenneth D. Merry { 740991554f2SKenneth D. Merry struct mprsas_softc *sassc; 741991554f2SKenneth D. Merry cam_status status; 74262a09ee9SAlexander Motin int unit, error = 0, reqs; 743991554f2SKenneth D. Merry 744991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 745757ff642SScott Long mpr_dprint(sc, MPR_INIT, "%s entered\n", __func__); 746991554f2SKenneth D. Merry 747991554f2SKenneth D. Merry sassc = malloc(sizeof(struct mprsas_softc), M_MPR, M_WAITOK|M_ZERO); 748991554f2SKenneth D. Merry if (!sassc) { 749757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, 750757ff642SScott Long "Cannot allocate SAS subsystem memory\n"); 751991554f2SKenneth D. Merry return (ENOMEM); 752991554f2SKenneth D. Merry } 753991554f2SKenneth D. Merry 754991554f2SKenneth D. Merry /* 755a2c14879SStephen McConnell * XXX MaxTargets could change during a reinit. Since we don't 756991554f2SKenneth D. Merry * resize the targets[] array during such an event, cache the value 757991554f2SKenneth D. Merry * of MaxTargets here so that we don't get into trouble later. This 758991554f2SKenneth D. Merry * should move into the reinit logic. 759991554f2SKenneth D. Merry */ 760327f2e6cSStephen McConnell sassc->maxtargets = sc->facts->MaxTargets + sc->facts->MaxVolumes; 761991554f2SKenneth D. Merry sassc->targets = malloc(sizeof(struct mprsas_target) * 762991554f2SKenneth D. Merry sassc->maxtargets, M_MPR, M_WAITOK|M_ZERO); 763991554f2SKenneth D. Merry if (!sassc->targets) { 764757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, 765757ff642SScott Long "Cannot allocate SAS target memory\n"); 766991554f2SKenneth D. Merry free(sassc, M_MPR); 767991554f2SKenneth D. Merry return (ENOMEM); 768991554f2SKenneth D. Merry } 769991554f2SKenneth D. Merry sc->sassc = sassc; 770991554f2SKenneth D. Merry sassc->sc = sc; 771991554f2SKenneth D. Merry 77262a09ee9SAlexander Motin reqs = sc->num_reqs - sc->num_prireqs - 1; 77362a09ee9SAlexander Motin if ((sassc->devq = cam_simq_alloc(reqs)) == NULL) { 774757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, "Cannot allocate SIMQ\n"); 775991554f2SKenneth D. Merry error = ENOMEM; 776991554f2SKenneth D. Merry goto out; 777991554f2SKenneth D. Merry } 778991554f2SKenneth D. Merry 779991554f2SKenneth D. Merry unit = device_get_unit(sc->mpr_dev); 780991554f2SKenneth D. Merry sassc->sim = cam_sim_alloc(mprsas_action, mprsas_poll, "mpr", sassc, 78162a09ee9SAlexander Motin unit, &sc->mpr_mtx, reqs, reqs, sassc->devq); 782991554f2SKenneth D. Merry if (sassc->sim == NULL) { 783757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, "Cannot allocate SIM\n"); 784991554f2SKenneth D. Merry error = EINVAL; 785991554f2SKenneth D. Merry goto out; 786991554f2SKenneth D. Merry } 787991554f2SKenneth D. Merry 788991554f2SKenneth D. Merry TAILQ_INIT(&sassc->ev_queue); 789991554f2SKenneth D. Merry 790991554f2SKenneth D. Merry /* Initialize taskqueue for Event Handling */ 791991554f2SKenneth D. Merry TASK_INIT(&sassc->ev_task, 0, mprsas_firmware_event_work, sc); 792991554f2SKenneth D. Merry sassc->ev_tq = taskqueue_create("mpr_taskq", M_NOWAIT | M_ZERO, 793991554f2SKenneth D. Merry taskqueue_thread_enqueue, &sassc->ev_tq); 794c2a0f07aSAlexander Motin taskqueue_start_threads(&sassc->ev_tq, 1, PRIBIO, "%s taskq", 795991554f2SKenneth D. Merry device_get_nameunit(sc->mpr_dev)); 796991554f2SKenneth D. Merry 797991554f2SKenneth D. Merry mpr_lock(sc); 798991554f2SKenneth D. Merry 799991554f2SKenneth D. Merry /* 800991554f2SKenneth D. Merry * XXX There should be a bus for every port on the adapter, but since 801991554f2SKenneth D. Merry * we're just going to fake the topology for now, we'll pretend that 802991554f2SKenneth D. Merry * everything is just a target on a single bus. 803991554f2SKenneth D. Merry */ 804991554f2SKenneth D. Merry if ((error = xpt_bus_register(sassc->sim, sc->mpr_dev, 0)) != 0) { 805757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, 806757ff642SScott Long "Error %d registering SCSI bus\n", error); 807991554f2SKenneth D. Merry mpr_unlock(sc); 808991554f2SKenneth D. Merry goto out; 809991554f2SKenneth D. Merry } 810991554f2SKenneth D. Merry 811991554f2SKenneth D. Merry /* 812a2c14879SStephen McConnell * Assume that discovery events will start right away. 813991554f2SKenneth D. Merry * 814991554f2SKenneth D. Merry * Hold off boot until discovery is complete. 815991554f2SKenneth D. Merry */ 816991554f2SKenneth D. Merry sassc->flags |= MPRSAS_IN_STARTUP | MPRSAS_IN_DISCOVERY; 817991554f2SKenneth D. Merry sc->sassc->startup_refcount = 0; 818991554f2SKenneth D. Merry mprsas_startup_increment(sassc); 819991554f2SKenneth D. Merry 820a2c14879SStephen McConnell callout_init(&sassc->discovery_callout, 1 /*mpsafe*/); 821991554f2SKenneth D. Merry 822991554f2SKenneth D. Merry /* 823991554f2SKenneth D. Merry * Register for async events so we can determine the EEDP 824991554f2SKenneth D. Merry * capabilities of devices. 825991554f2SKenneth D. Merry */ 826991554f2SKenneth D. Merry status = xpt_create_path(&sassc->path, /*periph*/NULL, 827991554f2SKenneth D. Merry cam_sim_path(sc->sassc->sim), CAM_TARGET_WILDCARD, 828991554f2SKenneth D. Merry CAM_LUN_WILDCARD); 829991554f2SKenneth D. Merry if (status != CAM_REQ_CMP) { 830757ff642SScott Long mpr_dprint(sc, MPR_INIT|MPR_ERROR, 831757ff642SScott Long "Error %#x creating sim path\n", status); 832991554f2SKenneth D. Merry sassc->path = NULL; 833991554f2SKenneth D. Merry } else { 834991554f2SKenneth D. Merry int event; 835991554f2SKenneth D. Merry 836991554f2SKenneth D. Merry #if (__FreeBSD_version >= 1000006) || \ 837991554f2SKenneth D. Merry ((__FreeBSD_version >= 901503) && (__FreeBSD_version < 1000000)) 838991554f2SKenneth D. Merry event = AC_ADVINFO_CHANGED | AC_FOUND_DEVICE; 839991554f2SKenneth D. Merry #else 840991554f2SKenneth D. Merry event = AC_FOUND_DEVICE; 841991554f2SKenneth D. Merry #endif 84207aa4de1SKenneth D. Merry 84307aa4de1SKenneth D. Merry /* 84407aa4de1SKenneth D. Merry * Prior to the CAM locking improvements, we can't call 84507aa4de1SKenneth D. Merry * xpt_register_async() with a particular path specified. 84607aa4de1SKenneth D. Merry * 84707aa4de1SKenneth D. Merry * If a path isn't specified, xpt_register_async() will 84807aa4de1SKenneth D. Merry * generate a wildcard path and acquire the XPT lock while 84907aa4de1SKenneth D. Merry * it calls xpt_action() to execute the XPT_SASYNC_CB CCB. 85007aa4de1SKenneth D. Merry * It will then drop the XPT lock once that is done. 85107aa4de1SKenneth D. Merry * 85207aa4de1SKenneth D. Merry * If a path is specified for xpt_register_async(), it will 85307aa4de1SKenneth D. Merry * not acquire and drop the XPT lock around the call to 85407aa4de1SKenneth D. Merry * xpt_action(). xpt_action() asserts that the caller 85507aa4de1SKenneth D. Merry * holds the SIM lock, so the SIM lock has to be held when 85607aa4de1SKenneth D. Merry * calling xpt_register_async() when the path is specified. 85707aa4de1SKenneth D. Merry * 85807aa4de1SKenneth D. Merry * But xpt_register_async calls xpt_for_all_devices(), 85907aa4de1SKenneth D. Merry * which calls xptbustraverse(), which will acquire each 86007aa4de1SKenneth D. Merry * SIM lock. When it traverses our particular bus, it will 86107aa4de1SKenneth D. Merry * necessarily acquire the SIM lock, which will lead to a 86207aa4de1SKenneth D. Merry * recursive lock acquisition. 86307aa4de1SKenneth D. Merry * 86407aa4de1SKenneth D. Merry * The CAM locking changes fix this problem by acquiring 86507aa4de1SKenneth D. Merry * the XPT topology lock around bus traversal in 86607aa4de1SKenneth D. Merry * xptbustraverse(), so the caller can hold the SIM lock 86707aa4de1SKenneth D. Merry * and it does not cause a recursive lock acquisition. 86807aa4de1SKenneth D. Merry * 86907aa4de1SKenneth D. Merry * These __FreeBSD_version values are approximate, especially 87007aa4de1SKenneth D. Merry * for stable/10, which is two months later than the actual 87107aa4de1SKenneth D. Merry * change. 87207aa4de1SKenneth D. Merry */ 87307aa4de1SKenneth D. Merry 87407aa4de1SKenneth D. Merry #if (__FreeBSD_version < 1000703) || \ 87507aa4de1SKenneth D. Merry ((__FreeBSD_version >= 1100000) && (__FreeBSD_version < 1100002)) 87607aa4de1SKenneth D. Merry mpr_unlock(sc); 87707aa4de1SKenneth D. Merry status = xpt_register_async(event, mprsas_async, sc, 87807aa4de1SKenneth D. Merry NULL); 87907aa4de1SKenneth D. Merry mpr_lock(sc); 88007aa4de1SKenneth D. Merry #else 881991554f2SKenneth D. Merry status = xpt_register_async(event, mprsas_async, sc, 882991554f2SKenneth D. Merry sassc->path); 88307aa4de1SKenneth D. Merry #endif 88407aa4de1SKenneth D. Merry 885991554f2SKenneth D. Merry if (status != CAM_REQ_CMP) { 886991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, 887991554f2SKenneth D. Merry "Error %#x registering async handler for " 888991554f2SKenneth D. Merry "AC_ADVINFO_CHANGED events\n", status); 889991554f2SKenneth D. Merry xpt_free_path(sassc->path); 890991554f2SKenneth D. Merry sassc->path = NULL; 891991554f2SKenneth D. Merry } 892991554f2SKenneth D. Merry } 893991554f2SKenneth D. Merry if (status != CAM_REQ_CMP) { 894991554f2SKenneth D. Merry /* 895991554f2SKenneth D. Merry * EEDP use is the exception, not the rule. 896991554f2SKenneth D. Merry * Warn the user, but do not fail to attach. 897991554f2SKenneth D. Merry */ 898991554f2SKenneth D. Merry mpr_printf(sc, "EEDP capabilities disabled.\n"); 899991554f2SKenneth D. Merry } 900991554f2SKenneth D. Merry 901991554f2SKenneth D. Merry mpr_unlock(sc); 902991554f2SKenneth D. Merry 903991554f2SKenneth D. Merry mprsas_register_events(sc); 904991554f2SKenneth D. Merry out: 905991554f2SKenneth D. Merry if (error) 906991554f2SKenneth D. Merry mpr_detach_sas(sc); 907757ff642SScott Long 908757ff642SScott Long mpr_dprint(sc, MPR_INIT, "%s exit, error= %d\n", __func__, error); 909991554f2SKenneth D. Merry return (error); 910991554f2SKenneth D. Merry } 911991554f2SKenneth D. Merry 912991554f2SKenneth D. Merry int 913991554f2SKenneth D. Merry mpr_detach_sas(struct mpr_softc *sc) 914991554f2SKenneth D. Merry { 915991554f2SKenneth D. Merry struct mprsas_softc *sassc; 916991554f2SKenneth D. Merry struct mprsas_lun *lun, *lun_tmp; 917991554f2SKenneth D. Merry struct mprsas_target *targ; 918991554f2SKenneth D. Merry int i; 919991554f2SKenneth D. Merry 920991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 921991554f2SKenneth D. Merry 922991554f2SKenneth D. Merry if (sc->sassc == NULL) 923991554f2SKenneth D. Merry return (0); 924991554f2SKenneth D. Merry 925991554f2SKenneth D. Merry sassc = sc->sassc; 926991554f2SKenneth D. Merry mpr_deregister_events(sc, sassc->mprsas_eh); 927991554f2SKenneth D. Merry 928991554f2SKenneth D. Merry /* 929991554f2SKenneth D. Merry * Drain and free the event handling taskqueue with the lock 930991554f2SKenneth D. Merry * unheld so that any parallel processing tasks drain properly 931991554f2SKenneth D. Merry * without deadlocking. 932991554f2SKenneth D. Merry */ 933991554f2SKenneth D. Merry if (sassc->ev_tq != NULL) 934991554f2SKenneth D. Merry taskqueue_free(sassc->ev_tq); 935991554f2SKenneth D. Merry 936991554f2SKenneth D. Merry /* Make sure CAM doesn't wedge if we had to bail out early. */ 937991554f2SKenneth D. Merry mpr_lock(sc); 938991554f2SKenneth D. Merry 9393c5ac992SScott Long while (sassc->startup_refcount != 0) 9403c5ac992SScott Long mprsas_startup_decrement(sassc); 9413c5ac992SScott Long 942991554f2SKenneth D. Merry /* Deregister our async handler */ 943991554f2SKenneth D. Merry if (sassc->path != NULL) { 944991554f2SKenneth D. Merry xpt_register_async(0, mprsas_async, sc, sassc->path); 945991554f2SKenneth D. Merry xpt_free_path(sassc->path); 946991554f2SKenneth D. Merry sassc->path = NULL; 947991554f2SKenneth D. Merry } 948991554f2SKenneth D. Merry 949991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_IN_STARTUP) 950991554f2SKenneth D. Merry xpt_release_simq(sassc->sim, 1); 951991554f2SKenneth D. Merry 952991554f2SKenneth D. Merry if (sassc->sim != NULL) { 953991554f2SKenneth D. Merry xpt_bus_deregister(cam_sim_path(sassc->sim)); 954991554f2SKenneth D. Merry cam_sim_free(sassc->sim, FALSE); 955991554f2SKenneth D. Merry } 956991554f2SKenneth D. Merry 957991554f2SKenneth D. Merry mpr_unlock(sc); 958991554f2SKenneth D. Merry 959991554f2SKenneth D. Merry if (sassc->devq != NULL) 960991554f2SKenneth D. Merry cam_simq_free(sassc->devq); 961991554f2SKenneth D. Merry 962991554f2SKenneth D. Merry for (i = 0; i < sassc->maxtargets; i++) { 963991554f2SKenneth D. Merry targ = &sassc->targets[i]; 964991554f2SKenneth D. Merry SLIST_FOREACH_SAFE(lun, &targ->luns, lun_link, lun_tmp) { 965991554f2SKenneth D. Merry free(lun, M_MPR); 966991554f2SKenneth D. Merry } 967991554f2SKenneth D. Merry } 968991554f2SKenneth D. Merry free(sassc->targets, M_MPR); 969991554f2SKenneth D. Merry free(sassc, M_MPR); 970991554f2SKenneth D. Merry sc->sassc = NULL; 971991554f2SKenneth D. Merry 972991554f2SKenneth D. Merry return (0); 973991554f2SKenneth D. Merry } 974991554f2SKenneth D. Merry 975991554f2SKenneth D. Merry void 976991554f2SKenneth D. Merry mprsas_discovery_end(struct mprsas_softc *sassc) 977991554f2SKenneth D. Merry { 978991554f2SKenneth D. Merry struct mpr_softc *sc = sassc->sc; 979991554f2SKenneth D. Merry 980991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 981991554f2SKenneth D. Merry 982991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_DISCOVERY_TIMEOUT_PENDING) 983991554f2SKenneth D. Merry callout_stop(&sassc->discovery_callout); 984991554f2SKenneth D. Merry 985327f2e6cSStephen McConnell /* 986327f2e6cSStephen McConnell * After discovery has completed, check the mapping table for any 987327f2e6cSStephen McConnell * missing devices and update their missing counts. Only do this once 988327f2e6cSStephen McConnell * whenever the driver is initialized so that missing counts aren't 989327f2e6cSStephen McConnell * updated unnecessarily. Note that just because discovery has 990327f2e6cSStephen McConnell * completed doesn't mean that events have been processed yet. The 991327f2e6cSStephen McConnell * check_devices function is a callout timer that checks if ALL devices 992327f2e6cSStephen McConnell * are missing. If so, it will wait a little longer for events to 993327f2e6cSStephen McConnell * complete and keep resetting itself until some device in the mapping 994327f2e6cSStephen McConnell * table is not missing, meaning that event processing has started. 995327f2e6cSStephen McConnell */ 996327f2e6cSStephen McConnell if (sc->track_mapping_events) { 997327f2e6cSStephen McConnell mpr_dprint(sc, MPR_XINFO | MPR_MAPPING, "Discovery has " 998327f2e6cSStephen McConnell "completed. Check for missing devices in the mapping " 999327f2e6cSStephen McConnell "table.\n"); 1000327f2e6cSStephen McConnell callout_reset(&sc->device_check_callout, 1001327f2e6cSStephen McConnell MPR_MISSING_CHECK_DELAY * hz, mpr_mapping_check_devices, 1002327f2e6cSStephen McConnell sc); 1003327f2e6cSStephen McConnell } 1004991554f2SKenneth D. Merry } 1005991554f2SKenneth D. Merry 1006991554f2SKenneth D. Merry static void 1007991554f2SKenneth D. Merry mprsas_action(struct cam_sim *sim, union ccb *ccb) 1008991554f2SKenneth D. Merry { 1009991554f2SKenneth D. Merry struct mprsas_softc *sassc; 1010991554f2SKenneth D. Merry 1011991554f2SKenneth D. Merry sassc = cam_sim_softc(sim); 1012991554f2SKenneth D. Merry 1013991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 1014a2c14879SStephen McConnell mpr_dprint(sassc->sc, MPR_TRACE, "ccb func_code 0x%x\n", 1015991554f2SKenneth D. Merry ccb->ccb_h.func_code); 1016991554f2SKenneth D. Merry mtx_assert(&sassc->sc->mpr_mtx, MA_OWNED); 1017991554f2SKenneth D. Merry 1018991554f2SKenneth D. Merry switch (ccb->ccb_h.func_code) { 1019991554f2SKenneth D. Merry case XPT_PATH_INQ: 1020991554f2SKenneth D. Merry { 1021991554f2SKenneth D. Merry struct ccb_pathinq *cpi = &ccb->cpi; 102232b0a21eSStephen McConnell struct mpr_softc *sc = sassc->sc; 1023991554f2SKenneth D. Merry 1024991554f2SKenneth D. Merry cpi->version_num = 1; 1025991554f2SKenneth D. Merry cpi->hba_inquiry = PI_SDTR_ABLE|PI_TAG_ABLE|PI_WIDE_16; 1026991554f2SKenneth D. Merry cpi->target_sprt = 0; 1027a371d6f9SKenneth D. Merry #if (__FreeBSD_version >= 1000039) || \ 1028a371d6f9SKenneth D. Merry ((__FreeBSD_version < 1000000) && (__FreeBSD_version >= 902502)) 1029991554f2SKenneth D. Merry cpi->hba_misc = PIM_NOBUSRESET | PIM_UNMAPPED | PIM_NOSCAN; 1030991554f2SKenneth D. Merry #else 1031991554f2SKenneth D. Merry cpi->hba_misc = PIM_NOBUSRESET | PIM_UNMAPPED; 1032991554f2SKenneth D. Merry #endif 1033991554f2SKenneth D. Merry cpi->hba_eng_cnt = 0; 1034991554f2SKenneth D. Merry cpi->max_target = sassc->maxtargets - 1; 1035991554f2SKenneth D. Merry cpi->max_lun = 255; 1036327f2e6cSStephen McConnell 1037327f2e6cSStephen McConnell /* 1038327f2e6cSStephen McConnell * initiator_id is set here to an ID outside the set of valid 1039327f2e6cSStephen McConnell * target IDs (including volumes). 1040327f2e6cSStephen McConnell */ 1041327f2e6cSStephen McConnell cpi->initiator_id = sassc->maxtargets; 10424195c7deSAlan Somers strlcpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN); 10434195c7deSAlan Somers strlcpy(cpi->hba_vid, "Avago Tech", HBA_IDLEN); 10444195c7deSAlan Somers strlcpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN); 1045991554f2SKenneth D. Merry cpi->unit_number = cam_sim_unit(sim); 1046991554f2SKenneth D. Merry cpi->bus_id = cam_sim_bus(sim); 1047991554f2SKenneth D. Merry /* 1048991554f2SKenneth D. Merry * XXXSLM-I think this needs to change based on config page or 1049991554f2SKenneth D. Merry * something instead of hardcoded to 150000. 1050991554f2SKenneth D. Merry */ 1051991554f2SKenneth D. Merry cpi->base_transfer_speed = 150000; 1052991554f2SKenneth D. Merry cpi->transport = XPORT_SAS; 1053991554f2SKenneth D. Merry cpi->transport_version = 0; 1054991554f2SKenneth D. Merry cpi->protocol = PROTO_SCSI; 1055991554f2SKenneth D. Merry cpi->protocol_version = SCSI_REV_SPC; 10564f5d6573SAlexander Motin cpi->maxio = sc->maxio; 1057a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1058991554f2SKenneth D. Merry break; 1059991554f2SKenneth D. Merry } 1060991554f2SKenneth D. Merry case XPT_GET_TRAN_SETTINGS: 1061991554f2SKenneth D. Merry { 1062991554f2SKenneth D. Merry struct ccb_trans_settings *cts; 1063991554f2SKenneth D. Merry struct ccb_trans_settings_sas *sas; 1064991554f2SKenneth D. Merry struct ccb_trans_settings_scsi *scsi; 1065991554f2SKenneth D. Merry struct mprsas_target *targ; 1066991554f2SKenneth D. Merry 1067991554f2SKenneth D. Merry cts = &ccb->cts; 1068991554f2SKenneth D. Merry sas = &cts->xport_specific.sas; 1069991554f2SKenneth D. Merry scsi = &cts->proto_specific.scsi; 1070991554f2SKenneth D. Merry 1071991554f2SKenneth D. Merry KASSERT(cts->ccb_h.target_id < sassc->maxtargets, 1072991554f2SKenneth D. Merry ("Target %d out of bounds in XPT_GET_TRAN_SETTINGS\n", 1073991554f2SKenneth D. Merry cts->ccb_h.target_id)); 1074991554f2SKenneth D. Merry targ = &sassc->targets[cts->ccb_h.target_id]; 1075991554f2SKenneth D. Merry if (targ->handle == 0x0) { 1076a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1077991554f2SKenneth D. Merry break; 1078991554f2SKenneth D. Merry } 1079991554f2SKenneth D. Merry 1080991554f2SKenneth D. Merry cts->protocol_version = SCSI_REV_SPC2; 1081991554f2SKenneth D. Merry cts->transport = XPORT_SAS; 1082991554f2SKenneth D. Merry cts->transport_version = 0; 1083991554f2SKenneth D. Merry 1084991554f2SKenneth D. Merry sas->valid = CTS_SAS_VALID_SPEED; 1085991554f2SKenneth D. Merry switch (targ->linkrate) { 1086991554f2SKenneth D. Merry case 0x08: 1087991554f2SKenneth D. Merry sas->bitrate = 150000; 1088991554f2SKenneth D. Merry break; 1089991554f2SKenneth D. Merry case 0x09: 1090991554f2SKenneth D. Merry sas->bitrate = 300000; 1091991554f2SKenneth D. Merry break; 1092991554f2SKenneth D. Merry case 0x0a: 1093991554f2SKenneth D. Merry sas->bitrate = 600000; 1094991554f2SKenneth D. Merry break; 109582315915SAlexander Motin case 0x0b: 109682315915SAlexander Motin sas->bitrate = 1200000; 109782315915SAlexander Motin break; 1098991554f2SKenneth D. Merry default: 1099991554f2SKenneth D. Merry sas->valid = 0; 1100991554f2SKenneth D. Merry } 1101991554f2SKenneth D. Merry 1102991554f2SKenneth D. Merry cts->protocol = PROTO_SCSI; 1103991554f2SKenneth D. Merry scsi->valid = CTS_SCSI_VALID_TQ; 1104991554f2SKenneth D. Merry scsi->flags = CTS_SCSI_FLAGS_TAG_ENB; 1105991554f2SKenneth D. Merry 1106a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1107991554f2SKenneth D. Merry break; 1108991554f2SKenneth D. Merry } 1109991554f2SKenneth D. Merry case XPT_CALC_GEOMETRY: 1110991554f2SKenneth D. Merry cam_calc_geometry(&ccb->ccg, /*extended*/1); 1111a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1112991554f2SKenneth D. Merry break; 1113991554f2SKenneth D. Merry case XPT_RESET_DEV: 11147a2a6a1aSStephen McConnell mpr_dprint(sassc->sc, MPR_XINFO, "mprsas_action " 11157a2a6a1aSStephen McConnell "XPT_RESET_DEV\n"); 1116991554f2SKenneth D. Merry mprsas_action_resetdev(sassc, ccb); 1117991554f2SKenneth D. Merry return; 1118991554f2SKenneth D. Merry case XPT_RESET_BUS: 1119991554f2SKenneth D. Merry case XPT_ABORT: 1120991554f2SKenneth D. Merry case XPT_TERM_IO: 11217a2a6a1aSStephen McConnell mpr_dprint(sassc->sc, MPR_XINFO, "mprsas_action faking success " 11227a2a6a1aSStephen McConnell "for abort or reset\n"); 1123a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1124991554f2SKenneth D. Merry break; 1125991554f2SKenneth D. Merry case XPT_SCSI_IO: 1126991554f2SKenneth D. Merry mprsas_action_scsiio(sassc, ccb); 1127991554f2SKenneth D. Merry return; 1128991554f2SKenneth D. Merry #if __FreeBSD_version >= 900026 1129991554f2SKenneth D. Merry case XPT_SMP_IO: 1130991554f2SKenneth D. Merry mprsas_action_smpio(sassc, ccb); 1131991554f2SKenneth D. Merry return; 1132991554f2SKenneth D. Merry #endif 1133991554f2SKenneth D. Merry default: 1134a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_FUNC_NOTAVAIL); 1135991554f2SKenneth D. Merry break; 1136991554f2SKenneth D. Merry } 1137991554f2SKenneth D. Merry xpt_done(ccb); 1138991554f2SKenneth D. Merry 1139991554f2SKenneth D. Merry } 1140991554f2SKenneth D. Merry 1141991554f2SKenneth D. Merry static void 1142991554f2SKenneth D. Merry mprsas_announce_reset(struct mpr_softc *sc, uint32_t ac_code, 1143991554f2SKenneth D. Merry target_id_t target_id, lun_id_t lun_id) 1144991554f2SKenneth D. Merry { 1145991554f2SKenneth D. Merry path_id_t path_id = cam_sim_path(sc->sassc->sim); 1146991554f2SKenneth D. Merry struct cam_path *path; 1147991554f2SKenneth D. Merry 1148991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s code %x target %d lun %jx\n", __func__, 1149991554f2SKenneth D. Merry ac_code, target_id, (uintmax_t)lun_id); 1150991554f2SKenneth D. Merry 1151991554f2SKenneth D. Merry if (xpt_create_path(&path, NULL, 1152991554f2SKenneth D. Merry path_id, target_id, lun_id) != CAM_REQ_CMP) { 1153991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "unable to create path for reset " 1154991554f2SKenneth D. Merry "notification\n"); 1155991554f2SKenneth D. Merry return; 1156991554f2SKenneth D. Merry } 1157991554f2SKenneth D. Merry 1158991554f2SKenneth D. Merry xpt_async(ac_code, path, NULL); 1159991554f2SKenneth D. Merry xpt_free_path(path); 1160991554f2SKenneth D. Merry } 1161991554f2SKenneth D. Merry 1162991554f2SKenneth D. Merry static void 1163991554f2SKenneth D. Merry mprsas_complete_all_commands(struct mpr_softc *sc) 1164991554f2SKenneth D. Merry { 1165991554f2SKenneth D. Merry struct mpr_command *cm; 1166991554f2SKenneth D. Merry int i; 1167991554f2SKenneth D. Merry int completed; 1168991554f2SKenneth D. Merry 1169991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 1170991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 1171991554f2SKenneth D. Merry 1172991554f2SKenneth D. Merry /* complete all commands with a NULL reply */ 1173991554f2SKenneth D. Merry for (i = 1; i < sc->num_reqs; i++) { 1174991554f2SKenneth D. Merry cm = &sc->commands[i]; 1175f0779b04SScott Long if (cm->cm_state == MPR_CM_STATE_FREE) 1176f0779b04SScott Long continue; 1177f0779b04SScott Long 1178f0779b04SScott Long cm->cm_state = MPR_CM_STATE_BUSY; 1179991554f2SKenneth D. Merry cm->cm_reply = NULL; 1180991554f2SKenneth D. Merry completed = 0; 1181991554f2SKenneth D. Merry 11828277ce2bSConrad Meyer if (cm->cm_flags & MPR_CM_FLAGS_SATA_ID_TIMEOUT) { 11838277ce2bSConrad Meyer MPASS(cm->cm_data); 11848277ce2bSConrad Meyer free(cm->cm_data, M_MPR); 11858277ce2bSConrad Meyer cm->cm_data = NULL; 11868277ce2bSConrad Meyer } 11878277ce2bSConrad Meyer 1188991554f2SKenneth D. Merry if (cm->cm_flags & MPR_CM_FLAGS_POLLED) 1189991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_COMPLETE; 1190991554f2SKenneth D. Merry 1191991554f2SKenneth D. Merry if (cm->cm_complete != NULL) { 1192991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 11937a2a6a1aSStephen McConnell "completing cm %p state %x ccb %p for diag reset\n", 11947a2a6a1aSStephen McConnell cm, cm->cm_state, cm->cm_ccb); 1195991554f2SKenneth D. Merry cm->cm_complete(sc, cm); 1196991554f2SKenneth D. Merry completed = 1; 1197f0779b04SScott Long } else if (cm->cm_flags & MPR_CM_FLAGS_WAKEUP) { 1198991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 1199991554f2SKenneth D. Merry "waking up cm %p state %x ccb %p for diag reset\n", 1200991554f2SKenneth D. Merry cm, cm->cm_state, cm->cm_ccb); 1201991554f2SKenneth D. Merry wakeup(cm); 1202991554f2SKenneth D. Merry completed = 1; 1203991554f2SKenneth D. Merry } 1204991554f2SKenneth D. Merry 1205991554f2SKenneth D. Merry if ((completed == 0) && (cm->cm_state != MPR_CM_STATE_FREE)) { 1206991554f2SKenneth D. Merry /* this should never happen, but if it does, log */ 1207991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 1208991554f2SKenneth D. Merry "cm %p state %x flags 0x%x ccb %p during diag " 1209991554f2SKenneth D. Merry "reset\n", cm, cm->cm_state, cm->cm_flags, 1210991554f2SKenneth D. Merry cm->cm_ccb); 1211991554f2SKenneth D. Merry } 1212991554f2SKenneth D. Merry } 1213f0779b04SScott Long 1214f0779b04SScott Long sc->io_cmds_active = 0; 1215991554f2SKenneth D. Merry } 1216991554f2SKenneth D. Merry 1217991554f2SKenneth D. Merry void 1218991554f2SKenneth D. Merry mprsas_handle_reinit(struct mpr_softc *sc) 1219991554f2SKenneth D. Merry { 1220991554f2SKenneth D. Merry int i; 1221991554f2SKenneth D. Merry 1222991554f2SKenneth D. Merry /* Go back into startup mode and freeze the simq, so that CAM 1223991554f2SKenneth D. Merry * doesn't send any commands until after we've rediscovered all 1224991554f2SKenneth D. Merry * targets and found the proper device handles for them. 1225991554f2SKenneth D. Merry * 1226991554f2SKenneth D. Merry * After the reset, portenable will trigger discovery, and after all 1227991554f2SKenneth D. Merry * discovery-related activities have finished, the simq will be 1228991554f2SKenneth D. Merry * released. 1229991554f2SKenneth D. Merry */ 1230991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INIT, "%s startup\n", __func__); 1231991554f2SKenneth D. Merry sc->sassc->flags |= MPRSAS_IN_STARTUP; 1232991554f2SKenneth D. Merry sc->sassc->flags |= MPRSAS_IN_DISCOVERY; 1233991554f2SKenneth D. Merry mprsas_startup_increment(sc->sassc); 1234991554f2SKenneth D. Merry 1235991554f2SKenneth D. Merry /* notify CAM of a bus reset */ 1236991554f2SKenneth D. Merry mprsas_announce_reset(sc, AC_BUS_RESET, CAM_TARGET_WILDCARD, 1237991554f2SKenneth D. Merry CAM_LUN_WILDCARD); 1238991554f2SKenneth D. Merry 1239991554f2SKenneth D. Merry /* complete and cleanup after all outstanding commands */ 1240991554f2SKenneth D. Merry mprsas_complete_all_commands(sc); 1241991554f2SKenneth D. Merry 1242a2c14879SStephen McConnell mpr_dprint(sc, MPR_INIT, "%s startup %u after command completion\n", 1243a2c14879SStephen McConnell __func__, sc->sassc->startup_refcount); 1244991554f2SKenneth D. Merry 1245991554f2SKenneth D. Merry /* zero all the target handles, since they may change after the 1246991554f2SKenneth D. Merry * reset, and we have to rediscover all the targets and use the new 1247991554f2SKenneth D. Merry * handles. 1248991554f2SKenneth D. Merry */ 1249991554f2SKenneth D. Merry for (i = 0; i < sc->sassc->maxtargets; i++) { 1250991554f2SKenneth D. Merry if (sc->sassc->targets[i].outstanding != 0) 1251991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INIT, "target %u outstanding %u\n", 1252991554f2SKenneth D. Merry i, sc->sassc->targets[i].outstanding); 1253991554f2SKenneth D. Merry sc->sassc->targets[i].handle = 0x0; 1254991554f2SKenneth D. Merry sc->sassc->targets[i].exp_dev_handle = 0x0; 1255991554f2SKenneth D. Merry sc->sassc->targets[i].outstanding = 0; 1256991554f2SKenneth D. Merry sc->sassc->targets[i].flags = MPRSAS_TARGET_INDIAGRESET; 1257991554f2SKenneth D. Merry } 1258991554f2SKenneth D. Merry } 1259991554f2SKenneth D. Merry static void 1260991554f2SKenneth D. Merry mprsas_tm_timeout(void *data) 1261991554f2SKenneth D. Merry { 1262991554f2SKenneth D. Merry struct mpr_command *tm = data; 1263991554f2SKenneth D. Merry struct mpr_softc *sc = tm->cm_sc; 1264991554f2SKenneth D. Merry 1265991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 1266991554f2SKenneth D. Merry 12677a2a6a1aSStephen McConnell mprsas_log_command(tm, MPR_INFO|MPR_RECOVERY, "task mgmt %p timed " 12687a2a6a1aSStephen McConnell "out\n", tm); 1269f0779b04SScott Long 1270f0779b04SScott Long KASSERT(tm->cm_state == MPR_CM_STATE_INQUEUE, 1271f0779b04SScott Long ("command not inqueue\n")); 1272f0779b04SScott Long 1273f0779b04SScott Long tm->cm_state = MPR_CM_STATE_BUSY; 1274991554f2SKenneth D. Merry mpr_reinit(sc); 1275991554f2SKenneth D. Merry } 1276991554f2SKenneth D. Merry 1277991554f2SKenneth D. Merry static void 12787a2a6a1aSStephen McConnell mprsas_logical_unit_reset_complete(struct mpr_softc *sc, struct mpr_command *tm) 1279991554f2SKenneth D. Merry { 1280991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 1281991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1282991554f2SKenneth D. Merry unsigned int cm_count = 0; 1283991554f2SKenneth D. Merry struct mpr_command *cm; 1284991554f2SKenneth D. Merry struct mprsas_target *targ; 1285991554f2SKenneth D. Merry 1286991554f2SKenneth D. Merry callout_stop(&tm->cm_callout); 1287991554f2SKenneth D. Merry 1288991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1289991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 1290991554f2SKenneth D. Merry targ = tm->cm_targ; 1291991554f2SKenneth D. Merry 1292991554f2SKenneth D. Merry /* 1293991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 1294991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 1295991554f2SKenneth D. Merry * task management commands don't have S/G lists. 1296991554f2SKenneth D. Merry */ 1297991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 12986eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_ERROR, 12996eea4f46SScott Long "%s: cm_flags = %#x for LUN reset! " 1300991554f2SKenneth D. Merry "This should not happen!\n", __func__, tm->cm_flags); 1301991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1302991554f2SKenneth D. Merry return; 1303991554f2SKenneth D. Merry } 1304991554f2SKenneth D. Merry 1305991554f2SKenneth D. Merry if (reply == NULL) { 13066eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "NULL reset reply for tm %p\n", 13076eea4f46SScott Long tm); 1308991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) { 1309991554f2SKenneth D. Merry /* this completion was due to a reset, just cleanup */ 13106eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing " 13116eea4f46SScott Long "reset, ignoring NULL LUN reset reply\n"); 1312991554f2SKenneth D. Merry targ->tm = NULL; 1313991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1314991554f2SKenneth D. Merry } 1315991554f2SKenneth D. Merry else { 1316991554f2SKenneth D. Merry /* we should have gotten a reply. */ 13176eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on " 13186eea4f46SScott Long "LUN reset attempt, resetting controller\n"); 1319991554f2SKenneth D. Merry mpr_reinit(sc); 1320991554f2SKenneth D. Merry } 1321991554f2SKenneth D. Merry return; 1322991554f2SKenneth D. Merry } 1323991554f2SKenneth D. Merry 13246eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 1325991554f2SKenneth D. Merry "logical unit reset status 0x%x code 0x%x count %u\n", 1326991554f2SKenneth D. Merry le16toh(reply->IOCStatus), le32toh(reply->ResponseCode), 1327991554f2SKenneth D. Merry le32toh(reply->TerminationCount)); 1328991554f2SKenneth D. Merry 13296eea4f46SScott Long /* 13306eea4f46SScott Long * See if there are any outstanding commands for this LUN. 1331991554f2SKenneth D. Merry * This could be made more efficient by using a per-LU data 1332991554f2SKenneth D. Merry * structure of some sort. 1333991554f2SKenneth D. Merry */ 1334991554f2SKenneth D. Merry TAILQ_FOREACH(cm, &targ->commands, cm_link) { 1335991554f2SKenneth D. Merry if (cm->cm_lun == tm->cm_lun) 1336991554f2SKenneth D. Merry cm_count++; 1337991554f2SKenneth D. Merry } 1338991554f2SKenneth D. Merry 1339991554f2SKenneth D. Merry if (cm_count == 0) { 13406eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 13416eea4f46SScott Long "Finished recovery after LUN reset for target %u\n", 13426eea4f46SScott Long targ->tid); 1343991554f2SKenneth D. Merry 13446eea4f46SScott Long mprsas_announce_reset(sc, AC_SENT_BDR, targ->tid, 1345991554f2SKenneth D. Merry tm->cm_lun); 1346991554f2SKenneth D. Merry 13476eea4f46SScott Long /* 13486eea4f46SScott Long * We've finished recovery for this logical unit. check and 1349991554f2SKenneth D. Merry * see if some other logical unit has a timedout command 1350991554f2SKenneth D. Merry * that needs to be processed. 1351991554f2SKenneth D. Merry */ 1352991554f2SKenneth D. Merry cm = TAILQ_FIRST(&targ->timedout_commands); 1353991554f2SKenneth D. Merry if (cm) { 13546eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 13556eea4f46SScott Long "More commands to abort for target %u\n", targ->tid); 1356991554f2SKenneth D. Merry mprsas_send_abort(sc, tm, cm); 13576eea4f46SScott Long } else { 1358991554f2SKenneth D. Merry targ->tm = NULL; 1359991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1360991554f2SKenneth D. Merry } 13616eea4f46SScott Long } else { 1362991554f2SKenneth D. Merry /* if we still have commands for this LUN, the reset 1363991554f2SKenneth D. Merry * effectively failed, regardless of the status reported. 1364991554f2SKenneth D. Merry * Escalate to a target reset. 1365991554f2SKenneth D. Merry */ 13666eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 13676eea4f46SScott Long "logical unit reset complete for target %u, but still " 13686eea4f46SScott Long "have %u command(s), sending target reset\n", targ->tid, 13696eea4f46SScott Long cm_count); 137089d1c21fSKashyap D Desai if (!targ->is_nvme || sc->custom_nvme_tm_handling) 1371991554f2SKenneth D. Merry mprsas_send_reset(sc, tm, 1372991554f2SKenneth D. Merry MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET); 137389d1c21fSKashyap D Desai else 137489d1c21fSKashyap D Desai mpr_reinit(sc); 1375991554f2SKenneth D. Merry } 1376991554f2SKenneth D. Merry } 1377991554f2SKenneth D. Merry 1378991554f2SKenneth D. Merry static void 1379991554f2SKenneth D. Merry mprsas_target_reset_complete(struct mpr_softc *sc, struct mpr_command *tm) 1380991554f2SKenneth D. Merry { 1381991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 1382991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1383991554f2SKenneth D. Merry struct mprsas_target *targ; 1384991554f2SKenneth D. Merry 1385991554f2SKenneth D. Merry callout_stop(&tm->cm_callout); 1386991554f2SKenneth D. Merry 1387991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1388991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 1389991554f2SKenneth D. Merry targ = tm->cm_targ; 1390991554f2SKenneth D. Merry 1391991554f2SKenneth D. Merry /* 1392991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 1393991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 1394991554f2SKenneth D. Merry * task management commands don't have S/G lists. 1395991554f2SKenneth D. Merry */ 1396991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 1397a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for target " 1398a2c14879SStephen McConnell "reset! This should not happen!\n", __func__, tm->cm_flags); 1399991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1400991554f2SKenneth D. Merry return; 1401991554f2SKenneth D. Merry } 1402991554f2SKenneth D. Merry 1403991554f2SKenneth D. Merry if (reply == NULL) { 14046eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 14056eea4f46SScott Long "NULL target reset reply for tm %p TaskMID %u\n", 14066eea4f46SScott Long tm, le16toh(req->TaskMID)); 1407991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) { 1408991554f2SKenneth D. Merry /* this completion was due to a reset, just cleanup */ 14096eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing " 14106eea4f46SScott Long "reset, ignoring NULL target reset reply\n"); 1411991554f2SKenneth D. Merry targ->tm = NULL; 1412991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1413991554f2SKenneth D. Merry } 1414991554f2SKenneth D. Merry else { 1415991554f2SKenneth D. Merry /* we should have gotten a reply. */ 14166eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on " 14176eea4f46SScott Long "target reset attempt, resetting controller\n"); 1418991554f2SKenneth D. Merry mpr_reinit(sc); 1419991554f2SKenneth D. Merry } 1420991554f2SKenneth D. Merry return; 1421991554f2SKenneth D. Merry } 1422991554f2SKenneth D. Merry 14236eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 1424991554f2SKenneth D. Merry "target reset status 0x%x code 0x%x count %u\n", 1425991554f2SKenneth D. Merry le16toh(reply->IOCStatus), le32toh(reply->ResponseCode), 1426991554f2SKenneth D. Merry le32toh(reply->TerminationCount)); 1427991554f2SKenneth D. Merry 1428991554f2SKenneth D. Merry if (targ->outstanding == 0) { 14296eea4f46SScott Long /* 14306eea4f46SScott Long * We've finished recovery for this target and all 1431991554f2SKenneth D. Merry * of its logical units. 1432991554f2SKenneth D. Merry */ 14336eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 14346eea4f46SScott Long "Finished reset recovery for target %u\n", targ->tid); 1435991554f2SKenneth D. Merry 1436991554f2SKenneth D. Merry mprsas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid, 1437991554f2SKenneth D. Merry CAM_LUN_WILDCARD); 1438991554f2SKenneth D. Merry 1439991554f2SKenneth D. Merry targ->tm = NULL; 1440991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 14416eea4f46SScott Long } else { 14426eea4f46SScott Long /* 14436eea4f46SScott Long * After a target reset, if this target still has 1444991554f2SKenneth D. Merry * outstanding commands, the reset effectively failed, 1445991554f2SKenneth D. Merry * regardless of the status reported. escalate. 1446991554f2SKenneth D. Merry */ 14476eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 14486eea4f46SScott Long "Target reset complete for target %u, but still have %u " 14496eea4f46SScott Long "command(s), resetting controller\n", targ->tid, 14506eea4f46SScott Long targ->outstanding); 1451991554f2SKenneth D. Merry mpr_reinit(sc); 1452991554f2SKenneth D. Merry } 1453991554f2SKenneth D. Merry } 1454991554f2SKenneth D. Merry 1455991554f2SKenneth D. Merry #define MPR_RESET_TIMEOUT 30 1456991554f2SKenneth D. Merry 1457a2c14879SStephen McConnell int 1458991554f2SKenneth D. Merry mprsas_send_reset(struct mpr_softc *sc, struct mpr_command *tm, uint8_t type) 1459991554f2SKenneth D. Merry { 1460991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1461991554f2SKenneth D. Merry struct mprsas_target *target; 146289d1c21fSKashyap D Desai int err, timeout; 1463991554f2SKenneth D. Merry 1464991554f2SKenneth D. Merry target = tm->cm_targ; 1465991554f2SKenneth D. Merry if (target->handle == 0) { 1466a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s null devhandle for target_id " 1467a2c14879SStephen McConnell "%d\n", __func__, target->tid); 1468991554f2SKenneth D. Merry return -1; 1469991554f2SKenneth D. Merry } 1470991554f2SKenneth D. Merry 1471991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1472991554f2SKenneth D. Merry req->DevHandle = htole16(target->handle); 1473991554f2SKenneth D. Merry req->TaskType = type; 1474991554f2SKenneth D. Merry 147589d1c21fSKashyap D Desai if (!target->is_nvme || sc->custom_nvme_tm_handling) { 147689d1c21fSKashyap D Desai timeout = MPR_RESET_TIMEOUT; 147789d1c21fSKashyap D Desai /* 147889d1c21fSKashyap D Desai * Target reset method = 147989d1c21fSKashyap D Desai * SAS Hard Link Reset / SATA Link Reset 148089d1c21fSKashyap D Desai */ 148189d1c21fSKashyap D Desai req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; 148289d1c21fSKashyap D Desai } else { 148389d1c21fSKashyap D Desai timeout = (target->controller_reset_timeout) ? ( 148489d1c21fSKashyap D Desai target->controller_reset_timeout) : (MPR_RESET_TIMEOUT); 148589d1c21fSKashyap D Desai /* PCIe Protocol Level Reset*/ 148689d1c21fSKashyap D Desai req->MsgFlags = 148789d1c21fSKashyap D Desai MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; 148889d1c21fSKashyap D Desai } 148989d1c21fSKashyap D Desai 1490991554f2SKenneth D. Merry if (type == MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET) { 1491991554f2SKenneth D. Merry /* XXX Need to handle invalid LUNs */ 1492991554f2SKenneth D. Merry MPR_SET_LUN(req->LUN, tm->cm_lun); 1493991554f2SKenneth D. Merry tm->cm_targ->logical_unit_resets++; 14946eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 14956eea4f46SScott Long "Sending logical unit reset to target %u lun %d\n", 14966eea4f46SScott Long target->tid, tm->cm_lun); 1497991554f2SKenneth D. Merry tm->cm_complete = mprsas_logical_unit_reset_complete; 1498a2c14879SStephen McConnell mprsas_prepare_for_tm(sc, tm, target, tm->cm_lun); 14996eea4f46SScott Long } else if (type == MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET) { 1500991554f2SKenneth D. Merry tm->cm_targ->target_resets++; 15016eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 15026eea4f46SScott Long "Sending target reset to target %u\n", target->tid); 1503991554f2SKenneth D. Merry tm->cm_complete = mprsas_target_reset_complete; 1504a2c14879SStephen McConnell mprsas_prepare_for_tm(sc, tm, target, CAM_LUN_WILDCARD); 1505991554f2SKenneth D. Merry } 1506991554f2SKenneth D. Merry else { 1507991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "unexpected reset type 0x%x\n", type); 1508991554f2SKenneth D. Merry return -1; 1509991554f2SKenneth D. Merry } 1510991554f2SKenneth D. Merry 1511991554f2SKenneth D. Merry if (target->encl_level_valid) { 15126eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 15136eea4f46SScott Long "At enclosure level %d, slot %d, connector name (%4s)\n", 15146eea4f46SScott Long target->encl_level, target->encl_slot, 15156eea4f46SScott Long target->connector_name); 1516991554f2SKenneth D. Merry } 1517991554f2SKenneth D. Merry 1518991554f2SKenneth D. Merry tm->cm_data = NULL; 1519991554f2SKenneth D. Merry tm->cm_complete_data = (void *)tm; 1520991554f2SKenneth D. Merry 152189d1c21fSKashyap D Desai callout_reset(&tm->cm_callout, timeout * hz, 1522991554f2SKenneth D. Merry mprsas_tm_timeout, tm); 1523991554f2SKenneth D. Merry 1524991554f2SKenneth D. Merry err = mpr_map_command(sc, tm); 1525991554f2SKenneth D. Merry if (err) 15266eea4f46SScott Long mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY, 1527a2c14879SStephen McConnell "error %d sending reset type %u\n", err, type); 1528991554f2SKenneth D. Merry 1529991554f2SKenneth D. Merry return err; 1530991554f2SKenneth D. Merry } 1531991554f2SKenneth D. Merry 1532991554f2SKenneth D. Merry 1533991554f2SKenneth D. Merry static void 1534991554f2SKenneth D. Merry mprsas_abort_complete(struct mpr_softc *sc, struct mpr_command *tm) 1535991554f2SKenneth D. Merry { 1536991554f2SKenneth D. Merry struct mpr_command *cm; 1537991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *reply; 1538991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1539991554f2SKenneth D. Merry struct mprsas_target *targ; 1540991554f2SKenneth D. Merry 1541991554f2SKenneth D. Merry callout_stop(&tm->cm_callout); 1542991554f2SKenneth D. Merry 1543991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1544991554f2SKenneth D. Merry reply = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 1545991554f2SKenneth D. Merry targ = tm->cm_targ; 1546991554f2SKenneth D. Merry 1547991554f2SKenneth D. Merry /* 1548991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 1549991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 1550991554f2SKenneth D. Merry * task management commands don't have S/G lists. 1551991554f2SKenneth D. Merry */ 1552991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 15536eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_ERROR, 1554991554f2SKenneth D. Merry "cm_flags = %#x for abort %p TaskMID %u!\n", 1555991554f2SKenneth D. Merry tm->cm_flags, tm, le16toh(req->TaskMID)); 1556991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 1557991554f2SKenneth D. Merry return; 1558991554f2SKenneth D. Merry } 1559991554f2SKenneth D. Merry 1560991554f2SKenneth D. Merry if (reply == NULL) { 15616eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 1562991554f2SKenneth D. Merry "NULL abort reply for tm %p TaskMID %u\n", 1563991554f2SKenneth D. Merry tm, le16toh(req->TaskMID)); 1564991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) { 1565991554f2SKenneth D. Merry /* this completion was due to a reset, just cleanup */ 15666eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "Hardware undergoing " 15676eea4f46SScott Long "reset, ignoring NULL abort reply\n"); 1568991554f2SKenneth D. Merry targ->tm = NULL; 1569991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 15706eea4f46SScott Long } else { 1571991554f2SKenneth D. Merry /* we should have gotten a reply. */ 15726eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, "NULL reply on " 15736eea4f46SScott Long "abort attempt, resetting controller\n"); 1574991554f2SKenneth D. Merry mpr_reinit(sc); 1575991554f2SKenneth D. Merry } 1576991554f2SKenneth D. Merry return; 1577991554f2SKenneth D. Merry } 1578991554f2SKenneth D. Merry 15796eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, 1580991554f2SKenneth D. Merry "abort TaskMID %u status 0x%x code 0x%x count %u\n", 1581991554f2SKenneth D. Merry le16toh(req->TaskMID), 1582991554f2SKenneth D. Merry le16toh(reply->IOCStatus), le32toh(reply->ResponseCode), 1583991554f2SKenneth D. Merry le32toh(reply->TerminationCount)); 1584991554f2SKenneth D. Merry 1585991554f2SKenneth D. Merry cm = TAILQ_FIRST(&tm->cm_targ->timedout_commands); 1586991554f2SKenneth D. Merry if (cm == NULL) { 15876eea4f46SScott Long /* 15886eea4f46SScott Long * if there are no more timedout commands, we're done with 1589991554f2SKenneth D. Merry * error recovery for this target. 1590991554f2SKenneth D. Merry */ 15916eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 15926eea4f46SScott Long "Finished abort recovery for target %u\n", targ->tid); 1593991554f2SKenneth D. Merry targ->tm = NULL; 1594991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 15956eea4f46SScott Long } else if (le16toh(req->TaskMID) != cm->cm_desc.Default.SMID) { 1596991554f2SKenneth D. Merry /* abort success, but we have more timedout commands to abort */ 15976eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 15986eea4f46SScott Long "Continuing abort recovery for target %u\n", targ->tid); 1599991554f2SKenneth D. Merry mprsas_send_abort(sc, tm, cm); 16006eea4f46SScott Long } else { 16016eea4f46SScott Long /* 16026eea4f46SScott Long * we didn't get a command completion, so the abort 1603991554f2SKenneth D. Merry * failed as far as we're concerned. escalate. 1604991554f2SKenneth D. Merry */ 16056eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 16066eea4f46SScott Long "Abort failed for target %u, sending logical unit reset\n", 16076eea4f46SScott Long targ->tid); 1608991554f2SKenneth D. Merry 1609991554f2SKenneth D. Merry mprsas_send_reset(sc, tm, 1610991554f2SKenneth D. Merry MPI2_SCSITASKMGMT_TASKTYPE_LOGICAL_UNIT_RESET); 1611991554f2SKenneth D. Merry } 1612991554f2SKenneth D. Merry } 1613991554f2SKenneth D. Merry 1614991554f2SKenneth D. Merry #define MPR_ABORT_TIMEOUT 5 1615991554f2SKenneth D. Merry 1616991554f2SKenneth D. Merry static int 1617991554f2SKenneth D. Merry mprsas_send_abort(struct mpr_softc *sc, struct mpr_command *tm, 1618991554f2SKenneth D. Merry struct mpr_command *cm) 1619991554f2SKenneth D. Merry { 1620991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 1621991554f2SKenneth D. Merry struct mprsas_target *targ; 162289d1c21fSKashyap D Desai int err, timeout; 1623991554f2SKenneth D. Merry 1624991554f2SKenneth D. Merry targ = cm->cm_targ; 1625991554f2SKenneth D. Merry if (targ->handle == 0) { 16266eea4f46SScott Long mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY, 16276eea4f46SScott Long "%s null devhandle for target_id %d\n", 1628991554f2SKenneth D. Merry __func__, cm->cm_ccb->ccb_h.target_id); 1629991554f2SKenneth D. Merry return -1; 1630991554f2SKenneth D. Merry } 1631991554f2SKenneth D. Merry 1632855fe445SScott Long mprsas_log_command(cm, MPR_RECOVERY|MPR_INFO, 1633991554f2SKenneth D. Merry "Aborting command %p\n", cm); 1634991554f2SKenneth D. Merry 1635991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 1636991554f2SKenneth D. Merry req->DevHandle = htole16(targ->handle); 1637991554f2SKenneth D. Merry req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK; 1638991554f2SKenneth D. Merry 1639991554f2SKenneth D. Merry /* XXX Need to handle invalid LUNs */ 1640991554f2SKenneth D. Merry MPR_SET_LUN(req->LUN, cm->cm_ccb->ccb_h.target_lun); 1641991554f2SKenneth D. Merry 1642991554f2SKenneth D. Merry req->TaskMID = htole16(cm->cm_desc.Default.SMID); 1643991554f2SKenneth D. Merry 1644991554f2SKenneth D. Merry tm->cm_data = NULL; 1645991554f2SKenneth D. Merry tm->cm_complete = mprsas_abort_complete; 1646991554f2SKenneth D. Merry tm->cm_complete_data = (void *)tm; 1647991554f2SKenneth D. Merry tm->cm_targ = cm->cm_targ; 1648991554f2SKenneth D. Merry tm->cm_lun = cm->cm_lun; 1649991554f2SKenneth D. Merry 165089d1c21fSKashyap D Desai if (!targ->is_nvme || sc->custom_nvme_tm_handling) 165189d1c21fSKashyap D Desai timeout = MPR_ABORT_TIMEOUT; 165289d1c21fSKashyap D Desai else 165389d1c21fSKashyap D Desai timeout = sc->nvme_abort_timeout; 165489d1c21fSKashyap D Desai 165589d1c21fSKashyap D Desai callout_reset(&tm->cm_callout, timeout * hz, 1656991554f2SKenneth D. Merry mprsas_tm_timeout, tm); 1657991554f2SKenneth D. Merry 1658991554f2SKenneth D. Merry targ->aborts++; 1659991554f2SKenneth D. Merry 1660a2c14879SStephen McConnell mprsas_prepare_for_tm(sc, tm, targ, tm->cm_lun); 1661a2c14879SStephen McConnell 1662991554f2SKenneth D. Merry err = mpr_map_command(sc, tm); 1663991554f2SKenneth D. Merry if (err) 16646eea4f46SScott Long mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY, 1665991554f2SKenneth D. Merry "error %d sending abort for cm %p SMID %u\n", 1666991554f2SKenneth D. Merry err, cm, req->TaskMID); 1667991554f2SKenneth D. Merry return err; 1668991554f2SKenneth D. Merry } 1669991554f2SKenneth D. Merry 1670991554f2SKenneth D. Merry static void 1671991554f2SKenneth D. Merry mprsas_scsiio_timeout(void *data) 1672991554f2SKenneth D. Merry { 16736eea4f46SScott Long sbintime_t elapsed, now; 16746eea4f46SScott Long union ccb *ccb; 1675991554f2SKenneth D. Merry struct mpr_softc *sc; 1676991554f2SKenneth D. Merry struct mpr_command *cm; 1677991554f2SKenneth D. Merry struct mprsas_target *targ; 1678991554f2SKenneth D. Merry 1679991554f2SKenneth D. Merry cm = (struct mpr_command *)data; 1680991554f2SKenneth D. Merry sc = cm->cm_sc; 16816eea4f46SScott Long ccb = cm->cm_ccb; 16826eea4f46SScott Long now = sbinuptime(); 1683991554f2SKenneth D. Merry 1684991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 1685991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 1686991554f2SKenneth D. Merry 16876eea4f46SScott Long mpr_dprint(sc, MPR_XINFO|MPR_RECOVERY, "Timeout checking cm %p\n", cm); 1688991554f2SKenneth D. Merry 1689991554f2SKenneth D. Merry /* 1690991554f2SKenneth D. Merry * Run the interrupt handler to make sure it's not pending. This 1691991554f2SKenneth D. Merry * isn't perfect because the command could have already completed 1692991554f2SKenneth D. Merry * and been re-used, though this is unlikely. 1693991554f2SKenneth D. Merry */ 1694991554f2SKenneth D. Merry mpr_intr_locked(sc); 1695f0779b04SScott Long if (cm->cm_state != MPR_CM_STATE_INQUEUE) { 1696991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, 1697991554f2SKenneth D. Merry "SCSI command %p almost timed out\n", cm); 1698991554f2SKenneth D. Merry return; 1699991554f2SKenneth D. Merry } 1700991554f2SKenneth D. Merry 1701991554f2SKenneth D. Merry if (cm->cm_ccb == NULL) { 1702991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "command timeout with NULL ccb\n"); 1703991554f2SKenneth D. Merry return; 1704991554f2SKenneth D. Merry } 1705991554f2SKenneth D. Merry 1706991554f2SKenneth D. Merry targ = cm->cm_targ; 1707991554f2SKenneth D. Merry targ->timeouts++; 1708991554f2SKenneth D. Merry 17096eea4f46SScott Long elapsed = now - ccb->ccb_h.qos.sim_data; 17106eea4f46SScott Long mprsas_log_command(cm, MPR_INFO|MPR_RECOVERY, 17116eea4f46SScott Long "Command timeout on target %u(0x%04x), %d set, %d.%d elapsed\n", 17126eea4f46SScott Long targ->tid, targ->handle, ccb->ccb_h.timeout, 17136eea4f46SScott Long sbintime_getsec(elapsed), elapsed & 0xffffffff); 1714991554f2SKenneth D. Merry if (targ->encl_level_valid) { 17156eea4f46SScott Long mpr_dprint(sc, MPR_INFO|MPR_RECOVERY, 17166eea4f46SScott Long "At enclosure level %d, slot %d, connector name (%4s)\n", 17176eea4f46SScott Long targ->encl_level, targ->encl_slot, targ->connector_name); 1718991554f2SKenneth D. Merry } 1719991554f2SKenneth D. Merry 1720991554f2SKenneth D. Merry /* XXX first, check the firmware state, to see if it's still 1721991554f2SKenneth D. Merry * operational. if not, do a diag reset. 1722991554f2SKenneth D. Merry */ 1723a2c14879SStephen McConnell mprsas_set_ccbstatus(cm->cm_ccb, CAM_CMD_TIMEOUT); 1724991554f2SKenneth D. Merry cm->cm_state = MPR_CM_STATE_TIMEDOUT; 1725991554f2SKenneth D. Merry TAILQ_INSERT_TAIL(&targ->timedout_commands, cm, cm_recovery); 1726991554f2SKenneth D. Merry 1727991554f2SKenneth D. Merry if (targ->tm != NULL) { 1728991554f2SKenneth D. Merry /* target already in recovery, just queue up another 1729991554f2SKenneth D. Merry * timedout command to be processed later. 1730991554f2SKenneth D. Merry */ 1731991554f2SKenneth D. Merry mpr_dprint(sc, MPR_RECOVERY, "queued timedout cm %p for " 1732991554f2SKenneth D. Merry "processing by tm %p\n", cm, targ->tm); 1733991554f2SKenneth D. Merry } 1734991554f2SKenneth D. Merry else if ((targ->tm = mprsas_alloc_tm(sc)) != NULL) { 1735991554f2SKenneth D. Merry 1736991554f2SKenneth D. Merry /* start recovery by aborting the first timedout command */ 17376eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY|MPR_INFO, 17386eea4f46SScott Long "Sending abort to target %u for SMID %d\n", targ->tid, 17396eea4f46SScott Long cm->cm_desc.Default.SMID); 17406eea4f46SScott Long mpr_dprint(sc, MPR_RECOVERY, "timedout cm %p allocated tm %p\n", 17416eea4f46SScott Long cm, targ->tm); 1742991554f2SKenneth D. Merry mprsas_send_abort(sc, targ->tm, cm); 1743991554f2SKenneth D. Merry } 1744991554f2SKenneth D. Merry else { 1745991554f2SKenneth D. Merry /* XXX queue this target up for recovery once a TM becomes 1746991554f2SKenneth D. Merry * available. The firmware only has a limited number of 1747991554f2SKenneth D. Merry * HighPriority credits for the high priority requests used 1748991554f2SKenneth D. Merry * for task management, and we ran out. 1749991554f2SKenneth D. Merry * 1750991554f2SKenneth D. Merry * Isilon: don't worry about this for now, since we have 1751991554f2SKenneth D. Merry * more credits than disks in an enclosure, and limit 1752991554f2SKenneth D. Merry * ourselves to one TM per target for recovery. 1753991554f2SKenneth D. Merry */ 17546eea4f46SScott Long mpr_dprint(sc, MPR_ERROR|MPR_RECOVERY, 17556eea4f46SScott Long "timedout cm %p failed to allocate a tm\n", cm); 1756991554f2SKenneth D. Merry } 1757991554f2SKenneth D. Merry } 1758991554f2SKenneth D. Merry 175967feec50SStephen McConnell /** 176067feec50SStephen McConnell * mprsas_build_nvme_unmap - Build Native NVMe DSM command equivalent 176167feec50SStephen McConnell * to SCSI Unmap. 176267feec50SStephen McConnell * Return 0 - for success, 176367feec50SStephen McConnell * 1 - to immediately return back the command with success status to CAM 176467feec50SStephen McConnell * negative value - to fallback to firmware path i.e. issue scsi unmap 176567feec50SStephen McConnell * to FW without any translation. 176667feec50SStephen McConnell */ 176767feec50SStephen McConnell static int 176867feec50SStephen McConnell mprsas_build_nvme_unmap(struct mpr_softc *sc, struct mpr_command *cm, 176967feec50SStephen McConnell union ccb *ccb, struct mprsas_target *targ) 177067feec50SStephen McConnell { 177167feec50SStephen McConnell Mpi26NVMeEncapsulatedRequest_t *req = NULL; 177267feec50SStephen McConnell struct ccb_scsiio *csio; 177367feec50SStephen McConnell struct unmap_parm_list *plist; 177467feec50SStephen McConnell struct nvme_dsm_range *nvme_dsm_ranges = NULL; 177567feec50SStephen McConnell struct nvme_command *c; 177667feec50SStephen McConnell int i, res; 177767feec50SStephen McConnell uint16_t ndesc, list_len, data_length; 177867feec50SStephen McConnell struct mpr_prp_page *prp_page_info; 177967feec50SStephen McConnell uint64_t nvme_dsm_ranges_dma_handle; 178067feec50SStephen McConnell 178167feec50SStephen McConnell csio = &ccb->csio; 178267feec50SStephen McConnell #if __FreeBSD_version >= 1100103 178367feec50SStephen McConnell list_len = (scsiio_cdb_ptr(csio)[7] << 8 | scsiio_cdb_ptr(csio)[8]); 178467feec50SStephen McConnell #else 178567feec50SStephen McConnell if (csio->ccb_h.flags & CAM_CDB_POINTER) { 178667feec50SStephen McConnell list_len = (ccb->csio.cdb_io.cdb_ptr[7] << 8 | 178767feec50SStephen McConnell ccb->csio.cdb_io.cdb_ptr[8]); 178867feec50SStephen McConnell } else { 178967feec50SStephen McConnell list_len = (ccb->csio.cdb_io.cdb_bytes[7] << 8 | 179067feec50SStephen McConnell ccb->csio.cdb_io.cdb_bytes[8]); 179167feec50SStephen McConnell } 179267feec50SStephen McConnell #endif 179367feec50SStephen McConnell if (!list_len) { 179467feec50SStephen McConnell mpr_dprint(sc, MPR_ERROR, "Parameter list length is Zero\n"); 179567feec50SStephen McConnell return -EINVAL; 179667feec50SStephen McConnell } 179767feec50SStephen McConnell 179867feec50SStephen McConnell plist = malloc(csio->dxfer_len, M_MPR, M_ZERO|M_NOWAIT); 179967feec50SStephen McConnell if (!plist) { 180067feec50SStephen McConnell mpr_dprint(sc, MPR_ERROR, "Unable to allocate memory to " 180167feec50SStephen McConnell "save UNMAP data\n"); 180267feec50SStephen McConnell return -ENOMEM; 180367feec50SStephen McConnell } 180467feec50SStephen McConnell 180567feec50SStephen McConnell /* Copy SCSI unmap data to a local buffer */ 180667feec50SStephen McConnell bcopy(csio->data_ptr, plist, csio->dxfer_len); 180767feec50SStephen McConnell 180867feec50SStephen McConnell /* return back the unmap command to CAM with success status, 180967feec50SStephen McConnell * if number of descripts is zero. 181067feec50SStephen McConnell */ 181167feec50SStephen McConnell ndesc = be16toh(plist->unmap_blk_desc_data_len) >> 4; 181267feec50SStephen McConnell if (!ndesc) { 181367feec50SStephen McConnell mpr_dprint(sc, MPR_XINFO, "Number of descriptors in " 181467feec50SStephen McConnell "UNMAP cmd is Zero\n"); 181567feec50SStephen McConnell res = 1; 181667feec50SStephen McConnell goto out; 181767feec50SStephen McConnell } 181867feec50SStephen McConnell 181967feec50SStephen McConnell data_length = ndesc * sizeof(struct nvme_dsm_range); 182067feec50SStephen McConnell if (data_length > targ->MDTS) { 182167feec50SStephen McConnell mpr_dprint(sc, MPR_ERROR, "data length: %d is greater than " 182267feec50SStephen McConnell "Device's MDTS: %d\n", data_length, targ->MDTS); 182367feec50SStephen McConnell res = -EINVAL; 182467feec50SStephen McConnell goto out; 182567feec50SStephen McConnell } 182667feec50SStephen McConnell 182767feec50SStephen McConnell prp_page_info = mpr_alloc_prp_page(sc); 182867feec50SStephen McConnell KASSERT(prp_page_info != NULL, ("%s: There is no PRP Page for " 182967feec50SStephen McConnell "UNMAP command.\n", __func__)); 183067feec50SStephen McConnell 183167feec50SStephen McConnell /* 183267feec50SStephen McConnell * Insert the allocated PRP page into the command's PRP page list. This 183367feec50SStephen McConnell * will be freed when the command is freed. 183467feec50SStephen McConnell */ 183567feec50SStephen McConnell TAILQ_INSERT_TAIL(&cm->cm_prp_page_list, prp_page_info, prp_page_link); 183667feec50SStephen McConnell 183767feec50SStephen McConnell nvme_dsm_ranges = (struct nvme_dsm_range *)prp_page_info->prp_page; 183867feec50SStephen McConnell nvme_dsm_ranges_dma_handle = prp_page_info->prp_page_busaddr; 183967feec50SStephen McConnell 184067feec50SStephen McConnell bzero(nvme_dsm_ranges, data_length); 184167feec50SStephen McConnell 184267feec50SStephen McConnell /* Convert SCSI unmap's descriptor data to NVMe DSM specific Range data 184367feec50SStephen McConnell * for each descriptors contained in SCSI UNMAP data. 184467feec50SStephen McConnell */ 184567feec50SStephen McConnell for (i = 0; i < ndesc; i++) { 184667feec50SStephen McConnell nvme_dsm_ranges[i].length = 184767feec50SStephen McConnell htole32(be32toh(plist->desc[i].nlb)); 184867feec50SStephen McConnell nvme_dsm_ranges[i].starting_lba = 184967feec50SStephen McConnell htole64(be64toh(plist->desc[i].slba)); 185067feec50SStephen McConnell nvme_dsm_ranges[i].attributes = 0; 185167feec50SStephen McConnell } 185267feec50SStephen McConnell 185367feec50SStephen McConnell /* Build MPI2.6's NVMe Encapsulated Request Message */ 185467feec50SStephen McConnell req = (Mpi26NVMeEncapsulatedRequest_t *)cm->cm_req; 185567feec50SStephen McConnell bzero(req, sizeof(*req)); 185667feec50SStephen McConnell req->DevHandle = htole16(targ->handle); 185767feec50SStephen McConnell req->Function = MPI2_FUNCTION_NVME_ENCAPSULATED; 185867feec50SStephen McConnell req->Flags = MPI26_NVME_FLAGS_WRITE; 185967feec50SStephen McConnell req->ErrorResponseBaseAddress.High = 186067feec50SStephen McConnell htole32((uint32_t)((uint64_t)cm->cm_sense_busaddr >> 32)); 186167feec50SStephen McConnell req->ErrorResponseBaseAddress.Low = 186267feec50SStephen McConnell htole32(cm->cm_sense_busaddr); 186367feec50SStephen McConnell req->ErrorResponseAllocationLength = 186467feec50SStephen McConnell htole16(sizeof(struct nvme_completion)); 186567feec50SStephen McConnell req->EncapsulatedCommandLength = 186667feec50SStephen McConnell htole16(sizeof(struct nvme_command)); 186767feec50SStephen McConnell req->DataLength = htole32(data_length); 186867feec50SStephen McConnell 186967feec50SStephen McConnell /* Build NVMe DSM command */ 187067feec50SStephen McConnell c = (struct nvme_command *) req->NVMe_Command; 18719544e6dcSChuck Tuffli c->opc = NVME_OPC_DATASET_MANAGEMENT; 187267feec50SStephen McConnell c->nsid = htole32(csio->ccb_h.target_lun + 1); 187367feec50SStephen McConnell c->cdw10 = htole32(ndesc - 1); 187467feec50SStephen McConnell c->cdw11 = htole32(NVME_DSM_ATTR_DEALLOCATE); 187567feec50SStephen McConnell 187667feec50SStephen McConnell cm->cm_length = data_length; 187767feec50SStephen McConnell cm->cm_data = NULL; 187867feec50SStephen McConnell 187967feec50SStephen McConnell cm->cm_complete = mprsas_scsiio_complete; 188067feec50SStephen McConnell cm->cm_complete_data = ccb; 188167feec50SStephen McConnell cm->cm_targ = targ; 188267feec50SStephen McConnell cm->cm_lun = csio->ccb_h.target_lun; 188367feec50SStephen McConnell cm->cm_ccb = ccb; 188467feec50SStephen McConnell 188567feec50SStephen McConnell cm->cm_desc.Default.RequestFlags = 188667feec50SStephen McConnell MPI26_REQ_DESCRIPT_FLAGS_PCIE_ENCAPSULATED; 188767feec50SStephen McConnell 18886eea4f46SScott Long csio->ccb_h.qos.sim_data = sbinuptime(); 188967feec50SStephen McConnell #if __FreeBSD_version >= 1000029 189067feec50SStephen McConnell callout_reset_sbt(&cm->cm_callout, SBT_1MS * ccb->ccb_h.timeout, 0, 189167feec50SStephen McConnell mprsas_scsiio_timeout, cm, 0); 189267feec50SStephen McConnell #else //__FreeBSD_version < 1000029 189367feec50SStephen McConnell callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000, 189467feec50SStephen McConnell mprsas_scsiio_timeout, cm); 189567feec50SStephen McConnell #endif //__FreeBSD_version >= 1000029 189667feec50SStephen McConnell 189767feec50SStephen McConnell targ->issued++; 189867feec50SStephen McConnell targ->outstanding++; 189967feec50SStephen McConnell TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link); 190067feec50SStephen McConnell ccb->ccb_h.status |= CAM_SIM_QUEUED; 190167feec50SStephen McConnell 190267feec50SStephen McConnell mprsas_log_command(cm, MPR_XINFO, "%s cm %p ccb %p outstanding %u\n", 190367feec50SStephen McConnell __func__, cm, ccb, targ->outstanding); 190467feec50SStephen McConnell 190518982e8fSStephen McConnell mpr_build_nvme_prp(sc, cm, req, 190618982e8fSStephen McConnell (void *)(uintptr_t)nvme_dsm_ranges_dma_handle, 0, data_length); 190767feec50SStephen McConnell mpr_map_command(sc, cm); 190867feec50SStephen McConnell 190967feec50SStephen McConnell out: 191067feec50SStephen McConnell free(plist, M_MPR); 191167feec50SStephen McConnell return 0; 191267feec50SStephen McConnell } 191367feec50SStephen McConnell 1914991554f2SKenneth D. Merry static void 1915991554f2SKenneth D. Merry mprsas_action_scsiio(struct mprsas_softc *sassc, union ccb *ccb) 1916991554f2SKenneth D. Merry { 1917991554f2SKenneth D. Merry MPI2_SCSI_IO_REQUEST *req; 1918991554f2SKenneth D. Merry struct ccb_scsiio *csio; 1919991554f2SKenneth D. Merry struct mpr_softc *sc; 1920991554f2SKenneth D. Merry struct mprsas_target *targ; 1921991554f2SKenneth D. Merry struct mprsas_lun *lun; 1922991554f2SKenneth D. Merry struct mpr_command *cm; 192367feec50SStephen McConnell uint8_t i, lba_byte, *ref_tag_addr, scsi_opcode; 1924991554f2SKenneth D. Merry uint16_t eedp_flags; 1925991554f2SKenneth D. Merry uint32_t mpi_control; 192667feec50SStephen McConnell int rc; 1927991554f2SKenneth D. Merry 1928991554f2SKenneth D. Merry sc = sassc->sc; 1929991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 1930991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 1931991554f2SKenneth D. Merry 1932991554f2SKenneth D. Merry csio = &ccb->csio; 1933a2c14879SStephen McConnell KASSERT(csio->ccb_h.target_id < sassc->maxtargets, 1934a2c14879SStephen McConnell ("Target %d out of bounds in XPT_SCSI_IO\n", 1935a2c14879SStephen McConnell csio->ccb_h.target_id)); 1936991554f2SKenneth D. Merry targ = &sassc->targets[csio->ccb_h.target_id]; 1937991554f2SKenneth D. Merry mpr_dprint(sc, MPR_TRACE, "ccb %p target flag %x\n", ccb, targ->flags); 1938991554f2SKenneth D. Merry if (targ->handle == 0x0) { 1939991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s NULL handle for target %u\n", 1940991554f2SKenneth D. Merry __func__, csio->ccb_h.target_id); 1941a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1942991554f2SKenneth D. Merry xpt_done(ccb); 1943991554f2SKenneth D. Merry return; 1944991554f2SKenneth D. Merry } 1945991554f2SKenneth D. Merry if (targ->flags & MPR_TARGET_FLAGS_RAID_COMPONENT) { 1946a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s Raid component no SCSI IO " 1947991554f2SKenneth D. Merry "supported %u\n", __func__, csio->ccb_h.target_id); 1948a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1949991554f2SKenneth D. Merry xpt_done(ccb); 1950991554f2SKenneth D. Merry return; 1951991554f2SKenneth D. Merry } 1952991554f2SKenneth D. Merry /* 1953991554f2SKenneth D. Merry * Sometimes, it is possible to get a command that is not "In 1954991554f2SKenneth D. Merry * Progress" and was actually aborted by the upper layer. Check for 1955991554f2SKenneth D. Merry * this here and complete the command without error. 1956991554f2SKenneth D. Merry */ 1957a2c14879SStephen McConnell if (mprsas_get_ccbstatus(ccb) != CAM_REQ_INPROG) { 1958991554f2SKenneth D. Merry mpr_dprint(sc, MPR_TRACE, "%s Command is not in progress for " 1959991554f2SKenneth D. Merry "target %u\n", __func__, csio->ccb_h.target_id); 1960991554f2SKenneth D. Merry xpt_done(ccb); 1961991554f2SKenneth D. Merry return; 1962991554f2SKenneth D. Merry } 1963991554f2SKenneth D. Merry /* 1964991554f2SKenneth D. Merry * If devinfo is 0 this will be a volume. In that case don't tell CAM 1965991554f2SKenneth D. Merry * that the volume has timed out. We want volumes to be enumerated 1966991554f2SKenneth D. Merry * until they are deleted/removed, not just failed. 1967991554f2SKenneth D. Merry */ 1968991554f2SKenneth D. Merry if (targ->flags & MPRSAS_TARGET_INREMOVAL) { 1969991554f2SKenneth D. Merry if (targ->devinfo == 0) 1970a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 1971991554f2SKenneth D. Merry else 1972a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SEL_TIMEOUT); 1973991554f2SKenneth D. Merry xpt_done(ccb); 1974991554f2SKenneth D. Merry return; 1975991554f2SKenneth D. Merry } 1976991554f2SKenneth D. Merry 1977991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_SHUTDOWN) != 0) { 1978a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s shutting down\n", __func__); 1979a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 1980a2c14879SStephen McConnell xpt_done(ccb); 1981a2c14879SStephen McConnell return; 1982a2c14879SStephen McConnell } 1983a2c14879SStephen McConnell 1984a2c14879SStephen McConnell /* 1985a2c14879SStephen McConnell * If target has a reset in progress, freeze the devq and return. The 1986a2c14879SStephen McConnell * devq will be released when the TM reset is finished. 1987a2c14879SStephen McConnell */ 1988a2c14879SStephen McConnell if (targ->flags & MPRSAS_TARGET_INRESET) { 1989a2c14879SStephen McConnell ccb->ccb_h.status = CAM_BUSY | CAM_DEV_QFRZN; 1990a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s: Freezing devq for target ID %d\n", 1991a2c14879SStephen McConnell __func__, targ->tid); 1992a2c14879SStephen McConnell xpt_freeze_devq(ccb->ccb_h.path, 1); 1993991554f2SKenneth D. Merry xpt_done(ccb); 1994991554f2SKenneth D. Merry return; 1995991554f2SKenneth D. Merry } 1996991554f2SKenneth D. Merry 1997991554f2SKenneth D. Merry cm = mpr_alloc_command(sc); 1998991554f2SKenneth D. Merry if (cm == NULL || (sc->mpr_flags & MPR_FLAGS_DIAGRESET)) { 1999991554f2SKenneth D. Merry if (cm != NULL) { 2000991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2001991554f2SKenneth D. Merry } 2002991554f2SKenneth D. Merry if ((sassc->flags & MPRSAS_QUEUE_FROZEN) == 0) { 2003991554f2SKenneth D. Merry xpt_freeze_simq(sassc->sim, 1); 2004991554f2SKenneth D. Merry sassc->flags |= MPRSAS_QUEUE_FROZEN; 2005991554f2SKenneth D. Merry } 2006991554f2SKenneth D. Merry ccb->ccb_h.status &= ~CAM_SIM_QUEUED; 2007991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_REQUEUE_REQ; 2008991554f2SKenneth D. Merry xpt_done(ccb); 2009991554f2SKenneth D. Merry return; 2010991554f2SKenneth D. Merry } 2011991554f2SKenneth D. Merry 201267feec50SStephen McConnell /* For NVME device's issue UNMAP command directly to NVME drives by 201367feec50SStephen McConnell * constructing equivalent native NVMe DataSetManagement command. 201467feec50SStephen McConnell */ 201567feec50SStephen McConnell #if __FreeBSD_version >= 1100103 201667feec50SStephen McConnell scsi_opcode = scsiio_cdb_ptr(csio)[0]; 201767feec50SStephen McConnell #else 201867feec50SStephen McConnell if (csio->ccb_h.flags & CAM_CDB_POINTER) 201967feec50SStephen McConnell scsi_opcode = csio->cdb_io.cdb_ptr[0]; 202067feec50SStephen McConnell else 202167feec50SStephen McConnell scsi_opcode = csio->cdb_io.cdb_bytes[0]; 202267feec50SStephen McConnell #endif 202367feec50SStephen McConnell if (scsi_opcode == UNMAP && 202467feec50SStephen McConnell targ->is_nvme && 202567feec50SStephen McConnell (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) { 202667feec50SStephen McConnell rc = mprsas_build_nvme_unmap(sc, cm, ccb, targ); 202767feec50SStephen McConnell if (rc == 1) { /* return command to CAM with success status */ 202867feec50SStephen McConnell mpr_free_command(sc, cm); 202967feec50SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 203067feec50SStephen McConnell xpt_done(ccb); 203167feec50SStephen McConnell return; 203267feec50SStephen McConnell } else if (!rc) /* Issued NVMe Encapsulated Request Message */ 203367feec50SStephen McConnell return; 203467feec50SStephen McConnell } 203567feec50SStephen McConnell 2036991554f2SKenneth D. Merry req = (MPI2_SCSI_IO_REQUEST *)cm->cm_req; 2037991554f2SKenneth D. Merry bzero(req, sizeof(*req)); 2038991554f2SKenneth D. Merry req->DevHandle = htole16(targ->handle); 2039991554f2SKenneth D. Merry req->Function = MPI2_FUNCTION_SCSI_IO_REQUEST; 2040991554f2SKenneth D. Merry req->MsgFlags = 0; 2041991554f2SKenneth D. Merry req->SenseBufferLowAddress = htole32(cm->cm_sense_busaddr); 2042991554f2SKenneth D. Merry req->SenseBufferLength = MPR_SENSE_LEN; 2043991554f2SKenneth D. Merry req->SGLFlags = 0; 2044991554f2SKenneth D. Merry req->ChainOffset = 0; 2045991554f2SKenneth D. Merry req->SGLOffset0 = 24; /* 32bit word offset to the SGL */ 2046991554f2SKenneth D. Merry req->SGLOffset1= 0; 2047991554f2SKenneth D. Merry req->SGLOffset2= 0; 2048991554f2SKenneth D. Merry req->SGLOffset3= 0; 2049991554f2SKenneth D. Merry req->SkipCount = 0; 2050991554f2SKenneth D. Merry req->DataLength = htole32(csio->dxfer_len); 2051991554f2SKenneth D. Merry req->BidirectionalDataLength = 0; 2052991554f2SKenneth D. Merry req->IoFlags = htole16(csio->cdb_len); 2053991554f2SKenneth D. Merry req->EEDPFlags = 0; 2054991554f2SKenneth D. Merry 2055991554f2SKenneth D. Merry /* Note: BiDirectional transfers are not supported */ 2056991554f2SKenneth D. Merry switch (csio->ccb_h.flags & CAM_DIR_MASK) { 2057991554f2SKenneth D. Merry case CAM_DIR_IN: 2058991554f2SKenneth D. Merry mpi_control = MPI2_SCSIIO_CONTROL_READ; 2059991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_DATAIN; 2060991554f2SKenneth D. Merry break; 2061991554f2SKenneth D. Merry case CAM_DIR_OUT: 2062991554f2SKenneth D. Merry mpi_control = MPI2_SCSIIO_CONTROL_WRITE; 2063991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_DATAOUT; 2064991554f2SKenneth D. Merry break; 2065991554f2SKenneth D. Merry case CAM_DIR_NONE: 2066991554f2SKenneth D. Merry default: 2067991554f2SKenneth D. Merry mpi_control = MPI2_SCSIIO_CONTROL_NODATATRANSFER; 2068991554f2SKenneth D. Merry break; 2069991554f2SKenneth D. Merry } 2070991554f2SKenneth D. Merry 2071991554f2SKenneth D. Merry if (csio->cdb_len == 32) 2072991554f2SKenneth D. Merry mpi_control |= 4 << MPI2_SCSIIO_CONTROL_ADDCDBLEN_SHIFT; 2073991554f2SKenneth D. Merry /* 2074991554f2SKenneth D. Merry * It looks like the hardware doesn't require an explicit tag 2075991554f2SKenneth D. Merry * number for each transaction. SAM Task Management not supported 2076991554f2SKenneth D. Merry * at the moment. 2077991554f2SKenneth D. Merry */ 2078991554f2SKenneth D. Merry switch (csio->tag_action) { 2079991554f2SKenneth D. Merry case MSG_HEAD_OF_Q_TAG: 2080991554f2SKenneth D. Merry mpi_control |= MPI2_SCSIIO_CONTROL_HEADOFQ; 2081991554f2SKenneth D. Merry break; 2082991554f2SKenneth D. Merry case MSG_ORDERED_Q_TAG: 2083991554f2SKenneth D. Merry mpi_control |= MPI2_SCSIIO_CONTROL_ORDEREDQ; 2084991554f2SKenneth D. Merry break; 2085991554f2SKenneth D. Merry case MSG_ACA_TASK: 2086991554f2SKenneth D. Merry mpi_control |= MPI2_SCSIIO_CONTROL_ACAQ; 2087991554f2SKenneth D. Merry break; 2088991554f2SKenneth D. Merry case CAM_TAG_ACTION_NONE: 2089991554f2SKenneth D. Merry case MSG_SIMPLE_Q_TAG: 2090991554f2SKenneth D. Merry default: 2091991554f2SKenneth D. Merry mpi_control |= MPI2_SCSIIO_CONTROL_SIMPLEQ; 2092991554f2SKenneth D. Merry break; 2093991554f2SKenneth D. Merry } 2094991554f2SKenneth D. Merry mpi_control |= sc->mapping_table[csio->ccb_h.target_id].TLR_bits; 2095991554f2SKenneth D. Merry req->Control = htole32(mpi_control); 2096991554f2SKenneth D. Merry 2097991554f2SKenneth D. Merry if (MPR_SET_LUN(req->LUN, csio->ccb_h.target_lun) != 0) { 2098991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2099a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_LUN_INVALID); 2100991554f2SKenneth D. Merry xpt_done(ccb); 2101991554f2SKenneth D. Merry return; 2102991554f2SKenneth D. Merry } 2103991554f2SKenneth D. Merry 2104991554f2SKenneth D. Merry if (csio->ccb_h.flags & CAM_CDB_POINTER) 2105991554f2SKenneth D. Merry bcopy(csio->cdb_io.cdb_ptr, &req->CDB.CDB32[0], csio->cdb_len); 2106fa699bb2SAlan Somers else { 2107fa699bb2SAlan Somers KASSERT(csio->cdb_len <= IOCDBLEN, 210867feec50SStephen McConnell ("cdb_len %d is greater than IOCDBLEN but CAM_CDB_POINTER " 210967feec50SStephen McConnell "is not set", csio->cdb_len)); 2110991554f2SKenneth D. Merry bcopy(csio->cdb_io.cdb_bytes, &req->CDB.CDB32[0],csio->cdb_len); 2111fa699bb2SAlan Somers } 2112991554f2SKenneth D. Merry req->IoFlags = htole16(csio->cdb_len); 2113991554f2SKenneth D. Merry 2114991554f2SKenneth D. Merry /* 2115991554f2SKenneth D. Merry * Check if EEDP is supported and enabled. If it is then check if the 2116991554f2SKenneth D. Merry * SCSI opcode could be using EEDP. If so, make sure the LUN exists and 2117991554f2SKenneth D. Merry * is formatted for EEDP support. If all of this is true, set CDB up 2118991554f2SKenneth D. Merry * for EEDP transfer. 2119991554f2SKenneth D. Merry */ 2120991554f2SKenneth D. Merry eedp_flags = op_code_prot[req->CDB.CDB32[0]]; 2121991554f2SKenneth D. Merry if (sc->eedp_enabled && eedp_flags) { 2122991554f2SKenneth D. Merry SLIST_FOREACH(lun, &targ->luns, lun_link) { 2123991554f2SKenneth D. Merry if (lun->lun_id == csio->ccb_h.target_lun) { 2124991554f2SKenneth D. Merry break; 2125991554f2SKenneth D. Merry } 2126991554f2SKenneth D. Merry } 2127991554f2SKenneth D. Merry 2128991554f2SKenneth D. Merry if ((lun != NULL) && (lun->eedp_formatted)) { 2129991554f2SKenneth D. Merry req->EEDPBlockSize = htole16(lun->eedp_block_size); 2130991554f2SKenneth D. Merry eedp_flags |= (MPI2_SCSIIO_EEDPFLAGS_INC_PRI_REFTAG | 2131991554f2SKenneth D. Merry MPI2_SCSIIO_EEDPFLAGS_CHECK_REFTAG | 2132991554f2SKenneth D. Merry MPI2_SCSIIO_EEDPFLAGS_CHECK_GUARD); 213367feec50SStephen McConnell if (sc->mpr_flags & MPR_FLAGS_GEN35_IOC) { 213467feec50SStephen McConnell eedp_flags |= 213567feec50SStephen McConnell MPI25_SCSIIO_EEDPFLAGS_APPTAG_DISABLE_MODE; 213667feec50SStephen McConnell } 2137991554f2SKenneth D. Merry req->EEDPFlags = htole16(eedp_flags); 2138991554f2SKenneth D. Merry 2139991554f2SKenneth D. Merry /* 2140991554f2SKenneth D. Merry * If CDB less than 32, fill in Primary Ref Tag with 2141991554f2SKenneth D. Merry * low 4 bytes of LBA. If CDB is 32, tag stuff is 2142991554f2SKenneth D. Merry * already there. Also, set protection bit. FreeBSD 2143991554f2SKenneth D. Merry * currently does not support CDBs bigger than 16, but 2144991554f2SKenneth D. Merry * the code doesn't hurt, and will be here for the 2145991554f2SKenneth D. Merry * future. 2146991554f2SKenneth D. Merry */ 2147991554f2SKenneth D. Merry if (csio->cdb_len != 32) { 2148991554f2SKenneth D. Merry lba_byte = (csio->cdb_len == 16) ? 6 : 2; 2149991554f2SKenneth D. Merry ref_tag_addr = (uint8_t *)&req->CDB.EEDP32. 2150991554f2SKenneth D. Merry PrimaryReferenceTag; 2151991554f2SKenneth D. Merry for (i = 0; i < 4; i++) { 2152991554f2SKenneth D. Merry *ref_tag_addr = 2153991554f2SKenneth D. Merry req->CDB.CDB32[lba_byte + i]; 2154991554f2SKenneth D. Merry ref_tag_addr++; 2155991554f2SKenneth D. Merry } 2156991554f2SKenneth D. Merry req->CDB.EEDP32.PrimaryReferenceTag = 2157991554f2SKenneth D. Merry htole32(req-> 2158991554f2SKenneth D. Merry CDB.EEDP32.PrimaryReferenceTag); 2159991554f2SKenneth D. Merry req->CDB.EEDP32.PrimaryApplicationTagMask = 2160991554f2SKenneth D. Merry 0xFFFF; 21618881681bSKenneth D. Merry req->CDB.CDB32[1] = 21628881681bSKenneth D. Merry (req->CDB.CDB32[1] & 0x1F) | 0x20; 2163991554f2SKenneth D. Merry } else { 2164991554f2SKenneth D. Merry eedp_flags |= 2165991554f2SKenneth D. Merry MPI2_SCSIIO_EEDPFLAGS_INC_PRI_APPTAG; 2166991554f2SKenneth D. Merry req->EEDPFlags = htole16(eedp_flags); 2167991554f2SKenneth D. Merry req->CDB.CDB32[10] = (req->CDB.CDB32[10] & 2168991554f2SKenneth D. Merry 0x1F) | 0x20; 2169991554f2SKenneth D. Merry } 2170991554f2SKenneth D. Merry } 2171991554f2SKenneth D. Merry } 2172991554f2SKenneth D. Merry 2173991554f2SKenneth D. Merry cm->cm_length = csio->dxfer_len; 2174991554f2SKenneth D. Merry if (cm->cm_length != 0) { 2175991554f2SKenneth D. Merry cm->cm_data = ccb; 2176991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_USE_CCB; 2177991554f2SKenneth D. Merry } else { 2178991554f2SKenneth D. Merry cm->cm_data = NULL; 2179991554f2SKenneth D. Merry } 2180991554f2SKenneth D. Merry cm->cm_sge = &req->SGL; 2181991554f2SKenneth D. Merry cm->cm_sglsize = (32 - 24) * 4; 2182991554f2SKenneth D. Merry cm->cm_complete = mprsas_scsiio_complete; 2183991554f2SKenneth D. Merry cm->cm_complete_data = ccb; 2184991554f2SKenneth D. Merry cm->cm_targ = targ; 2185991554f2SKenneth D. Merry cm->cm_lun = csio->ccb_h.target_lun; 2186991554f2SKenneth D. Merry cm->cm_ccb = ccb; 2187991554f2SKenneth D. Merry /* 2188991554f2SKenneth D. Merry * If using FP desc type, need to set a bit in IoFlags (SCSI IO is 0) 2189991554f2SKenneth D. Merry * and set descriptor type. 2190991554f2SKenneth D. Merry */ 2191991554f2SKenneth D. Merry if (targ->scsi_req_desc_type == 2192991554f2SKenneth D. Merry MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO) { 2193991554f2SKenneth D. Merry req->IoFlags |= MPI25_SCSIIO_IOFLAGS_FAST_PATH; 2194991554f2SKenneth D. Merry cm->cm_desc.FastPathSCSIIO.RequestFlags = 2195991554f2SKenneth D. Merry MPI25_REQ_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO; 219667feec50SStephen McConnell if (!sc->atomic_desc_capable) { 219767feec50SStephen McConnell cm->cm_desc.FastPathSCSIIO.DevHandle = 219867feec50SStephen McConnell htole16(targ->handle); 219967feec50SStephen McConnell } 2200991554f2SKenneth D. Merry } else { 2201991554f2SKenneth D. Merry cm->cm_desc.SCSIIO.RequestFlags = 2202991554f2SKenneth D. Merry MPI2_REQ_DESCRIPT_FLAGS_SCSI_IO; 220367feec50SStephen McConnell if (!sc->atomic_desc_capable) 2204991554f2SKenneth D. Merry cm->cm_desc.SCSIIO.DevHandle = htole16(targ->handle); 2205991554f2SKenneth D. Merry } 2206991554f2SKenneth D. Merry 22076eea4f46SScott Long csio->ccb_h.qos.sim_data = sbinuptime(); 2208407073a0SStephen McConnell #if __FreeBSD_version >= 1000029 220985c9dd9dSSteven Hartland callout_reset_sbt(&cm->cm_callout, SBT_1MS * ccb->ccb_h.timeout, 0, 221085c9dd9dSSteven Hartland mprsas_scsiio_timeout, cm, 0); 2211407073a0SStephen McConnell #else //__FreeBSD_version < 1000029 2212407073a0SStephen McConnell callout_reset(&cm->cm_callout, (ccb->ccb_h.timeout * hz) / 1000, 2213407073a0SStephen McConnell mprsas_scsiio_timeout, cm); 2214407073a0SStephen McConnell #endif //__FreeBSD_version >= 1000029 2215991554f2SKenneth D. Merry 2216991554f2SKenneth D. Merry targ->issued++; 2217991554f2SKenneth D. Merry targ->outstanding++; 2218991554f2SKenneth D. Merry TAILQ_INSERT_TAIL(&targ->commands, cm, cm_link); 2219991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_SIM_QUEUED; 2220991554f2SKenneth D. Merry 2221991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, "%s cm %p ccb %p outstanding %u\n", 2222991554f2SKenneth D. Merry __func__, cm, ccb, targ->outstanding); 2223991554f2SKenneth D. Merry 2224991554f2SKenneth D. Merry mpr_map_command(sc, cm); 2225991554f2SKenneth D. Merry return; 2226991554f2SKenneth D. Merry } 2227991554f2SKenneth D. Merry 2228991554f2SKenneth D. Merry /** 2229991554f2SKenneth D. Merry * mpr_sc_failed_io_info - translated non-succesfull SCSI_IO request 2230991554f2SKenneth D. Merry */ 2231991554f2SKenneth D. Merry static void 2232991554f2SKenneth D. Merry mpr_sc_failed_io_info(struct mpr_softc *sc, struct ccb_scsiio *csio, 2233991554f2SKenneth D. Merry Mpi2SCSIIOReply_t *mpi_reply, struct mprsas_target *targ) 2234991554f2SKenneth D. Merry { 2235991554f2SKenneth D. Merry u32 response_info; 2236991554f2SKenneth D. Merry u8 *response_bytes; 2237991554f2SKenneth D. Merry u16 ioc_status = le16toh(mpi_reply->IOCStatus) & 2238991554f2SKenneth D. Merry MPI2_IOCSTATUS_MASK; 2239991554f2SKenneth D. Merry u8 scsi_state = mpi_reply->SCSIState; 2240991554f2SKenneth D. Merry u8 scsi_status = mpi_reply->SCSIStatus; 2241991554f2SKenneth D. Merry char *desc_ioc_state = NULL; 2242991554f2SKenneth D. Merry char *desc_scsi_status = NULL; 2243991554f2SKenneth D. Merry u32 log_info = le32toh(mpi_reply->IOCLogInfo); 2244991554f2SKenneth D. Merry 2245991554f2SKenneth D. Merry if (log_info == 0x31170000) 2246991554f2SKenneth D. Merry return; 2247991554f2SKenneth D. Merry 22482bf620cbSScott Long desc_ioc_state = mpr_describe_table(mpr_iocstatus_string, 22492bf620cbSScott Long ioc_status); 22502bf620cbSScott Long desc_scsi_status = mpr_describe_table(mpr_scsi_status_string, 22512bf620cbSScott Long scsi_status); 2252991554f2SKenneth D. Merry 2253991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "\thandle(0x%04x), ioc_status(%s)(0x%04x)\n", 2254991554f2SKenneth D. Merry le16toh(mpi_reply->DevHandle), desc_ioc_state, ioc_status); 2255991554f2SKenneth D. Merry if (targ->encl_level_valid) { 2256991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "At enclosure level %d, slot %d, " 2257991554f2SKenneth D. Merry "connector name (%4s)\n", targ->encl_level, targ->encl_slot, 2258991554f2SKenneth D. Merry targ->connector_name); 2259991554f2SKenneth D. Merry } 22602bf620cbSScott Long 22612bf620cbSScott Long /* 22622bf620cbSScott Long * We can add more detail about underflow data here 2263991554f2SKenneth D. Merry * TO-DO 22642bf620cbSScott Long */ 2265991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "\tscsi_status(%s)(0x%02x), " 22662bf620cbSScott Long "scsi_state %b\n", desc_scsi_status, scsi_status, 22672bf620cbSScott Long scsi_state, "\20" "\1AutosenseValid" "\2AutosenseFailed" 22682bf620cbSScott Long "\3NoScsiStatus" "\4Terminated" "\5Response InfoValid"); 2269991554f2SKenneth D. Merry 2270991554f2SKenneth D. Merry if (sc->mpr_debug & MPR_XINFO && 2271991554f2SKenneth D. Merry scsi_state & MPI2_SCSI_STATE_AUTOSENSE_VALID) { 2272991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "-> Sense Buffer Data : Start :\n"); 2273991554f2SKenneth D. Merry scsi_sense_print(csio); 2274991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "-> Sense Buffer Data : End :\n"); 2275991554f2SKenneth D. Merry } 2276991554f2SKenneth D. Merry 2277991554f2SKenneth D. Merry if (scsi_state & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) { 2278991554f2SKenneth D. Merry response_info = le32toh(mpi_reply->ResponseInfo); 2279991554f2SKenneth D. Merry response_bytes = (u8 *)&response_info; 22802bf620cbSScott Long mpr_dprint(sc, MPR_XINFO, "response code(0x%01x): %s\n", 22812bf620cbSScott Long response_bytes[0], 22822bf620cbSScott Long mpr_describe_table(mpr_scsi_taskmgmt_string, 22832bf620cbSScott Long response_bytes[0])); 2284991554f2SKenneth D. Merry } 2285991554f2SKenneth D. Merry } 2286991554f2SKenneth D. Merry 228767feec50SStephen McConnell /** mprsas_nvme_trans_status_code 228867feec50SStephen McConnell * 228967feec50SStephen McConnell * Convert Native NVMe command error status to 229067feec50SStephen McConnell * equivalent SCSI error status. 229167feec50SStephen McConnell * 229267feec50SStephen McConnell * Returns appropriate scsi_status 229367feec50SStephen McConnell */ 229467feec50SStephen McConnell static u8 22950d787e9bSWojciech Macek mprsas_nvme_trans_status_code(uint16_t nvme_status, 229667feec50SStephen McConnell struct mpr_command *cm) 229767feec50SStephen McConnell { 229867feec50SStephen McConnell u8 status = MPI2_SCSI_STATUS_GOOD; 229967feec50SStephen McConnell int skey, asc, ascq; 230067feec50SStephen McConnell union ccb *ccb = cm->cm_complete_data; 230167feec50SStephen McConnell int returned_sense_len; 23020d787e9bSWojciech Macek uint8_t sct, sc; 23030d787e9bSWojciech Macek 23040d787e9bSWojciech Macek sct = NVME_STATUS_GET_SCT(nvme_status); 23050d787e9bSWojciech Macek sc = NVME_STATUS_GET_SC(nvme_status); 230667feec50SStephen McConnell 230767feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 230867feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 230967feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 231067feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 231167feec50SStephen McConnell 23120d787e9bSWojciech Macek switch (sct) { 231367feec50SStephen McConnell case NVME_SCT_GENERIC: 23140d787e9bSWojciech Macek switch (sc) { 231567feec50SStephen McConnell case NVME_SC_SUCCESS: 231667feec50SStephen McConnell status = MPI2_SCSI_STATUS_GOOD; 231767feec50SStephen McConnell skey = SSD_KEY_NO_SENSE; 231867feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 231967feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 232067feec50SStephen McConnell break; 232167feec50SStephen McConnell case NVME_SC_INVALID_OPCODE: 232267feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 232367feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 232467feec50SStephen McConnell asc = SCSI_ASC_ILLEGAL_COMMAND; 232567feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 232667feec50SStephen McConnell break; 232767feec50SStephen McConnell case NVME_SC_INVALID_FIELD: 232867feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 232967feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 233067feec50SStephen McConnell asc = SCSI_ASC_INVALID_CDB; 233167feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 233267feec50SStephen McConnell break; 233367feec50SStephen McConnell case NVME_SC_DATA_TRANSFER_ERROR: 233467feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 233567feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 233667feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 233767feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 233867feec50SStephen McConnell break; 233967feec50SStephen McConnell case NVME_SC_ABORTED_POWER_LOSS: 234067feec50SStephen McConnell status = MPI2_SCSI_STATUS_TASK_ABORTED; 234167feec50SStephen McConnell skey = SSD_KEY_ABORTED_COMMAND; 234267feec50SStephen McConnell asc = SCSI_ASC_WARNING; 234367feec50SStephen McConnell ascq = SCSI_ASCQ_POWER_LOSS_EXPECTED; 234467feec50SStephen McConnell break; 234567feec50SStephen McConnell case NVME_SC_INTERNAL_DEVICE_ERROR: 234667feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 234767feec50SStephen McConnell skey = SSD_KEY_HARDWARE_ERROR; 234867feec50SStephen McConnell asc = SCSI_ASC_INTERNAL_TARGET_FAILURE; 234967feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 235067feec50SStephen McConnell break; 235167feec50SStephen McConnell case NVME_SC_ABORTED_BY_REQUEST: 235267feec50SStephen McConnell case NVME_SC_ABORTED_SQ_DELETION: 235367feec50SStephen McConnell case NVME_SC_ABORTED_FAILED_FUSED: 235467feec50SStephen McConnell case NVME_SC_ABORTED_MISSING_FUSED: 235567feec50SStephen McConnell status = MPI2_SCSI_STATUS_TASK_ABORTED; 235667feec50SStephen McConnell skey = SSD_KEY_ABORTED_COMMAND; 235767feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 235867feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 235967feec50SStephen McConnell break; 236067feec50SStephen McConnell case NVME_SC_INVALID_NAMESPACE_OR_FORMAT: 236167feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 236267feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 236367feec50SStephen McConnell asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID; 236467feec50SStephen McConnell ascq = SCSI_ASCQ_INVALID_LUN_ID; 236567feec50SStephen McConnell break; 236667feec50SStephen McConnell case NVME_SC_LBA_OUT_OF_RANGE: 236767feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 236867feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 236967feec50SStephen McConnell asc = SCSI_ASC_ILLEGAL_BLOCK; 237067feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 237167feec50SStephen McConnell break; 237267feec50SStephen McConnell case NVME_SC_CAPACITY_EXCEEDED: 237367feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 237467feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 237567feec50SStephen McConnell asc = SCSI_ASC_NO_SENSE; 237667feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 237767feec50SStephen McConnell break; 237867feec50SStephen McConnell case NVME_SC_NAMESPACE_NOT_READY: 237967feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 238067feec50SStephen McConnell skey = SSD_KEY_NOT_READY; 238167feec50SStephen McConnell asc = SCSI_ASC_LUN_NOT_READY; 238267feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 238367feec50SStephen McConnell break; 238467feec50SStephen McConnell } 238567feec50SStephen McConnell break; 238667feec50SStephen McConnell case NVME_SCT_COMMAND_SPECIFIC: 23870d787e9bSWojciech Macek switch (sc) { 238867feec50SStephen McConnell case NVME_SC_INVALID_FORMAT: 238967feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 239067feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 239167feec50SStephen McConnell asc = SCSI_ASC_FORMAT_COMMAND_FAILED; 239267feec50SStephen McConnell ascq = SCSI_ASCQ_FORMAT_COMMAND_FAILED; 239367feec50SStephen McConnell break; 239467feec50SStephen McConnell case NVME_SC_CONFLICTING_ATTRIBUTES: 239567feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 239667feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 239767feec50SStephen McConnell asc = SCSI_ASC_INVALID_CDB; 239867feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 239967feec50SStephen McConnell break; 240067feec50SStephen McConnell } 240167feec50SStephen McConnell break; 240267feec50SStephen McConnell case NVME_SCT_MEDIA_ERROR: 24030d787e9bSWojciech Macek switch (sc) { 240467feec50SStephen McConnell case NVME_SC_WRITE_FAULTS: 240567feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 240667feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 240767feec50SStephen McConnell asc = SCSI_ASC_PERIPHERAL_DEV_WRITE_FAULT; 240867feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 240967feec50SStephen McConnell break; 241067feec50SStephen McConnell case NVME_SC_UNRECOVERED_READ_ERROR: 241167feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 241267feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 241367feec50SStephen McConnell asc = SCSI_ASC_UNRECOVERED_READ_ERROR; 241467feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 241567feec50SStephen McConnell break; 241667feec50SStephen McConnell case NVME_SC_GUARD_CHECK_ERROR: 241767feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 241867feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 241967feec50SStephen McConnell asc = SCSI_ASC_LOG_BLOCK_GUARD_CHECK_FAILED; 242067feec50SStephen McConnell ascq = SCSI_ASCQ_LOG_BLOCK_GUARD_CHECK_FAILED; 242167feec50SStephen McConnell break; 242267feec50SStephen McConnell case NVME_SC_APPLICATION_TAG_CHECK_ERROR: 242367feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 242467feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 242567feec50SStephen McConnell asc = SCSI_ASC_LOG_BLOCK_APPTAG_CHECK_FAILED; 242667feec50SStephen McConnell ascq = SCSI_ASCQ_LOG_BLOCK_APPTAG_CHECK_FAILED; 242767feec50SStephen McConnell break; 242867feec50SStephen McConnell case NVME_SC_REFERENCE_TAG_CHECK_ERROR: 242967feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 243067feec50SStephen McConnell skey = SSD_KEY_MEDIUM_ERROR; 243167feec50SStephen McConnell asc = SCSI_ASC_LOG_BLOCK_REFTAG_CHECK_FAILED; 243267feec50SStephen McConnell ascq = SCSI_ASCQ_LOG_BLOCK_REFTAG_CHECK_FAILED; 243367feec50SStephen McConnell break; 243467feec50SStephen McConnell case NVME_SC_COMPARE_FAILURE: 243567feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 243667feec50SStephen McConnell skey = SSD_KEY_MISCOMPARE; 243767feec50SStephen McConnell asc = SCSI_ASC_MISCOMPARE_DURING_VERIFY; 243867feec50SStephen McConnell ascq = SCSI_ASCQ_CAUSE_NOT_REPORTABLE; 243967feec50SStephen McConnell break; 244067feec50SStephen McConnell case NVME_SC_ACCESS_DENIED: 244167feec50SStephen McConnell status = MPI2_SCSI_STATUS_CHECK_CONDITION; 244267feec50SStephen McConnell skey = SSD_KEY_ILLEGAL_REQUEST; 244367feec50SStephen McConnell asc = SCSI_ASC_ACCESS_DENIED_INVALID_LUN_ID; 244467feec50SStephen McConnell ascq = SCSI_ASCQ_INVALID_LUN_ID; 244567feec50SStephen McConnell break; 244667feec50SStephen McConnell } 244767feec50SStephen McConnell break; 244867feec50SStephen McConnell } 244967feec50SStephen McConnell 245067feec50SStephen McConnell returned_sense_len = sizeof(struct scsi_sense_data); 245167feec50SStephen McConnell if (returned_sense_len < ccb->csio.sense_len) 245267feec50SStephen McConnell ccb->csio.sense_resid = ccb->csio.sense_len - 245367feec50SStephen McConnell returned_sense_len; 245467feec50SStephen McConnell else 245567feec50SStephen McConnell ccb->csio.sense_resid = 0; 245667feec50SStephen McConnell 245767feec50SStephen McConnell scsi_set_sense_data(&ccb->csio.sense_data, SSD_TYPE_FIXED, 245867feec50SStephen McConnell 1, skey, asc, ascq, SSD_ELEM_NONE); 245967feec50SStephen McConnell ccb->ccb_h.status |= CAM_AUTOSNS_VALID; 246067feec50SStephen McConnell 246167feec50SStephen McConnell return status; 246267feec50SStephen McConnell } 246367feec50SStephen McConnell 246467feec50SStephen McConnell /** mprsas_complete_nvme_unmap 246567feec50SStephen McConnell * 246667feec50SStephen McConnell * Complete native NVMe command issued using NVMe Encapsulated 246767feec50SStephen McConnell * Request Message. 246867feec50SStephen McConnell */ 246967feec50SStephen McConnell static u8 247067feec50SStephen McConnell mprsas_complete_nvme_unmap(struct mpr_softc *sc, struct mpr_command *cm) 247167feec50SStephen McConnell { 247267feec50SStephen McConnell Mpi26NVMeEncapsulatedErrorReply_t *mpi_reply; 247367feec50SStephen McConnell struct nvme_completion *nvme_completion = NULL; 247467feec50SStephen McConnell u8 scsi_status = MPI2_SCSI_STATUS_GOOD; 247567feec50SStephen McConnell 247667feec50SStephen McConnell mpi_reply =(Mpi26NVMeEncapsulatedErrorReply_t *)cm->cm_reply; 247767feec50SStephen McConnell if (le16toh(mpi_reply->ErrorResponseCount)){ 247867feec50SStephen McConnell nvme_completion = (struct nvme_completion *)cm->cm_sense; 247967feec50SStephen McConnell scsi_status = mprsas_nvme_trans_status_code( 248067feec50SStephen McConnell nvme_completion->status, cm); 248167feec50SStephen McConnell } 248267feec50SStephen McConnell return scsi_status; 248367feec50SStephen McConnell } 248467feec50SStephen McConnell 2485991554f2SKenneth D. Merry static void 2486991554f2SKenneth D. Merry mprsas_scsiio_complete(struct mpr_softc *sc, struct mpr_command *cm) 2487991554f2SKenneth D. Merry { 2488991554f2SKenneth D. Merry MPI2_SCSI_IO_REPLY *rep; 2489991554f2SKenneth D. Merry union ccb *ccb; 2490991554f2SKenneth D. Merry struct ccb_scsiio *csio; 2491991554f2SKenneth D. Merry struct mprsas_softc *sassc; 2492991554f2SKenneth D. Merry struct scsi_vpd_supported_page_list *vpd_list = NULL; 249367feec50SStephen McConnell u8 *TLR_bits, TLR_on, *scsi_cdb; 2494991554f2SKenneth D. Merry int dir = 0, i; 2495991554f2SKenneth D. Merry u16 alloc_len; 2496a2c14879SStephen McConnell struct mprsas_target *target; 2497a2c14879SStephen McConnell target_id_t target_id; 2498991554f2SKenneth D. Merry 2499991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 2500991554f2SKenneth D. Merry mpr_dprint(sc, MPR_TRACE, 2501991554f2SKenneth D. Merry "cm %p SMID %u ccb %p reply %p outstanding %u\n", cm, 2502991554f2SKenneth D. Merry cm->cm_desc.Default.SMID, cm->cm_ccb, cm->cm_reply, 2503991554f2SKenneth D. Merry cm->cm_targ->outstanding); 2504991554f2SKenneth D. Merry 2505991554f2SKenneth D. Merry callout_stop(&cm->cm_callout); 2506991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 2507991554f2SKenneth D. Merry 2508991554f2SKenneth D. Merry sassc = sc->sassc; 2509991554f2SKenneth D. Merry ccb = cm->cm_complete_data; 2510991554f2SKenneth D. Merry csio = &ccb->csio; 2511a2c14879SStephen McConnell target_id = csio->ccb_h.target_id; 2512991554f2SKenneth D. Merry rep = (MPI2_SCSI_IO_REPLY *)cm->cm_reply; 2513991554f2SKenneth D. Merry /* 2514991554f2SKenneth D. Merry * XXX KDM if the chain allocation fails, does it matter if we do 2515991554f2SKenneth D. Merry * the sync and unload here? It is simpler to do it in every case, 2516991554f2SKenneth D. Merry * assuming it doesn't cause problems. 2517991554f2SKenneth D. Merry */ 2518991554f2SKenneth D. Merry if (cm->cm_data != NULL) { 2519991554f2SKenneth D. Merry if (cm->cm_flags & MPR_CM_FLAGS_DATAIN) 2520991554f2SKenneth D. Merry dir = BUS_DMASYNC_POSTREAD; 2521991554f2SKenneth D. Merry else if (cm->cm_flags & MPR_CM_FLAGS_DATAOUT) 2522991554f2SKenneth D. Merry dir = BUS_DMASYNC_POSTWRITE; 2523991554f2SKenneth D. Merry bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, dir); 2524991554f2SKenneth D. Merry bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); 2525991554f2SKenneth D. Merry } 2526991554f2SKenneth D. Merry 2527991554f2SKenneth D. Merry cm->cm_targ->completed++; 2528991554f2SKenneth D. Merry cm->cm_targ->outstanding--; 2529991554f2SKenneth D. Merry TAILQ_REMOVE(&cm->cm_targ->commands, cm, cm_link); 2530991554f2SKenneth D. Merry ccb->ccb_h.status &= ~(CAM_STATUS_MASK | CAM_SIM_QUEUED); 2531991554f2SKenneth D. Merry 2532991554f2SKenneth D. Merry if (cm->cm_state == MPR_CM_STATE_TIMEDOUT) { 2533991554f2SKenneth D. Merry TAILQ_REMOVE(&cm->cm_targ->timedout_commands, cm, cm_recovery); 2534f0779b04SScott Long cm->cm_state = MPR_CM_STATE_BUSY; 2535991554f2SKenneth D. Merry if (cm->cm_reply != NULL) 2536991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2537991554f2SKenneth D. Merry "completed timedout cm %p ccb %p during recovery " 2538991554f2SKenneth D. Merry "ioc %x scsi %x state %x xfer %u\n", cm, cm->cm_ccb, 2539991554f2SKenneth D. Merry le16toh(rep->IOCStatus), rep->SCSIStatus, 2540991554f2SKenneth D. Merry rep->SCSIState, le32toh(rep->TransferCount)); 2541991554f2SKenneth D. Merry else 2542991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2543991554f2SKenneth D. Merry "completed timedout cm %p ccb %p during recovery\n", 2544991554f2SKenneth D. Merry cm, cm->cm_ccb); 2545991554f2SKenneth D. Merry } else if (cm->cm_targ->tm != NULL) { 2546991554f2SKenneth D. Merry if (cm->cm_reply != NULL) 2547991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2548991554f2SKenneth D. Merry "completed cm %p ccb %p during recovery " 2549991554f2SKenneth D. Merry "ioc %x scsi %x state %x xfer %u\n", 2550991554f2SKenneth D. Merry cm, cm->cm_ccb, le16toh(rep->IOCStatus), 2551991554f2SKenneth D. Merry rep->SCSIStatus, rep->SCSIState, 2552991554f2SKenneth D. Merry le32toh(rep->TransferCount)); 2553991554f2SKenneth D. Merry else 2554991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2555991554f2SKenneth D. Merry "completed cm %p ccb %p during recovery\n", 2556991554f2SKenneth D. Merry cm, cm->cm_ccb); 2557991554f2SKenneth D. Merry } else if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) { 2558991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_RECOVERY, 2559991554f2SKenneth D. Merry "reset completed cm %p ccb %p\n", cm, cm->cm_ccb); 2560991554f2SKenneth D. Merry } 2561991554f2SKenneth D. Merry 2562991554f2SKenneth D. Merry if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 2563991554f2SKenneth D. Merry /* 2564991554f2SKenneth D. Merry * We ran into an error after we tried to map the command, 2565991554f2SKenneth D. Merry * so we're getting a callback without queueing the command 2566991554f2SKenneth D. Merry * to the hardware. So we set the status here, and it will 2567991554f2SKenneth D. Merry * be retained below. We'll go through the "fast path", 2568991554f2SKenneth D. Merry * because there can be no reply when we haven't actually 2569991554f2SKenneth D. Merry * gone out to the hardware. 2570991554f2SKenneth D. Merry */ 2571a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQUEUE_REQ); 2572991554f2SKenneth D. Merry 2573991554f2SKenneth D. Merry /* 2574991554f2SKenneth D. Merry * Currently the only error included in the mask is 2575991554f2SKenneth D. Merry * MPR_CM_FLAGS_CHAIN_FAILED, which means we're out of 2576991554f2SKenneth D. Merry * chain frames. We need to freeze the queue until we get 2577991554f2SKenneth D. Merry * a command that completed without this error, which will 2578991554f2SKenneth D. Merry * hopefully have some chain frames attached that we can 2579991554f2SKenneth D. Merry * use. If we wanted to get smarter about it, we would 2580991554f2SKenneth D. Merry * only unfreeze the queue in this condition when we're 2581991554f2SKenneth D. Merry * sure that we're getting some chain frames back. That's 2582991554f2SKenneth D. Merry * probably unnecessary. 2583991554f2SKenneth D. Merry */ 2584991554f2SKenneth D. Merry if ((sassc->flags & MPRSAS_QUEUE_FROZEN) == 0) { 2585991554f2SKenneth D. Merry xpt_freeze_simq(sassc->sim, 1); 2586991554f2SKenneth D. Merry sassc->flags |= MPRSAS_QUEUE_FROZEN; 25876c85e33eSScott Long mpr_dprint(sc, MPR_XINFO, "Error sending command, " 2588991554f2SKenneth D. Merry "freezing SIM queue\n"); 2589991554f2SKenneth D. Merry } 2590991554f2SKenneth D. Merry } 2591991554f2SKenneth D. Merry 2592991554f2SKenneth D. Merry /* 259367feec50SStephen McConnell * Point to the SCSI CDB, which is dependent on the CAM_CDB_POINTER 259467feec50SStephen McConnell * flag, and use it in a few places in the rest of this function for 259567feec50SStephen McConnell * convenience. Use the macro if available. 259667feec50SStephen McConnell */ 259767feec50SStephen McConnell #if __FreeBSD_version >= 1100103 259867feec50SStephen McConnell scsi_cdb = scsiio_cdb_ptr(csio); 259967feec50SStephen McConnell #else 260067feec50SStephen McConnell if (csio->ccb_h.flags & CAM_CDB_POINTER) 260167feec50SStephen McConnell scsi_cdb = csio->cdb_io.cdb_ptr; 260267feec50SStephen McConnell else 260367feec50SStephen McConnell scsi_cdb = csio->cdb_io.cdb_bytes; 260467feec50SStephen McConnell #endif 260567feec50SStephen McConnell 260667feec50SStephen McConnell /* 2607991554f2SKenneth D. Merry * If this is a Start Stop Unit command and it was issued by the driver 2608991554f2SKenneth D. Merry * during shutdown, decrement the refcount to account for all of the 2609991554f2SKenneth D. Merry * commands that were sent. All SSU commands should be completed before 2610991554f2SKenneth D. Merry * shutdown completes, meaning SSU_refcount will be 0 after SSU_started 2611991554f2SKenneth D. Merry * is TRUE. 2612991554f2SKenneth D. Merry */ 261367feec50SStephen McConnell if (sc->SSU_started && (scsi_cdb[0] == START_STOP_UNIT)) { 2614991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INFO, "Decrementing SSU count.\n"); 2615991554f2SKenneth D. Merry sc->SSU_refcount--; 2616991554f2SKenneth D. Merry } 2617991554f2SKenneth D. Merry 2618991554f2SKenneth D. Merry /* Take the fast path to completion */ 2619991554f2SKenneth D. Merry if (cm->cm_reply == NULL) { 2620a2c14879SStephen McConnell if (mprsas_get_ccbstatus(ccb) == CAM_REQ_INPROG) { 2621991554f2SKenneth D. Merry if ((sc->mpr_flags & MPR_FLAGS_DIAGRESET) != 0) 2622a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SCSI_BUS_RESET); 2623991554f2SKenneth D. Merry else { 2624a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 2625a2c14879SStephen McConnell csio->scsi_status = SCSI_STATUS_OK; 2626991554f2SKenneth D. Merry } 2627991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_QUEUE_FROZEN) { 2628991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_RELEASE_SIMQ; 2629991554f2SKenneth D. Merry sassc->flags &= ~MPRSAS_QUEUE_FROZEN; 2630991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, 2631991554f2SKenneth D. Merry "Unfreezing SIM queue\n"); 2632991554f2SKenneth D. Merry } 2633991554f2SKenneth D. Merry } 2634991554f2SKenneth D. Merry 2635991554f2SKenneth D. Merry /* 2636991554f2SKenneth D. Merry * There are two scenarios where the status won't be 2637991554f2SKenneth D. Merry * CAM_REQ_CMP. The first is if MPR_CM_FLAGS_ERROR_MASK is 2638991554f2SKenneth D. Merry * set, the second is in the MPR_FLAGS_DIAGRESET above. 2639991554f2SKenneth D. Merry */ 2640a2c14879SStephen McConnell if (mprsas_get_ccbstatus(ccb) != CAM_REQ_CMP) { 2641991554f2SKenneth D. Merry /* 2642991554f2SKenneth D. Merry * Freeze the dev queue so that commands are 2643a2c14879SStephen McConnell * executed in the correct order after error 2644991554f2SKenneth D. Merry * recovery. 2645991554f2SKenneth D. Merry */ 2646991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_DEV_QFRZN; 2647991554f2SKenneth D. Merry xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1); 2648991554f2SKenneth D. Merry } 2649991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2650991554f2SKenneth D. Merry xpt_done(ccb); 2651991554f2SKenneth D. Merry return; 2652991554f2SKenneth D. Merry } 2653991554f2SKenneth D. Merry 265467feec50SStephen McConnell target = &sassc->targets[target_id]; 265567feec50SStephen McConnell if (scsi_cdb[0] == UNMAP && 265667feec50SStephen McConnell target->is_nvme && 265767feec50SStephen McConnell (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) { 265867feec50SStephen McConnell rep->SCSIStatus = mprsas_complete_nvme_unmap(sc, cm); 265967feec50SStephen McConnell csio->scsi_status = rep->SCSIStatus; 266067feec50SStephen McConnell } 266167feec50SStephen McConnell 2662991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, 2663991554f2SKenneth D. Merry "ioc %x scsi %x state %x xfer %u\n", 2664991554f2SKenneth D. Merry le16toh(rep->IOCStatus), rep->SCSIStatus, rep->SCSIState, 2665991554f2SKenneth D. Merry le32toh(rep->TransferCount)); 2666991554f2SKenneth D. Merry 2667991554f2SKenneth D. Merry switch (le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK) { 2668991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_DATA_UNDERRUN: 2669991554f2SKenneth D. Merry csio->resid = cm->cm_length - le32toh(rep->TransferCount); 2670991554f2SKenneth D. Merry /* FALLTHROUGH */ 2671991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SUCCESS: 2672991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR: 2673991554f2SKenneth D. Merry if ((le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK) == 2674991554f2SKenneth D. Merry MPI2_IOCSTATUS_SCSI_RECOVERED_ERROR) 2675991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, "recovered error\n"); 2676991554f2SKenneth D. Merry 2677991554f2SKenneth D. Merry /* Completion failed at the transport level. */ 2678991554f2SKenneth D. Merry if (rep->SCSIState & (MPI2_SCSI_STATE_NO_SCSI_STATUS | 2679991554f2SKenneth D. Merry MPI2_SCSI_STATE_TERMINATED)) { 2680a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 2681991554f2SKenneth D. Merry break; 2682991554f2SKenneth D. Merry } 2683991554f2SKenneth D. Merry 2684991554f2SKenneth D. Merry /* In a modern packetized environment, an autosense failure 2685991554f2SKenneth D. Merry * implies that there's not much else that can be done to 2686991554f2SKenneth D. Merry * recover the command. 2687991554f2SKenneth D. Merry */ 2688991554f2SKenneth D. Merry if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_FAILED) { 2689a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_AUTOSENSE_FAIL); 2690991554f2SKenneth D. Merry break; 2691991554f2SKenneth D. Merry } 2692991554f2SKenneth D. Merry 2693991554f2SKenneth D. Merry /* 2694991554f2SKenneth D. Merry * CAM doesn't care about SAS Response Info data, but if this is 2695991554f2SKenneth D. Merry * the state check if TLR should be done. If not, clear the 2696991554f2SKenneth D. Merry * TLR_bits for the target. 2697991554f2SKenneth D. Merry */ 2698991554f2SKenneth D. Merry if ((rep->SCSIState & MPI2_SCSI_STATE_RESPONSE_INFO_VALID) && 2699991554f2SKenneth D. Merry ((le32toh(rep->ResponseInfo) & MPI2_SCSI_RI_MASK_REASONCODE) 2700991554f2SKenneth D. Merry == MPR_SCSI_RI_INVALID_FRAME)) { 2701a2c14879SStephen McConnell sc->mapping_table[target_id].TLR_bits = 2702991554f2SKenneth D. Merry (u8)MPI2_SCSIIO_CONTROL_NO_TLR; 2703991554f2SKenneth D. Merry } 2704991554f2SKenneth D. Merry 2705991554f2SKenneth D. Merry /* 2706991554f2SKenneth D. Merry * Intentionally override the normal SCSI status reporting 2707991554f2SKenneth D. Merry * for these two cases. These are likely to happen in a 2708991554f2SKenneth D. Merry * multi-initiator environment, and we want to make sure that 2709991554f2SKenneth D. Merry * CAM retries these commands rather than fail them. 2710991554f2SKenneth D. Merry */ 2711991554f2SKenneth D. Merry if ((rep->SCSIStatus == MPI2_SCSI_STATUS_COMMAND_TERMINATED) || 2712991554f2SKenneth D. Merry (rep->SCSIStatus == MPI2_SCSI_STATUS_TASK_ABORTED)) { 2713a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_ABORTED); 2714991554f2SKenneth D. Merry break; 2715991554f2SKenneth D. Merry } 2716991554f2SKenneth D. Merry 2717991554f2SKenneth D. Merry /* Handle normal status and sense */ 2718991554f2SKenneth D. Merry csio->scsi_status = rep->SCSIStatus; 2719991554f2SKenneth D. Merry if (rep->SCSIStatus == MPI2_SCSI_STATUS_GOOD) 2720a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 2721991554f2SKenneth D. Merry else 2722a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SCSI_STATUS_ERROR); 2723991554f2SKenneth D. Merry 2724991554f2SKenneth D. Merry if (rep->SCSIState & MPI2_SCSI_STATE_AUTOSENSE_VALID) { 2725991554f2SKenneth D. Merry int sense_len, returned_sense_len; 2726991554f2SKenneth D. Merry 2727991554f2SKenneth D. Merry returned_sense_len = min(le32toh(rep->SenseCount), 2728991554f2SKenneth D. Merry sizeof(struct scsi_sense_data)); 2729991554f2SKenneth D. Merry if (returned_sense_len < csio->sense_len) 2730991554f2SKenneth D. Merry csio->sense_resid = csio->sense_len - 2731991554f2SKenneth D. Merry returned_sense_len; 2732991554f2SKenneth D. Merry else 2733991554f2SKenneth D. Merry csio->sense_resid = 0; 2734991554f2SKenneth D. Merry 2735991554f2SKenneth D. Merry sense_len = min(returned_sense_len, 2736991554f2SKenneth D. Merry csio->sense_len - csio->sense_resid); 2737991554f2SKenneth D. Merry bzero(&csio->sense_data, sizeof(csio->sense_data)); 2738991554f2SKenneth D. Merry bcopy(cm->cm_sense, &csio->sense_data, sense_len); 2739991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_AUTOSNS_VALID; 2740991554f2SKenneth D. Merry } 2741991554f2SKenneth D. Merry 2742991554f2SKenneth D. Merry /* 2743991554f2SKenneth D. Merry * Check if this is an INQUIRY command. If it's a VPD inquiry, 2744991554f2SKenneth D. Merry * and it's page code 0 (Supported Page List), and there is 2745991554f2SKenneth D. Merry * inquiry data, and this is for a sequential access device, and 2746991554f2SKenneth D. Merry * the device is an SSP target, and TLR is supported by the 2747991554f2SKenneth D. Merry * controller, turn the TLR_bits value ON if page 0x90 is 2748991554f2SKenneth D. Merry * supported. 2749991554f2SKenneth D. Merry */ 275067feec50SStephen McConnell if ((scsi_cdb[0] == INQUIRY) && 275167feec50SStephen McConnell (scsi_cdb[1] & SI_EVPD) && 275267feec50SStephen McConnell (scsi_cdb[2] == SVPD_SUPPORTED_PAGE_LIST) && 2753991554f2SKenneth D. Merry ((csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) && 2754d2b4e18bSKenneth D. Merry (csio->data_ptr != NULL) && 2755d2b4e18bSKenneth D. Merry ((csio->data_ptr[0] & 0x1f) == T_SEQUENTIAL) && 2756d2b4e18bSKenneth D. Merry (sc->control_TLR) && 2757a2c14879SStephen McConnell (sc->mapping_table[target_id].device_info & 2758991554f2SKenneth D. Merry MPI2_SAS_DEVICE_INFO_SSP_TARGET)) { 2759991554f2SKenneth D. Merry vpd_list = (struct scsi_vpd_supported_page_list *) 2760991554f2SKenneth D. Merry csio->data_ptr; 2761a2c14879SStephen McConnell TLR_bits = &sc->mapping_table[target_id].TLR_bits; 2762991554f2SKenneth D. Merry *TLR_bits = (u8)MPI2_SCSIIO_CONTROL_NO_TLR; 2763991554f2SKenneth D. Merry TLR_on = (u8)MPI2_SCSIIO_CONTROL_TLR_ON; 276467feec50SStephen McConnell alloc_len = ((u16)scsi_cdb[3] << 8) + scsi_cdb[4]; 2765d2b4e18bSKenneth D. Merry alloc_len -= csio->resid; 2766991554f2SKenneth D. Merry for (i = 0; i < MIN(vpd_list->length, alloc_len); i++) { 2767991554f2SKenneth D. Merry if (vpd_list->list[i] == 0x90) { 2768991554f2SKenneth D. Merry *TLR_bits = TLR_on; 2769991554f2SKenneth D. Merry break; 2770991554f2SKenneth D. Merry } 2771991554f2SKenneth D. Merry } 2772991554f2SKenneth D. Merry } 2773a2c14879SStephen McConnell 2774a2c14879SStephen McConnell /* 2775a2c14879SStephen McConnell * If this is a SATA direct-access end device, mark it so that 2776a2c14879SStephen McConnell * a SCSI StartStopUnit command will be sent to it when the 2777a2c14879SStephen McConnell * driver is being shutdown. 2778a2c14879SStephen McConnell */ 277967feec50SStephen McConnell if ((scsi_cdb[0] == INQUIRY) && 2780fa699bb2SAlan Somers (csio->data_ptr != NULL) && 2781a2c14879SStephen McConnell ((csio->data_ptr[0] & 0x1f) == T_DIRECT) && 2782a2c14879SStephen McConnell (sc->mapping_table[target_id].device_info & 2783a2c14879SStephen McConnell MPI2_SAS_DEVICE_INFO_SATA_DEVICE) && 2784a2c14879SStephen McConnell ((sc->mapping_table[target_id].device_info & 2785a2c14879SStephen McConnell MPI2_SAS_DEVICE_INFO_MASK_DEVICE_TYPE) == 2786a2c14879SStephen McConnell MPI2_SAS_DEVICE_INFO_END_DEVICE)) { 2787a2c14879SStephen McConnell target = &sassc->targets[target_id]; 2788a2c14879SStephen McConnell target->supports_SSU = TRUE; 2789a2c14879SStephen McConnell mpr_dprint(sc, MPR_XINFO, "Target %d supports SSU\n", 2790a2c14879SStephen McConnell target_id); 2791a2c14879SStephen McConnell } 2792991554f2SKenneth D. Merry break; 2793991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_INVALID_DEVHANDLE: 2794991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_DEVICE_NOT_THERE: 2795991554f2SKenneth D. Merry /* 2796991554f2SKenneth D. Merry * If devinfo is 0 this will be a volume. In that case don't 2797991554f2SKenneth D. Merry * tell CAM that the volume is not there. We want volumes to 2798991554f2SKenneth D. Merry * be enumerated until they are deleted/removed, not just 2799991554f2SKenneth D. Merry * failed. 2800991554f2SKenneth D. Merry */ 2801991554f2SKenneth D. Merry if (cm->cm_targ->devinfo == 0) 2802a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 2803991554f2SKenneth D. Merry else 2804a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 2805991554f2SKenneth D. Merry break; 2806991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_SGL: 2807991554f2SKenneth D. Merry mpr_print_scsiio_cmd(sc, cm); 2808a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_UNREC_HBA_ERROR); 2809991554f2SKenneth D. Merry break; 2810991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_TASK_TERMINATED: 2811991554f2SKenneth D. Merry /* 2812991554f2SKenneth D. Merry * This is one of the responses that comes back when an I/O 2813991554f2SKenneth D. Merry * has been aborted. If it is because of a timeout that we 2814991554f2SKenneth D. Merry * initiated, just set the status to CAM_CMD_TIMEOUT. 2815991554f2SKenneth D. Merry * Otherwise set it to CAM_REQ_ABORTED. The effect on the 2816991554f2SKenneth D. Merry * command is the same (it gets retried, subject to the 2817991554f2SKenneth D. Merry * retry counter), the only difference is what gets printed 2818991554f2SKenneth D. Merry * on the console. 2819991554f2SKenneth D. Merry */ 2820991554f2SKenneth D. Merry if (cm->cm_state == MPR_CM_STATE_TIMEDOUT) 2821a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_CMD_TIMEOUT); 2822991554f2SKenneth D. Merry else 2823a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_ABORTED); 2824991554f2SKenneth D. Merry break; 2825991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_DATA_OVERRUN: 2826991554f2SKenneth D. Merry /* resid is ignored for this condition */ 2827991554f2SKenneth D. Merry csio->resid = 0; 2828a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DATA_RUN_ERR); 2829991554f2SKenneth D. Merry break; 2830991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_IOC_TERMINATED: 2831991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_EXT_TERMINATED: 2832991554f2SKenneth D. Merry /* 28336adfa7edSAlan Somers * These can sometimes be transient transport-related 28346adfa7edSAlan Somers * errors, and sometimes persistent drive-related errors. 28356adfa7edSAlan Somers * We used to retry these without decrementing the retry 28366adfa7edSAlan Somers * count by returning CAM_REQUEUE_REQ. Unfortunately, if 28376adfa7edSAlan Somers * we hit a persistent drive problem that returns one of 28386adfa7edSAlan Somers * these error codes, we would retry indefinitely. So, 28396adfa7edSAlan Somers * return CAM_REQ_CMP_ERROR so that we decrement the retry 28406adfa7edSAlan Somers * count and avoid infinite retries. We're taking the 28416adfa7edSAlan Somers * potential risk of flagging false failures in the event 28426adfa7edSAlan Somers * of a topology-related error (e.g. a SAS expander problem 28436adfa7edSAlan Somers * causes a command addressed to a drive to fail), but 28446adfa7edSAlan Somers * avoiding getting into an infinite retry loop. 2845991554f2SKenneth D. Merry */ 28466adfa7edSAlan Somers mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 28476eea4f46SScott Long mpr_dprint(sc, MPR_INFO, 28482bf620cbSScott Long "Controller reported %s tgt %u SMID %u loginfo %x\n", 28492bf620cbSScott Long mpr_describe_table(mpr_iocstatus_string, 28502bf620cbSScott Long le16toh(rep->IOCStatus) & MPI2_IOCSTATUS_MASK), 28512bf620cbSScott Long target_id, cm->cm_desc.Default.SMID, 28526eea4f46SScott Long le32toh(rep->IOCLogInfo)); 28536eea4f46SScott Long mpr_dprint(sc, MPR_XINFO, 28546eea4f46SScott Long "SCSIStatus %x SCSIState %x xfercount %u\n", 2855694cb8b8SScott Long rep->SCSIStatus, rep->SCSIState, 2856991554f2SKenneth D. Merry le32toh(rep->TransferCount)); 2857991554f2SKenneth D. Merry break; 2858991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_FUNCTION: 2859991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INTERNAL_ERROR: 2860991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_VPID: 2861991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_FIELD: 2862991554f2SKenneth D. Merry case MPI2_IOCSTATUS_INVALID_STATE: 2863991554f2SKenneth D. Merry case MPI2_IOCSTATUS_OP_STATE_NOT_SUPPORTED: 2864991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_IO_DATA_ERROR: 2865991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_PROTOCOL_ERROR: 2866991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_RESIDUAL_MISMATCH: 2867991554f2SKenneth D. Merry case MPI2_IOCSTATUS_SCSI_TASK_MGMT_FAILED: 2868991554f2SKenneth D. Merry default: 2869991554f2SKenneth D. Merry mprsas_log_command(cm, MPR_XINFO, 2870694cb8b8SScott Long "completed ioc %x loginfo %x scsi %x state %x xfer %u\n", 2871694cb8b8SScott Long le16toh(rep->IOCStatus), le32toh(rep->IOCLogInfo), 2872694cb8b8SScott Long rep->SCSIStatus, rep->SCSIState, 2873991554f2SKenneth D. Merry le32toh(rep->TransferCount)); 2874991554f2SKenneth D. Merry csio->resid = cm->cm_length; 287567feec50SStephen McConnell 287667feec50SStephen McConnell if (scsi_cdb[0] == UNMAP && 287767feec50SStephen McConnell target->is_nvme && 287867feec50SStephen McConnell (csio->ccb_h.flags & CAM_DATA_MASK) == CAM_DATA_VADDR) 287967feec50SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 288067feec50SStephen McConnell else 2881a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 288267feec50SStephen McConnell 2883991554f2SKenneth D. Merry break; 2884991554f2SKenneth D. Merry } 2885991554f2SKenneth D. Merry 2886991554f2SKenneth D. Merry mpr_sc_failed_io_info(sc, csio, rep, cm->cm_targ); 2887991554f2SKenneth D. Merry 2888991554f2SKenneth D. Merry if (sassc->flags & MPRSAS_QUEUE_FROZEN) { 2889991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_RELEASE_SIMQ; 2890991554f2SKenneth D. Merry sassc->flags &= ~MPRSAS_QUEUE_FROZEN; 2891991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "Command completed, unfreezing SIM " 2892991554f2SKenneth D. Merry "queue\n"); 2893991554f2SKenneth D. Merry } 2894991554f2SKenneth D. Merry 2895a2c14879SStephen McConnell if (mprsas_get_ccbstatus(ccb) != CAM_REQ_CMP) { 2896991554f2SKenneth D. Merry ccb->ccb_h.status |= CAM_DEV_QFRZN; 2897991554f2SKenneth D. Merry xpt_freeze_devq(ccb->ccb_h.path, /*count*/ 1); 2898991554f2SKenneth D. Merry } 2899991554f2SKenneth D. Merry 2900991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2901991554f2SKenneth D. Merry xpt_done(ccb); 2902991554f2SKenneth D. Merry } 2903991554f2SKenneth D. Merry 2904991554f2SKenneth D. Merry #if __FreeBSD_version >= 900026 2905991554f2SKenneth D. Merry static void 2906991554f2SKenneth D. Merry mprsas_smpio_complete(struct mpr_softc *sc, struct mpr_command *cm) 2907991554f2SKenneth D. Merry { 2908991554f2SKenneth D. Merry MPI2_SMP_PASSTHROUGH_REPLY *rpl; 2909991554f2SKenneth D. Merry MPI2_SMP_PASSTHROUGH_REQUEST *req; 2910991554f2SKenneth D. Merry uint64_t sasaddr; 2911991554f2SKenneth D. Merry union ccb *ccb; 2912991554f2SKenneth D. Merry 2913991554f2SKenneth D. Merry ccb = cm->cm_complete_data; 2914991554f2SKenneth D. Merry 2915991554f2SKenneth D. Merry /* 2916991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 2917991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and SMP 2918991554f2SKenneth D. Merry * commands require two S/G elements only. That should be handled 2919991554f2SKenneth D. Merry * in the standard request size. 2920991554f2SKenneth D. Merry */ 2921991554f2SKenneth D. Merry if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 2922a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x on SMP " 2923a2c14879SStephen McConnell "request!\n", __func__, cm->cm_flags); 2924a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 2925991554f2SKenneth D. Merry goto bailout; 2926991554f2SKenneth D. Merry } 2927991554f2SKenneth D. Merry 2928991554f2SKenneth D. Merry rpl = (MPI2_SMP_PASSTHROUGH_REPLY *)cm->cm_reply; 2929991554f2SKenneth D. Merry if (rpl == NULL) { 2930991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: NULL cm_reply!\n", __func__); 2931a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 2932991554f2SKenneth D. Merry goto bailout; 2933991554f2SKenneth D. Merry } 2934991554f2SKenneth D. Merry 2935991554f2SKenneth D. Merry req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; 2936991554f2SKenneth D. Merry sasaddr = le32toh(req->SASAddress.Low); 2937991554f2SKenneth D. Merry sasaddr |= ((uint64_t)(le32toh(req->SASAddress.High))) << 32; 2938991554f2SKenneth D. Merry 2939991554f2SKenneth D. Merry if ((le16toh(rpl->IOCStatus) & MPI2_IOCSTATUS_MASK) != 2940991554f2SKenneth D. Merry MPI2_IOCSTATUS_SUCCESS || 2941991554f2SKenneth D. Merry rpl->SASStatus != MPI2_SASSTATUS_SUCCESS) { 2942991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s: IOCStatus %04x SASStatus %02x\n", 2943991554f2SKenneth D. Merry __func__, le16toh(rpl->IOCStatus), rpl->SASStatus); 2944a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 2945991554f2SKenneth D. Merry goto bailout; 2946991554f2SKenneth D. Merry } 2947991554f2SKenneth D. Merry 2948a2c14879SStephen McConnell mpr_dprint(sc, MPR_XINFO, "%s: SMP request to SAS address %#jx " 2949a2c14879SStephen McConnell "completed successfully\n", __func__, (uintmax_t)sasaddr); 2950991554f2SKenneth D. Merry 2951991554f2SKenneth D. Merry if (ccb->smpio.smp_response[2] == SMP_FR_ACCEPTED) 2952a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 2953991554f2SKenneth D. Merry else 2954a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SMP_STATUS_ERROR); 2955991554f2SKenneth D. Merry 2956991554f2SKenneth D. Merry bailout: 2957991554f2SKenneth D. Merry /* 2958991554f2SKenneth D. Merry * We sync in both directions because we had DMAs in the S/G list 2959991554f2SKenneth D. Merry * in both directions. 2960991554f2SKenneth D. Merry */ 2961991554f2SKenneth D. Merry bus_dmamap_sync(sc->buffer_dmat, cm->cm_dmamap, 2962991554f2SKenneth D. Merry BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); 2963991554f2SKenneth D. Merry bus_dmamap_unload(sc->buffer_dmat, cm->cm_dmamap); 2964991554f2SKenneth D. Merry mpr_free_command(sc, cm); 2965991554f2SKenneth D. Merry xpt_done(ccb); 2966991554f2SKenneth D. Merry } 2967991554f2SKenneth D. Merry 2968991554f2SKenneth D. Merry static void 29697a2a6a1aSStephen McConnell mprsas_send_smpcmd(struct mprsas_softc *sassc, union ccb *ccb, uint64_t sasaddr) 2970991554f2SKenneth D. Merry { 2971991554f2SKenneth D. Merry struct mpr_command *cm; 2972991554f2SKenneth D. Merry uint8_t *request, *response; 2973991554f2SKenneth D. Merry MPI2_SMP_PASSTHROUGH_REQUEST *req; 2974991554f2SKenneth D. Merry struct mpr_softc *sc; 2975991554f2SKenneth D. Merry struct sglist *sg; 2976991554f2SKenneth D. Merry int error; 2977991554f2SKenneth D. Merry 2978991554f2SKenneth D. Merry sc = sassc->sc; 2979991554f2SKenneth D. Merry sg = NULL; 2980991554f2SKenneth D. Merry error = 0; 2981991554f2SKenneth D. Merry 2982c503306dSKenneth D. Merry #if (__FreeBSD_version >= 1000028) || \ 2983c503306dSKenneth D. Merry ((__FreeBSD_version >= 902001) && (__FreeBSD_version < 1000000)) 2984991554f2SKenneth D. Merry switch (ccb->ccb_h.flags & CAM_DATA_MASK) { 2985991554f2SKenneth D. Merry case CAM_DATA_PADDR: 2986991554f2SKenneth D. Merry case CAM_DATA_SG_PADDR: 2987991554f2SKenneth D. Merry /* 2988991554f2SKenneth D. Merry * XXX We don't yet support physical addresses here. 2989991554f2SKenneth D. Merry */ 2990991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: physical addresses not " 2991991554f2SKenneth D. Merry "supported\n", __func__); 2992a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID); 2993991554f2SKenneth D. Merry xpt_done(ccb); 2994991554f2SKenneth D. Merry return; 2995991554f2SKenneth D. Merry case CAM_DATA_SG: 2996991554f2SKenneth D. Merry /* 2997991554f2SKenneth D. Merry * The chip does not support more than one buffer for the 2998991554f2SKenneth D. Merry * request or response. 2999991554f2SKenneth D. Merry */ 3000991554f2SKenneth D. Merry if ((ccb->smpio.smp_request_sglist_cnt > 1) 3001991554f2SKenneth D. Merry || (ccb->smpio.smp_response_sglist_cnt > 1)) { 30027a2a6a1aSStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: multiple request or " 30037a2a6a1aSStephen McConnell "response buffer segments not supported for SMP\n", 30047a2a6a1aSStephen McConnell __func__); 3005a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID); 3006991554f2SKenneth D. Merry xpt_done(ccb); 3007991554f2SKenneth D. Merry return; 3008991554f2SKenneth D. Merry } 3009991554f2SKenneth D. Merry 3010991554f2SKenneth D. Merry /* 3011991554f2SKenneth D. Merry * The CAM_SCATTER_VALID flag was originally implemented 3012991554f2SKenneth D. Merry * for the XPT_SCSI_IO CCB, which only has one data pointer. 3013991554f2SKenneth D. Merry * We have two. So, just take that flag to mean that we 3014991554f2SKenneth D. Merry * might have S/G lists, and look at the S/G segment count 3015991554f2SKenneth D. Merry * to figure out whether that is the case for each individual 3016991554f2SKenneth D. Merry * buffer. 3017991554f2SKenneth D. Merry */ 3018991554f2SKenneth D. Merry if (ccb->smpio.smp_request_sglist_cnt != 0) { 3019991554f2SKenneth D. Merry bus_dma_segment_t *req_sg; 3020991554f2SKenneth D. Merry 3021991554f2SKenneth D. Merry req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request; 3022991554f2SKenneth D. Merry request = (uint8_t *)(uintptr_t)req_sg[0].ds_addr; 3023991554f2SKenneth D. Merry } else 3024991554f2SKenneth D. Merry request = ccb->smpio.smp_request; 3025991554f2SKenneth D. Merry 3026991554f2SKenneth D. Merry if (ccb->smpio.smp_response_sglist_cnt != 0) { 3027991554f2SKenneth D. Merry bus_dma_segment_t *rsp_sg; 3028991554f2SKenneth D. Merry 3029991554f2SKenneth D. Merry rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response; 3030991554f2SKenneth D. Merry response = (uint8_t *)(uintptr_t)rsp_sg[0].ds_addr; 3031991554f2SKenneth D. Merry } else 3032991554f2SKenneth D. Merry response = ccb->smpio.smp_response; 3033991554f2SKenneth D. Merry break; 3034991554f2SKenneth D. Merry case CAM_DATA_VADDR: 3035991554f2SKenneth D. Merry request = ccb->smpio.smp_request; 3036991554f2SKenneth D. Merry response = ccb->smpio.smp_response; 3037991554f2SKenneth D. Merry break; 3038991554f2SKenneth D. Merry default: 3039a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID); 3040991554f2SKenneth D. Merry xpt_done(ccb); 3041991554f2SKenneth D. Merry return; 3042991554f2SKenneth D. Merry } 3043c503306dSKenneth D. Merry #else /* __FreeBSD_version < 1000028 */ 3044991554f2SKenneth D. Merry /* 3045991554f2SKenneth D. Merry * XXX We don't yet support physical addresses here. 3046991554f2SKenneth D. Merry */ 3047991554f2SKenneth D. Merry if (ccb->ccb_h.flags & (CAM_DATA_PHYS|CAM_SG_LIST_PHYS)) { 3048a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: physical addresses not " 3049a2c14879SStephen McConnell "supported\n", __func__); 3050a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID); 3051991554f2SKenneth D. Merry xpt_done(ccb); 3052991554f2SKenneth D. Merry return; 3053991554f2SKenneth D. Merry } 3054991554f2SKenneth D. Merry 3055991554f2SKenneth D. Merry /* 3056991554f2SKenneth D. Merry * If the user wants to send an S/G list, check to make sure they 3057991554f2SKenneth D. Merry * have single buffers. 3058991554f2SKenneth D. Merry */ 3059991554f2SKenneth D. Merry if (ccb->ccb_h.flags & CAM_SCATTER_VALID) { 3060991554f2SKenneth D. Merry /* 3061991554f2SKenneth D. Merry * The chip does not support more than one buffer for the 3062991554f2SKenneth D. Merry * request or response. 3063991554f2SKenneth D. Merry */ 3064991554f2SKenneth D. Merry if ((ccb->smpio.smp_request_sglist_cnt > 1) 3065991554f2SKenneth D. Merry || (ccb->smpio.smp_response_sglist_cnt > 1)) { 3066991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: multiple request or " 3067991554f2SKenneth D. Merry "response buffer segments not supported for SMP\n", 3068991554f2SKenneth D. Merry __func__); 3069a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_INVALID); 3070991554f2SKenneth D. Merry xpt_done(ccb); 3071991554f2SKenneth D. Merry return; 3072991554f2SKenneth D. Merry } 3073991554f2SKenneth D. Merry 3074991554f2SKenneth D. Merry /* 3075991554f2SKenneth D. Merry * The CAM_SCATTER_VALID flag was originally implemented 3076991554f2SKenneth D. Merry * for the XPT_SCSI_IO CCB, which only has one data pointer. 3077991554f2SKenneth D. Merry * We have two. So, just take that flag to mean that we 3078991554f2SKenneth D. Merry * might have S/G lists, and look at the S/G segment count 3079991554f2SKenneth D. Merry * to figure out whether that is the case for each individual 3080991554f2SKenneth D. Merry * buffer. 3081991554f2SKenneth D. Merry */ 3082991554f2SKenneth D. Merry if (ccb->smpio.smp_request_sglist_cnt != 0) { 3083991554f2SKenneth D. Merry bus_dma_segment_t *req_sg; 3084991554f2SKenneth D. Merry 3085991554f2SKenneth D. Merry req_sg = (bus_dma_segment_t *)ccb->smpio.smp_request; 3086c503306dSKenneth D. Merry request = (uint8_t *)(uintptr_t)req_sg[0].ds_addr; 3087991554f2SKenneth D. Merry } else 3088991554f2SKenneth D. Merry request = ccb->smpio.smp_request; 3089991554f2SKenneth D. Merry 3090991554f2SKenneth D. Merry if (ccb->smpio.smp_response_sglist_cnt != 0) { 3091991554f2SKenneth D. Merry bus_dma_segment_t *rsp_sg; 3092991554f2SKenneth D. Merry 3093991554f2SKenneth D. Merry rsp_sg = (bus_dma_segment_t *)ccb->smpio.smp_response; 3094c503306dSKenneth D. Merry response = (uint8_t *)(uintptr_t)rsp_sg[0].ds_addr; 3095991554f2SKenneth D. Merry } else 3096991554f2SKenneth D. Merry response = ccb->smpio.smp_response; 3097991554f2SKenneth D. Merry } else { 3098991554f2SKenneth D. Merry request = ccb->smpio.smp_request; 3099991554f2SKenneth D. Merry response = ccb->smpio.smp_response; 3100991554f2SKenneth D. Merry } 3101c503306dSKenneth D. Merry #endif /* __FreeBSD_version < 1000028 */ 3102991554f2SKenneth D. Merry 3103991554f2SKenneth D. Merry cm = mpr_alloc_command(sc); 3104991554f2SKenneth D. Merry if (cm == NULL) { 31057a2a6a1aSStephen McConnell mpr_dprint(sc, MPR_ERROR, "%s: cannot allocate command\n", 31067a2a6a1aSStephen McConnell __func__); 3107a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL); 3108991554f2SKenneth D. Merry xpt_done(ccb); 3109991554f2SKenneth D. Merry return; 3110991554f2SKenneth D. Merry } 3111991554f2SKenneth D. Merry 3112991554f2SKenneth D. Merry req = (MPI2_SMP_PASSTHROUGH_REQUEST *)cm->cm_req; 3113991554f2SKenneth D. Merry bzero(req, sizeof(*req)); 3114991554f2SKenneth D. Merry req->Function = MPI2_FUNCTION_SMP_PASSTHROUGH; 3115991554f2SKenneth D. Merry 3116991554f2SKenneth D. Merry /* Allow the chip to use any route to this SAS address. */ 3117991554f2SKenneth D. Merry req->PhysicalPort = 0xff; 3118991554f2SKenneth D. Merry 3119991554f2SKenneth D. Merry req->RequestDataLength = htole16(ccb->smpio.smp_request_len); 3120991554f2SKenneth D. Merry req->SGLFlags = 3121991554f2SKenneth D. Merry MPI2_SGLFLAGS_SYSTEM_ADDRESS_SPACE | MPI2_SGLFLAGS_SGL_TYPE_MPI; 3122991554f2SKenneth D. Merry 3123991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, "%s: sending SMP request to SAS address " 3124991554f2SKenneth D. Merry "%#jx\n", __func__, (uintmax_t)sasaddr); 3125991554f2SKenneth D. Merry 3126991554f2SKenneth D. Merry mpr_init_sge(cm, req, &req->SGL); 3127991554f2SKenneth D. Merry 3128991554f2SKenneth D. Merry /* 3129991554f2SKenneth D. Merry * Set up a uio to pass into mpr_map_command(). This allows us to 3130991554f2SKenneth D. Merry * do one map command, and one busdma call in there. 3131991554f2SKenneth D. Merry */ 3132991554f2SKenneth D. Merry cm->cm_uio.uio_iov = cm->cm_iovec; 3133991554f2SKenneth D. Merry cm->cm_uio.uio_iovcnt = 2; 3134991554f2SKenneth D. Merry cm->cm_uio.uio_segflg = UIO_SYSSPACE; 3135991554f2SKenneth D. Merry 3136991554f2SKenneth D. Merry /* 3137991554f2SKenneth D. Merry * The read/write flag isn't used by busdma, but set it just in 3138991554f2SKenneth D. Merry * case. This isn't exactly accurate, either, since we're going in 3139991554f2SKenneth D. Merry * both directions. 3140991554f2SKenneth D. Merry */ 3141991554f2SKenneth D. Merry cm->cm_uio.uio_rw = UIO_WRITE; 3142991554f2SKenneth D. Merry 3143991554f2SKenneth D. Merry cm->cm_iovec[0].iov_base = request; 3144991554f2SKenneth D. Merry cm->cm_iovec[0].iov_len = le16toh(req->RequestDataLength); 3145991554f2SKenneth D. Merry cm->cm_iovec[1].iov_base = response; 3146991554f2SKenneth D. Merry cm->cm_iovec[1].iov_len = ccb->smpio.smp_response_len; 3147991554f2SKenneth D. Merry 3148991554f2SKenneth D. Merry cm->cm_uio.uio_resid = cm->cm_iovec[0].iov_len + 3149991554f2SKenneth D. Merry cm->cm_iovec[1].iov_len; 3150991554f2SKenneth D. Merry 3151991554f2SKenneth D. Merry /* 3152991554f2SKenneth D. Merry * Trigger a warning message in mpr_data_cb() for the user if we 3153991554f2SKenneth D. Merry * wind up exceeding two S/G segments. The chip expects one 3154991554f2SKenneth D. Merry * segment for the request and another for the response. 3155991554f2SKenneth D. Merry */ 3156991554f2SKenneth D. Merry cm->cm_max_segs = 2; 3157991554f2SKenneth D. Merry 3158991554f2SKenneth D. Merry cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 3159991554f2SKenneth D. Merry cm->cm_complete = mprsas_smpio_complete; 3160991554f2SKenneth D. Merry cm->cm_complete_data = ccb; 3161991554f2SKenneth D. Merry 3162991554f2SKenneth D. Merry /* 3163991554f2SKenneth D. Merry * Tell the mapping code that we're using a uio, and that this is 3164991554f2SKenneth D. Merry * an SMP passthrough request. There is a little special-case 3165991554f2SKenneth D. Merry * logic there (in mpr_data_cb()) to handle the bidirectional 3166991554f2SKenneth D. Merry * transfer. 3167991554f2SKenneth D. Merry */ 3168991554f2SKenneth D. Merry cm->cm_flags |= MPR_CM_FLAGS_USE_UIO | MPR_CM_FLAGS_SMP_PASS | 3169991554f2SKenneth D. Merry MPR_CM_FLAGS_DATAIN | MPR_CM_FLAGS_DATAOUT; 3170991554f2SKenneth D. Merry 3171991554f2SKenneth D. Merry /* The chip data format is little endian. */ 3172991554f2SKenneth D. Merry req->SASAddress.High = htole32(sasaddr >> 32); 3173991554f2SKenneth D. Merry req->SASAddress.Low = htole32(sasaddr); 3174991554f2SKenneth D. Merry 3175991554f2SKenneth D. Merry /* 3176991554f2SKenneth D. Merry * XXX Note that we don't have a timeout/abort mechanism here. 3177991554f2SKenneth D. Merry * From the manual, it looks like task management requests only 3178991554f2SKenneth D. Merry * work for SCSI IO and SATA passthrough requests. We may need to 3179991554f2SKenneth D. Merry * have a mechanism to retry requests in the event of a chip reset 3180991554f2SKenneth D. Merry * at least. Hopefully the chip will insure that any errors short 3181991554f2SKenneth D. Merry * of that are relayed back to the driver. 3182991554f2SKenneth D. Merry */ 3183991554f2SKenneth D. Merry error = mpr_map_command(sc, cm); 3184991554f2SKenneth D. Merry if ((error != 0) && (error != EINPROGRESS)) { 3185991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: error %d returned from " 3186991554f2SKenneth D. Merry "mpr_map_command()\n", __func__, error); 3187991554f2SKenneth D. Merry goto bailout_error; 3188991554f2SKenneth D. Merry } 3189991554f2SKenneth D. Merry 3190991554f2SKenneth D. Merry return; 3191991554f2SKenneth D. Merry 3192991554f2SKenneth D. Merry bailout_error: 3193991554f2SKenneth D. Merry mpr_free_command(sc, cm); 3194a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL); 3195991554f2SKenneth D. Merry xpt_done(ccb); 3196991554f2SKenneth D. Merry return; 3197991554f2SKenneth D. Merry } 3198991554f2SKenneth D. Merry 3199991554f2SKenneth D. Merry static void 3200991554f2SKenneth D. Merry mprsas_action_smpio(struct mprsas_softc *sassc, union ccb *ccb) 3201991554f2SKenneth D. Merry { 3202991554f2SKenneth D. Merry struct mpr_softc *sc; 3203991554f2SKenneth D. Merry struct mprsas_target *targ; 3204991554f2SKenneth D. Merry uint64_t sasaddr = 0; 3205991554f2SKenneth D. Merry 3206991554f2SKenneth D. Merry sc = sassc->sc; 3207991554f2SKenneth D. Merry 3208991554f2SKenneth D. Merry /* 3209991554f2SKenneth D. Merry * Make sure the target exists. 3210991554f2SKenneth D. Merry */ 3211991554f2SKenneth D. Merry KASSERT(ccb->ccb_h.target_id < sassc->maxtargets, 3212991554f2SKenneth D. Merry ("Target %d out of bounds in XPT_SMP_IO\n", ccb->ccb_h.target_id)); 3213991554f2SKenneth D. Merry targ = &sassc->targets[ccb->ccb_h.target_id]; 3214991554f2SKenneth D. Merry if (targ->handle == 0x0) { 3215991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: target %d does not exist!\n", 3216991554f2SKenneth D. Merry __func__, ccb->ccb_h.target_id); 3217a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_SEL_TIMEOUT); 3218991554f2SKenneth D. Merry xpt_done(ccb); 3219991554f2SKenneth D. Merry return; 3220991554f2SKenneth D. Merry } 3221991554f2SKenneth D. Merry 3222991554f2SKenneth D. Merry /* 3223991554f2SKenneth D. Merry * If this device has an embedded SMP target, we'll talk to it 3224991554f2SKenneth D. Merry * directly. 3225991554f2SKenneth D. Merry * figure out what the expander's address is. 3226991554f2SKenneth D. Merry */ 3227991554f2SKenneth D. Merry if ((targ->devinfo & MPI2_SAS_DEVICE_INFO_SMP_TARGET) != 0) 3228991554f2SKenneth D. Merry sasaddr = targ->sasaddr; 3229991554f2SKenneth D. Merry 3230991554f2SKenneth D. Merry /* 3231991554f2SKenneth D. Merry * If we don't have a SAS address for the expander yet, try 3232991554f2SKenneth D. Merry * grabbing it from the page 0x83 information cached in the 3233991554f2SKenneth D. Merry * transport layer for this target. LSI expanders report the 3234991554f2SKenneth D. Merry * expander SAS address as the port-associated SAS address in 3235991554f2SKenneth D. Merry * Inquiry VPD page 0x83. Maxim expanders don't report it in page 3236991554f2SKenneth D. Merry * 0x83. 3237991554f2SKenneth D. Merry * 3238991554f2SKenneth D. Merry * XXX KDM disable this for now, but leave it commented out so that 3239991554f2SKenneth D. Merry * it is obvious that this is another possible way to get the SAS 3240991554f2SKenneth D. Merry * address. 3241991554f2SKenneth D. Merry * 3242991554f2SKenneth D. Merry * The parent handle method below is a little more reliable, and 3243991554f2SKenneth D. Merry * the other benefit is that it works for devices other than SES 3244991554f2SKenneth D. Merry * devices. So you can send a SMP request to a da(4) device and it 3245991554f2SKenneth D. Merry * will get routed to the expander that device is attached to. 3246991554f2SKenneth D. Merry * (Assuming the da(4) device doesn't contain an SMP target...) 3247991554f2SKenneth D. Merry */ 3248991554f2SKenneth D. Merry #if 0 3249991554f2SKenneth D. Merry if (sasaddr == 0) 3250991554f2SKenneth D. Merry sasaddr = xpt_path_sas_addr(ccb->ccb_h.path); 3251991554f2SKenneth D. Merry #endif 3252991554f2SKenneth D. Merry 3253991554f2SKenneth D. Merry /* 3254991554f2SKenneth D. Merry * If we still don't have a SAS address for the expander, look for 3255991554f2SKenneth D. Merry * the parent device of this device, which is probably the expander. 3256991554f2SKenneth D. Merry */ 3257991554f2SKenneth D. Merry if (sasaddr == 0) { 3258991554f2SKenneth D. Merry #ifdef OLD_MPR_PROBE 3259991554f2SKenneth D. Merry struct mprsas_target *parent_target; 3260991554f2SKenneth D. Merry #endif 3261991554f2SKenneth D. Merry 3262991554f2SKenneth D. Merry if (targ->parent_handle == 0x0) { 3263991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d does not have " 3264991554f2SKenneth D. Merry "a valid parent handle!\n", __func__, targ->handle); 3265a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3266991554f2SKenneth D. Merry goto bailout; 3267991554f2SKenneth D. Merry } 3268991554f2SKenneth D. Merry #ifdef OLD_MPR_PROBE 3269991554f2SKenneth D. Merry parent_target = mprsas_find_target_by_handle(sassc, 0, 3270991554f2SKenneth D. Merry targ->parent_handle); 3271991554f2SKenneth D. Merry 3272991554f2SKenneth D. Merry if (parent_target == NULL) { 3273991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d does not have " 3274991554f2SKenneth D. Merry "a valid parent target!\n", __func__, targ->handle); 3275a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3276991554f2SKenneth D. Merry goto bailout; 3277991554f2SKenneth D. Merry } 3278991554f2SKenneth D. Merry 3279991554f2SKenneth D. Merry if ((parent_target->devinfo & 3280991554f2SKenneth D. Merry MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) { 3281991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent %d " 3282991554f2SKenneth D. Merry "does not have an SMP target!\n", __func__, 3283991554f2SKenneth D. Merry targ->handle, parent_target->handle); 3284a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3285991554f2SKenneth D. Merry goto bailout; 3286991554f2SKenneth D. Merry } 3287991554f2SKenneth D. Merry 3288991554f2SKenneth D. Merry sasaddr = parent_target->sasaddr; 3289991554f2SKenneth D. Merry #else /* OLD_MPR_PROBE */ 3290991554f2SKenneth D. Merry if ((targ->parent_devinfo & 3291991554f2SKenneth D. Merry MPI2_SAS_DEVICE_INFO_SMP_TARGET) == 0) { 3292991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent %d " 3293991554f2SKenneth D. Merry "does not have an SMP target!\n", __func__, 3294991554f2SKenneth D. Merry targ->handle, targ->parent_handle); 3295a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3296991554f2SKenneth D. Merry goto bailout; 3297991554f2SKenneth D. Merry 3298991554f2SKenneth D. Merry } 3299991554f2SKenneth D. Merry if (targ->parent_sasaddr == 0x0) { 3300991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: handle %d parent handle " 3301991554f2SKenneth D. Merry "%d does not have a valid SAS address!\n", __func__, 3302991554f2SKenneth D. Merry targ->handle, targ->parent_handle); 3303a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3304991554f2SKenneth D. Merry goto bailout; 3305991554f2SKenneth D. Merry } 3306991554f2SKenneth D. Merry 3307991554f2SKenneth D. Merry sasaddr = targ->parent_sasaddr; 3308991554f2SKenneth D. Merry #endif /* OLD_MPR_PROBE */ 3309991554f2SKenneth D. Merry 3310991554f2SKenneth D. Merry } 3311991554f2SKenneth D. Merry 3312991554f2SKenneth D. Merry if (sasaddr == 0) { 3313991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INFO, "%s: unable to find SAS address for " 3314991554f2SKenneth D. Merry "handle %d\n", __func__, targ->handle); 3315a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_DEV_NOT_THERE); 3316991554f2SKenneth D. Merry goto bailout; 3317991554f2SKenneth D. Merry } 3318991554f2SKenneth D. Merry mprsas_send_smpcmd(sassc, ccb, sasaddr); 3319991554f2SKenneth D. Merry 3320991554f2SKenneth D. Merry return; 3321991554f2SKenneth D. Merry 3322991554f2SKenneth D. Merry bailout: 3323991554f2SKenneth D. Merry xpt_done(ccb); 3324991554f2SKenneth D. Merry 3325991554f2SKenneth D. Merry } 3326991554f2SKenneth D. Merry #endif //__FreeBSD_version >= 900026 3327991554f2SKenneth D. Merry 3328991554f2SKenneth D. Merry static void 3329991554f2SKenneth D. Merry mprsas_action_resetdev(struct mprsas_softc *sassc, union ccb *ccb) 3330991554f2SKenneth D. Merry { 3331991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 3332991554f2SKenneth D. Merry struct mpr_softc *sc; 3333991554f2SKenneth D. Merry struct mpr_command *tm; 3334991554f2SKenneth D. Merry struct mprsas_target *targ; 3335991554f2SKenneth D. Merry 3336991554f2SKenneth D. Merry MPR_FUNCTRACE(sassc->sc); 3337991554f2SKenneth D. Merry mtx_assert(&sassc->sc->mpr_mtx, MA_OWNED); 3338991554f2SKenneth D. Merry 33397a2a6a1aSStephen McConnell KASSERT(ccb->ccb_h.target_id < sassc->maxtargets, ("Target %d out of " 33407a2a6a1aSStephen McConnell "bounds in XPT_RESET_DEV\n", ccb->ccb_h.target_id)); 3341991554f2SKenneth D. Merry sc = sassc->sc; 33423921a9f7SScott Long tm = mprsas_alloc_tm(sc); 3343991554f2SKenneth D. Merry if (tm == NULL) { 33447a2a6a1aSStephen McConnell mpr_dprint(sc, MPR_ERROR, "command alloc failure in " 33457a2a6a1aSStephen McConnell "mprsas_action_resetdev\n"); 3346a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_RESRC_UNAVAIL); 3347991554f2SKenneth D. Merry xpt_done(ccb); 3348991554f2SKenneth D. Merry return; 3349991554f2SKenneth D. Merry } 3350991554f2SKenneth D. Merry 3351991554f2SKenneth D. Merry targ = &sassc->targets[ccb->ccb_h.target_id]; 3352991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 3353991554f2SKenneth D. Merry req->DevHandle = htole16(targ->handle); 3354991554f2SKenneth D. Merry req->TaskType = MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET; 3355991554f2SKenneth D. Merry 335689d1c21fSKashyap D Desai if (!targ->is_nvme || sc->custom_nvme_tm_handling) { 3357991554f2SKenneth D. Merry /* SAS Hard Link Reset / SATA Link Reset */ 3358991554f2SKenneth D. Merry req->MsgFlags = MPI2_SCSITASKMGMT_MSGFLAGS_LINK_RESET; 335989d1c21fSKashyap D Desai } else { 336089d1c21fSKashyap D Desai /* PCIe Protocol Level Reset*/ 336189d1c21fSKashyap D Desai req->MsgFlags = 336289d1c21fSKashyap D Desai MPI26_SCSITASKMGMT_MSGFLAGS_PROTOCOL_LVL_RST_PCIE; 336389d1c21fSKashyap D Desai } 3364991554f2SKenneth D. Merry 3365991554f2SKenneth D. Merry tm->cm_data = NULL; 3366991554f2SKenneth D. Merry tm->cm_complete = mprsas_resetdev_complete; 3367991554f2SKenneth D. Merry tm->cm_complete_data = ccb; 3368a2c14879SStephen McConnell 3369a2c14879SStephen McConnell mpr_dprint(sc, MPR_INFO, "%s: Sending reset for target ID %d\n", 3370a2c14879SStephen McConnell __func__, targ->tid); 3371991554f2SKenneth D. Merry tm->cm_targ = targ; 3372a2c14879SStephen McConnell 337346b9415fSScott Long mprsas_prepare_for_tm(sc, tm, targ, CAM_LUN_WILDCARD); 3374991554f2SKenneth D. Merry mpr_map_command(sc, tm); 3375991554f2SKenneth D. Merry } 3376991554f2SKenneth D. Merry 3377991554f2SKenneth D. Merry static void 3378991554f2SKenneth D. Merry mprsas_resetdev_complete(struct mpr_softc *sc, struct mpr_command *tm) 3379991554f2SKenneth D. Merry { 3380991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REPLY *resp; 3381991554f2SKenneth D. Merry union ccb *ccb; 3382991554f2SKenneth D. Merry 3383991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 3384991554f2SKenneth D. Merry mtx_assert(&sc->mpr_mtx, MA_OWNED); 3385991554f2SKenneth D. Merry 3386991554f2SKenneth D. Merry resp = (MPI2_SCSI_TASK_MANAGE_REPLY *)tm->cm_reply; 3387991554f2SKenneth D. Merry ccb = tm->cm_complete_data; 3388991554f2SKenneth D. Merry 3389991554f2SKenneth D. Merry /* 3390991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 3391991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 3392991554f2SKenneth D. Merry * task management commands don't have S/G lists. 3393991554f2SKenneth D. Merry */ 3394991554f2SKenneth D. Merry if ((tm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 3395991554f2SKenneth D. Merry MPI2_SCSI_TASK_MANAGE_REQUEST *req; 3396991554f2SKenneth D. Merry 3397991554f2SKenneth D. Merry req = (MPI2_SCSI_TASK_MANAGE_REQUEST *)tm->cm_req; 3398991554f2SKenneth D. Merry 3399991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for reset of " 3400991554f2SKenneth D. Merry "handle %#04x! This should not happen!\n", __func__, 3401991554f2SKenneth D. Merry tm->cm_flags, req->DevHandle); 3402a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 3403991554f2SKenneth D. Merry goto bailout; 3404991554f2SKenneth D. Merry } 3405991554f2SKenneth D. Merry 34067a2a6a1aSStephen McConnell mpr_dprint(sc, MPR_XINFO, "%s: IOCStatus = 0x%x ResponseCode = 0x%x\n", 34077a2a6a1aSStephen McConnell __func__, le16toh(resp->IOCStatus), le32toh(resp->ResponseCode)); 3408991554f2SKenneth D. Merry 3409991554f2SKenneth D. Merry if (le32toh(resp->ResponseCode) == MPI2_SCSITASKMGMT_RSP_TM_COMPLETE) { 3410a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP); 3411991554f2SKenneth D. Merry mprsas_announce_reset(sc, AC_SENT_BDR, tm->cm_targ->tid, 3412991554f2SKenneth D. Merry CAM_LUN_WILDCARD); 3413991554f2SKenneth D. Merry } 3414991554f2SKenneth D. Merry else 3415a2c14879SStephen McConnell mprsas_set_ccbstatus(ccb, CAM_REQ_CMP_ERR); 3416991554f2SKenneth D. Merry 3417991554f2SKenneth D. Merry bailout: 3418991554f2SKenneth D. Merry 3419991554f2SKenneth D. Merry mprsas_free_tm(sc, tm); 3420991554f2SKenneth D. Merry xpt_done(ccb); 3421991554f2SKenneth D. Merry } 3422991554f2SKenneth D. Merry 3423991554f2SKenneth D. Merry static void 3424991554f2SKenneth D. Merry mprsas_poll(struct cam_sim *sim) 3425991554f2SKenneth D. Merry { 3426991554f2SKenneth D. Merry struct mprsas_softc *sassc; 3427991554f2SKenneth D. Merry 3428991554f2SKenneth D. Merry sassc = cam_sim_softc(sim); 3429991554f2SKenneth D. Merry 3430991554f2SKenneth D. Merry if (sassc->sc->mpr_debug & MPR_TRACE) { 3431991554f2SKenneth D. Merry /* frequent debug messages during a panic just slow 3432991554f2SKenneth D. Merry * everything down too much. 3433991554f2SKenneth D. Merry */ 3434a2c14879SStephen McConnell mpr_dprint(sassc->sc, MPR_XINFO, "%s clearing MPR_TRACE\n", 3435a2c14879SStephen McConnell __func__); 3436991554f2SKenneth D. Merry sassc->sc->mpr_debug &= ~MPR_TRACE; 3437991554f2SKenneth D. Merry } 3438991554f2SKenneth D. Merry 3439991554f2SKenneth D. Merry mpr_intr_locked(sassc->sc); 3440991554f2SKenneth D. Merry } 3441991554f2SKenneth D. Merry 3442991554f2SKenneth D. Merry static void 3443991554f2SKenneth D. Merry mprsas_async(void *callback_arg, uint32_t code, struct cam_path *path, 3444991554f2SKenneth D. Merry void *arg) 3445991554f2SKenneth D. Merry { 3446991554f2SKenneth D. Merry struct mpr_softc *sc; 3447991554f2SKenneth D. Merry 3448991554f2SKenneth D. Merry sc = (struct mpr_softc *)callback_arg; 3449991554f2SKenneth D. Merry 3450991554f2SKenneth D. Merry switch (code) { 3451991554f2SKenneth D. Merry #if (__FreeBSD_version >= 1000006) || \ 3452991554f2SKenneth D. Merry ((__FreeBSD_version >= 901503) && (__FreeBSD_version < 1000000)) 3453991554f2SKenneth D. Merry case AC_ADVINFO_CHANGED: { 3454991554f2SKenneth D. Merry struct mprsas_target *target; 3455991554f2SKenneth D. Merry struct mprsas_softc *sassc; 3456991554f2SKenneth D. Merry struct scsi_read_capacity_data_long rcap_buf; 3457991554f2SKenneth D. Merry struct ccb_dev_advinfo cdai; 3458991554f2SKenneth D. Merry struct mprsas_lun *lun; 3459991554f2SKenneth D. Merry lun_id_t lunid; 3460991554f2SKenneth D. Merry int found_lun; 3461991554f2SKenneth D. Merry uintptr_t buftype; 3462991554f2SKenneth D. Merry 3463991554f2SKenneth D. Merry buftype = (uintptr_t)arg; 3464991554f2SKenneth D. Merry 3465991554f2SKenneth D. Merry found_lun = 0; 3466991554f2SKenneth D. Merry sassc = sc->sassc; 3467991554f2SKenneth D. Merry 3468991554f2SKenneth D. Merry /* 3469991554f2SKenneth D. Merry * We're only interested in read capacity data changes. 3470991554f2SKenneth D. Merry */ 3471991554f2SKenneth D. Merry if (buftype != CDAI_TYPE_RCAPLONG) 3472991554f2SKenneth D. Merry break; 3473991554f2SKenneth D. Merry 3474991554f2SKenneth D. Merry /* 347507aa4de1SKenneth D. Merry * See the comment in mpr_attach_sas() for a detailed 347607aa4de1SKenneth D. Merry * explanation. In these versions of FreeBSD we register 347707aa4de1SKenneth D. Merry * for all events and filter out the events that don't 347807aa4de1SKenneth D. Merry * apply to us. 347907aa4de1SKenneth D. Merry */ 348007aa4de1SKenneth D. Merry #if (__FreeBSD_version < 1000703) || \ 348107aa4de1SKenneth D. Merry ((__FreeBSD_version >= 1100000) && (__FreeBSD_version < 1100002)) 348207aa4de1SKenneth D. Merry if (xpt_path_path_id(path) != sassc->sim->path_id) 348307aa4de1SKenneth D. Merry break; 348407aa4de1SKenneth D. Merry #endif 348507aa4de1SKenneth D. Merry 348607aa4de1SKenneth D. Merry /* 3487991554f2SKenneth D. Merry * We should have a handle for this, but check to make sure. 3488991554f2SKenneth D. Merry */ 3489991554f2SKenneth D. Merry KASSERT(xpt_path_target_id(path) < sassc->maxtargets, 3490991554f2SKenneth D. Merry ("Target %d out of bounds in mprsas_async\n", 3491991554f2SKenneth D. Merry xpt_path_target_id(path))); 3492991554f2SKenneth D. Merry target = &sassc->targets[xpt_path_target_id(path)]; 3493991554f2SKenneth D. Merry if (target->handle == 0) 3494991554f2SKenneth D. Merry break; 3495991554f2SKenneth D. Merry 3496991554f2SKenneth D. Merry lunid = xpt_path_lun_id(path); 3497991554f2SKenneth D. Merry 3498991554f2SKenneth D. Merry SLIST_FOREACH(lun, &target->luns, lun_link) { 3499991554f2SKenneth D. Merry if (lun->lun_id == lunid) { 3500991554f2SKenneth D. Merry found_lun = 1; 3501991554f2SKenneth D. Merry break; 3502991554f2SKenneth D. Merry } 3503991554f2SKenneth D. Merry } 3504991554f2SKenneth D. Merry 3505991554f2SKenneth D. Merry if (found_lun == 0) { 3506991554f2SKenneth D. Merry lun = malloc(sizeof(struct mprsas_lun), M_MPR, 3507991554f2SKenneth D. Merry M_NOWAIT | M_ZERO); 3508991554f2SKenneth D. Merry if (lun == NULL) { 3509991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "Unable to alloc " 3510991554f2SKenneth D. Merry "LUN for EEDP support.\n"); 3511991554f2SKenneth D. Merry break; 3512991554f2SKenneth D. Merry } 3513991554f2SKenneth D. Merry lun->lun_id = lunid; 3514991554f2SKenneth D. Merry SLIST_INSERT_HEAD(&target->luns, lun, lun_link); 3515991554f2SKenneth D. Merry } 3516991554f2SKenneth D. Merry 3517991554f2SKenneth D. Merry bzero(&rcap_buf, sizeof(rcap_buf)); 3518991554f2SKenneth D. Merry xpt_setup_ccb(&cdai.ccb_h, path, CAM_PRIORITY_NORMAL); 3519991554f2SKenneth D. Merry cdai.ccb_h.func_code = XPT_DEV_ADVINFO; 3520991554f2SKenneth D. Merry cdai.ccb_h.flags = CAM_DIR_IN; 3521991554f2SKenneth D. Merry cdai.buftype = CDAI_TYPE_RCAPLONG; 35226c0541faSKenneth D. Merry #if (__FreeBSD_version >= 1100061) || \ 35236c0541faSKenneth D. Merry ((__FreeBSD_version >= 1001510) && (__FreeBSD_version < 1100000)) 3524e8577fb4SKenneth D. Merry cdai.flags = CDAI_FLAG_NONE; 3525e8577fb4SKenneth D. Merry #else 3526991554f2SKenneth D. Merry cdai.flags = 0; 3527e8577fb4SKenneth D. Merry #endif 3528991554f2SKenneth D. Merry cdai.bufsiz = sizeof(rcap_buf); 3529991554f2SKenneth D. Merry cdai.buf = (uint8_t *)&rcap_buf; 3530991554f2SKenneth D. Merry xpt_action((union ccb *)&cdai); 3531991554f2SKenneth D. Merry if ((cdai.ccb_h.status & CAM_DEV_QFRZN) != 0) 3532991554f2SKenneth D. Merry cam_release_devq(cdai.ccb_h.path, 0, 0, 0, FALSE); 3533991554f2SKenneth D. Merry 3534a2c14879SStephen McConnell if ((mprsas_get_ccbstatus((union ccb *)&cdai) == CAM_REQ_CMP) 3535991554f2SKenneth D. Merry && (rcap_buf.prot & SRC16_PROT_EN)) { 35368881681bSKenneth D. Merry switch (rcap_buf.prot & SRC16_P_TYPE) { 35378881681bSKenneth D. Merry case SRC16_PTYPE_1: 35388881681bSKenneth D. Merry case SRC16_PTYPE_3: 3539991554f2SKenneth D. Merry lun->eedp_formatted = TRUE; 35408881681bSKenneth D. Merry lun->eedp_block_size = 35418881681bSKenneth D. Merry scsi_4btoul(rcap_buf.length); 35428881681bSKenneth D. Merry break; 35438881681bSKenneth D. Merry case SRC16_PTYPE_2: 35448881681bSKenneth D. Merry default: 35458881681bSKenneth D. Merry lun->eedp_formatted = FALSE; 35468881681bSKenneth D. Merry lun->eedp_block_size = 0; 35478881681bSKenneth D. Merry break; 35488881681bSKenneth D. Merry } 3549991554f2SKenneth D. Merry } else { 3550991554f2SKenneth D. Merry lun->eedp_formatted = FALSE; 3551991554f2SKenneth D. Merry lun->eedp_block_size = 0; 3552991554f2SKenneth D. Merry } 3553991554f2SKenneth D. Merry break; 3554991554f2SKenneth D. Merry } 3555991554f2SKenneth D. Merry #endif 3556991554f2SKenneth D. Merry case AC_FOUND_DEVICE: { 3557991554f2SKenneth D. Merry struct ccb_getdev *cgd; 3558991554f2SKenneth D. Merry 355907aa4de1SKenneth D. Merry /* 356007aa4de1SKenneth D. Merry * See the comment in mpr_attach_sas() for a detailed 356107aa4de1SKenneth D. Merry * explanation. In these versions of FreeBSD we register 356207aa4de1SKenneth D. Merry * for all events and filter out the events that don't 356307aa4de1SKenneth D. Merry * apply to us. 356407aa4de1SKenneth D. Merry */ 356507aa4de1SKenneth D. Merry #if (__FreeBSD_version < 1000703) || \ 356607aa4de1SKenneth D. Merry ((__FreeBSD_version >= 1100000) && (__FreeBSD_version < 1100002)) 356707aa4de1SKenneth D. Merry if (xpt_path_path_id(path) != sc->sassc->sim->path_id) 356807aa4de1SKenneth D. Merry break; 356907aa4de1SKenneth D. Merry #endif 357007aa4de1SKenneth D. Merry 3571991554f2SKenneth D. Merry cgd = arg; 3572991554f2SKenneth D. Merry #if (__FreeBSD_version < 901503) || \ 3573991554f2SKenneth D. Merry ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000006)) 3574991554f2SKenneth D. Merry mprsas_check_eedp(sc, path, cgd); 3575991554f2SKenneth D. Merry #endif 3576991554f2SKenneth D. Merry break; 3577991554f2SKenneth D. Merry } 3578991554f2SKenneth D. Merry default: 3579991554f2SKenneth D. Merry break; 3580991554f2SKenneth D. Merry } 3581991554f2SKenneth D. Merry } 3582991554f2SKenneth D. Merry 3583991554f2SKenneth D. Merry #if (__FreeBSD_version < 901503) || \ 3584991554f2SKenneth D. Merry ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000006)) 3585991554f2SKenneth D. Merry static void 3586991554f2SKenneth D. Merry mprsas_check_eedp(struct mpr_softc *sc, struct cam_path *path, 3587991554f2SKenneth D. Merry struct ccb_getdev *cgd) 3588991554f2SKenneth D. Merry { 3589991554f2SKenneth D. Merry struct mprsas_softc *sassc = sc->sassc; 3590991554f2SKenneth D. Merry struct ccb_scsiio *csio; 3591991554f2SKenneth D. Merry struct scsi_read_capacity_16 *scsi_cmd; 3592991554f2SKenneth D. Merry struct scsi_read_capacity_eedp *rcap_buf; 3593991554f2SKenneth D. Merry path_id_t pathid; 3594991554f2SKenneth D. Merry target_id_t targetid; 3595991554f2SKenneth D. Merry lun_id_t lunid; 3596991554f2SKenneth D. Merry union ccb *ccb; 3597991554f2SKenneth D. Merry struct cam_path *local_path; 3598991554f2SKenneth D. Merry struct mprsas_target *target; 3599991554f2SKenneth D. Merry struct mprsas_lun *lun; 3600991554f2SKenneth D. Merry uint8_t found_lun; 3601991554f2SKenneth D. Merry char path_str[64]; 3602991554f2SKenneth D. Merry 3603991554f2SKenneth D. Merry pathid = cam_sim_path(sassc->sim); 3604991554f2SKenneth D. Merry targetid = xpt_path_target_id(path); 3605991554f2SKenneth D. Merry lunid = xpt_path_lun_id(path); 3606991554f2SKenneth D. Merry 36077a2a6a1aSStephen McConnell KASSERT(targetid < sassc->maxtargets, ("Target %d out of bounds in " 36087a2a6a1aSStephen McConnell "mprsas_check_eedp\n", targetid)); 3609991554f2SKenneth D. Merry target = &sassc->targets[targetid]; 3610991554f2SKenneth D. Merry if (target->handle == 0x0) 3611991554f2SKenneth D. Merry return; 3612991554f2SKenneth D. Merry 3613991554f2SKenneth D. Merry /* 3614991554f2SKenneth D. Merry * Determine if the device is EEDP capable. 3615991554f2SKenneth D. Merry * 3616991554f2SKenneth D. Merry * If this flag is set in the inquiry data, the device supports 3617991554f2SKenneth D. Merry * protection information, and must support the 16 byte read capacity 36187a2a6a1aSStephen McConnell * command, otherwise continue without sending read cap 16. 3619991554f2SKenneth D. Merry */ 3620991554f2SKenneth D. Merry if ((cgd->inq_data.spc3_flags & SPC3_SID_PROTECT) == 0) 3621991554f2SKenneth D. Merry return; 3622991554f2SKenneth D. Merry 3623991554f2SKenneth D. Merry /* 3624991554f2SKenneth D. Merry * Issue a READ CAPACITY 16 command. This info is used to determine if 3625991554f2SKenneth D. Merry * the LUN is formatted for EEDP support. 3626991554f2SKenneth D. Merry */ 3627991554f2SKenneth D. Merry ccb = xpt_alloc_ccb_nowait(); 3628991554f2SKenneth D. Merry if (ccb == NULL) { 3629991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "Unable to alloc CCB for EEDP " 3630991554f2SKenneth D. Merry "support.\n"); 3631991554f2SKenneth D. Merry return; 3632991554f2SKenneth D. Merry } 3633991554f2SKenneth D. Merry 36347a2a6a1aSStephen McConnell if (xpt_create_path(&local_path, xpt_periph, pathid, targetid, lunid) != 36357a2a6a1aSStephen McConnell CAM_REQ_CMP) { 3636991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "Unable to create path for EEDP " 36377a2a6a1aSStephen McConnell "support.\n"); 3638991554f2SKenneth D. Merry xpt_free_ccb(ccb); 3639991554f2SKenneth D. Merry return; 3640991554f2SKenneth D. Merry } 3641991554f2SKenneth D. Merry 3642991554f2SKenneth D. Merry /* 3643991554f2SKenneth D. Merry * If LUN is already in list, don't create a new one. 3644991554f2SKenneth D. Merry */ 3645991554f2SKenneth D. Merry found_lun = FALSE; 3646991554f2SKenneth D. Merry SLIST_FOREACH(lun, &target->luns, lun_link) { 3647991554f2SKenneth D. Merry if (lun->lun_id == lunid) { 3648991554f2SKenneth D. Merry found_lun = TRUE; 3649991554f2SKenneth D. Merry break; 3650991554f2SKenneth D. Merry } 3651991554f2SKenneth D. Merry } 3652991554f2SKenneth D. Merry if (!found_lun) { 3653991554f2SKenneth D. Merry lun = malloc(sizeof(struct mprsas_lun), M_MPR, 3654991554f2SKenneth D. Merry M_NOWAIT | M_ZERO); 3655991554f2SKenneth D. Merry if (lun == NULL) { 3656991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "Unable to alloc LUN for " 3657991554f2SKenneth D. Merry "EEDP support.\n"); 3658991554f2SKenneth D. Merry xpt_free_path(local_path); 3659991554f2SKenneth D. Merry xpt_free_ccb(ccb); 3660991554f2SKenneth D. Merry return; 3661991554f2SKenneth D. Merry } 3662991554f2SKenneth D. Merry lun->lun_id = lunid; 3663991554f2SKenneth D. Merry SLIST_INSERT_HEAD(&target->luns, lun, lun_link); 3664991554f2SKenneth D. Merry } 3665991554f2SKenneth D. Merry 3666991554f2SKenneth D. Merry xpt_path_string(local_path, path_str, sizeof(path_str)); 3667991554f2SKenneth D. Merry mpr_dprint(sc, MPR_INFO, "Sending read cap: path %s handle %d\n", 3668991554f2SKenneth D. Merry path_str, target->handle); 3669991554f2SKenneth D. Merry 3670991554f2SKenneth D. Merry /* 3671991554f2SKenneth D. Merry * Issue a READ CAPACITY 16 command for the LUN. The 3672991554f2SKenneth D. Merry * mprsas_read_cap_done function will load the read cap info into the 3673991554f2SKenneth D. Merry * LUN struct. 3674991554f2SKenneth D. Merry */ 3675991554f2SKenneth D. Merry rcap_buf = malloc(sizeof(struct scsi_read_capacity_eedp), M_MPR, 3676991554f2SKenneth D. Merry M_NOWAIT | M_ZERO); 3677991554f2SKenneth D. Merry if (rcap_buf == NULL) { 3678a2c14879SStephen McConnell mpr_dprint(sc, MPR_ERROR, "Unable to alloc read capacity " 3679991554f2SKenneth D. Merry "buffer for EEDP support.\n"); 3680991554f2SKenneth D. Merry xpt_free_path(ccb->ccb_h.path); 3681991554f2SKenneth D. Merry xpt_free_ccb(ccb); 3682991554f2SKenneth D. Merry return; 3683991554f2SKenneth D. Merry } 3684991554f2SKenneth D. Merry xpt_setup_ccb(&ccb->ccb_h, local_path, CAM_PRIORITY_XPT); 3685991554f2SKenneth D. Merry csio = &ccb->csio; 3686991554f2SKenneth D. Merry csio->ccb_h.func_code = XPT_SCSI_IO; 3687991554f2SKenneth D. Merry csio->ccb_h.flags = CAM_DIR_IN; 3688991554f2SKenneth D. Merry csio->ccb_h.retry_count = 4; 3689991554f2SKenneth D. Merry csio->ccb_h.cbfcnp = mprsas_read_cap_done; 3690991554f2SKenneth D. Merry csio->ccb_h.timeout = 60000; 3691991554f2SKenneth D. Merry csio->data_ptr = (uint8_t *)rcap_buf; 3692991554f2SKenneth D. Merry csio->dxfer_len = sizeof(struct scsi_read_capacity_eedp); 3693991554f2SKenneth D. Merry csio->sense_len = MPR_SENSE_LEN; 3694991554f2SKenneth D. Merry csio->cdb_len = sizeof(*scsi_cmd); 3695991554f2SKenneth D. Merry csio->tag_action = MSG_SIMPLE_Q_TAG; 3696991554f2SKenneth D. Merry 3697991554f2SKenneth D. Merry scsi_cmd = (struct scsi_read_capacity_16 *)&csio->cdb_io.cdb_bytes; 3698991554f2SKenneth D. Merry bzero(scsi_cmd, sizeof(*scsi_cmd)); 3699991554f2SKenneth D. Merry scsi_cmd->opcode = 0x9E; 3700991554f2SKenneth D. Merry scsi_cmd->service_action = SRC16_SERVICE_ACTION; 3701991554f2SKenneth D. Merry ((uint8_t *)scsi_cmd)[13] = sizeof(struct scsi_read_capacity_eedp); 3702991554f2SKenneth D. Merry 3703991554f2SKenneth D. Merry ccb->ccb_h.ppriv_ptr1 = sassc; 3704991554f2SKenneth D. Merry xpt_action(ccb); 3705991554f2SKenneth D. Merry } 3706991554f2SKenneth D. Merry 3707991554f2SKenneth D. Merry static void 3708991554f2SKenneth D. Merry mprsas_read_cap_done(struct cam_periph *periph, union ccb *done_ccb) 3709991554f2SKenneth D. Merry { 3710991554f2SKenneth D. Merry struct mprsas_softc *sassc; 3711991554f2SKenneth D. Merry struct mprsas_target *target; 3712991554f2SKenneth D. Merry struct mprsas_lun *lun; 3713991554f2SKenneth D. Merry struct scsi_read_capacity_eedp *rcap_buf; 3714991554f2SKenneth D. Merry 3715991554f2SKenneth D. Merry if (done_ccb == NULL) 3716991554f2SKenneth D. Merry return; 3717991554f2SKenneth D. Merry 3718991554f2SKenneth D. Merry /* Driver need to release devq, it Scsi command is 3719991554f2SKenneth D. Merry * generated by driver internally. 3720991554f2SKenneth D. Merry * Currently there is a single place where driver 3721991554f2SKenneth D. Merry * calls scsi command internally. In future if driver 3722991554f2SKenneth D. Merry * calls more scsi command internally, it needs to release 3723991554f2SKenneth D. Merry * devq internally, since those command will not go back to 3724991554f2SKenneth D. Merry * cam_periph. 3725991554f2SKenneth D. Merry */ 3726991554f2SKenneth D. Merry if ((done_ccb->ccb_h.status & CAM_DEV_QFRZN) ) { 3727991554f2SKenneth D. Merry done_ccb->ccb_h.status &= ~CAM_DEV_QFRZN; 3728991554f2SKenneth D. Merry xpt_release_devq(done_ccb->ccb_h.path, 3729991554f2SKenneth D. Merry /*count*/ 1, /*run_queue*/TRUE); 3730991554f2SKenneth D. Merry } 3731991554f2SKenneth D. Merry 3732991554f2SKenneth D. Merry rcap_buf = (struct scsi_read_capacity_eedp *)done_ccb->csio.data_ptr; 3733991554f2SKenneth D. Merry 3734991554f2SKenneth D. Merry /* 3735991554f2SKenneth D. Merry * Get the LUN ID for the path and look it up in the LUN list for the 3736991554f2SKenneth D. Merry * target. 3737991554f2SKenneth D. Merry */ 3738991554f2SKenneth D. Merry sassc = (struct mprsas_softc *)done_ccb->ccb_h.ppriv_ptr1; 37397a2a6a1aSStephen McConnell KASSERT(done_ccb->ccb_h.target_id < sassc->maxtargets, ("Target %d out " 37407a2a6a1aSStephen McConnell "of bounds in mprsas_read_cap_done\n", done_ccb->ccb_h.target_id)); 3741991554f2SKenneth D. Merry target = &sassc->targets[done_ccb->ccb_h.target_id]; 3742991554f2SKenneth D. Merry SLIST_FOREACH(lun, &target->luns, lun_link) { 3743991554f2SKenneth D. Merry if (lun->lun_id != done_ccb->ccb_h.target_lun) 3744991554f2SKenneth D. Merry continue; 3745991554f2SKenneth D. Merry 3746991554f2SKenneth D. Merry /* 3747991554f2SKenneth D. Merry * Got the LUN in the target's LUN list. Fill it in with EEDP 3748991554f2SKenneth D. Merry * info. If the READ CAP 16 command had some SCSI error (common 3749991554f2SKenneth D. Merry * if command is not supported), mark the lun as not supporting 3750991554f2SKenneth D. Merry * EEDP and set the block size to 0. 3751991554f2SKenneth D. Merry */ 3752a2c14879SStephen McConnell if ((mprsas_get_ccbstatus(done_ccb) != CAM_REQ_CMP) || 3753a2c14879SStephen McConnell (done_ccb->csio.scsi_status != SCSI_STATUS_OK)) { 3754991554f2SKenneth D. Merry lun->eedp_formatted = FALSE; 3755991554f2SKenneth D. Merry lun->eedp_block_size = 0; 3756991554f2SKenneth D. Merry break; 3757991554f2SKenneth D. Merry } 3758991554f2SKenneth D. Merry 3759991554f2SKenneth D. Merry if (rcap_buf->protect & 0x01) { 3760a2c14879SStephen McConnell mpr_dprint(sassc->sc, MPR_INFO, "LUN %d for target ID " 3761a2c14879SStephen McConnell "%d is formatted for EEDP support.\n", 3762a2c14879SStephen McConnell done_ccb->ccb_h.target_lun, 3763991554f2SKenneth D. Merry done_ccb->ccb_h.target_id); 3764991554f2SKenneth D. Merry lun->eedp_formatted = TRUE; 3765991554f2SKenneth D. Merry lun->eedp_block_size = scsi_4btoul(rcap_buf->length); 3766991554f2SKenneth D. Merry } 3767991554f2SKenneth D. Merry break; 3768991554f2SKenneth D. Merry } 3769991554f2SKenneth D. Merry 3770991554f2SKenneth D. Merry // Finished with this CCB and path. 3771991554f2SKenneth D. Merry free(rcap_buf, M_MPR); 3772991554f2SKenneth D. Merry xpt_free_path(done_ccb->ccb_h.path); 3773991554f2SKenneth D. Merry xpt_free_ccb(done_ccb); 3774991554f2SKenneth D. Merry } 3775991554f2SKenneth D. Merry #endif /* (__FreeBSD_version < 901503) || \ 3776991554f2SKenneth D. Merry ((__FreeBSD_version >= 1000000) && (__FreeBSD_version < 1000006)) */ 3777991554f2SKenneth D. Merry 3778a2c14879SStephen McConnell /* 3779a2c14879SStephen McConnell * Set the INRESET flag for this target so that no I/O will be sent to 3780a2c14879SStephen McConnell * the target until the reset has completed. If an I/O request does 3781a2c14879SStephen McConnell * happen, the devq will be frozen. The CCB holds the path which is 3782a2c14879SStephen McConnell * used to release the devq. The devq is released and the CCB is freed 3783a2c14879SStephen McConnell * when the TM completes. 3784a2c14879SStephen McConnell */ 3785b7f1ee79SScott Long void 3786b7f1ee79SScott Long mprsas_prepare_for_tm(struct mpr_softc *sc, struct mpr_command *tm, 3787b7f1ee79SScott Long struct mprsas_target *target, lun_id_t lun_id) 3788b7f1ee79SScott Long { 3789b7f1ee79SScott Long union ccb *ccb; 3790b7f1ee79SScott Long path_id_t path_id; 3791b7f1ee79SScott Long 3792a2c14879SStephen McConnell ccb = xpt_alloc_ccb_nowait(); 3793a2c14879SStephen McConnell if (ccb) { 3794a2c14879SStephen McConnell path_id = cam_sim_path(sc->sassc->sim); 3795a2c14879SStephen McConnell if (xpt_create_path(&ccb->ccb_h.path, xpt_periph, path_id, 3796a2c14879SStephen McConnell target->tid, lun_id) != CAM_REQ_CMP) { 3797a2c14879SStephen McConnell xpt_free_ccb(ccb); 3798a2c14879SStephen McConnell } else { 3799a2c14879SStephen McConnell tm->cm_ccb = ccb; 3800a2c14879SStephen McConnell tm->cm_targ = target; 3801a2c14879SStephen McConnell target->flags |= MPRSAS_TARGET_INRESET; 3802a2c14879SStephen McConnell } 3803a2c14879SStephen McConnell } 3804a2c14879SStephen McConnell } 3805a2c14879SStephen McConnell 3806991554f2SKenneth D. Merry int 3807991554f2SKenneth D. Merry mprsas_startup(struct mpr_softc *sc) 3808991554f2SKenneth D. Merry { 3809991554f2SKenneth D. Merry /* 3810991554f2SKenneth D. Merry * Send the port enable message and set the wait_for_port_enable flag. 3811991554f2SKenneth D. Merry * This flag helps to keep the simq frozen until all discovery events 3812991554f2SKenneth D. Merry * are processed. 3813991554f2SKenneth D. Merry */ 3814991554f2SKenneth D. Merry sc->wait_for_port_enable = 1; 3815991554f2SKenneth D. Merry mprsas_send_portenable(sc); 3816991554f2SKenneth D. Merry return (0); 3817991554f2SKenneth D. Merry } 3818991554f2SKenneth D. Merry 3819991554f2SKenneth D. Merry static int 3820991554f2SKenneth D. Merry mprsas_send_portenable(struct mpr_softc *sc) 3821991554f2SKenneth D. Merry { 3822991554f2SKenneth D. Merry MPI2_PORT_ENABLE_REQUEST *request; 3823991554f2SKenneth D. Merry struct mpr_command *cm; 3824991554f2SKenneth D. Merry 3825991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 3826991554f2SKenneth D. Merry 3827991554f2SKenneth D. Merry if ((cm = mpr_alloc_command(sc)) == NULL) 3828991554f2SKenneth D. Merry return (EBUSY); 3829991554f2SKenneth D. Merry request = (MPI2_PORT_ENABLE_REQUEST *)cm->cm_req; 3830991554f2SKenneth D. Merry request->Function = MPI2_FUNCTION_PORT_ENABLE; 3831991554f2SKenneth D. Merry request->MsgFlags = 0; 3832991554f2SKenneth D. Merry request->VP_ID = 0; 3833991554f2SKenneth D. Merry cm->cm_desc.Default.RequestFlags = MPI2_REQ_DESCRIPT_FLAGS_DEFAULT_TYPE; 3834991554f2SKenneth D. Merry cm->cm_complete = mprsas_portenable_complete; 3835991554f2SKenneth D. Merry cm->cm_data = NULL; 3836991554f2SKenneth D. Merry cm->cm_sge = NULL; 3837991554f2SKenneth D. Merry 3838991554f2SKenneth D. Merry mpr_map_command(sc, cm); 3839991554f2SKenneth D. Merry mpr_dprint(sc, MPR_XINFO, 3840991554f2SKenneth D. Merry "mpr_send_portenable finished cm %p req %p complete %p\n", 3841991554f2SKenneth D. Merry cm, cm->cm_req, cm->cm_complete); 3842991554f2SKenneth D. Merry return (0); 3843991554f2SKenneth D. Merry } 3844991554f2SKenneth D. Merry 3845991554f2SKenneth D. Merry static void 3846991554f2SKenneth D. Merry mprsas_portenable_complete(struct mpr_softc *sc, struct mpr_command *cm) 3847991554f2SKenneth D. Merry { 3848991554f2SKenneth D. Merry MPI2_PORT_ENABLE_REPLY *reply; 3849991554f2SKenneth D. Merry struct mprsas_softc *sassc; 3850991554f2SKenneth D. Merry 3851991554f2SKenneth D. Merry MPR_FUNCTRACE(sc); 3852991554f2SKenneth D. Merry sassc = sc->sassc; 3853991554f2SKenneth D. Merry 3854991554f2SKenneth D. Merry /* 3855991554f2SKenneth D. Merry * Currently there should be no way we can hit this case. It only 3856991554f2SKenneth D. Merry * happens when we have a failure to allocate chain frames, and 3857991554f2SKenneth D. Merry * port enable commands don't have S/G lists. 3858991554f2SKenneth D. Merry */ 3859991554f2SKenneth D. Merry if ((cm->cm_flags & MPR_CM_FLAGS_ERROR_MASK) != 0) { 3860991554f2SKenneth D. Merry mpr_dprint(sc, MPR_ERROR, "%s: cm_flags = %#x for port enable! " 3861991554f2SKenneth D. Merry "This should not happen!\n", __func__, cm->cm_flags); 3862991554f2SKenneth D. Merry } 3863991554f2SKenneth D. Merry 3864991554f2SKenneth D. Merry reply = (MPI2_PORT_ENABLE_REPLY *)cm->cm_reply; 3865991554f2SKenneth D. Merry if (reply == NULL) 3866991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "Portenable NULL reply\n"); 3867991554f2SKenneth D. Merry else if (le16toh(reply->IOCStatus & MPI2_IOCSTATUS_MASK) != 3868991554f2SKenneth D. Merry MPI2_IOCSTATUS_SUCCESS) 3869991554f2SKenneth D. Merry mpr_dprint(sc, MPR_FAULT, "Portenable failed\n"); 3870991554f2SKenneth D. Merry 3871991554f2SKenneth D. Merry mpr_free_command(sc, cm); 3872991554f2SKenneth D. Merry /* 3873991554f2SKenneth D. Merry * Done waiting for port enable to complete. Decrement the refcount. 3874991554f2SKenneth D. Merry * If refcount is 0, discovery is complete and a rescan of the bus can 3875991554f2SKenneth D. Merry * take place. 3876991554f2SKenneth D. Merry */ 3877991554f2SKenneth D. Merry sc->wait_for_port_enable = 0; 3878991554f2SKenneth D. Merry sc->port_enable_complete = 1; 3879991554f2SKenneth D. Merry wakeup(&sc->port_enable_complete); 3880991554f2SKenneth D. Merry mprsas_startup_decrement(sassc); 3881991554f2SKenneth D. Merry } 3882991554f2SKenneth D. Merry 3883991554f2SKenneth D. Merry int 3884991554f2SKenneth D. Merry mprsas_check_id(struct mprsas_softc *sassc, int id) 3885991554f2SKenneth D. Merry { 3886991554f2SKenneth D. Merry struct mpr_softc *sc = sassc->sc; 3887991554f2SKenneth D. Merry char *ids; 3888991554f2SKenneth D. Merry char *name; 3889991554f2SKenneth D. Merry 3890991554f2SKenneth D. Merry ids = &sc->exclude_ids[0]; 3891991554f2SKenneth D. Merry while((name = strsep(&ids, ",")) != NULL) { 3892991554f2SKenneth D. Merry if (name[0] == '\0') 3893991554f2SKenneth D. Merry continue; 3894991554f2SKenneth D. Merry if (strtol(name, NULL, 0) == (long)id) 3895991554f2SKenneth D. Merry return (1); 3896991554f2SKenneth D. Merry } 3897991554f2SKenneth D. Merry 3898991554f2SKenneth D. Merry return (0); 3899991554f2SKenneth D. Merry } 3900a2c14879SStephen McConnell 3901a2c14879SStephen McConnell void 3902a2c14879SStephen McConnell mprsas_realloc_targets(struct mpr_softc *sc, int maxtargets) 3903a2c14879SStephen McConnell { 3904a2c14879SStephen McConnell struct mprsas_softc *sassc; 3905a2c14879SStephen McConnell struct mprsas_lun *lun, *lun_tmp; 3906a2c14879SStephen McConnell struct mprsas_target *targ; 3907a2c14879SStephen McConnell int i; 3908a2c14879SStephen McConnell 3909a2c14879SStephen McConnell sassc = sc->sassc; 3910a2c14879SStephen McConnell /* 3911a2c14879SStephen McConnell * The number of targets is based on IOC Facts, so free all of 3912a2c14879SStephen McConnell * the allocated LUNs for each target and then the target buffer 3913a2c14879SStephen McConnell * itself. 3914a2c14879SStephen McConnell */ 3915a2c14879SStephen McConnell for (i=0; i< maxtargets; i++) { 3916a2c14879SStephen McConnell targ = &sassc->targets[i]; 3917a2c14879SStephen McConnell SLIST_FOREACH_SAFE(lun, &targ->luns, lun_link, lun_tmp) { 3918a2c14879SStephen McConnell free(lun, M_MPR); 3919a2c14879SStephen McConnell } 3920a2c14879SStephen McConnell } 3921a2c14879SStephen McConnell free(sassc->targets, M_MPR); 3922a2c14879SStephen McConnell 3923a2c14879SStephen McConnell sassc->targets = malloc(sizeof(struct mprsas_target) * maxtargets, 3924a2c14879SStephen McConnell M_MPR, M_WAITOK|M_ZERO); 3925a2c14879SStephen McConnell if (!sassc->targets) { 3926a2c14879SStephen McConnell panic("%s failed to alloc targets with error %d\n", 3927a2c14879SStephen McConnell __func__, ENOMEM); 3928a2c14879SStephen McConnell } 3929a2c14879SStephen McConnell } 3930