Index: head/sys/dev/mfi/mfi.c =================================================================== --- head/sys/dev/mfi/mfi.c (revision 222588) +++ head/sys/dev/mfi/mfi.c (revision 222589) @@ -1,2545 +1,2545 @@ /*- * Copyright (c) 2006 IronPort Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 2007 LSI Corp. * Copyright (c) 2007 Rajesh Prabhakaran. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_mfi.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static int mfi_alloc_commands(struct mfi_softc *); static int mfi_comms_init(struct mfi_softc *); static int mfi_wait_command(struct mfi_softc *, struct mfi_command *); static int mfi_get_controller_info(struct mfi_softc *); static int mfi_get_log_state(struct mfi_softc *, struct mfi_evt_log_state **); static int mfi_parse_entries(struct mfi_softc *, int, int); static int mfi_dcmd_command(struct mfi_softc *, struct mfi_command **, uint32_t, void **, size_t); static void mfi_data_cb(void *, bus_dma_segment_t *, int, int); static void mfi_startup(void *arg); static void mfi_intr(void *arg); static void mfi_ldprobe(struct mfi_softc *sc); static int mfi_aen_register(struct mfi_softc *sc, int seq, int locale); static void mfi_aen_complete(struct mfi_command *); static int mfi_aen_setup(struct mfi_softc *, uint32_t); static int mfi_add_ld(struct mfi_softc *sc, int); static void mfi_add_ld_complete(struct mfi_command *); static struct mfi_command * mfi_bio_command(struct mfi_softc *); static void mfi_bio_complete(struct mfi_command *); static int mfi_mapcmd(struct mfi_softc *, struct mfi_command *); static int mfi_send_frame(struct mfi_softc *, struct mfi_command *); static void mfi_complete(struct mfi_softc *, struct mfi_command *); static int mfi_abort(struct mfi_softc *, struct mfi_command *); static int mfi_linux_ioctl_int(struct cdev *, u_long, caddr_t, int, struct thread *); static void mfi_timeout(void *); static int mfi_user_command(struct mfi_softc *, struct mfi_ioc_passthru *); static void mfi_enable_intr_xscale(struct mfi_softc *sc); static void mfi_enable_intr_ppc(struct mfi_softc *sc); static int32_t mfi_read_fw_status_xscale(struct mfi_softc *sc); static int32_t mfi_read_fw_status_ppc(struct mfi_softc *sc); static int mfi_check_clear_intr_xscale(struct mfi_softc *sc); static int mfi_check_clear_intr_ppc(struct mfi_softc *sc); static void mfi_issue_cmd_xscale(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt); static void mfi_issue_cmd_ppc(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt); SYSCTL_NODE(_hw, OID_AUTO, mfi, CTLFLAG_RD, 0, "MFI driver parameters"); static int mfi_event_locale = MFI_EVT_LOCALE_ALL; TUNABLE_INT("hw.mfi.event_locale", &mfi_event_locale); SYSCTL_INT(_hw_mfi, OID_AUTO, event_locale, CTLFLAG_RW, &mfi_event_locale, 0, "event message locale"); static int mfi_event_class = MFI_EVT_CLASS_INFO; TUNABLE_INT("hw.mfi.event_class", &mfi_event_class); SYSCTL_INT(_hw_mfi, OID_AUTO, event_class, CTLFLAG_RW, &mfi_event_class, 0, "event message class"); static int mfi_max_cmds = 128; TUNABLE_INT("hw.mfi.max_cmds", &mfi_max_cmds); SYSCTL_INT(_hw_mfi, OID_AUTO, max_cmds, CTLFLAG_RD, &mfi_max_cmds, 0, "Max commands"); /* Management interface */ static d_open_t mfi_open; static d_close_t mfi_close; static d_ioctl_t mfi_ioctl; static d_poll_t mfi_poll; static struct cdevsw mfi_cdevsw = { .d_version = D_VERSION, .d_flags = 0, .d_open = mfi_open, .d_close = mfi_close, .d_ioctl = mfi_ioctl, .d_poll = mfi_poll, .d_name = "mfi", }; MALLOC_DEFINE(M_MFIBUF, "mfibuf", "Buffers for the MFI driver"); #define MFI_INQ_LENGTH SHORT_INQUIRY_LENGTH static void mfi_enable_intr_xscale(struct mfi_softc *sc) { MFI_WRITE4(sc, MFI_OMSK, 0x01); } static void mfi_enable_intr_ppc(struct mfi_softc *sc) { MFI_WRITE4(sc, MFI_ODCR0, 0xFFFFFFFF); if (sc->mfi_flags & MFI_FLAGS_1078) { MFI_WRITE4(sc, MFI_OMSK, ~MFI_1078_EIM); } else if (sc->mfi_flags & MFI_FLAGS_GEN2) { MFI_WRITE4(sc, MFI_OMSK, ~MFI_GEN2_EIM); } } static int32_t mfi_read_fw_status_xscale(struct mfi_softc *sc) { return MFI_READ4(sc, MFI_OMSG0); } static int32_t mfi_read_fw_status_ppc(struct mfi_softc *sc) { return MFI_READ4(sc, MFI_OSP0); } static int mfi_check_clear_intr_xscale(struct mfi_softc *sc) { int32_t status; status = MFI_READ4(sc, MFI_OSTS); if ((status & MFI_OSTS_INTR_VALID) == 0) return 1; MFI_WRITE4(sc, MFI_OSTS, status); return 0; } static int mfi_check_clear_intr_ppc(struct mfi_softc *sc) { int32_t status; status = MFI_READ4(sc, MFI_OSTS); if (sc->mfi_flags & MFI_FLAGS_1078) { if (!(status & MFI_1078_RM)) { return 1; } } else if (sc->mfi_flags & MFI_FLAGS_GEN2) { if (!(status & MFI_GEN2_RM)) { return 1; } } MFI_WRITE4(sc, MFI_ODCR0, status); return 0; } static void mfi_issue_cmd_xscale(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt) { MFI_WRITE4(sc, MFI_IQP,(bus_add >>3)|frame_cnt); } static void mfi_issue_cmd_ppc(struct mfi_softc *sc,uint32_t bus_add,uint32_t frame_cnt) { MFI_WRITE4(sc, MFI_IQP, (bus_add |frame_cnt <<1)|1 ); } static int mfi_transition_firmware(struct mfi_softc *sc) { uint32_t fw_state, cur_state; int max_wait, i; fw_state = sc->mfi_read_fw_status(sc)& MFI_FWSTATE_MASK; while (fw_state != MFI_FWSTATE_READY) { if (bootverbose) device_printf(sc->mfi_dev, "Waiting for firmware to " "become ready\n"); cur_state = fw_state; switch (fw_state) { case MFI_FWSTATE_FAULT: device_printf(sc->mfi_dev, "Firmware fault\n"); return (ENXIO); case MFI_FWSTATE_WAIT_HANDSHAKE: MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_CLEAR_HANDSHAKE); max_wait = 2; break; case MFI_FWSTATE_OPERATIONAL: MFI_WRITE4(sc, MFI_IDB, MFI_FWINIT_READY); max_wait = 10; break; case MFI_FWSTATE_UNDEFINED: case MFI_FWSTATE_BB_INIT: max_wait = 2; break; case MFI_FWSTATE_FW_INIT: case MFI_FWSTATE_DEVICE_SCAN: case MFI_FWSTATE_FLUSH_CACHE: max_wait = 20; break; default: device_printf(sc->mfi_dev,"Unknown firmware state %d\n", fw_state); return (ENXIO); } for (i = 0; i < (max_wait * 10); i++) { fw_state = sc->mfi_read_fw_status(sc) & MFI_FWSTATE_MASK; if (fw_state == cur_state) DELAY(100000); else break; } if (fw_state == cur_state) { device_printf(sc->mfi_dev, "firmware stuck in state " "%#x\n", fw_state); return (ENXIO); } } return (0); } static void mfi_addr32_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { uint32_t *addr; addr = arg; *addr = segs[0].ds_addr; } int mfi_attach(struct mfi_softc *sc) { uint32_t status; int error, commsz, framessz, sensesz; int frames, unit, max_fw_sge; device_printf(sc->mfi_dev, "Megaraid SAS driver Ver 3.00 \n"); mtx_init(&sc->mfi_io_lock, "MFI I/O lock", NULL, MTX_DEF); sx_init(&sc->mfi_config_lock, "MFI config"); TAILQ_INIT(&sc->mfi_ld_tqh); TAILQ_INIT(&sc->mfi_aen_pids); TAILQ_INIT(&sc->mfi_cam_ccbq); mfi_initq_free(sc); mfi_initq_ready(sc); mfi_initq_busy(sc); mfi_initq_bio(sc); if (sc->mfi_flags & MFI_FLAGS_1064R) { sc->mfi_enable_intr = mfi_enable_intr_xscale; sc->mfi_read_fw_status = mfi_read_fw_status_xscale; sc->mfi_check_clear_intr = mfi_check_clear_intr_xscale; sc->mfi_issue_cmd = mfi_issue_cmd_xscale; } else { sc->mfi_enable_intr = mfi_enable_intr_ppc; sc->mfi_read_fw_status = mfi_read_fw_status_ppc; sc->mfi_check_clear_intr = mfi_check_clear_intr_ppc; sc->mfi_issue_cmd = mfi_issue_cmd_ppc; } /* Before we get too far, see if the firmware is working */ if ((error = mfi_transition_firmware(sc)) != 0) { device_printf(sc->mfi_dev, "Firmware not in READY state, " "error %d\n", error); return (ENXIO); } /* * Get information needed for sizing the contiguous memory for the * frame pool. Size down the sgl parameter since we know that * we will never need more than what's required for MAXPHYS. * It would be nice if these constants were available at runtime * instead of compile time. */ status = sc->mfi_read_fw_status(sc); sc->mfi_max_fw_cmds = status & MFI_FWSTATE_MAXCMD_MASK; max_fw_sge = (status & MFI_FWSTATE_MAXSGL_MASK) >> 16; sc->mfi_max_sge = min(max_fw_sge, ((MFI_MAXPHYS / PAGE_SIZE) + 1)); /* * Create the dma tag for data buffers. Used both for block I/O * and for various internal data queries. */ if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR, /* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ BUS_SPACE_MAXSIZE_32BIT,/* maxsize */ sc->mfi_max_sge, /* nsegments */ BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ BUS_DMA_ALLOCNOW, /* flags */ busdma_lock_mutex, /* lockfunc */ &sc->mfi_io_lock, /* lockfuncarg */ &sc->mfi_buffer_dmat)) { device_printf(sc->mfi_dev, "Cannot allocate buffer DMA tag\n"); return (ENOMEM); } /* * Allocate DMA memory for the comms queues. Keep it under 4GB for * efficiency. The mfi_hwcomms struct includes space for 1 reply queue * entry, so the calculated size here will be will be 1 more than * mfi_max_fw_cmds. This is apparently a requirement of the hardware. */ commsz = (sizeof(uint32_t) * sc->mfi_max_fw_cmds) + sizeof(struct mfi_hwcomms); if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ 1, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ commsz, /* maxsize */ 1, /* msegments */ commsz, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->mfi_comms_dmat)) { device_printf(sc->mfi_dev, "Cannot allocate comms DMA tag\n"); return (ENOMEM); } if (bus_dmamem_alloc(sc->mfi_comms_dmat, (void **)&sc->mfi_comms, BUS_DMA_NOWAIT, &sc->mfi_comms_dmamap)) { device_printf(sc->mfi_dev, "Cannot allocate comms memory\n"); return (ENOMEM); } bzero(sc->mfi_comms, commsz); bus_dmamap_load(sc->mfi_comms_dmat, sc->mfi_comms_dmamap, sc->mfi_comms, commsz, mfi_addr32_cb, &sc->mfi_comms_busaddr, 0); /* * Allocate DMA memory for the command frames. Keep them in the * lower 4GB for efficiency. Calculate the size of the commands at * the same time; each command is one 64 byte frame plus a set of * additional frames for holding sg lists or other data. * The assumption here is that the SG list will start at the second * frame and not use the unused bytes in the first frame. While this * isn't technically correct, it simplifies the calculation and allows * for command frames that might be larger than an mfi_io_frame. */ if (sizeof(bus_addr_t) == 8) { sc->mfi_sge_size = sizeof(struct mfi_sg64); sc->mfi_flags |= MFI_FLAGS_SG64; } else { sc->mfi_sge_size = sizeof(struct mfi_sg32); } frames = (sc->mfi_sge_size * sc->mfi_max_sge - 1) / MFI_FRAME_SIZE + 2; sc->mfi_cmd_size = frames * MFI_FRAME_SIZE; framessz = sc->mfi_cmd_size * sc->mfi_max_fw_cmds; if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ 64, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ framessz, /* maxsize */ 1, /* nsegments */ framessz, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->mfi_frames_dmat)) { device_printf(sc->mfi_dev, "Cannot allocate frame DMA tag\n"); return (ENOMEM); } if (bus_dmamem_alloc(sc->mfi_frames_dmat, (void **)&sc->mfi_frames, BUS_DMA_NOWAIT, &sc->mfi_frames_dmamap)) { device_printf(sc->mfi_dev, "Cannot allocate frames memory\n"); return (ENOMEM); } bzero(sc->mfi_frames, framessz); bus_dmamap_load(sc->mfi_frames_dmat, sc->mfi_frames_dmamap, sc->mfi_frames, framessz, mfi_addr32_cb, &sc->mfi_frames_busaddr,0); /* * Allocate DMA memory for the frame sense data. Keep them in the * lower 4GB for efficiency */ sensesz = sc->mfi_max_fw_cmds * MFI_SENSE_LEN; if (bus_dma_tag_create( sc->mfi_parent_dmat, /* parent */ 4, 0, /* algnmnt, boundary */ BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ BUS_SPACE_MAXADDR, /* highaddr */ NULL, NULL, /* filter, filterarg */ sensesz, /* maxsize */ 1, /* nsegments */ sensesz, /* maxsegsize */ 0, /* flags */ NULL, NULL, /* lockfunc, lockarg */ &sc->mfi_sense_dmat)) { device_printf(sc->mfi_dev, "Cannot allocate sense DMA tag\n"); return (ENOMEM); } if (bus_dmamem_alloc(sc->mfi_sense_dmat, (void **)&sc->mfi_sense, BUS_DMA_NOWAIT, &sc->mfi_sense_dmamap)) { device_printf(sc->mfi_dev, "Cannot allocate sense memory\n"); return (ENOMEM); } bus_dmamap_load(sc->mfi_sense_dmat, sc->mfi_sense_dmamap, sc->mfi_sense, sensesz, mfi_addr32_cb, &sc->mfi_sense_busaddr, 0); if ((error = mfi_alloc_commands(sc)) != 0) return (error); if ((error = mfi_comms_init(sc)) != 0) return (error); if ((error = mfi_get_controller_info(sc)) != 0) return (error); mtx_lock(&sc->mfi_io_lock); if ((error = mfi_aen_setup(sc, 0), 0) != 0) { mtx_unlock(&sc->mfi_io_lock); return (error); } mtx_unlock(&sc->mfi_io_lock); /* * Set up the interrupt handler. XXX This should happen in * mfi_pci.c */ sc->mfi_irq_rid = 0; if ((sc->mfi_irq = bus_alloc_resource_any(sc->mfi_dev, SYS_RES_IRQ, &sc->mfi_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(sc->mfi_dev, "Cannot allocate interrupt\n"); return (EINVAL); } if (bus_setup_intr(sc->mfi_dev, sc->mfi_irq, INTR_MPSAFE|INTR_TYPE_BIO, NULL, mfi_intr, sc, &sc->mfi_intr)) { device_printf(sc->mfi_dev, "Cannot set up interrupt\n"); return (EINVAL); } /* Register a config hook to probe the bus for arrays */ sc->mfi_ich.ich_func = mfi_startup; sc->mfi_ich.ich_arg = sc; if (config_intrhook_establish(&sc->mfi_ich) != 0) { device_printf(sc->mfi_dev, "Cannot establish configuration " "hook\n"); return (EINVAL); } /* * Register a shutdown handler. */ if ((sc->mfi_eh = EVENTHANDLER_REGISTER(shutdown_final, mfi_shutdown, sc, SHUTDOWN_PRI_DEFAULT)) == NULL) { device_printf(sc->mfi_dev, "Warning: shutdown event " "registration failed\n"); } /* * Create the control device for doing management */ unit = device_get_unit(sc->mfi_dev); sc->mfi_cdev = make_dev(&mfi_cdevsw, unit, UID_ROOT, GID_OPERATOR, 0640, "mfi%d", unit); if (unit == 0) make_dev_alias(sc->mfi_cdev, "megaraid_sas_ioctl_node"); if (sc->mfi_cdev != NULL) sc->mfi_cdev->si_drv1 = sc; SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->mfi_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->mfi_dev)), OID_AUTO, "delete_busy_volumes", CTLFLAG_RW, &sc->mfi_delete_busy_volumes, 0, "Allow removal of busy volumes"); SYSCTL_ADD_INT(device_get_sysctl_ctx(sc->mfi_dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->mfi_dev)), OID_AUTO, "keep_deleted_volumes", CTLFLAG_RW, &sc->mfi_keep_deleted_volumes, 0, "Don't detach the mfid device for a busy volume that is deleted"); device_add_child(sc->mfi_dev, "mfip", -1); bus_generic_attach(sc->mfi_dev); /* Start the timeout watchdog */ callout_init(&sc->mfi_watchdog_callout, CALLOUT_MPSAFE); callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz, mfi_timeout, sc); return (0); } static int mfi_alloc_commands(struct mfi_softc *sc) { struct mfi_command *cm; int i, ncmds; /* * XXX Should we allocate all the commands up front, or allocate on * demand later like 'aac' does? */ ncmds = MIN(mfi_max_cmds, sc->mfi_max_fw_cmds); if (bootverbose) device_printf(sc->mfi_dev, "Max fw cmds= %d, sizing driver " "pool to %d\n", sc->mfi_max_fw_cmds, ncmds); sc->mfi_commands = malloc(sizeof(struct mfi_command) * ncmds, M_MFIBUF, M_WAITOK | M_ZERO); for (i = 0; i < ncmds; i++) { cm = &sc->mfi_commands[i]; cm->cm_frame = (union mfi_frame *)((uintptr_t)sc->mfi_frames + sc->mfi_cmd_size * i); cm->cm_frame_busaddr = sc->mfi_frames_busaddr + sc->mfi_cmd_size * i; cm->cm_frame->header.context = i; cm->cm_sense = &sc->mfi_sense[i]; cm->cm_sense_busaddr= sc->mfi_sense_busaddr + MFI_SENSE_LEN * i; cm->cm_sc = sc; cm->cm_index = i; if (bus_dmamap_create(sc->mfi_buffer_dmat, 0, &cm->cm_dmamap) == 0) mfi_release_command(cm); else break; sc->mfi_total_cmds++; } return (0); } void mfi_release_command(struct mfi_command *cm) { struct mfi_frame_header *hdr; uint32_t *hdr_data; /* * Zero out the important fields of the frame, but make sure the * context field is preserved. For efficiency, handle the fields * as 32 bit words. Clear out the first S/G entry too for safety. */ hdr = &cm->cm_frame->header; if (cm->cm_data != NULL && hdr->sg_count) { cm->cm_sg->sg32[0].len = 0; cm->cm_sg->sg32[0].addr = 0; } hdr_data = (uint32_t *)cm->cm_frame; hdr_data[0] = 0; /* cmd, sense_len, cmd_status, scsi_status */ hdr_data[1] = 0; /* target_id, lun_id, cdb_len, sg_count */ hdr_data[4] = 0; /* flags, timeout */ hdr_data[5] = 0; /* data_len */ cm->cm_extra_frames = 0; cm->cm_flags = 0; cm->cm_complete = NULL; cm->cm_private = NULL; cm->cm_data = NULL; cm->cm_sg = 0; cm->cm_total_frame_size = 0; mfi_enqueue_free(cm); } static int mfi_dcmd_command(struct mfi_softc *sc, struct mfi_command **cmp, uint32_t opcode, void **bufp, size_t bufsize) { struct mfi_command *cm; struct mfi_dcmd_frame *dcmd; void *buf = NULL; mtx_assert(&sc->mfi_io_lock, MA_OWNED); cm = mfi_dequeue_free(sc); if (cm == NULL) return (EBUSY); if ((bufsize > 0) && (bufp != NULL)) { if (*bufp == NULL) { buf = malloc(bufsize, M_MFIBUF, M_NOWAIT|M_ZERO); if (buf == NULL) { mfi_release_command(cm); return (ENOMEM); } *bufp = buf; } else { buf = *bufp; } } dcmd = &cm->cm_frame->dcmd; bzero(dcmd->mbox, MFI_MBOX_SIZE); dcmd->header.cmd = MFI_CMD_DCMD; dcmd->header.timeout = 0; dcmd->header.flags = 0; dcmd->header.data_len = bufsize; dcmd->opcode = opcode; cm->cm_sg = &dcmd->sgl; cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE; cm->cm_flags = 0; cm->cm_data = buf; cm->cm_private = buf; cm->cm_len = bufsize; *cmp = cm; if ((bufp != NULL) && (*bufp == NULL) && (buf != NULL)) *bufp = buf; return (0); } static int mfi_comms_init(struct mfi_softc *sc) { struct mfi_command *cm; struct mfi_init_frame *init; struct mfi_init_qinfo *qinfo; int error; mtx_lock(&sc->mfi_io_lock); if ((cm = mfi_dequeue_free(sc)) == NULL) return (EBUSY); /* * Abuse the SG list area of the frame to hold the init_qinfo * object; */ init = &cm->cm_frame->init; qinfo = (struct mfi_init_qinfo *)((uintptr_t)init + MFI_FRAME_SIZE); bzero(qinfo, sizeof(struct mfi_init_qinfo)); qinfo->rq_entries = sc->mfi_max_fw_cmds + 1; qinfo->rq_addr_lo = sc->mfi_comms_busaddr + offsetof(struct mfi_hwcomms, hw_reply_q); qinfo->pi_addr_lo = sc->mfi_comms_busaddr + offsetof(struct mfi_hwcomms, hw_pi); qinfo->ci_addr_lo = sc->mfi_comms_busaddr + offsetof(struct mfi_hwcomms, hw_ci); init->header.cmd = MFI_CMD_INIT; init->header.data_len = sizeof(struct mfi_init_qinfo); init->qinfo_new_addr_lo = cm->cm_frame_busaddr + MFI_FRAME_SIZE; cm->cm_data = NULL; cm->cm_flags = MFI_CMD_POLLED; if ((error = mfi_mapcmd(sc, cm)) != 0) { device_printf(sc->mfi_dev, "failed to send init command\n"); mtx_unlock(&sc->mfi_io_lock); return (error); } mfi_release_command(cm); mtx_unlock(&sc->mfi_io_lock); return (0); } static int mfi_get_controller_info(struct mfi_softc *sc) { struct mfi_command *cm = NULL; struct mfi_ctrl_info *ci = NULL; uint32_t max_sectors_1, max_sectors_2; int error; mtx_lock(&sc->mfi_io_lock); error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_GETINFO, (void **)&ci, sizeof(*ci)); if (error) goto out; cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED; if ((error = mfi_mapcmd(sc, cm)) != 0) { device_printf(sc->mfi_dev, "Failed to get controller info\n"); sc->mfi_max_io = (sc->mfi_max_sge - 1) * PAGE_SIZE / MFI_SECTOR_LEN; error = 0; goto out; } bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); max_sectors_1 = (1 << ci->stripe_sz_ops.min) * ci->max_strips_per_io; max_sectors_2 = ci->max_request_size; sc->mfi_max_io = min(max_sectors_1, max_sectors_2); out: if (ci) free(ci, M_MFIBUF); if (cm) mfi_release_command(cm); mtx_unlock(&sc->mfi_io_lock); return (error); } static int mfi_get_log_state(struct mfi_softc *sc, struct mfi_evt_log_state **log_state) { struct mfi_command *cm = NULL; int error; error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_EVENT_GETINFO, (void **)log_state, sizeof(**log_state)); if (error) goto out; cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED; if ((error = mfi_mapcmd(sc, cm)) != 0) { device_printf(sc->mfi_dev, "Failed to get log state\n"); goto out; } bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); out: if (cm) mfi_release_command(cm); return (error); } static int mfi_aen_setup(struct mfi_softc *sc, uint32_t seq_start) { struct mfi_evt_log_state *log_state = NULL; union mfi_evt class_locale; int error = 0; uint32_t seq; class_locale.members.reserved = 0; class_locale.members.locale = mfi_event_locale; - class_locale.members.class = mfi_event_class; + class_locale.members.evt_class = mfi_event_class; if (seq_start == 0) { error = mfi_get_log_state(sc, &log_state); if (error) { if (log_state) free(log_state, M_MFIBUF); return (error); } /* * Walk through any events that fired since the last * shutdown. */ mfi_parse_entries(sc, log_state->shutdown_seq_num, log_state->newest_seq_num); seq = log_state->newest_seq_num; } else seq = seq_start; mfi_aen_register(sc, seq, class_locale.word); free(log_state, M_MFIBUF); return 0; } static int mfi_wait_command(struct mfi_softc *sc, struct mfi_command *cm) { mtx_assert(&sc->mfi_io_lock, MA_OWNED); cm->cm_complete = NULL; /* * MegaCli can issue a DCMD of 0. In this case do nothing * and return 0 to it as status */ if (cm->cm_frame->dcmd.opcode == 0) { cm->cm_frame->header.cmd_status = MFI_STAT_OK; cm->cm_error = 0; return (cm->cm_error); } mfi_enqueue_ready(cm); mfi_startio(sc); if ((cm->cm_flags & MFI_CMD_COMPLETED) == 0) msleep(cm, &sc->mfi_io_lock, PRIBIO, "mfiwait", 0); return (cm->cm_error); } void mfi_free(struct mfi_softc *sc) { struct mfi_command *cm; int i; callout_drain(&sc->mfi_watchdog_callout); if (sc->mfi_cdev != NULL) destroy_dev(sc->mfi_cdev); if (sc->mfi_total_cmds != 0) { for (i = 0; i < sc->mfi_total_cmds; i++) { cm = &sc->mfi_commands[i]; bus_dmamap_destroy(sc->mfi_buffer_dmat, cm->cm_dmamap); } free(sc->mfi_commands, M_MFIBUF); } if (sc->mfi_intr) bus_teardown_intr(sc->mfi_dev, sc->mfi_irq, sc->mfi_intr); if (sc->mfi_irq != NULL) bus_release_resource(sc->mfi_dev, SYS_RES_IRQ, sc->mfi_irq_rid, sc->mfi_irq); if (sc->mfi_sense_busaddr != 0) bus_dmamap_unload(sc->mfi_sense_dmat, sc->mfi_sense_dmamap); if (sc->mfi_sense != NULL) bus_dmamem_free(sc->mfi_sense_dmat, sc->mfi_sense, sc->mfi_sense_dmamap); if (sc->mfi_sense_dmat != NULL) bus_dma_tag_destroy(sc->mfi_sense_dmat); if (sc->mfi_frames_busaddr != 0) bus_dmamap_unload(sc->mfi_frames_dmat, sc->mfi_frames_dmamap); if (sc->mfi_frames != NULL) bus_dmamem_free(sc->mfi_frames_dmat, sc->mfi_frames, sc->mfi_frames_dmamap); if (sc->mfi_frames_dmat != NULL) bus_dma_tag_destroy(sc->mfi_frames_dmat); if (sc->mfi_comms_busaddr != 0) bus_dmamap_unload(sc->mfi_comms_dmat, sc->mfi_comms_dmamap); if (sc->mfi_comms != NULL) bus_dmamem_free(sc->mfi_comms_dmat, sc->mfi_comms, sc->mfi_comms_dmamap); if (sc->mfi_comms_dmat != NULL) bus_dma_tag_destroy(sc->mfi_comms_dmat); if (sc->mfi_buffer_dmat != NULL) bus_dma_tag_destroy(sc->mfi_buffer_dmat); if (sc->mfi_parent_dmat != NULL) bus_dma_tag_destroy(sc->mfi_parent_dmat); if (mtx_initialized(&sc->mfi_io_lock)) { mtx_destroy(&sc->mfi_io_lock); sx_destroy(&sc->mfi_config_lock); } return; } static void mfi_startup(void *arg) { struct mfi_softc *sc; sc = (struct mfi_softc *)arg; config_intrhook_disestablish(&sc->mfi_ich); sc->mfi_enable_intr(sc); sx_xlock(&sc->mfi_config_lock); mtx_lock(&sc->mfi_io_lock); mfi_ldprobe(sc); mtx_unlock(&sc->mfi_io_lock); sx_xunlock(&sc->mfi_config_lock); } static void mfi_intr(void *arg) { struct mfi_softc *sc; struct mfi_command *cm; uint32_t pi, ci, context; sc = (struct mfi_softc *)arg; if (sc->mfi_check_clear_intr(sc)) return; pi = sc->mfi_comms->hw_pi; ci = sc->mfi_comms->hw_ci; mtx_lock(&sc->mfi_io_lock); while (ci != pi) { context = sc->mfi_comms->hw_reply_q[ci]; if (context < sc->mfi_max_fw_cmds) { cm = &sc->mfi_commands[context]; mfi_remove_busy(cm); cm->cm_error = 0; mfi_complete(sc, cm); } if (++ci == (sc->mfi_max_fw_cmds + 1)) { ci = 0; } } sc->mfi_comms->hw_ci = ci; /* Give defered I/O a chance to run */ if (sc->mfi_flags & MFI_FLAGS_QFRZN) sc->mfi_flags &= ~MFI_FLAGS_QFRZN; mfi_startio(sc); mtx_unlock(&sc->mfi_io_lock); return; } int mfi_shutdown(struct mfi_softc *sc) { struct mfi_dcmd_frame *dcmd; struct mfi_command *cm; int error; mtx_lock(&sc->mfi_io_lock); error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_SHUTDOWN, NULL, 0); if (error) { mtx_unlock(&sc->mfi_io_lock); return (error); } if (sc->mfi_aen_cm != NULL) mfi_abort(sc, sc->mfi_aen_cm); dcmd = &cm->cm_frame->dcmd; dcmd->header.flags = MFI_FRAME_DIR_NONE; cm->cm_flags = MFI_CMD_POLLED; cm->cm_data = NULL; if ((error = mfi_mapcmd(sc, cm)) != 0) { device_printf(sc->mfi_dev, "Failed to shutdown controller\n"); } mfi_release_command(cm); mtx_unlock(&sc->mfi_io_lock); return (error); } static void mfi_ldprobe(struct mfi_softc *sc) { struct mfi_frame_header *hdr; struct mfi_command *cm = NULL; struct mfi_ld_list *list = NULL; struct mfi_disk *ld; int error, i; sx_assert(&sc->mfi_config_lock, SA_XLOCKED); mtx_assert(&sc->mfi_io_lock, MA_OWNED); error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_LIST, (void **)&list, sizeof(*list)); if (error) goto out; cm->cm_flags = MFI_CMD_DATAIN; if (mfi_wait_command(sc, cm) != 0) { device_printf(sc->mfi_dev, "Failed to get device listing\n"); goto out; } hdr = &cm->cm_frame->header; if (hdr->cmd_status != MFI_STAT_OK) { device_printf(sc->mfi_dev, "MFI_DCMD_LD_GET_LIST failed %x\n", hdr->cmd_status); goto out; } for (i = 0; i < list->ld_count; i++) { TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) { if (ld->ld_id == list->ld_list[i].ld.v.target_id) goto skip_add; } mfi_add_ld(sc, list->ld_list[i].ld.v.target_id); skip_add:; } out: if (list) free(list, M_MFIBUF); if (cm) mfi_release_command(cm); return; } /* * The timestamp is the number of seconds since 00:00 Jan 1, 2000. If * the bits in 24-31 are all set, then it is the number of seconds since * boot. */ static const char * format_timestamp(uint32_t timestamp) { static char buffer[32]; if ((timestamp & 0xff000000) == 0xff000000) snprintf(buffer, sizeof(buffer), "boot + %us", timestamp & 0x00ffffff); else snprintf(buffer, sizeof(buffer), "%us", timestamp); return (buffer); } static const char * format_class(int8_t class) { static char buffer[6]; switch (class) { case MFI_EVT_CLASS_DEBUG: return ("debug"); case MFI_EVT_CLASS_PROGRESS: return ("progress"); case MFI_EVT_CLASS_INFO: return ("info"); case MFI_EVT_CLASS_WARNING: return ("WARN"); case MFI_EVT_CLASS_CRITICAL: return ("CRIT"); case MFI_EVT_CLASS_FATAL: return ("FATAL"); case MFI_EVT_CLASS_DEAD: return ("DEAD"); default: snprintf(buffer, sizeof(buffer), "%d", class); return (buffer); } } static void mfi_decode_evt(struct mfi_softc *sc, struct mfi_evt_detail *detail) { device_printf(sc->mfi_dev, "%d (%s/0x%04x/%s) - %s\n", detail->seq, - format_timestamp(detail->time), detail->class.members.locale, - format_class(detail->class.members.class), detail->description); + format_timestamp(detail->time), detail->evt_class.members.locale, + format_class(detail->evt_class.members.evt_class), detail->description); } static int mfi_aen_register(struct mfi_softc *sc, int seq, int locale) { struct mfi_command *cm; struct mfi_dcmd_frame *dcmd; union mfi_evt current_aen, prior_aen; struct mfi_evt_detail *ed = NULL; int error = 0; current_aen.word = locale; if (sc->mfi_aen_cm != NULL) { prior_aen.word = ((uint32_t *)&sc->mfi_aen_cm->cm_frame->dcmd.mbox)[1]; - if (prior_aen.members.class <= current_aen.members.class && + if (prior_aen.members.evt_class <= current_aen.members.evt_class && !((prior_aen.members.locale & current_aen.members.locale) ^current_aen.members.locale)) { return (0); } else { prior_aen.members.locale |= current_aen.members.locale; - if (prior_aen.members.class - < current_aen.members.class) - current_aen.members.class = - prior_aen.members.class; + if (prior_aen.members.evt_class + < current_aen.members.evt_class) + current_aen.members.evt_class = + prior_aen.members.evt_class; mfi_abort(sc, sc->mfi_aen_cm); } } error = mfi_dcmd_command(sc, &cm, MFI_DCMD_CTRL_EVENT_WAIT, (void **)&ed, sizeof(*ed)); if (error) { goto out; } dcmd = &cm->cm_frame->dcmd; ((uint32_t *)&dcmd->mbox)[0] = seq; ((uint32_t *)&dcmd->mbox)[1] = locale; cm->cm_flags = MFI_CMD_DATAIN; cm->cm_complete = mfi_aen_complete; sc->mfi_aen_cm = cm; mfi_enqueue_ready(cm); mfi_startio(sc); out: return (error); } static void mfi_aen_complete(struct mfi_command *cm) { struct mfi_frame_header *hdr; struct mfi_softc *sc; struct mfi_evt_detail *detail; struct mfi_aen *mfi_aen_entry, *tmp; int seq = 0, aborted = 0; sc = cm->cm_sc; hdr = &cm->cm_frame->header; if (sc->mfi_aen_cm == NULL) return; if (sc->mfi_aen_cm->cm_aen_abort || hdr->cmd_status == 0xff) { sc->mfi_aen_cm->cm_aen_abort = 0; aborted = 1; } else { sc->mfi_aen_triggered = 1; if (sc->mfi_poll_waiting) { sc->mfi_poll_waiting = 0; selwakeup(&sc->mfi_select); } detail = cm->cm_data; /* * XXX If this function is too expensive or is recursive, then * events should be put onto a queue and processed later. */ mfi_decode_evt(sc, detail); seq = detail->seq + 1; TAILQ_FOREACH_SAFE(mfi_aen_entry, &sc->mfi_aen_pids, aen_link, tmp) { TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry, aen_link); PROC_LOCK(mfi_aen_entry->p); psignal(mfi_aen_entry->p, SIGIO); PROC_UNLOCK(mfi_aen_entry->p); free(mfi_aen_entry, M_MFIBUF); } } free(cm->cm_data, M_MFIBUF); sc->mfi_aen_cm = NULL; wakeup(&sc->mfi_aen_cm); mfi_release_command(cm); /* set it up again so the driver can catch more events */ if (!aborted) { mfi_aen_setup(sc, seq); } } #define MAX_EVENTS 15 static int mfi_parse_entries(struct mfi_softc *sc, int start_seq, int stop_seq) { struct mfi_command *cm; struct mfi_dcmd_frame *dcmd; struct mfi_evt_list *el; union mfi_evt class_locale; int error, i, seq, size; class_locale.members.reserved = 0; class_locale.members.locale = mfi_event_locale; - class_locale.members.class = mfi_event_class; + class_locale.members.evt_class = mfi_event_class; size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) * (MAX_EVENTS - 1); el = malloc(size, M_MFIBUF, M_NOWAIT | M_ZERO); if (el == NULL) return (ENOMEM); for (seq = start_seq;;) { if ((cm = mfi_dequeue_free(sc)) == NULL) { free(el, M_MFIBUF); return (EBUSY); } dcmd = &cm->cm_frame->dcmd; bzero(dcmd->mbox, MFI_MBOX_SIZE); dcmd->header.cmd = MFI_CMD_DCMD; dcmd->header.timeout = 0; dcmd->header.data_len = size; dcmd->opcode = MFI_DCMD_CTRL_EVENT_GET; ((uint32_t *)&dcmd->mbox)[0] = seq; ((uint32_t *)&dcmd->mbox)[1] = class_locale.word; cm->cm_sg = &dcmd->sgl; cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE; cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_POLLED; cm->cm_data = el; cm->cm_len = size; if ((error = mfi_mapcmd(sc, cm)) != 0) { device_printf(sc->mfi_dev, "Failed to get controller entries\n"); mfi_release_command(cm); break; } bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, BUS_DMASYNC_POSTREAD); bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); if (dcmd->header.cmd_status == MFI_STAT_NOT_FOUND) { mfi_release_command(cm); break; } if (dcmd->header.cmd_status != MFI_STAT_OK) { device_printf(sc->mfi_dev, "Error %d fetching controller entries\n", dcmd->header.cmd_status); mfi_release_command(cm); break; } mfi_release_command(cm); for (i = 0; i < el->count; i++) { /* * If this event is newer than 'stop_seq' then * break out of the loop. Note that the log * is a circular buffer so we have to handle * the case that our stop point is earlier in * the buffer than our start point. */ if (el->event[i].seq >= stop_seq) { if (start_seq <= stop_seq) break; else if (el->event[i].seq < start_seq) break; } mfi_decode_evt(sc, &el->event[i]); } seq = el->event[el->count - 1].seq + 1; } free(el, M_MFIBUF); return (0); } static int mfi_add_ld(struct mfi_softc *sc, int id) { struct mfi_command *cm; struct mfi_dcmd_frame *dcmd = NULL; struct mfi_ld_info *ld_info = NULL; int error; mtx_assert(&sc->mfi_io_lock, MA_OWNED); error = mfi_dcmd_command(sc, &cm, MFI_DCMD_LD_GET_INFO, (void **)&ld_info, sizeof(*ld_info)); if (error) { device_printf(sc->mfi_dev, "Failed to allocate for MFI_DCMD_LD_GET_INFO %d\n", error); if (ld_info) free(ld_info, M_MFIBUF); return (error); } cm->cm_flags = MFI_CMD_DATAIN; dcmd = &cm->cm_frame->dcmd; dcmd->mbox[0] = id; if (mfi_wait_command(sc, cm) != 0) { device_printf(sc->mfi_dev, "Failed to get logical drive: %d\n", id); free(ld_info, M_MFIBUF); return (0); } mfi_add_ld_complete(cm); return (0); } static void mfi_add_ld_complete(struct mfi_command *cm) { struct mfi_frame_header *hdr; struct mfi_ld_info *ld_info; struct mfi_softc *sc; device_t child; sc = cm->cm_sc; hdr = &cm->cm_frame->header; ld_info = cm->cm_private; if (hdr->cmd_status != MFI_STAT_OK) { free(ld_info, M_MFIBUF); mfi_release_command(cm); return; } mfi_release_command(cm); mtx_unlock(&sc->mfi_io_lock); mtx_lock(&Giant); if ((child = device_add_child(sc->mfi_dev, "mfid", -1)) == NULL) { device_printf(sc->mfi_dev, "Failed to add logical disk\n"); free(ld_info, M_MFIBUF); mtx_unlock(&Giant); mtx_lock(&sc->mfi_io_lock); return; } device_set_ivars(child, ld_info); device_set_desc(child, "MFI Logical Disk"); bus_generic_attach(sc->mfi_dev); mtx_unlock(&Giant); mtx_lock(&sc->mfi_io_lock); } static struct mfi_command * mfi_bio_command(struct mfi_softc *sc) { struct mfi_io_frame *io; struct mfi_command *cm; struct bio *bio; int flags, blkcount; if ((cm = mfi_dequeue_free(sc)) == NULL) return (NULL); if ((bio = mfi_dequeue_bio(sc)) == NULL) { mfi_release_command(cm); return (NULL); } io = &cm->cm_frame->io; switch (bio->bio_cmd & 0x03) { case BIO_READ: io->header.cmd = MFI_CMD_LD_READ; flags = MFI_CMD_DATAIN; break; case BIO_WRITE: io->header.cmd = MFI_CMD_LD_WRITE; flags = MFI_CMD_DATAOUT; break; default: panic("Invalid bio command"); } /* Cheat with the sector length to avoid a non-constant division */ blkcount = (bio->bio_bcount + MFI_SECTOR_LEN - 1) / MFI_SECTOR_LEN; io->header.target_id = (uintptr_t)bio->bio_driver1; io->header.timeout = 0; io->header.flags = 0; io->header.sense_len = MFI_SENSE_LEN; io->header.data_len = blkcount; io->sense_addr_lo = cm->cm_sense_busaddr; io->sense_addr_hi = 0; io->lba_hi = (bio->bio_pblkno & 0xffffffff00000000) >> 32; io->lba_lo = bio->bio_pblkno & 0xffffffff; cm->cm_complete = mfi_bio_complete; cm->cm_private = bio; cm->cm_data = bio->bio_data; cm->cm_len = bio->bio_bcount; cm->cm_sg = &io->sgl; cm->cm_total_frame_size = MFI_IO_FRAME_SIZE; cm->cm_flags = flags; return (cm); } static void mfi_bio_complete(struct mfi_command *cm) { struct bio *bio; struct mfi_frame_header *hdr; struct mfi_softc *sc; bio = cm->cm_private; hdr = &cm->cm_frame->header; sc = cm->cm_sc; if ((hdr->cmd_status != 0) || (hdr->scsi_status != 0)) { bio->bio_flags |= BIO_ERROR; bio->bio_error = EIO; device_printf(sc->mfi_dev, "I/O error, status= %d " "scsi_status= %d\n", hdr->cmd_status, hdr->scsi_status); mfi_print_sense(cm->cm_sc, cm->cm_sense); } else if (cm->cm_error != 0) { bio->bio_flags |= BIO_ERROR; } mfi_release_command(cm); mfi_disk_complete(bio); } void mfi_startio(struct mfi_softc *sc) { struct mfi_command *cm; struct ccb_hdr *ccbh; for (;;) { /* Don't bother if we're short on resources */ if (sc->mfi_flags & MFI_FLAGS_QFRZN) break; /* Try a command that has already been prepared */ cm = mfi_dequeue_ready(sc); if (cm == NULL) { if ((ccbh = TAILQ_FIRST(&sc->mfi_cam_ccbq)) != NULL) cm = sc->mfi_cam_start(ccbh); } /* Nope, so look for work on the bioq */ if (cm == NULL) cm = mfi_bio_command(sc); /* No work available, so exit */ if (cm == NULL) break; /* Send the command to the controller */ if (mfi_mapcmd(sc, cm) != 0) { mfi_requeue_ready(cm); break; } } } static int mfi_mapcmd(struct mfi_softc *sc, struct mfi_command *cm) { int error, polled; mtx_assert(&sc->mfi_io_lock, MA_OWNED); if (cm->cm_data != NULL) { polled = (cm->cm_flags & MFI_CMD_POLLED) ? BUS_DMA_NOWAIT : 0; error = bus_dmamap_load(sc->mfi_buffer_dmat, cm->cm_dmamap, cm->cm_data, cm->cm_len, mfi_data_cb, cm, polled); if (error == EINPROGRESS) { sc->mfi_flags |= MFI_FLAGS_QFRZN; return (0); } } else { error = mfi_send_frame(sc, cm); } return (error); } static void mfi_data_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error) { struct mfi_frame_header *hdr; struct mfi_command *cm; union mfi_sgl *sgl; struct mfi_softc *sc; int i, dir; cm = (struct mfi_command *)arg; sc = cm->cm_sc; hdr = &cm->cm_frame->header; sgl = cm->cm_sg; if (error) { printf("error %d in callback\n", error); cm->cm_error = error; mfi_complete(sc, cm); return; } if ((sc->mfi_flags & MFI_FLAGS_SG64) == 0) { for (i = 0; i < nsegs; i++) { sgl->sg32[i].addr = segs[i].ds_addr; sgl->sg32[i].len = segs[i].ds_len; } } else { for (i = 0; i < nsegs; i++) { sgl->sg64[i].addr = segs[i].ds_addr; sgl->sg64[i].len = segs[i].ds_len; } hdr->flags |= MFI_FRAME_SGL64; } hdr->sg_count = nsegs; dir = 0; if (cm->cm_flags & MFI_CMD_DATAIN) { dir |= BUS_DMASYNC_PREREAD; hdr->flags |= MFI_FRAME_DIR_READ; } if (cm->cm_flags & MFI_CMD_DATAOUT) { dir |= BUS_DMASYNC_PREWRITE; hdr->flags |= MFI_FRAME_DIR_WRITE; } bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, dir); cm->cm_flags |= MFI_CMD_MAPPED; /* * Instead of calculating the total number of frames in the * compound frame, it's already assumed that there will be at * least 1 frame, so don't compensate for the modulo of the * following division. */ cm->cm_total_frame_size += (sc->mfi_sge_size * nsegs); cm->cm_extra_frames = (cm->cm_total_frame_size - 1) / MFI_FRAME_SIZE; mfi_send_frame(sc, cm); return; } static int mfi_send_frame(struct mfi_softc *sc, struct mfi_command *cm) { struct mfi_frame_header *hdr; int tm = MFI_POLL_TIMEOUT_SECS * 1000; hdr = &cm->cm_frame->header; if ((cm->cm_flags & MFI_CMD_POLLED) == 0) { cm->cm_timestamp = time_uptime; mfi_enqueue_busy(cm); } else { hdr->cmd_status = 0xff; hdr->flags |= MFI_FRAME_DONT_POST_IN_REPLY_QUEUE; } /* * The bus address of the command is aligned on a 64 byte boundary, * leaving the least 6 bits as zero. For whatever reason, the * hardware wants the address shifted right by three, leaving just * 3 zero bits. These three bits are then used as a prefetching * hint for the hardware to predict how many frames need to be * fetched across the bus. If a command has more than 8 frames * then the 3 bits are set to 0x7 and the firmware uses other * information in the command to determine the total amount to fetch. * However, FreeBSD doesn't support I/O larger than 128K, so 8 frames * is enough for both 32bit and 64bit systems. */ if (cm->cm_extra_frames > 7) cm->cm_extra_frames = 7; sc->mfi_issue_cmd(sc,cm->cm_frame_busaddr,cm->cm_extra_frames); if ((cm->cm_flags & MFI_CMD_POLLED) == 0) return (0); /* This is a polled command, so busy-wait for it to complete. */ while (hdr->cmd_status == 0xff) { DELAY(1000); tm -= 1; if (tm <= 0) break; } if (hdr->cmd_status == 0xff) { device_printf(sc->mfi_dev, "Frame %p timed out " "command 0x%X\n", hdr, cm->cm_frame->dcmd.opcode); return (ETIMEDOUT); } return (0); } static void mfi_complete(struct mfi_softc *sc, struct mfi_command *cm) { int dir; if ((cm->cm_flags & MFI_CMD_MAPPED) != 0) { dir = 0; if (cm->cm_flags & MFI_CMD_DATAIN) dir |= BUS_DMASYNC_POSTREAD; if (cm->cm_flags & MFI_CMD_DATAOUT) dir |= BUS_DMASYNC_POSTWRITE; bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, dir); bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); cm->cm_flags &= ~MFI_CMD_MAPPED; } cm->cm_flags |= MFI_CMD_COMPLETED; if (cm->cm_complete != NULL) cm->cm_complete(cm); else wakeup(cm); } static int mfi_abort(struct mfi_softc *sc, struct mfi_command *cm_abort) { struct mfi_command *cm; struct mfi_abort_frame *abort; int i = 0; mtx_assert(&sc->mfi_io_lock, MA_OWNED); if ((cm = mfi_dequeue_free(sc)) == NULL) { return (EBUSY); } abort = &cm->cm_frame->abort; abort->header.cmd = MFI_CMD_ABORT; abort->header.flags = 0; abort->abort_context = cm_abort->cm_frame->header.context; abort->abort_mfi_addr_lo = cm_abort->cm_frame_busaddr; abort->abort_mfi_addr_hi = 0; cm->cm_data = NULL; cm->cm_flags = MFI_CMD_POLLED; sc->mfi_aen_cm->cm_aen_abort = 1; mfi_mapcmd(sc, cm); mfi_release_command(cm); while (i < 5 && sc->mfi_aen_cm != NULL) { msleep(&sc->mfi_aen_cm, &sc->mfi_io_lock, 0, "mfiabort", 5 * hz); i++; } return (0); } int mfi_dump_blocks(struct mfi_softc *sc, int id, uint64_t lba, void *virt, int len) { struct mfi_command *cm; struct mfi_io_frame *io; int error; if ((cm = mfi_dequeue_free(sc)) == NULL) return (EBUSY); io = &cm->cm_frame->io; io->header.cmd = MFI_CMD_LD_WRITE; io->header.target_id = id; io->header.timeout = 0; io->header.flags = 0; io->header.sense_len = MFI_SENSE_LEN; io->header.data_len = (len + MFI_SECTOR_LEN - 1) / MFI_SECTOR_LEN; io->sense_addr_lo = cm->cm_sense_busaddr; io->sense_addr_hi = 0; io->lba_hi = (lba & 0xffffffff00000000) >> 32; io->lba_lo = lba & 0xffffffff; cm->cm_data = virt; cm->cm_len = len; cm->cm_sg = &io->sgl; cm->cm_total_frame_size = MFI_IO_FRAME_SIZE; cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAOUT; error = mfi_mapcmd(sc, cm); bus_dmamap_sync(sc->mfi_buffer_dmat, cm->cm_dmamap, BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(sc->mfi_buffer_dmat, cm->cm_dmamap); mfi_release_command(cm); return (error); } static int mfi_open(struct cdev *dev, int flags, int fmt, struct thread *td) { struct mfi_softc *sc; int error; sc = dev->si_drv1; mtx_lock(&sc->mfi_io_lock); if (sc->mfi_detaching) error = ENXIO; else { sc->mfi_flags |= MFI_FLAGS_OPEN; error = 0; } mtx_unlock(&sc->mfi_io_lock); return (error); } static int mfi_close(struct cdev *dev, int flags, int fmt, struct thread *td) { struct mfi_softc *sc; struct mfi_aen *mfi_aen_entry, *tmp; sc = dev->si_drv1; mtx_lock(&sc->mfi_io_lock); sc->mfi_flags &= ~MFI_FLAGS_OPEN; TAILQ_FOREACH_SAFE(mfi_aen_entry, &sc->mfi_aen_pids, aen_link, tmp) { if (mfi_aen_entry->p == curproc) { TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry, aen_link); free(mfi_aen_entry, M_MFIBUF); } } mtx_unlock(&sc->mfi_io_lock); return (0); } static int mfi_config_lock(struct mfi_softc *sc, uint32_t opcode) { switch (opcode) { case MFI_DCMD_LD_DELETE: case MFI_DCMD_CFG_ADD: case MFI_DCMD_CFG_CLEAR: sx_xlock(&sc->mfi_config_lock); return (1); default: return (0); } } static void mfi_config_unlock(struct mfi_softc *sc, int locked) { if (locked) sx_xunlock(&sc->mfi_config_lock); } /* Perform pre-issue checks on commands from userland and possibly veto them. */ static int mfi_check_command_pre(struct mfi_softc *sc, struct mfi_command *cm) { struct mfi_disk *ld, *ld2; int error; mtx_assert(&sc->mfi_io_lock, MA_OWNED); error = 0; switch (cm->cm_frame->dcmd.opcode) { case MFI_DCMD_LD_DELETE: TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) { if (ld->ld_id == cm->cm_frame->dcmd.mbox[0]) break; } if (ld == NULL) error = ENOENT; else error = mfi_disk_disable(ld); break; case MFI_DCMD_CFG_CLEAR: TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) { error = mfi_disk_disable(ld); if (error) break; } if (error) { TAILQ_FOREACH(ld2, &sc->mfi_ld_tqh, ld_link) { if (ld2 == ld) break; mfi_disk_enable(ld2); } } break; default: break; } return (error); } /* Perform post-issue checks on commands from userland. */ static void mfi_check_command_post(struct mfi_softc *sc, struct mfi_command *cm) { struct mfi_disk *ld, *ldn; switch (cm->cm_frame->dcmd.opcode) { case MFI_DCMD_LD_DELETE: TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) { if (ld->ld_id == cm->cm_frame->dcmd.mbox[0]) break; } KASSERT(ld != NULL, ("volume dissappeared")); if (cm->cm_frame->header.cmd_status == MFI_STAT_OK) { mtx_unlock(&sc->mfi_io_lock); mtx_lock(&Giant); device_delete_child(sc->mfi_dev, ld->ld_dev); mtx_unlock(&Giant); mtx_lock(&sc->mfi_io_lock); } else mfi_disk_enable(ld); break; case MFI_DCMD_CFG_CLEAR: if (cm->cm_frame->header.cmd_status == MFI_STAT_OK) { mtx_unlock(&sc->mfi_io_lock); mtx_lock(&Giant); TAILQ_FOREACH_SAFE(ld, &sc->mfi_ld_tqh, ld_link, ldn) { device_delete_child(sc->mfi_dev, ld->ld_dev); } mtx_unlock(&Giant); mtx_lock(&sc->mfi_io_lock); } else { TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) mfi_disk_enable(ld); } break; case MFI_DCMD_CFG_ADD: mfi_ldprobe(sc); break; case MFI_DCMD_CFG_FOREIGN_IMPORT: mfi_ldprobe(sc); break; } } static int mfi_user_command(struct mfi_softc *sc, struct mfi_ioc_passthru *ioc) { struct mfi_command *cm; struct mfi_dcmd_frame *dcmd; void *ioc_buf = NULL; uint32_t context; int error = 0, locked; if (ioc->buf_size > 0) { ioc_buf = malloc(ioc->buf_size, M_MFIBUF, M_WAITOK); if (ioc_buf == NULL) { return (ENOMEM); } error = copyin(ioc->buf, ioc_buf, ioc->buf_size); if (error) { device_printf(sc->mfi_dev, "failed to copyin\n"); free(ioc_buf, M_MFIBUF); return (error); } } locked = mfi_config_lock(sc, ioc->ioc_frame.opcode); mtx_lock(&sc->mfi_io_lock); while ((cm = mfi_dequeue_free(sc)) == NULL) msleep(mfi_user_command, &sc->mfi_io_lock, 0, "mfiioc", hz); /* Save context for later */ context = cm->cm_frame->header.context; dcmd = &cm->cm_frame->dcmd; bcopy(&ioc->ioc_frame, dcmd, sizeof(struct mfi_dcmd_frame)); cm->cm_sg = &dcmd->sgl; cm->cm_total_frame_size = MFI_DCMD_FRAME_SIZE; cm->cm_data = ioc_buf; cm->cm_len = ioc->buf_size; /* restore context */ cm->cm_frame->header.context = context; /* Cheat since we don't know if we're writing or reading */ cm->cm_flags = MFI_CMD_DATAIN | MFI_CMD_DATAOUT; error = mfi_check_command_pre(sc, cm); if (error) goto out; error = mfi_wait_command(sc, cm); if (error) { device_printf(sc->mfi_dev, "ioctl failed %d\n", error); goto out; } bcopy(dcmd, &ioc->ioc_frame, sizeof(struct mfi_dcmd_frame)); mfi_check_command_post(sc, cm); out: mfi_release_command(cm); mtx_unlock(&sc->mfi_io_lock); mfi_config_unlock(sc, locked); if (ioc->buf_size > 0) error = copyout(ioc_buf, ioc->buf, ioc->buf_size); if (ioc_buf) free(ioc_buf, M_MFIBUF); return (error); } #ifdef __amd64__ #define PTRIN(p) ((void *)(uintptr_t)(p)) #else #define PTRIN(p) (p) #endif static int mfi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { struct mfi_softc *sc; union mfi_statrequest *ms; struct mfi_ioc_packet *ioc; #ifdef __amd64__ struct mfi_ioc_packet32 *ioc32; #endif struct mfi_ioc_aen *aen; struct mfi_command *cm = NULL; uint32_t context; union mfi_sense_ptr sense_ptr; uint8_t *data = NULL, *temp; int i; struct mfi_ioc_passthru *iop = (struct mfi_ioc_passthru *)arg; #ifdef __amd64__ struct mfi_ioc_passthru32 *iop32 = (struct mfi_ioc_passthru32 *)arg; struct mfi_ioc_passthru iop_swab; #endif int error, locked; sc = dev->si_drv1; error = 0; switch (cmd) { case MFIIO_STATS: ms = (union mfi_statrequest *)arg; switch (ms->ms_item) { case MFIQ_FREE: case MFIQ_BIO: case MFIQ_READY: case MFIQ_BUSY: bcopy(&sc->mfi_qstat[ms->ms_item], &ms->ms_qstat, sizeof(struct mfi_qstat)); break; default: error = ENOIOCTL; break; } break; case MFIIO_QUERY_DISK: { struct mfi_query_disk *qd; struct mfi_disk *ld; qd = (struct mfi_query_disk *)arg; mtx_lock(&sc->mfi_io_lock); TAILQ_FOREACH(ld, &sc->mfi_ld_tqh, ld_link) { if (ld->ld_id == qd->array_id) break; } if (ld == NULL) { qd->present = 0; mtx_unlock(&sc->mfi_io_lock); return (0); } qd->present = 1; if (ld->ld_flags & MFI_DISK_FLAGS_OPEN) qd->open = 1; bzero(qd->devname, SPECNAMELEN + 1); snprintf(qd->devname, SPECNAMELEN, "mfid%d", ld->ld_unit); mtx_unlock(&sc->mfi_io_lock); break; } case MFI_CMD: #ifdef __amd64__ case MFI_CMD32: #endif { devclass_t devclass; ioc = (struct mfi_ioc_packet *)arg; int adapter; adapter = ioc->mfi_adapter_no; if (device_get_unit(sc->mfi_dev) == 0 && adapter != 0) { devclass = devclass_find("mfi"); sc = devclass_get_softc(devclass, adapter); } mtx_lock(&sc->mfi_io_lock); if ((cm = mfi_dequeue_free(sc)) == NULL) { mtx_unlock(&sc->mfi_io_lock); return (EBUSY); } mtx_unlock(&sc->mfi_io_lock); locked = 0; /* * save off original context since copying from user * will clobber some data */ context = cm->cm_frame->header.context; bcopy(ioc->mfi_frame.raw, cm->cm_frame, 2 * MFI_DCMD_FRAME_SIZE); /* this isn't quite right */ cm->cm_total_frame_size = (sizeof(union mfi_sgl) * ioc->mfi_sge_count) + ioc->mfi_sgl_off; if (ioc->mfi_sge_count) { cm->cm_sg = (union mfi_sgl *)&cm->cm_frame->bytes[ioc->mfi_sgl_off]; } cm->cm_flags = 0; if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN) cm->cm_flags |= MFI_CMD_DATAIN; if (cm->cm_frame->header.flags & MFI_FRAME_DATAOUT) cm->cm_flags |= MFI_CMD_DATAOUT; /* Legacy app shim */ if (cm->cm_flags == 0) cm->cm_flags |= MFI_CMD_DATAIN | MFI_CMD_DATAOUT; cm->cm_len = cm->cm_frame->header.data_len; if (cm->cm_len && (cm->cm_flags & (MFI_CMD_DATAIN | MFI_CMD_DATAOUT))) { cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF, M_WAITOK | M_ZERO); if (cm->cm_data == NULL) { device_printf(sc->mfi_dev, "Malloc failed\n"); goto out; } } else { cm->cm_data = 0; } /* restore header context */ cm->cm_frame->header.context = context; temp = data; if (cm->cm_flags & MFI_CMD_DATAOUT) { for (i = 0; i < ioc->mfi_sge_count; i++) { #ifdef __amd64__ if (cmd == MFI_CMD) { /* Native */ error = copyin(ioc->mfi_sgl[i].iov_base, temp, ioc->mfi_sgl[i].iov_len); } else { void *temp_convert; /* 32bit */ ioc32 = (struct mfi_ioc_packet32 *)ioc; temp_convert = PTRIN(ioc32->mfi_sgl[i].iov_base); error = copyin(temp_convert, temp, ioc32->mfi_sgl[i].iov_len); } #else error = copyin(ioc->mfi_sgl[i].iov_base, temp, ioc->mfi_sgl[i].iov_len); #endif if (error != 0) { device_printf(sc->mfi_dev, "Copy in failed\n"); goto out; } temp = &temp[ioc->mfi_sgl[i].iov_len]; } } if (cm->cm_frame->header.cmd == MFI_CMD_DCMD) locked = mfi_config_lock(sc, cm->cm_frame->dcmd.opcode); if (cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) { cm->cm_frame->pass.sense_addr_lo = cm->cm_sense_busaddr; cm->cm_frame->pass.sense_addr_hi = 0; } mtx_lock(&sc->mfi_io_lock); error = mfi_check_command_pre(sc, cm); if (error) { mtx_unlock(&sc->mfi_io_lock); goto out; } if ((error = mfi_wait_command(sc, cm)) != 0) { device_printf(sc->mfi_dev, "Controller polled failed\n"); mtx_unlock(&sc->mfi_io_lock); goto out; } mfi_check_command_post(sc, cm); mtx_unlock(&sc->mfi_io_lock); temp = data; if (cm->cm_flags & MFI_CMD_DATAIN) { for (i = 0; i < ioc->mfi_sge_count; i++) { #ifdef __amd64__ if (cmd == MFI_CMD) { /* Native */ error = copyout(temp, ioc->mfi_sgl[i].iov_base, ioc->mfi_sgl[i].iov_len); } else { void *temp_convert; /* 32bit */ ioc32 = (struct mfi_ioc_packet32 *)ioc; temp_convert = PTRIN(ioc32->mfi_sgl[i].iov_base); error = copyout(temp, temp_convert, ioc32->mfi_sgl[i].iov_len); } #else error = copyout(temp, ioc->mfi_sgl[i].iov_base, ioc->mfi_sgl[i].iov_len); #endif if (error != 0) { device_printf(sc->mfi_dev, "Copy out failed\n"); goto out; } temp = &temp[ioc->mfi_sgl[i].iov_len]; } } if (ioc->mfi_sense_len) { /* get user-space sense ptr then copy out sense */ bcopy(&((struct mfi_ioc_packet*)arg) ->mfi_frame.raw[ioc->mfi_sense_off], &sense_ptr.sense_ptr_data[0], sizeof(sense_ptr.sense_ptr_data)); #ifdef __amd64__ if (cmd != MFI_CMD) { /* * not 64bit native so zero out any address * over 32bit */ sense_ptr.addr.high = 0; } #endif error = copyout(cm->cm_sense, sense_ptr.user_space, ioc->mfi_sense_len); if (error != 0) { device_printf(sc->mfi_dev, "Copy out failed\n"); goto out; } } ioc->mfi_frame.hdr.cmd_status = cm->cm_frame->header.cmd_status; out: mfi_config_unlock(sc, locked); if (data) free(data, M_MFIBUF); if (cm) { mtx_lock(&sc->mfi_io_lock); mfi_release_command(cm); mtx_unlock(&sc->mfi_io_lock); } break; } case MFI_SET_AEN: aen = (struct mfi_ioc_aen *)arg; error = mfi_aen_register(sc, aen->aen_seq_num, aen->aen_class_locale); break; case MFI_LINUX_CMD_2: /* Firmware Linux ioctl shim */ { devclass_t devclass; struct mfi_linux_ioc_packet l_ioc; int adapter; devclass = devclass_find("mfi"); if (devclass == NULL) return (ENOENT); error = copyin(arg, &l_ioc, sizeof(l_ioc)); if (error) return (error); adapter = l_ioc.lioc_adapter_no; sc = devclass_get_softc(devclass, adapter); if (sc == NULL) return (ENOENT); return (mfi_linux_ioctl_int(sc->mfi_cdev, cmd, arg, flag, td)); break; } case MFI_LINUX_SET_AEN_2: /* AEN Linux ioctl shim */ { devclass_t devclass; struct mfi_linux_ioc_aen l_aen; int adapter; devclass = devclass_find("mfi"); if (devclass == NULL) return (ENOENT); error = copyin(arg, &l_aen, sizeof(l_aen)); if (error) return (error); adapter = l_aen.laen_adapter_no; sc = devclass_get_softc(devclass, adapter); if (sc == NULL) return (ENOENT); return (mfi_linux_ioctl_int(sc->mfi_cdev, cmd, arg, flag, td)); break; } #ifdef __amd64__ case MFIIO_PASSTHRU32: iop_swab.ioc_frame = iop32->ioc_frame; iop_swab.buf_size = iop32->buf_size; iop_swab.buf = PTRIN(iop32->buf); iop = &iop_swab; /* FALLTHROUGH */ #endif case MFIIO_PASSTHRU: error = mfi_user_command(sc, iop); #ifdef __amd64__ if (cmd == MFIIO_PASSTHRU32) iop32->ioc_frame = iop_swab.ioc_frame; #endif break; default: device_printf(sc->mfi_dev, "IOCTL 0x%lx not handled\n", cmd); error = ENOENT; break; } return (error); } static int mfi_linux_ioctl_int(struct cdev *dev, u_long cmd, caddr_t arg, int flag, struct thread *td) { struct mfi_softc *sc; struct mfi_linux_ioc_packet l_ioc; struct mfi_linux_ioc_aen l_aen; struct mfi_command *cm = NULL; struct mfi_aen *mfi_aen_entry; union mfi_sense_ptr sense_ptr; uint32_t context; uint8_t *data = NULL, *temp; int i; int error, locked; sc = dev->si_drv1; error = 0; switch (cmd) { case MFI_LINUX_CMD_2: /* Firmware Linux ioctl shim */ error = copyin(arg, &l_ioc, sizeof(l_ioc)); if (error != 0) return (error); if (l_ioc.lioc_sge_count > MAX_LINUX_IOCTL_SGE) { return (EINVAL); } mtx_lock(&sc->mfi_io_lock); if ((cm = mfi_dequeue_free(sc)) == NULL) { mtx_unlock(&sc->mfi_io_lock); return (EBUSY); } mtx_unlock(&sc->mfi_io_lock); locked = 0; /* * save off original context since copying from user * will clobber some data */ context = cm->cm_frame->header.context; bcopy(l_ioc.lioc_frame.raw, cm->cm_frame, 2 * MFI_DCMD_FRAME_SIZE); /* this isn't quite right */ cm->cm_total_frame_size = (sizeof(union mfi_sgl) * l_ioc.lioc_sge_count) + l_ioc.lioc_sgl_off; if (l_ioc.lioc_sge_count) cm->cm_sg = (union mfi_sgl *)&cm->cm_frame->bytes[l_ioc.lioc_sgl_off]; cm->cm_flags = 0; if (cm->cm_frame->header.flags & MFI_FRAME_DATAIN) cm->cm_flags |= MFI_CMD_DATAIN; if (cm->cm_frame->header.flags & MFI_FRAME_DATAOUT) cm->cm_flags |= MFI_CMD_DATAOUT; cm->cm_len = cm->cm_frame->header.data_len; if (cm->cm_len && (cm->cm_flags & (MFI_CMD_DATAIN | MFI_CMD_DATAOUT))) { cm->cm_data = data = malloc(cm->cm_len, M_MFIBUF, M_WAITOK | M_ZERO); if (cm->cm_data == NULL) { device_printf(sc->mfi_dev, "Malloc failed\n"); goto out; } } else { cm->cm_data = 0; } /* restore header context */ cm->cm_frame->header.context = context; temp = data; if (cm->cm_flags & MFI_CMD_DATAOUT) { for (i = 0; i < l_ioc.lioc_sge_count; i++) { error = copyin(PTRIN(l_ioc.lioc_sgl[i].iov_base), temp, l_ioc.lioc_sgl[i].iov_len); if (error != 0) { device_printf(sc->mfi_dev, "Copy in failed\n"); goto out; } temp = &temp[l_ioc.lioc_sgl[i].iov_len]; } } if (cm->cm_frame->header.cmd == MFI_CMD_DCMD) locked = mfi_config_lock(sc, cm->cm_frame->dcmd.opcode); if (cm->cm_frame->header.cmd == MFI_CMD_PD_SCSI_IO) { cm->cm_frame->pass.sense_addr_lo = cm->cm_sense_busaddr; cm->cm_frame->pass.sense_addr_hi = 0; } mtx_lock(&sc->mfi_io_lock); error = mfi_check_command_pre(sc, cm); if (error) { mtx_unlock(&sc->mfi_io_lock); goto out; } if ((error = mfi_wait_command(sc, cm)) != 0) { device_printf(sc->mfi_dev, "Controller polled failed\n"); mtx_unlock(&sc->mfi_io_lock); goto out; } mfi_check_command_post(sc, cm); mtx_unlock(&sc->mfi_io_lock); temp = data; if (cm->cm_flags & MFI_CMD_DATAIN) { for (i = 0; i < l_ioc.lioc_sge_count; i++) { error = copyout(temp, PTRIN(l_ioc.lioc_sgl[i].iov_base), l_ioc.lioc_sgl[i].iov_len); if (error != 0) { device_printf(sc->mfi_dev, "Copy out failed\n"); goto out; } temp = &temp[l_ioc.lioc_sgl[i].iov_len]; } } if (l_ioc.lioc_sense_len) { /* get user-space sense ptr then copy out sense */ bcopy(&((struct mfi_linux_ioc_packet*)arg) ->lioc_frame.raw[l_ioc.lioc_sense_off], &sense_ptr.sense_ptr_data[0], sizeof(sense_ptr.sense_ptr_data)); #ifdef __amd64__ /* * only 32bit Linux support so zero out any * address over 32bit */ sense_ptr.addr.high = 0; #endif error = copyout(cm->cm_sense, sense_ptr.user_space, l_ioc.lioc_sense_len); if (error != 0) { device_printf(sc->mfi_dev, "Copy out failed\n"); goto out; } } error = copyout(&cm->cm_frame->header.cmd_status, &((struct mfi_linux_ioc_packet*)arg) ->lioc_frame.hdr.cmd_status, 1); if (error != 0) { device_printf(sc->mfi_dev, "Copy out failed\n"); goto out; } out: mfi_config_unlock(sc, locked); if (data) free(data, M_MFIBUF); if (cm) { mtx_lock(&sc->mfi_io_lock); mfi_release_command(cm); mtx_unlock(&sc->mfi_io_lock); } return (error); case MFI_LINUX_SET_AEN_2: /* AEN Linux ioctl shim */ error = copyin(arg, &l_aen, sizeof(l_aen)); if (error != 0) return (error); printf("AEN IMPLEMENTED for pid %d\n", curproc->p_pid); mfi_aen_entry = malloc(sizeof(struct mfi_aen), M_MFIBUF, M_WAITOK); mtx_lock(&sc->mfi_io_lock); if (mfi_aen_entry != NULL) { mfi_aen_entry->p = curproc; TAILQ_INSERT_TAIL(&sc->mfi_aen_pids, mfi_aen_entry, aen_link); } error = mfi_aen_register(sc, l_aen.laen_seq_num, l_aen.laen_class_locale); if (error != 0) { TAILQ_REMOVE(&sc->mfi_aen_pids, mfi_aen_entry, aen_link); free(mfi_aen_entry, M_MFIBUF); } mtx_unlock(&sc->mfi_io_lock); return (error); default: device_printf(sc->mfi_dev, "IOCTL 0x%lx not handled\n", cmd); error = ENOENT; break; } return (error); } static int mfi_poll(struct cdev *dev, int poll_events, struct thread *td) { struct mfi_softc *sc; int revents = 0; sc = dev->si_drv1; if (poll_events & (POLLIN | POLLRDNORM)) { if (sc->mfi_aen_triggered != 0) { revents |= poll_events & (POLLIN | POLLRDNORM); sc->mfi_aen_triggered = 0; } if (sc->mfi_aen_triggered == 0 && sc->mfi_aen_cm == NULL) { revents |= POLLERR; } } if (revents == 0) { if (poll_events & (POLLIN | POLLRDNORM)) { sc->mfi_poll_waiting = 1; selrecord(td, &sc->mfi_select); } } return revents; } static void mfi_dump_all(void) { struct mfi_softc *sc; struct mfi_command *cm; devclass_t dc; time_t deadline; int timedout; int i; dc = devclass_find("mfi"); if (dc == NULL) { printf("No mfi dev class\n"); return; } for (i = 0; ; i++) { sc = devclass_get_softc(dc, i); if (sc == NULL) break; device_printf(sc->mfi_dev, "Dumping\n\n"); timedout = 0; deadline = time_uptime - MFI_CMD_TIMEOUT; mtx_lock(&sc->mfi_io_lock); TAILQ_FOREACH(cm, &sc->mfi_busy, cm_link) { if (cm->cm_timestamp < deadline) { device_printf(sc->mfi_dev, "COMMAND %p TIMEOUT AFTER %d SECONDS\n", cm, (int)(time_uptime - cm->cm_timestamp)); MFI_PRINT_CMD(cm); timedout++; } } #if 0 if (timedout) MFI_DUMP_CMDS(SC); #endif mtx_unlock(&sc->mfi_io_lock); } return; } static void mfi_timeout(void *data) { struct mfi_softc *sc = (struct mfi_softc *)data; struct mfi_command *cm; time_t deadline; int timedout = 0; deadline = time_uptime - MFI_CMD_TIMEOUT; mtx_lock(&sc->mfi_io_lock); TAILQ_FOREACH(cm, &sc->mfi_busy, cm_link) { if (sc->mfi_aen_cm == cm) continue; if ((sc->mfi_aen_cm != cm) && (cm->cm_timestamp < deadline)) { device_printf(sc->mfi_dev, "COMMAND %p TIMEOUT AFTER %d SECONDS\n", cm, (int)(time_uptime - cm->cm_timestamp)); MFI_PRINT_CMD(cm); MFI_VALIDATE_CMD(sc, cm); timedout++; } } #if 0 if (timedout) MFI_DUMP_CMDS(SC); #endif mtx_unlock(&sc->mfi_io_lock); callout_reset(&sc->mfi_watchdog_callout, MFI_CMD_TIMEOUT * hz, mfi_timeout, sc); if (0) mfi_dump_all(); return; } Index: head/sys/dev/mfi/mfireg.h =================================================================== --- head/sys/dev/mfi/mfireg.h (revision 222588) +++ head/sys/dev/mfi/mfireg.h (revision 222589) @@ -1,1237 +1,1237 @@ /*- * Copyright (c) 2006 IronPort Systems * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /*- * Copyright (c) 2007 LSI Corp. * Copyright (c) 2007 Rajesh Prabhakaran. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifndef _MFIREG_H #define _MFIREG_H #include __FBSDID("$FreeBSD$"); /* * MegaRAID SAS MFI firmware definitions * * Calling this driver 'MegaRAID SAS' is a bit misleading. It's a completely * new firmware interface from the old AMI MegaRAID one, and there is no * reason why this interface should be limited to just SAS. In any case, LSI * seems to also call this interface 'MFI', so that will be used here. */ /* * Start with the register set. All registers are 32 bits wide. * The usual Intel IOP style setup. */ #define MFI_IMSG0 0x10 /* Inbound message 0 */ #define MFI_IMSG1 0x14 /* Inbound message 1 */ #define MFI_OMSG0 0x18 /* Outbound message 0 */ #define MFI_OMSG1 0x1c /* Outbound message 1 */ #define MFI_IDB 0x20 /* Inbound doorbell */ #define MFI_ISTS 0x24 /* Inbound interrupt status */ #define MFI_IMSK 0x28 /* Inbound interrupt mask */ #define MFI_ODB 0x2c /* Outbound doorbell */ #define MFI_OSTS 0x30 /* Outbound interrupt status */ #define MFI_OMSK 0x34 /* Outbound interrupt mask */ #define MFI_IQP 0x40 /* Inbound queue port */ #define MFI_OQP 0x44 /* Outbound queue port */ /* * 1078 specific related register */ #define MFI_ODR0 0x9c /* outbound doorbell register0 */ #define MFI_ODCR0 0xa0 /* outbound doorbell clear register0 */ #define MFI_OSP0 0xb0 /* outbound scratch pad0 */ #define MFI_1078_EIM 0x80000004 /* 1078 enable intrrupt mask */ #define MFI_RMI 0x2 /* reply message interrupt */ #define MFI_1078_RM 0x80000000 /* reply 1078 message interrupt */ #define MFI_ODC 0x4 /* outbound doorbell change interrupt */ /* * GEN2 specific changes */ #define MFI_GEN2_EIM 0x00000005 /* GEN2 enable interrupt mask */ #define MFI_GEN2_RM 0x00000001 /* reply GEN2 message interrupt */ /* Bits for MFI_OSTS */ #define MFI_OSTS_INTR_VALID 0x00000002 /* * Firmware state values. Found in OMSG0 during initialization. */ #define MFI_FWSTATE_MASK 0xf0000000 #define MFI_FWSTATE_UNDEFINED 0x00000000 #define MFI_FWSTATE_BB_INIT 0x10000000 #define MFI_FWSTATE_FW_INIT 0x40000000 #define MFI_FWSTATE_WAIT_HANDSHAKE 0x60000000 #define MFI_FWSTATE_FW_INIT_2 0x70000000 #define MFI_FWSTATE_DEVICE_SCAN 0x80000000 #define MFI_FWSTATE_FLUSH_CACHE 0xa0000000 #define MFI_FWSTATE_READY 0xb0000000 #define MFI_FWSTATE_OPERATIONAL 0xc0000000 #define MFI_FWSTATE_FAULT 0xf0000000 #define MFI_FWSTATE_MAXSGL_MASK 0x00ff0000 #define MFI_FWSTATE_MAXCMD_MASK 0x0000ffff /* * Control bits to drive the card to ready state. These go into the IDB * register. */ #define MFI_FWINIT_ABORT 0x00000000 /* Abort all pending commands */ #define MFI_FWINIT_READY 0x00000002 /* Move from operational to ready */ #define MFI_FWINIT_MFIMODE 0x00000004 /* unknown */ #define MFI_FWINIT_CLEAR_HANDSHAKE 0x00000008 /* Respond to WAIT_HANDSHAKE */ /* MFI Commands */ typedef enum { MFI_CMD_INIT = 0x00, MFI_CMD_LD_READ, MFI_CMD_LD_WRITE, MFI_CMD_LD_SCSI_IO, MFI_CMD_PD_SCSI_IO, MFI_CMD_DCMD, MFI_CMD_ABORT, MFI_CMD_SMP, MFI_CMD_STP } mfi_cmd_t; /* Direct commands */ typedef enum { MFI_DCMD_CTRL_GETINFO = 0x01010000, MFI_DCMD_CTRL_MFC_DEFAULTS_GET =0x010e0201, MFI_DCMD_CTRL_MFC_DEFAULTS_SET =0x010e0202, MFI_DCMD_CTRL_FLUSHCACHE = 0x01101000, MFI_DCMD_CTRL_SHUTDOWN = 0x01050000, MFI_DCMD_CTRL_EVENT_GETINFO = 0x01040100, MFI_DCMD_CTRL_EVENT_GET = 0x01040300, MFI_DCMD_CTRL_EVENT_WAIT = 0x01040500, MFI_DCMD_PR_GET_STATUS = 0x01070100, MFI_DCMD_PR_GET_PROPERTIES = 0x01070200, MFI_DCMD_PR_SET_PROPERTIES = 0x01070300, MFI_DCMD_PR_START = 0x01070400, MFI_DCMD_PR_STOP = 0x01070500, MFI_DCMD_TIME_SECS_GET = 0x01080201, MFI_DCMD_FLASH_FW_OPEN = 0x010f0100, MFI_DCMD_FLASH_FW_DOWNLOAD = 0x010f0200, MFI_DCMD_FLASH_FW_FLASH = 0x010f0300, MFI_DCMD_FLASH_FW_CLOSE = 0x010f0400, MFI_DCMD_PD_GET_LIST = 0x02010000, MFI_DCMD_PD_GET_INFO = 0x02020000, MFI_DCMD_PD_STATE_SET = 0x02030100, MFI_DCMD_PD_REBUILD_START = 0x02040100, MFI_DCMD_PD_REBUILD_ABORT = 0x02040200, MFI_DCMD_PD_CLEAR_START = 0x02050100, MFI_DCMD_PD_CLEAR_ABORT = 0x02050200, MFI_DCMD_PD_GET_PROGRESS = 0x02060000, MFI_DCMD_PD_LOCATE_START = 0x02070100, MFI_DCMD_PD_LOCATE_STOP = 0x02070200, MFI_DCMD_LD_GET_LIST = 0x03010000, MFI_DCMD_LD_GET_INFO = 0x03020000, MFI_DCMD_LD_GET_PROP = 0x03030000, MFI_DCMD_LD_SET_PROP = 0x03040000, MFI_DCMD_LD_INIT_START = 0x03060100, MFI_DCMD_LD_DELETE = 0x03090000, MFI_DCMD_CFG_READ = 0x04010000, MFI_DCMD_CFG_ADD = 0x04020000, MFI_DCMD_CFG_CLEAR = 0x04030000, MFI_DCMD_CFG_MAKE_SPARE = 0x04040000, MFI_DCMD_CFG_REMOVE_SPARE = 0x04050000, MFI_DCMD_CFG_FOREIGN_IMPORT = 0x04060400, MFI_DCMD_BBU_GET_STATUS = 0x05010000, MFI_DCMD_BBU_GET_CAPACITY_INFO =0x05020000, MFI_DCMD_BBU_GET_DESIGN_INFO = 0x05030000, MFI_DCMD_CLUSTER = 0x08000000, MFI_DCMD_CLUSTER_RESET_ALL = 0x08010100, MFI_DCMD_CLUSTER_RESET_LD = 0x08010200 } mfi_dcmd_t; /* Modifiers for MFI_DCMD_CTRL_FLUSHCACHE */ #define MFI_FLUSHCACHE_CTRL 0x01 #define MFI_FLUSHCACHE_DISK 0x02 /* Modifiers for MFI_DCMD_CTRL_SHUTDOWN */ #define MFI_SHUTDOWN_SPINDOWN 0x01 /* * MFI Frame flags */ #define MFI_FRAME_POST_IN_REPLY_QUEUE 0x0000 #define MFI_FRAME_DONT_POST_IN_REPLY_QUEUE 0x0001 #define MFI_FRAME_SGL32 0x0000 #define MFI_FRAME_SGL64 0x0002 #define MFI_FRAME_SENSE32 0x0000 #define MFI_FRAME_SENSE64 0x0004 #define MFI_FRAME_DIR_NONE 0x0000 #define MFI_FRAME_DIR_WRITE 0x0008 #define MFI_FRAME_DIR_READ 0x0010 #define MFI_FRAME_DIR_BOTH 0x0018 /* MFI Status codes */ typedef enum { MFI_STAT_OK = 0x00, MFI_STAT_INVALID_CMD, MFI_STAT_INVALID_DCMD, MFI_STAT_INVALID_PARAMETER, MFI_STAT_INVALID_SEQUENCE_NUMBER, MFI_STAT_ABORT_NOT_POSSIBLE, MFI_STAT_APP_HOST_CODE_NOT_FOUND, MFI_STAT_APP_IN_USE, MFI_STAT_APP_NOT_INITIALIZED, MFI_STAT_ARRAY_INDEX_INVALID, MFI_STAT_ARRAY_ROW_NOT_EMPTY, MFI_STAT_CONFIG_RESOURCE_CONFLICT, MFI_STAT_DEVICE_NOT_FOUND, MFI_STAT_DRIVE_TOO_SMALL, MFI_STAT_FLASH_ALLOC_FAIL, MFI_STAT_FLASH_BUSY, MFI_STAT_FLASH_ERROR = 0x10, MFI_STAT_FLASH_IMAGE_BAD, MFI_STAT_FLASH_IMAGE_INCOMPLETE, MFI_STAT_FLASH_NOT_OPEN, MFI_STAT_FLASH_NOT_STARTED, MFI_STAT_FLUSH_FAILED, MFI_STAT_HOST_CODE_NOT_FOUNT, MFI_STAT_LD_CC_IN_PROGRESS, MFI_STAT_LD_INIT_IN_PROGRESS, MFI_STAT_LD_LBA_OUT_OF_RANGE, MFI_STAT_LD_MAX_CONFIGURED, MFI_STAT_LD_NOT_OPTIMAL, MFI_STAT_LD_RBLD_IN_PROGRESS, MFI_STAT_LD_RECON_IN_PROGRESS, MFI_STAT_LD_WRONG_RAID_LEVEL, MFI_STAT_MAX_SPARES_EXCEEDED, MFI_STAT_MEMORY_NOT_AVAILABLE = 0x20, MFI_STAT_MFC_HW_ERROR, MFI_STAT_NO_HW_PRESENT, MFI_STAT_NOT_FOUND, MFI_STAT_NOT_IN_ENCL, MFI_STAT_PD_CLEAR_IN_PROGRESS, MFI_STAT_PD_TYPE_WRONG, MFI_STAT_PR_DISABLED, MFI_STAT_ROW_INDEX_INVALID, MFI_STAT_SAS_CONFIG_INVALID_ACTION, MFI_STAT_SAS_CONFIG_INVALID_DATA, MFI_STAT_SAS_CONFIG_INVALID_PAGE, MFI_STAT_SAS_CONFIG_INVALID_TYPE, MFI_STAT_SCSI_DONE_WITH_ERROR, MFI_STAT_SCSI_IO_FAILED, MFI_STAT_SCSI_RESERVATION_CONFLICT, MFI_STAT_SHUTDOWN_FAILED = 0x30, MFI_STAT_TIME_NOT_SET, MFI_STAT_WRONG_STATE, MFI_STAT_LD_OFFLINE, MFI_STAT_PEER_NOTIFICATION_REJECTED, MFI_STAT_PEER_NOTIFICATION_FAILED, MFI_STAT_RESERVATION_IN_PROGRESS, MFI_STAT_I2C_ERRORS_DETECTED, MFI_STAT_PCI_ERRORS_DETECTED, MFI_STAT_DIAG_FAILED, MFI_STAT_BOOT_MSG_PENDING, MFI_STAT_FOREIGN_CONFIG_INCOMPLETE, MFI_STAT_INVALID_STATUS = 0xFF } mfi_status_t; typedef enum { MFI_EVT_CLASS_DEBUG = -2, MFI_EVT_CLASS_PROGRESS = -1, MFI_EVT_CLASS_INFO = 0, MFI_EVT_CLASS_WARNING = 1, MFI_EVT_CLASS_CRITICAL = 2, MFI_EVT_CLASS_FATAL = 3, MFI_EVT_CLASS_DEAD = 4 } mfi_evt_class_t; typedef enum { MFI_EVT_LOCALE_LD = 0x0001, MFI_EVT_LOCALE_PD = 0x0002, MFI_EVT_LOCALE_ENCL = 0x0004, MFI_EVT_LOCALE_BBU = 0x0008, MFI_EVT_LOCALE_SAS = 0x0010, MFI_EVT_LOCALE_CTRL = 0x0020, MFI_EVT_LOCALE_CONFIG = 0x0040, MFI_EVT_LOCALE_CLUSTER = 0x0080, MFI_EVT_LOCALE_ALL = 0xffff } mfi_evt_locale_t; typedef enum { MR_EVT_ARGS_NONE = 0x00, MR_EVT_ARGS_CDB_SENSE, MR_EVT_ARGS_LD, MR_EVT_ARGS_LD_COUNT, MR_EVT_ARGS_LD_LBA, MR_EVT_ARGS_LD_OWNER, MR_EVT_ARGS_LD_LBA_PD_LBA, MR_EVT_ARGS_LD_PROG, MR_EVT_ARGS_LD_STATE, MR_EVT_ARGS_LD_STRIP, MR_EVT_ARGS_PD, MR_EVT_ARGS_PD_ERR, MR_EVT_ARGS_PD_LBA, MR_EVT_ARGS_PD_LBA_LD, MR_EVT_ARGS_PD_PROG, MR_EVT_ARGS_PD_STATE, MR_EVT_ARGS_PCI, MR_EVT_ARGS_RATE, MR_EVT_ARGS_STR, MR_EVT_ARGS_TIME, MR_EVT_ARGS_ECC } mfi_evt_args; typedef enum { MR_LD_CACHE_WRITE_BACK = 0x01, MR_LD_CACHE_WRITE_ADAPTIVE = 0x02, MR_LD_CACHE_READ_AHEAD = 0x04, MR_LD_CACHE_READ_ADAPTIVE = 0x08, MR_LD_CACHE_WRITE_CACHE_BAD_BBU=0x10, MR_LD_CACHE_ALLOW_WRITE_CACHE = 0x20, MR_LD_CACHE_ALLOW_READ_CACHE = 0x40 } mfi_ld_cache; #define MR_LD_CACHE_MASK 0x7f #define MR_LD_CACHE_POLICY_READ_AHEAD_NONE 0 #define MR_LD_CACHE_POLICY_READ_AHEAD_ALWAYS MR_LD_CACHE_READ_AHEAD #define MR_LD_CACHE_POLICY_READ_AHEAD_ADAPTIVE \ (MR_LD_CACHE_READ_AHEAD | MR_LD_CACHE_READ_ADAPTIVE) #define MR_LD_CACHE_POLICY_WRITE_THROUGH 0 #define MR_LD_CACHE_POLICY_WRITE_BACK MR_LD_CACHE_WRITE_BACK #define MR_LD_CACHE_POLICY_IO_CACHED \ (MR_LD_CACHE_ALLOW_WRITE_CACHE | MR_LD_CACHE_ALLOW_READ_CACHE) #define MR_LD_CACHE_POLICY_IO_DIRECT 0 typedef enum { MR_PD_CACHE_UNCHANGED = 0, MR_PD_CACHE_ENABLE = 1, MR_PD_CACHE_DISABLE = 2 } mfi_pd_cache; /* * Other propertities and definitions */ #define MFI_MAX_PD_CHANNELS 2 #define MFI_MAX_LD_CHANNELS 2 #define MFI_MAX_CHANNELS (MFI_MAX_PD_CHANNELS + MFI_MAX_LD_CHANNELS) #define MFI_MAX_CHANNEL_DEVS 128 #define MFI_DEFAULT_ID -1 #define MFI_MAX_LUN 8 #define MFI_MAX_LD 64 #define MFI_MAX_PD 256 #define MFI_FRAME_SIZE 64 #define MFI_MBOX_SIZE 12 /* Firmware flashing can take 40s */ #define MFI_POLL_TIMEOUT_SECS 50 /* Allow for speedier math calculations */ #define MFI_SECTOR_LEN 512 /* Scatter Gather elements */ struct mfi_sg32 { uint32_t addr; uint32_t len; } __packed; struct mfi_sg64 { uint64_t addr; uint32_t len; } __packed; union mfi_sgl { struct mfi_sg32 sg32[1]; struct mfi_sg64 sg64[1]; } __packed; /* Message frames. All messages have a common header */ struct mfi_frame_header { uint8_t cmd; uint8_t sense_len; uint8_t cmd_status; uint8_t scsi_status; uint8_t target_id; uint8_t lun_id; uint8_t cdb_len; uint8_t sg_count; uint32_t context; uint32_t pad0; uint16_t flags; #define MFI_FRAME_DATAOUT 0x08 #define MFI_FRAME_DATAIN 0x10 uint16_t timeout; uint32_t data_len; } __packed; struct mfi_init_frame { struct mfi_frame_header header; uint32_t qinfo_new_addr_lo; uint32_t qinfo_new_addr_hi; uint32_t qinfo_old_addr_lo; uint32_t qinfo_old_addr_hi; uint32_t reserved[6]; } __packed; #define MFI_IO_FRAME_SIZE 40 struct mfi_io_frame { struct mfi_frame_header header; uint32_t sense_addr_lo; uint32_t sense_addr_hi; uint32_t lba_lo; uint32_t lba_hi; union mfi_sgl sgl; } __packed; #define MFI_PASS_FRAME_SIZE 48 struct mfi_pass_frame { struct mfi_frame_header header; uint32_t sense_addr_lo; uint32_t sense_addr_hi; uint8_t cdb[16]; union mfi_sgl sgl; } __packed; #define MFI_DCMD_FRAME_SIZE 40 struct mfi_dcmd_frame { struct mfi_frame_header header; uint32_t opcode; uint8_t mbox[MFI_MBOX_SIZE]; union mfi_sgl sgl; } __packed; struct mfi_abort_frame { struct mfi_frame_header header; uint32_t abort_context; uint32_t pad; uint32_t abort_mfi_addr_lo; uint32_t abort_mfi_addr_hi; uint32_t reserved[6]; } __packed; struct mfi_smp_frame { struct mfi_frame_header header; uint64_t sas_addr; union { struct mfi_sg32 sg32[2]; struct mfi_sg64 sg64[2]; } sgl; } __packed; struct mfi_stp_frame { struct mfi_frame_header header; uint16_t fis[10]; uint32_t stp_flags; union { struct mfi_sg32 sg32[2]; struct mfi_sg64 sg64[2]; } sgl; } __packed; union mfi_frame { struct mfi_frame_header header; struct mfi_init_frame init; struct mfi_io_frame io; struct mfi_pass_frame pass; struct mfi_dcmd_frame dcmd; struct mfi_abort_frame abort; struct mfi_smp_frame smp; struct mfi_stp_frame stp; uint8_t bytes[MFI_FRAME_SIZE]; }; #define MFI_SENSE_LEN 128 struct mfi_sense { uint8_t data[MFI_SENSE_LEN]; }; /* The queue init structure that is passed with the init message */ struct mfi_init_qinfo { uint32_t flags; uint32_t rq_entries; uint32_t rq_addr_lo; uint32_t rq_addr_hi; uint32_t pi_addr_lo; uint32_t pi_addr_hi; uint32_t ci_addr_lo; uint32_t ci_addr_hi; } __packed; /* SAS (?) controller properties, part of mfi_ctrl_info */ struct mfi_ctrl_props { uint16_t seq_num; uint16_t pred_fail_poll_interval; uint16_t intr_throttle_cnt; uint16_t intr_throttle_timeout; uint8_t rebuild_rate; uint8_t patrol_read_rate; uint8_t bgi_rate; uint8_t cc_rate; uint8_t recon_rate; uint8_t cache_flush_interval; uint8_t spinup_drv_cnt; uint8_t spinup_delay; uint8_t cluster_enable; uint8_t coercion_mode; uint8_t alarm_enable; uint8_t disable_auto_rebuild; uint8_t disable_battery_warn; uint8_t ecc_bucket_size; uint16_t ecc_bucket_leak_rate; uint8_t restore_hotspare_on_insertion; uint8_t expose_encl_devices; uint8_t reserved[38]; } __packed; /* PCI information about the card. */ struct mfi_info_pci { uint16_t vendor; uint16_t device; uint16_t subvendor; uint16_t subdevice; uint8_t reserved[24]; } __packed; /* Host (front end) interface information */ struct mfi_info_host { uint8_t type; #define MFI_INFO_HOST_PCIX 0x01 #define MFI_INFO_HOST_PCIE 0x02 #define MFI_INFO_HOST_ISCSI 0x04 #define MFI_INFO_HOST_SAS3G 0x08 uint8_t reserved[6]; uint8_t port_count; uint64_t port_addr[8]; } __packed; /* Device (back end) interface information */ struct mfi_info_device { uint8_t type; #define MFI_INFO_DEV_SPI 0x01 #define MFI_INFO_DEV_SAS3G 0x02 #define MFI_INFO_DEV_SATA1 0x04 #define MFI_INFO_DEV_SATA3G 0x08 uint8_t reserved[6]; uint8_t port_count; uint64_t port_addr[8]; } __packed; /* Firmware component information */ struct mfi_info_component { char name[8]; char version[32]; char build_date[16]; char build_time[16]; } __packed; /* Controller default settings */ struct mfi_defaults { uint64_t sas_addr; uint8_t phy_polarity; uint8_t background_rate; uint8_t stripe_size; uint8_t flush_time; uint8_t write_back; uint8_t read_ahead; uint8_t cache_when_bbu_bad; uint8_t cached_io; uint8_t smart_mode; uint8_t alarm_disable; uint8_t coercion; uint8_t zrc_config; uint8_t dirty_led_shows_drive_activity; uint8_t bios_continue_on_error; uint8_t spindown_mode; uint8_t allowed_device_types; uint8_t allow_mix_in_enclosure; uint8_t allow_mix_in_ld; uint8_t allow_sata_in_cluster; uint8_t max_chained_enclosures; uint8_t disable_ctrl_r; uint8_t enabel_web_bios; uint8_t phy_polarity_split; uint8_t direct_pd_mapping; uint8_t bios_enumerate_lds; uint8_t restored_hot_spare_on_insertion; uint8_t expose_enclosure_devices; uint8_t maintain_pd_fail_history; uint8_t resv[28]; } __packed; /* Controller default settings */ struct mfi_bios_data { uint16_t boot_target_id; uint8_t do_not_int_13; uint8_t continue_on_error; uint8_t verbose; uint8_t geometry; uint8_t expose_all_drives; uint8_t reserved[56]; uint8_t check_sum; } __packed; /* SAS (?) controller info, returned from MFI_DCMD_CTRL_GETINFO. */ struct mfi_ctrl_info { struct mfi_info_pci pci; struct mfi_info_host host; struct mfi_info_device device; /* Firmware components that are present and active. */ uint32_t image_check_word; uint32_t image_component_count; struct mfi_info_component image_component[8]; /* Firmware components that have been flashed but are inactive */ uint32_t pending_image_component_count; struct mfi_info_component pending_image_component[8]; uint8_t max_arms; uint8_t max_spans; uint8_t max_arrays; uint8_t max_lds; char product_name[80]; char serial_number[32]; uint32_t hw_present; #define MFI_INFO_HW_BBU 0x01 #define MFI_INFO_HW_ALARM 0x02 #define MFI_INFO_HW_NVRAM 0x04 #define MFI_INFO_HW_UART 0x08 uint32_t current_fw_time; uint16_t max_cmds; uint16_t max_sg_elements; uint32_t max_request_size; uint16_t lds_present; uint16_t lds_degraded; uint16_t lds_offline; uint16_t pd_present; uint16_t pd_disks_present; uint16_t pd_disks_pred_failure; uint16_t pd_disks_failed; uint16_t nvram_size; uint16_t memory_size; uint16_t flash_size; uint16_t ram_correctable_errors; uint16_t ram_uncorrectable_errors; uint8_t cluster_allowed; uint8_t cluster_active; uint16_t max_strips_per_io; uint32_t raid_levels; #define MFI_INFO_RAID_0 0x01 #define MFI_INFO_RAID_1 0x02 #define MFI_INFO_RAID_5 0x04 #define MFI_INFO_RAID_1E 0x08 #define MFI_INFO_RAID_6 0x10 uint32_t adapter_ops; #define MFI_INFO_AOPS_RBLD_RATE 0x0001 #define MFI_INFO_AOPS_CC_RATE 0x0002 #define MFI_INFO_AOPS_BGI_RATE 0x0004 #define MFI_INFO_AOPS_RECON_RATE 0x0008 #define MFI_INFO_AOPS_PATROL_RATE 0x0010 #define MFI_INFO_AOPS_ALARM_CONTROL 0x0020 #define MFI_INFO_AOPS_CLUSTER_SUPPORTED 0x0040 #define MFI_INFO_AOPS_BBU 0x0080 #define MFI_INFO_AOPS_SPANNING_ALLOWED 0x0100 #define MFI_INFO_AOPS_DEDICATED_SPARES 0x0200 #define MFI_INFO_AOPS_REVERTIBLE_SPARES 0x0400 #define MFI_INFO_AOPS_FOREIGN_IMPORT 0x0800 #define MFI_INFO_AOPS_SELF_DIAGNOSTIC 0x1000 #define MFI_INFO_AOPS_MIXED_ARRAY 0x2000 #define MFI_INFO_AOPS_GLOBAL_SPARES 0x4000 uint32_t ld_ops; #define MFI_INFO_LDOPS_READ_POLICY 0x01 #define MFI_INFO_LDOPS_WRITE_POLICY 0x02 #define MFI_INFO_LDOPS_IO_POLICY 0x04 #define MFI_INFO_LDOPS_ACCESS_POLICY 0x08 #define MFI_INFO_LDOPS_DISK_CACHE_POLICY 0x10 struct { uint8_t min; uint8_t max; uint8_t reserved[2]; } __packed stripe_sz_ops; uint32_t pd_ops; #define MFI_INFO_PDOPS_FORCE_ONLINE 0x01 #define MFI_INFO_PDOPS_FORCE_OFFLINE 0x02 #define MFI_INFO_PDOPS_FORCE_REBUILD 0x04 uint32_t pd_mix_support; #define MFI_INFO_PDMIX_SAS 0x01 #define MFI_INFO_PDMIX_SATA 0x02 #define MFI_INFO_PDMIX_ENCL 0x04 #define MFI_INFO_PDMIX_LD 0x08 #define MFI_INFO_PDMIX_SATA_CLUSTER 0x10 uint8_t ecc_bucket_count; uint8_t reserved2[11]; struct mfi_ctrl_props properties; char package_version[0x60]; uint8_t pad[0x800 - 0x6a0]; } __packed; /* keep track of an event. */ union mfi_evt { struct { uint16_t locale; uint8_t reserved; - int8_t class; + int8_t evt_class; } members; uint32_t word; } __packed; /* event log state. */ struct mfi_evt_log_state { uint32_t newest_seq_num; uint32_t oldest_seq_num; uint32_t clear_seq_num; uint32_t shutdown_seq_num; uint32_t boot_seq_num; } __packed; struct mfi_progress { uint16_t progress; uint16_t elapsed_seconds; } __packed; struct mfi_evt_ld { uint16_t target_id; uint8_t ld_index; uint8_t reserved; } __packed; struct mfi_evt_pd { uint16_t device_id; uint8_t enclosure_index; uint8_t slot_number; } __packed; /* SAS (?) event detail, returned from MFI_DCMD_CTRL_EVENT_WAIT. */ struct mfi_evt_detail { uint32_t seq; uint32_t time; uint32_t code; - union mfi_evt class; + union mfi_evt evt_class; uint8_t arg_type; uint8_t reserved1[15]; union { struct { struct mfi_evt_pd pd; uint8_t cdb_len; uint8_t sense_len; uint8_t reserved[2]; uint8_t cdb[16]; uint8_t sense[64]; } cdb_sense; struct mfi_evt_ld ld; struct { struct mfi_evt_ld ld; uint64_t count; } ld_count; struct { uint64_t lba; struct mfi_evt_ld ld; } ld_lba; struct { struct mfi_evt_ld ld; uint32_t pre_owner; uint32_t new_owner; } ld_owner; struct { uint64_t ld_lba; uint64_t pd_lba; struct mfi_evt_ld ld; struct mfi_evt_pd pd; } ld_lba_pd_lba; struct { struct mfi_evt_ld ld; struct mfi_progress prog; } ld_prog; struct { struct mfi_evt_ld ld; uint32_t prev_state; uint32_t new_state; } ld_state; struct { uint64_t strip; struct mfi_evt_ld ld; } ld_strip; struct mfi_evt_pd pd; struct { struct mfi_evt_pd pd; uint32_t err; } pd_err; struct { uint64_t lba; struct mfi_evt_pd pd; } pd_lba; struct { uint64_t lba; struct mfi_evt_pd pd; struct mfi_evt_ld ld; } pd_lba_ld; struct { struct mfi_evt_pd pd; struct mfi_progress prog; } pd_prog; struct { struct mfi_evt_pd ld; uint32_t prev_state; uint32_t new_state; } pd_state; struct { uint16_t venderId; uint16_t deviceId; uint16_t subVenderId; uint16_t subDeviceId; } pci; uint32_t rate; char str[96]; struct { uint32_t rtc; uint16_t elapsedSeconds; } time; struct { uint32_t ecar; uint32_t elog; char str[64]; } ecc; uint8_t b[96]; uint16_t s[48]; uint32_t w[24]; uint64_t d[12]; } args; char description[128]; } __packed; struct mfi_evt_list { uint32_t count; uint32_t reserved; struct mfi_evt_detail event[1]; } __packed; union mfi_pd_ref { struct { uint16_t device_id; uint16_t seq_num; } v; uint32_t ref; } __packed; union mfi_pd_ddf_type { struct { union { struct { uint16_t forced_pd_guid : 1; uint16_t in_vd : 1; uint16_t is_global_spare : 1; uint16_t is_spare : 1; uint16_t is_foreign : 1; uint16_t reserved : 7; uint16_t intf : 4; } pd_type; uint16_t type; } v; uint16_t reserved; } ddf; struct { uint32_t reserved; } non_disk; uint32_t type; } __packed; struct mfi_pd_progress { uint32_t active; #define MFI_PD_PROGRESS_REBUILD (1<<0) #define MFI_PD_PROGRESS_PATROL (1<<1) #define MFI_PD_PROGRESS_CLEAR (1<<2) struct mfi_progress rbld; struct mfi_progress patrol; struct mfi_progress clear; struct mfi_progress reserved[4]; } __packed; struct mfi_pd_info { union mfi_pd_ref ref; uint8_t inquiry_data[96]; uint8_t vpd_page83[64]; uint8_t not_supported; uint8_t scsi_dev_type; uint8_t connected_port_bitmap; uint8_t device_speed; uint32_t media_err_count; uint32_t other_err_count; uint32_t pred_fail_count; uint32_t last_pred_fail_event_seq_num; uint16_t fw_state; /* MFI_PD_STATE_* */ uint8_t disabled_for_removal; uint8_t link_speed; union mfi_pd_ddf_type state; struct { uint8_t count; uint8_t is_path_broken; uint8_t reserved[6]; uint64_t sas_addr[4]; } path_info; uint64_t raw_size; uint64_t non_coerced_size; uint64_t coerced_size; uint16_t encl_device_id; uint8_t encl_index; uint8_t slot_number; struct mfi_pd_progress prog_info; uint8_t bad_block_table_full; uint8_t unusable_in_current_config; uint8_t vpd_page83_ext[64]; uint8_t reserved[512-358]; } __packed; struct mfi_pd_address { uint16_t device_id; uint16_t encl_device_id; uint8_t encl_index; uint8_t slot_number; uint8_t scsi_dev_type; /* 0 = disk */ uint8_t connect_port_bitmap; uint64_t sas_addr[2]; } __packed; struct mfi_pd_list { uint32_t size; uint32_t count; struct mfi_pd_address addr[0]; } __packed; enum mfi_pd_state { MFI_PD_STATE_UNCONFIGURED_GOOD = 0x00, MFI_PD_STATE_UNCONFIGURED_BAD = 0x01, MFI_PD_STATE_HOT_SPARE = 0x02, MFI_PD_STATE_OFFLINE = 0x10, MFI_PD_STATE_FAILED = 0x11, MFI_PD_STATE_REBUILD = 0x14, MFI_PD_STATE_ONLINE = 0x18, MFI_PD_STATE_COPYBACK = 0x20, MFI_PD_STATE_SYSTEM = 0x40 }; union mfi_ld_ref { struct { uint8_t target_id; uint8_t reserved; uint16_t seq; } v; uint32_t ref; } __packed; struct mfi_ld_list { uint32_t ld_count; uint32_t reserved1; struct { union mfi_ld_ref ld; uint8_t state; uint8_t reserved2[3]; uint64_t size; } ld_list[MFI_MAX_LD]; } __packed; enum mfi_ld_access { MFI_LD_ACCESS_RW = 0, MFI_LD_ACCSSS_RO = 2, MFI_LD_ACCESS_BLOCKED = 3, }; #define MFI_LD_ACCESS_MASK 3 enum mfi_ld_state { MFI_LD_STATE_OFFLINE = 0, MFI_LD_STATE_PARTIALLY_DEGRADED = 1, MFI_LD_STATE_DEGRADED = 2, MFI_LD_STATE_OPTIMAL = 3 }; struct mfi_ld_props { union mfi_ld_ref ld; char name[16]; uint8_t default_cache_policy; uint8_t access_policy; uint8_t disk_cache_policy; uint8_t current_cache_policy; uint8_t no_bgi; uint8_t reserved[7]; } __packed; struct mfi_ld_params { uint8_t primary_raid_level; uint8_t raid_level_qualifier; uint8_t secondary_raid_level; uint8_t stripe_size; uint8_t num_drives; uint8_t span_depth; uint8_t state; uint8_t init_state; #define MFI_LD_PARAMS_INIT_NO 0 #define MFI_LD_PARAMS_INIT_QUICK 1 #define MFI_LD_PARAMS_INIT_FULL 2 uint8_t is_consistent; uint8_t reserved[23]; } __packed; struct mfi_ld_progress { uint32_t active; #define MFI_LD_PROGRESS_CC (1<<0) #define MFI_LD_PROGRESS_BGI (1<<1) #define MFI_LD_PROGRESS_FGI (1<<2) #define MFI_LD_PROGRESS_RECON (1<<3) struct mfi_progress cc; struct mfi_progress bgi; struct mfi_progress fgi; struct mfi_progress recon; struct mfi_progress reserved[4]; } __packed; struct mfi_span { uint64_t start_block; uint64_t num_blocks; uint16_t array_ref; uint8_t reserved[6]; } __packed; #define MFI_MAX_SPAN_DEPTH 8 struct mfi_ld_config { struct mfi_ld_props properties; struct mfi_ld_params params; struct mfi_span span[MFI_MAX_SPAN_DEPTH]; } __packed; struct mfi_ld_info { struct mfi_ld_config ld_config; uint64_t size; struct mfi_ld_progress progress; uint16_t cluster_owner; uint8_t reconstruct_active; uint8_t reserved1[1]; uint8_t vpd_page83[64]; uint8_t reserved2[16]; } __packed; #define MAX_ARRAYS 16 struct mfi_spare { union mfi_pd_ref ref; uint8_t spare_type; #define MFI_SPARE_DEDICATED (1 << 0) #define MFI_SPARE_REVERTIBLE (1 << 1) #define MFI_SPARE_ENCL_AFFINITY (1 << 2) uint8_t reserved[2]; uint8_t array_count; uint16_t array_ref[MAX_ARRAYS]; } __packed; struct mfi_array { uint64_t size; uint8_t num_drives; uint8_t reserved; uint16_t array_ref; uint8_t pad[20]; struct { union mfi_pd_ref ref; /* 0xffff == missing drive */ uint16_t fw_state; /* MFI_PD_STATE_* */ struct { uint8_t pd; uint8_t slot; } encl; } pd[0]; } __packed; struct mfi_config_data { uint32_t size; uint16_t array_count; uint16_t array_size; uint16_t log_drv_count; uint16_t log_drv_size; uint16_t spares_count; uint16_t spares_size; uint8_t reserved[16]; struct mfi_array array[0]; struct mfi_ld_config ld[0]; struct mfi_spare spare[0]; } __packed; struct mfi_bbu_capacity_info { uint16_t relative_charge; uint16_t absolute_charge; uint16_t remaining_capacity; uint16_t full_charge_capacity; uint16_t run_time_to_empty; uint16_t average_time_to_empty; uint16_t average_time_to_full; uint16_t cycle_count; uint16_t max_error; uint16_t remaining_capacity_alarm; uint16_t remaining_time_alarm; uint8_t reserved[26]; } __packed; struct mfi_bbu_design_info { uint32_t mfg_date; uint16_t design_capacity; uint16_t design_voltage; uint16_t spec_info; uint16_t serial_number; uint16_t pack_stat_config; uint8_t mfg_name[12]; uint8_t device_name[8]; uint8_t device_chemistry[8]; uint8_t mfg_data[8]; uint8_t reserved[17]; } __packed; struct mfi_ibbu_state { uint16_t gas_guage_status; uint16_t relative_charge; uint16_t charger_system_state; uint16_t charger_system_ctrl; uint16_t charging_current; uint16_t absolute_charge; uint16_t max_error; uint8_t reserved[18]; } __packed; struct mfi_bbu_state { uint16_t gas_guage_status; uint16_t relative_charge; uint16_t charger_status; uint16_t remaining_capacity; uint16_t full_charge_capacity; uint8_t is_SOH_good; uint8_t reserved[21]; } __packed; union mfi_bbu_status_detail { struct mfi_ibbu_state ibbu; struct mfi_bbu_state bbu; }; struct mfi_bbu_status { uint8_t battery_type; #define MFI_BBU_TYPE_NONE 0 #define MFI_BBU_TYPE_IBBU 1 #define MFI_BBU_TYPE_BBU 2 uint8_t reserved; uint16_t voltage; int16_t current; uint16_t temperature; uint32_t fw_status; #define MFI_BBU_STATE_PACK_MISSING (1 << 0) #define MFI_BBU_STATE_VOLTAGE_LOW (1 << 1) #define MFI_BBU_STATE_TEMPERATURE_HIGH (1 << 2) #define MFI_BBU_STATE_CHARGE_ACTIVE (1 << 0) #define MFI_BBU_STATE_DISCHARGE_ACTIVE (1 << 0) uint8_t pad[20]; union mfi_bbu_status_detail detail; } __packed; enum mfi_pr_state { MFI_PR_STATE_STOPPED = 0, MFI_PR_STATE_READY = 1, MFI_PR_STATE_ACTIVE = 2, MFI_PR_STATE_ABORTED = 0xff }; struct mfi_pr_status { uint32_t num_iteration; uint8_t state; uint8_t num_pd_done; uint8_t reserved[10]; }; enum mfi_pr_opmode { MFI_PR_OPMODE_AUTO = 0, MFI_PR_OPMODE_MANUAL = 1, MFI_PR_OPMODE_DISABLED = 2 }; struct mfi_pr_properties { uint8_t op_mode; uint8_t max_pd; uint8_t reserved; uint8_t exclude_ld_count; uint16_t excluded_ld[MFI_MAX_LD]; uint8_t cur_pd_map[MFI_MAX_PD / 8]; uint8_t last_pd_map[MFI_MAX_PD / 8]; uint32_t next_exec; uint32_t exec_freq; uint32_t clear_freq; }; #define MFI_SCSI_MAX_TARGETS 128 #define MFI_SCSI_MAX_LUNS 8 #define MFI_SCSI_INITIATOR_ID 255 #define MFI_SCSI_MAX_CMDS 8 #define MFI_SCSI_MAX_CDB_LEN 16 #endif /* _MFIREG_H */ Index: head/usr.sbin/mfiutil/mfi_evt.c =================================================================== --- head/usr.sbin/mfiutil/mfi_evt.c (revision 222588) +++ head/usr.sbin/mfiutil/mfi_evt.c (revision 222589) @@ -1,679 +1,679 @@ /*- * Copyright (c) 2008, 2009 Yahoo!, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The names of the authors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include "mfiutil.h" static int mfi_event_get_info(int fd, struct mfi_evt_log_state *info, uint8_t *statusp) { return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GETINFO, info, sizeof(struct mfi_evt_log_state), NULL, 0, statusp)); } static int mfi_get_events(int fd, struct mfi_evt_list *list, int num_events, union mfi_evt filter, uint32_t start_seq, uint8_t *statusp) { uint32_t mbox[2]; size_t size; mbox[0] = start_seq; mbox[1] = filter.word; size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) * (num_events - 1); return (mfi_dcmd_command(fd, MFI_DCMD_CTRL_EVENT_GET, list, size, (uint8_t *)&mbox, sizeof(mbox), statusp)); } static int show_logstate(int ac, char **av) { struct mfi_evt_log_state info; int error, fd; if (ac != 1) { warnx("show logstate: extra arguments"); return (EINVAL); } fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } if (mfi_event_get_info(fd, &info, NULL) < 0) { error = errno; warn("Failed to get event log info"); return (error); } printf("mfi%d Event Log Sequence Numbers:\n", mfi_unit); printf(" Newest Seq #: %u\n", info.newest_seq_num); printf(" Oldest Seq #: %u\n", info.oldest_seq_num); printf(" Clear Seq #: %u\n", info.clear_seq_num); printf("Shutdown Seq #: %u\n", info.shutdown_seq_num); printf(" Boot Seq #: %u\n", info.boot_seq_num); close(fd); return (0); } MFI_COMMAND(show, logstate, show_logstate); static int parse_seq(struct mfi_evt_log_state *info, char *arg, uint32_t *seq) { char *cp; long val; if (strcasecmp(arg, "newest") == 0) { *seq = info->newest_seq_num; return (0); } if (strcasecmp(arg, "oldest") == 0) { *seq = info->oldest_seq_num; return (0); } if (strcasecmp(arg, "clear") == 0) { *seq = info->clear_seq_num; return (0); } if (strcasecmp(arg, "shutdown") == 0) { *seq = info->shutdown_seq_num; return (0); } if (strcasecmp(arg, "boot") == 0) { *seq = info->boot_seq_num; return (0); } val = strtol(arg, &cp, 0); if (*cp != '\0' || val < 0) { errno = EINVAL; return (-1); } *seq = val; return (0); } static int parse_locale(char *arg, uint16_t *locale) { char *cp; long val; if (strncasecmp(arg, "vol", 3) == 0 || strcasecmp(arg, "ld") == 0) { *locale = MFI_EVT_LOCALE_LD; return (0); } if (strncasecmp(arg, "drive", 5) == 0 || strcasecmp(arg, "pd") == 0) { *locale = MFI_EVT_LOCALE_PD; return (0); } if (strncasecmp(arg, "encl", 4) == 0) { *locale = MFI_EVT_LOCALE_ENCL; return (0); } if (strncasecmp(arg, "batt", 4) == 0 || strncasecmp(arg, "bbu", 3) == 0) { *locale = MFI_EVT_LOCALE_BBU; return (0); } if (strcasecmp(arg, "sas") == 0) { *locale = MFI_EVT_LOCALE_SAS; return (0); } if (strcasecmp(arg, "ctrl") == 0 || strncasecmp(arg, "cont", 4) == 0) { *locale = MFI_EVT_LOCALE_CTRL; return (0); } if (strcasecmp(arg, "config") == 0) { *locale = MFI_EVT_LOCALE_CONFIG; return (0); } if (strcasecmp(arg, "cluster") == 0) { *locale = MFI_EVT_LOCALE_CLUSTER; return (0); } if (strcasecmp(arg, "all") == 0) { *locale = MFI_EVT_LOCALE_ALL; return (0); } val = strtol(arg, &cp, 0); if (*cp != '\0' || val < 0 || val > 0xffff) { errno = EINVAL; return (-1); } *locale = val; return (0); } static int parse_class(char *arg, int8_t *class) { char *cp; long val; if (strcasecmp(arg, "debug") == 0) { *class = MFI_EVT_CLASS_DEBUG; return (0); } if (strncasecmp(arg, "prog", 4) == 0) { *class = MFI_EVT_CLASS_PROGRESS; return (0); } if (strncasecmp(arg, "info", 4) == 0) { *class = MFI_EVT_CLASS_INFO; return (0); } if (strncasecmp(arg, "warn", 4) == 0) { *class = MFI_EVT_CLASS_WARNING; return (0); } if (strncasecmp(arg, "crit", 4) == 0) { *class = MFI_EVT_CLASS_CRITICAL; return (0); } if (strcasecmp(arg, "fatal") == 0) { *class = MFI_EVT_CLASS_FATAL; return (0); } if (strcasecmp(arg, "dead") == 0) { *class = MFI_EVT_CLASS_DEAD; return (0); } val = strtol(arg, &cp, 0); if (*cp != '\0' || val < -128 || val > 127) { errno = EINVAL; return (-1); } *class = val; return (0); } /* * The timestamp is the number of seconds since 00:00 Jan 1, 2000. If * the bits in 24-31 are all set, then it is the number of seconds since * boot. */ static const char * format_timestamp(uint32_t timestamp) { static char buffer[32]; static time_t base; time_t t; struct tm tm; if ((timestamp & 0xff000000) == 0xff000000) { snprintf(buffer, sizeof(buffer), "boot + %us", timestamp & 0x00ffffff); return (buffer); } if (base == 0) { /* Compute 00:00 Jan 1, 2000 offset. */ bzero(&tm, sizeof(tm)); tm.tm_mday = 1; tm.tm_year = (2000 - 1900); base = mktime(&tm); } if (base == -1) { snprintf(buffer, sizeof(buffer), "%us", timestamp); return (buffer); } t = base + timestamp; strftime(buffer, sizeof(buffer), "%+", localtime(&t)); return (buffer); } static const char * format_locale(uint16_t locale) { static char buffer[8]; switch (locale) { case MFI_EVT_LOCALE_LD: return ("VOLUME"); case MFI_EVT_LOCALE_PD: return ("DRIVE"); case MFI_EVT_LOCALE_ENCL: return ("ENCL"); case MFI_EVT_LOCALE_BBU: return ("BATTERY"); case MFI_EVT_LOCALE_SAS: return ("SAS"); case MFI_EVT_LOCALE_CTRL: return ("CTRL"); case MFI_EVT_LOCALE_CONFIG: return ("CONFIG"); case MFI_EVT_LOCALE_CLUSTER: return ("CLUSTER"); case MFI_EVT_LOCALE_ALL: return ("ALL"); default: snprintf(buffer, sizeof(buffer), "0x%04x", locale); return (buffer); } } static const char * format_class(int8_t class) { static char buffer[6]; switch (class) { case MFI_EVT_CLASS_DEBUG: return ("debug"); case MFI_EVT_CLASS_PROGRESS: return ("progress"); case MFI_EVT_CLASS_INFO: return ("info"); case MFI_EVT_CLASS_WARNING: return ("WARN"); case MFI_EVT_CLASS_CRITICAL: return ("CRIT"); case MFI_EVT_CLASS_FATAL: return ("FATAL"); case MFI_EVT_CLASS_DEAD: return ("DEAD"); default: snprintf(buffer, sizeof(buffer), "%d", class); return (buffer); } } /* Simulates %D from kernel printf(9). */ static void simple_hex(void *ptr, size_t length, const char *separator) { unsigned char *cp; u_int i; if (length == 0) return; cp = ptr; printf("%02x", cp[0]); for (i = 1; i < length; i++) printf("%s%02x", separator, cp[i]); } static const char * pdrive_location(struct mfi_evt_pd *pd) { static char buffer[16]; if (pd->enclosure_index == 0) snprintf(buffer, sizeof(buffer), "%02d(s%d)", pd->device_id, pd->slot_number); else snprintf(buffer, sizeof(buffer), "%02d(e%d/s%d)", pd->device_id, pd->enclosure_index, pd->slot_number); return (buffer); } static const char * volume_name(int fd, struct mfi_evt_ld *ld) { return (mfi_volume_name(fd, ld->target_id)); } /* Ripped from sys/dev/mfi/mfi.c. */ static void mfi_decode_evt(int fd, struct mfi_evt_detail *detail, int verbose) { printf("%5d (%s/%s/%s) - ", detail->seq, format_timestamp(detail->time), - format_locale(detail->class.members.locale), - format_class(detail->class.members.class)); + format_locale(detail->evt_class.members.locale), + format_class(detail->evt_class.members.evt_class)); switch (detail->arg_type) { case MR_EVT_ARGS_NONE: break; case MR_EVT_ARGS_CDB_SENSE: if (verbose) { printf("PD %s CDB ", pdrive_location(&detail->args.cdb_sense.pd) ); simple_hex(detail->args.cdb_sense.cdb, detail->args.cdb_sense.cdb_len, ":"); printf(" Sense "); simple_hex(detail->args.cdb_sense.sense, detail->args.cdb_sense.sense_len, ":"); printf(":\n "); } break; case MR_EVT_ARGS_LD: printf("VOL %s event: ", volume_name(fd, &detail->args.ld)); break; case MR_EVT_ARGS_LD_COUNT: printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld)); if (verbose) { printf(" count %lld: ", (long long)detail->args.ld_count.count); } printf(": "); break; case MR_EVT_ARGS_LD_LBA: printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld)); if (verbose) { printf(" lba %lld", (long long)detail->args.ld_lba.lba); } printf(": "); break; case MR_EVT_ARGS_LD_OWNER: printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld)); if (verbose) { printf(" owner changed: prior %d, new %d", detail->args.ld_owner.pre_owner, detail->args.ld_owner.new_owner); } printf(": "); break; case MR_EVT_ARGS_LD_LBA_PD_LBA: printf("VOL %s", volume_name(fd, &detail->args.ld_count.ld)); if (verbose) { printf(" lba %lld, physical drive PD %s lba %lld", (long long)detail->args.ld_lba_pd_lba.ld_lba, pdrive_location(&detail->args.ld_lba_pd_lba.pd), (long long)detail->args.ld_lba_pd_lba.pd_lba); } printf(": "); break; case MR_EVT_ARGS_LD_PROG: printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld)); if (verbose) { printf(" progress %d%% in %ds", detail->args.ld_prog.prog.progress/655, detail->args.ld_prog.prog.elapsed_seconds); } printf(": "); break; case MR_EVT_ARGS_LD_STATE: printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld)); if (verbose) { printf(" state prior %s new %s", mfi_ldstate(detail->args.ld_state.prev_state), mfi_ldstate(detail->args.ld_state.new_state)); } printf(": "); break; case MR_EVT_ARGS_LD_STRIP: printf("VOL %s", volume_name(fd, &detail->args.ld_prog.ld)); if (verbose) { printf(" strip %lld", (long long)detail->args.ld_strip.strip); } printf(": "); break; case MR_EVT_ARGS_PD: if (verbose) { printf("PD %s event: ", pdrive_location(&detail->args.pd)); } break; case MR_EVT_ARGS_PD_ERR: if (verbose) { printf("PD %s err %d: ", pdrive_location(&detail->args.pd_err.pd), detail->args.pd_err.err); } break; case MR_EVT_ARGS_PD_LBA: if (verbose) { printf("PD %s lba %lld: ", pdrive_location(&detail->args.pd_lba.pd), (long long)detail->args.pd_lba.lba); } break; case MR_EVT_ARGS_PD_LBA_LD: if (verbose) { printf("PD %s lba %lld VOL %s: ", pdrive_location(&detail->args.pd_lba_ld.pd), (long long)detail->args.pd_lba.lba, volume_name(fd, &detail->args.pd_lba_ld.ld)); } break; case MR_EVT_ARGS_PD_PROG: if (verbose) { printf("PD %s progress %d%% seconds %ds: ", pdrive_location(&detail->args.pd_prog.pd), detail->args.pd_prog.prog.progress/655, detail->args.pd_prog.prog.elapsed_seconds); } break; case MR_EVT_ARGS_PD_STATE: if (verbose) { printf("PD %s state prior %s new %s: ", pdrive_location(&detail->args.pd_prog.pd), mfi_pdstate(detail->args.pd_state.prev_state), mfi_pdstate(detail->args.pd_state.new_state)); } break; case MR_EVT_ARGS_PCI: if (verbose) { printf("PCI 0x%04x 0x%04x 0x%04x 0x%04x: ", detail->args.pci.venderId, detail->args.pci.deviceId, detail->args.pci.subVenderId, detail->args.pci.subDeviceId); } break; case MR_EVT_ARGS_RATE: if (verbose) { printf("Rebuild rate %d: ", detail->args.rate); } break; case MR_EVT_ARGS_TIME: if (verbose) { printf("Adapter time %s; %d seconds since power on: ", format_timestamp(detail->args.time.rtc), detail->args.time.elapsedSeconds); } break; case MR_EVT_ARGS_ECC: if (verbose) { printf("Adapter ECC %x,%x: %s: ", detail->args.ecc.ecar, detail->args.ecc.elog, detail->args.ecc.str); } break; default: if (verbose) { printf("Type %d: ", detail->arg_type); } break; } printf("%s\n", detail->description); } static int show_events(int ac, char **av) { struct mfi_evt_log_state info; struct mfi_evt_list *list; union mfi_evt filter; long val; char *cp; ssize_t size; uint32_t seq, start, stop; uint8_t status; int ch, error, fd, num_events, verbose; u_int i; fd = mfi_open(mfi_unit); if (fd < 0) { error = errno; warn("mfi_open"); return (error); } if (mfi_event_get_info(fd, &info, NULL) < 0) { error = errno; warn("Failed to get event log info"); return (error); } /* Default settings. */ num_events = 15; filter.members.reserved = 0; filter.members.locale = MFI_EVT_LOCALE_ALL; - filter.members.class = MFI_EVT_CLASS_WARNING; + filter.members.evt_class = MFI_EVT_CLASS_WARNING; start = info.boot_seq_num; stop = info.newest_seq_num; verbose = 0; /* Parse any options. */ optind = 1; while ((ch = getopt(ac, av, "c:l:n:v")) != -1) { switch (ch) { case 'c': - if (parse_class(optarg, &filter.members.class) < 0) { + if (parse_class(optarg, &filter.members.evt_class) < 0) { error = errno; warn("Error parsing event class"); return (error); } break; case 'l': if (parse_locale(optarg, &filter.members.locale) < 0) { error = errno; warn("Error parsing event locale"); return (error); } break; case 'n': val = strtol(optarg, &cp, 0); if (*cp != '\0' || val <= 0) { warnx("Invalid event count"); return (EINVAL); } num_events = val; break; case 'v': verbose = 1; break; case '?': default: return (EINVAL); } } ac -= optind; av += optind; /* Determine buffer size and validate it. */ size = sizeof(struct mfi_evt_list) + sizeof(struct mfi_evt_detail) * (num_events - 1); if (size > getpagesize()) { warnx("Event count is too high"); return (EINVAL); } /* Handle optional start and stop sequence numbers. */ if (ac > 2) { warnx("show events: extra arguments"); return (EINVAL); } if (ac > 0 && parse_seq(&info, av[0], &start) < 0) { error = errno; warn("Error parsing starting sequence number"); return (error); } if (ac > 1 && parse_seq(&info, av[1], &stop) < 0) { error = errno; warn("Error parsing ending sequence number"); return (error); } list = malloc(size); if (list == NULL) { warnx("malloc failed"); return (ENOMEM); } for (seq = start;;) { if (mfi_get_events(fd, list, num_events, filter, seq, &status) < 0) { error = errno; warn("Failed to fetch events"); return (error); } if (status == MFI_STAT_NOT_FOUND) { if (seq == start) warnx("No matching events found"); break; } if (status != MFI_STAT_OK) { warnx("Error fetching events: %s", mfi_status(status)); return (EIO); } for (i = 0; i < list->count; i++) { /* * If this event is newer than 'stop_seq' then * break out of the loop. Note that the log * is a circular buffer so we have to handle * the case that our stop point is earlier in * the buffer than our start point. */ if (list->event[i].seq >= stop) { if (start <= stop) break; else if (list->event[i].seq < start) break; } mfi_decode_evt(fd, &list->event[i], verbose); } /* * XXX: If the event's seq # is the end of the buffer * then this probably won't do the right thing. We * need to know the size of the buffer somehow. */ seq = list->event[list->count - 1].seq + 1; } free(list); close(fd); return (0); } MFI_COMMAND(show, events, show_events);