----
This patch adds bio(4) support for mfii(4).
from mfi(4) with following addition/fixes,
* make "bioctl -R" work after hot swapping
Currently "bioctl -R" works only if disk state is "Offline" (set by
"bioctl -O") and it doesn't work for "Failed" disk.
To make it work with hot swapped disk, report unused ("unconfigured" in
MegaRAID) disk to userland, and handle it properly when rebuilding.
mfii_ioctl_disk():
to check if disk is missing, mar_pd_state in struct mfi_array shouldn't
be used. when disk is missing, it reports MFI_PD_UNCONFIG_GOOD(0x00)
which shouldn't be right. instead, check if mar_pd.mfp_id is 0xffff as
same as mfiutil(8) on FreeBSD.
if there is unused (MFI_PD_UNCONFIG_GOOD or MFI_PD_UNCONFIG_BAD) disk
(i.e. after hot swapping), report it to userland as "Unused" instead of
"Failed" disk.
mfii_ioctl_setstate():
to use unused disk for rebuilding, disk state must be "uncofigured good",
and must not be "foreign". then, disk must be set as global hot spare.
new functions, mfii_makegood() and mfii_makespare(), handle it before
rebuilding.
if disk state is "Offline", handle it as same as before.
rename following variables (cosmetic change),
struct mfi_pd_details *info -> struct mfi_pd_details *pd
struct mfi_pd_list *pd -> struct mfi_pd_list *pl
* don't return on error while updating sensors
* fix RAID level of spanned logical disk
refer mpa_span_depth to determine whether it's spanned.
XXX: RAID0+0 is reported as RAID0, ok? (0 * 10 = 0, not 00)
* report cachecade disk in ioctl/sensor
if scsi_get_link() returns NULL, report(assume) it as 'cache(cade)' disk.
---
sys/dev/ic/mfireg.h | 13 +
sys/dev/pci/mfii.c | 1508 +++++++++++++++++++++++++++++++++++++++++++++++++--
2 files changed, 1474 insertions(+), 47 deletions(-)
diff --git a/sys/dev/ic/mfireg.h b/sys/dev/ic/mfireg.h
index ed93402..70e828d 100644
--- a/sys/dev/ic/mfireg.h
+++ b/sys/dev/ic/mfireg.h
@@ -139,6 +139,9 @@
#define MR_DCMD_CONF_GET 0x04010000
#define MR_DCMD_CFG_ADD 0x04020000
#define MR_DCMD_CFG_CLEAR 0x04030000
+#define MR_DCMD_CFG_MAKE_SPARE 0x04040000
+#define MR_DCMD_CFG_FOREIGN_SCAN 0x04060100
+#define MR_DCMD_CFG_FOREIGN_CLEAR 0x04060500
#define MR_DCMD_BBU_GET_STATUS 0x05010000
#define MR_DCMD_BBU_GET_CAPACITY_INFO 0x05020000
#define MR_DCMD_BBU_GET_DESIGN_INFO 0x05030000
@@ -1228,3 +1231,13 @@ struct mfi_pr_properties {
uint32_t exec_freq;
uint32_t clear_freq;
} __packed;
+
+/* We currently don't know the full details of the following struct */
+struct mfi_foreign_scan_cfg {
+ char data[24];
+};
+
+struct mfi_foreign_scan_info {
+ uint32_t count; /* Number of foreign configs found */
+ struct mfi_foreign_scan_cfg cfgs[8];
+};
diff --git a/sys/dev/pci/mfii.c b/sys/dev/pci/mfii.c
index 2a63a94..0a7e175 100644
--- a/sys/dev/pci/mfii.c
+++ b/sys/dev/pci/mfii.c
@@ -22,10 +22,14 @@
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/device.h>
+#include <sys/dkio.h>
#include <sys/pool.h>
#include <sys/task.h>
#include <sys/atomic.h>
+#include <sys/sensors.h>
+#include <sys/rwlock.h>
+#include <dev/biovar.h>
#include <dev/pci/pcidevs.h>
#include <dev/pci/pcivar.h>
@@ -307,9 +311,63 @@ struct mfii_softc {
struct mfii_pd_softc *sc_pd;
struct scsi_iopool sc_iopool;
+ /* save some useful information for logical drives that is missing
+ * in sc_ld_list
+ */
+ struct {
+ uint32_t ld_present;
+ char ld_dev[16]; /* device name sd? */
+ } sc_ld[MFI_MAX_LD];
+
+ /* scsi ioctl from sd device */
+ int (*sc_ioctl)(struct device *, u_long, caddr_t);
+
+ uint32_t sc_ld_cnt;
+
+ /* bio */
+ struct mfi_conf *sc_cfg;
struct mfi_ctrl_info sc_info;
+ struct mfi_ld_list sc_ld_list;
+ struct mfi_ld_details *sc_ld_details; /* array to all logical disks */
+ int sc_no_pd; /* used physical disks */
+ int sc_ld_sz; /* sizeof sc_ld_details */
+
+ /* mgmt lock */
+ struct rwlock sc_lock;
+
+ /* sensors */
+ struct ksensordev sc_sensordev;
+ struct ksensor *sc_bbu;
+ struct ksensor *sc_bbu_status;
+ struct ksensor *sc_sensors;
};
+#ifdef MFII_DEBUG
+#define DPRINTF(x...) do { if (mfii_debug) printf(x); } while(0)
+#define DNPRINTF(n,x...) do { if (mfii_debug & n) printf(x); } while(0)
+#define MFII_D_CMD 0x0001
+#define MFII_D_INTR 0x0002
+#define MFII_D_MISC 0x0004
+#define MFII_D_DMA 0x0008
+#define MFII_D_IOCTL 0x0010
+#define MFII_D_RW 0x0020
+#define MFII_D_MEM 0x0040
+#define MFII_D_CCB 0x0080
+uint32_t mfii_debug = 0
+/* | MFII_D_CMD */
+/* | MFII_D_INTR */
+/* | MFII_D_MISC */
+/* | MFII_D_DMA */
+/* | MFII_D_IOCTL */
+/* | MFII_D_RW */
+/* | MFII_D_MEM */
+/* | MFII_D_CCB */
+ ;
+#else
+#define DPRINTF(x...)
+#define DNPRINTF(n,x...)
+#endif
+
int mfii_match(struct device *, void *, void *);
void mfii_attach(struct device *, struct device *, void *);
int mfii_detach(struct device *, int);
@@ -329,13 +387,15 @@ struct cfdriver mfii_cd = {
void mfii_scsi_cmd(struct scsi_xfer *);
void mfii_scsi_cmd_done(struct mfii_softc *, struct mfii_ccb *);
+int mfii_scsi_ioctl(struct scsi_link *, u_long, caddr_t, int);
+int mfii_ioctl_cache(struct scsi_link *, u_long, struct dk_cache *);
struct scsi_adapter mfii_switch = {
mfii_scsi_cmd,
scsi_minphys,
NULL, /* probe */
NULL, /* unprobe */
- NULL /* ioctl */
+ mfii_scsi_ioctl
};
void mfii_pd_scsi_cmd(struct scsi_xfer *);
@@ -383,9 +443,11 @@ int mfii_load_mfa(struct mfii_softc
*, struct mfii_ccb *,
int mfii_mfa_poll(struct mfii_softc *, struct mfii_ccb *);
-int mfii_mgmt(struct mfii_softc *, struct mfii_ccb *,
- u_int32_t, const union mfi_mbox *,
- void *, size_t, int);
+int mfii_mgmt(struct mfii_softc *, uint32_t, uint32_t,
+ size_t, void *, const union mfi_mbox *);
+int mfii_do_mgmt(struct mfii_softc *, struct mfii_ccb *,
+ uint32_t, uint32_t, size_t, void *,
+ const union mfi_mbox *);
void mfii_empty_done(struct mfii_softc *, struct mfii_ccb *);
int mfii_scsi_cmd_io(struct mfii_softc *,
@@ -419,6 +481,42 @@ void mfii_aen_pd_remove(struct
mfii_softc *,
void mfii_aen_pd_state_change(struct mfii_softc *,
const struct mfi_evtarg_pd_state *);
+#if NBIO > 0
+int mfii_ioctl(struct device *, u_long, caddr_t);
+int mfii_bio_getitall(struct mfii_softc *);
+int mfii_ioctl_inq(struct mfii_softc *, struct bioc_inq *);
+int mfii_ioctl_vol(struct mfii_softc *, struct bioc_vol *);
+int mfii_ioctl_disk(struct mfii_softc *, struct bioc_disk *);
+int mfii_ioctl_alarm(struct mfii_softc *, struct bioc_alarm *);
+int mfii_ioctl_blink(struct mfii_softc *sc, struct bioc_blink *);
+int mfii_ioctl_setstate(struct mfii_softc *,
+ struct bioc_setstate *);
+int mfii_ioctl_patrol(struct mfii_softc *sc, struct bioc_patrol *);
+int mfii_bio_hs(struct mfii_softc *, int, int, void *);
+
+#ifndef SMALL_KERNEL
+static const char *mfi_bbu_indicators[] = {
+ "pack missing",
+ "voltage low",
+ "temp high",
+ "charge active",
+ "discharge active",
+ "learn cycle req'd",
+ "learn cycle active",
+ "learn cycle failed",
+ "learn cycle timeout",
+ "I2C errors",
+ "replace pack",
+ "low capacity",
+ "periodic learn req'd"
+};
+
+int mfii_create_sensors(struct mfii_softc *);
+void mfii_refresh_sensors(void *);
+void mfii_bbu(struct mfii_softc *);
+#endif /* SMALL_KERNEL */
+#endif /* NBIO > 0 */
+
/*
* mfii boards support asynchronous (and non-polled) completion of
* dcmds by proxying them through a passthru mpii command that points
@@ -549,7 +647,7 @@ mfii_attach(struct device *parent, struct device *self,
void *aux)
pci_intr_handle_t ih;
struct scsibus_attach_args saa;
u_int32_t status, scpad2, scpad3;
- int chain_frame_sz, nsge_in_io, nsge_in_chain;
+ int chain_frame_sz, nsge_in_io, nsge_in_chain, i;
/* init sc */
sc->sc_iop = mfii_find_iop(aux);
@@ -560,6 +658,8 @@ mfii_attach(struct device *parent, struct device *self,
void *aux)
mtx_init(&sc->sc_reply_postq_mtx, IPL_BIO);
scsi_iopool_init(&sc->sc_iopool, sc, mfii_get_ccb, mfii_put_ccb);
+ rw_init(&sc->sc_lock, "mfii_lock");
+
sc->sc_aen_ccb = NULL;
task_set(&sc->sc_aen_task, mfii_aen, sc);
@@ -618,15 +718,13 @@ mfii_attach(struct device *parent, struct device *self, void *aux)
while ((sc->sc_max_sgl << 1) <= (nsge_in_io + nsge_in_chain))
sc->sc_max_sgl <<= 1;
-#ifdef MFI_DEBUG
- printf("%s: OSP 0x%08x, OSP2 0x%08x, OSP3 0x%08x\n",
- DEVNAME(sc), status, scpad2, scpad3);
- printf("%s: max_fw_cmds %d, max_cmds %d\n",
- DEVNAME(sc), sc->sc_max_fw_cmds, sc->sc_max_cmds);
- printf("%s: nsge_in_io %d, nsge_in_chain %d, "
- "max_sgl %d\n", DEVNAME(sc), nsge_in_io, nsge_in_chain,
+ DNPRINTF(MFII_D_MISC, "%s: OSP 0x%08x, OSP2 0x%08x, OSP3 0x%08x\n",
+ __func__, status, scpad2, scpad3);
+ DNPRINTF(MFII_D_MISC, "%s: max_fw_cmds %d, max_cmds %d\n",
+ __func__, sc->sc_max_fw_cmds, sc->sc_max_cmds);
+ DNPRINTF(MFII_D_MISC, "%s: nsge_in_io %d, nsge_in_chain %d, "
+ "max_sgl %d\n", __func__, nsge_in_io, nsge_in_chain,
sc->sc_max_sgl);
-#endif
/* sense memory */
CTASSERT(sizeof(struct mfi_sense) == MFI_SENSE_SIZE);
@@ -692,6 +790,10 @@ mfii_attach(struct device *parent, struct device *self,
void *aux)
if (sc->sc_ih == NULL)
goto free_sgl;
+ sc->sc_ld_cnt = sc->sc_info.mci_lds_present;
+ for (i = 0; i < sc->sc_ld_cnt; i++)
+ sc->sc_ld[i].ld_present = 1;
+
sc->sc_link.openings = sc->sc_max_cmds;
sc->sc_link.adapter_softc = sc;
sc->sc_link.adapter = &mfii_switch;
@@ -702,7 +804,8 @@ mfii_attach(struct device *parent, struct device *self,
void *aux)
memset(&saa, 0, sizeof(saa));
saa.saa_sc_link = &sc->sc_link;
- config_found(&sc->sc_dev, &saa, scsiprint);
+ sc->sc_scsibus = (struct scsibus_softc *)
+ config_found(&sc->sc_dev, &saa, scsiprint);
mfii_syspd(sc);
@@ -715,6 +818,18 @@ mfii_attach(struct device *parent, struct device *self, void *aux)
mfii_write(sc, MFI_OSTS, 0xffffffff);
mfii_write(sc, MFI_OMSK, ~MFII_OSTS_INTR_VALID);
+#if NBIO > 0
+ if (bio_register(&sc->sc_dev, mfii_ioctl) != 0)
+ panic("%s: controller registration failed", DEVNAME(sc));
+ else
+ sc->sc_ioctl = mfii_ioctl;
+
+#ifndef SMALL_KERNEL
+ if (mfii_create_sensors(sc) != 0)
+ printf("%s: unable to create sensors\n", DEVNAME(sc));
+#endif
+#endif /* NBIO > 0 */
+
return;
intr_disestablish:
pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
@@ -753,17 +868,14 @@ mfii_dev_handles_update(struct mfii_softc *sc)
{
struct mfii_ld_map *lm;
uint16_t *dev_handles = NULL;
- struct mfii_ccb *ccb;
int i;
int rv = 0;
lm = malloc(sizeof(*lm), M_TEMP, M_WAITOK|M_ZERO);
- ccb = scsi_io_get(&sc->sc_iopool, 0);
- rv = mfii_mgmt(sc, ccb, MR_DCMD_LD_MAP_GET_INFO, NULL,
- lm, sizeof(*lm), SCSI_DATA_IN|SCSI_NOSLEEP);
+ rv = mfii_mgmt(sc, MR_DCMD_LD_MAP_GET_INFO, MFII_DATA_IN, sizeof(*lm),
+ lm, NULL);
- scsi_io_put(&sc->sc_iopool, ccb);
if (rv != 0) {
rv = EIO;
goto free_lm;
@@ -837,6 +949,23 @@ mfii_detach(struct device *self, int flags)
if (sc->sc_ih == NULL)
return (0);
+#ifndef SMALL_KERNEL
+ if (sc->sc_sensors) {
+ sensordev_deinstall(&sc->sc_sensordev);
+ free(sc->sc_sensors, M_DEVBUF,
+ sc->sc_ld_cnt * sizeof(struct ksensor));
+ }
+
+ if (sc->sc_bbu) {
+ free(sc->sc_bbu, M_DEVBUF, 4 * sizeof(*sc->sc_bbu));
+ }
+
+ if (sc->sc_bbu_status) {
+ free(sc->sc_bbu_status, M_DEVBUF,
+ sizeof(*sc->sc_bbu_status) * sizeof(mfi_bbu_indicators));
+ }
+#endif /* SMALL_KERNEL */
+
mfii_aen_unregister(sc);
pci_intr_disestablish(sc->sc_pc, sc->sc_ih);
mfii_dmamem_free(sc, sc->sc_sgl);
@@ -953,9 +1082,10 @@ mfii_aen_register(struct mfii_softc *sc)
}
memset(&mel, 0, sizeof(mel));
+ mfii_scrub_ccb(ccb);
- rv = mfii_mgmt(sc, ccb, MR_DCMD_CTRL_EVENT_GET_INFO, NULL,
- &mel, sizeof(mel), SCSI_DATA_IN|SCSI_NOSLEEP);
+ rv = mfii_do_mgmt(sc, ccb, MR_DCMD_CTRL_EVENT_GET_INFO, MFII_DATA_IN,
+ sizeof(mel), &mel, NULL);
if (rv != 0) {
scsi_io_put(&sc->sc_iopool, ccb);
printf("%s: unable to get event info\n", DEVNAME(sc));
@@ -1198,13 +1328,10 @@ mfii_transition_firmware(struct mfii_softc *sc)
int
mfii_get_info(struct mfii_softc *sc)
{
- struct mfii_ccb *ccb;
int rv;
- ccb = scsi_io_get(&sc->sc_iopool, 0);
- rv = mfii_mgmt(sc, ccb, MR_DCMD_CTRL_GET_INFO, NULL,
- &sc->sc_info, sizeof(sc->sc_info), SCSI_DATA_IN|SCSI_NOSLEEP);
- scsi_io_put(&sc->sc_iopool, ccb);
+ rv = mfii_mgmt(sc, MR_DCMD_CTRL_GET_INFO, MFII_DATA_IN,
+ sizeof(sc->sc_info), &sc->sc_info, NULL);
if (rv != 0)
return (rv);
@@ -1487,39 +1614,53 @@ mfii_exec_done(struct mfii_softc *sc, struct mfii_ccb
*ccb)
}
int
-mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
- u_int32_t opc, const union mfi_mbox *mbox, void *buf, size_t len,
- int flags)
+mfii_mgmt(struct mfii_softc *sc, uint32_t opc, uint32_t dir, size_t len,
+ void *buf, const union mfi_mbox *mbox)
+{
+ struct mfii_ccb *ccb;
+ int rv;
+
+ ccb = scsi_io_get(&sc->sc_iopool, 0);
+ mfii_scrub_ccb(ccb);
+ rv = mfii_do_mgmt(sc, ccb, opc, dir, len, buf, mbox);
+ scsi_io_put(&sc->sc_iopool, ccb);
+
+ return (rv);
+}
+
+int
+mfii_do_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb, uint32_t opc,
+ uint32_t dir, size_t len, void *buf, const union mfi_mbox *mbox)
{
struct mpii_msg_scsi_io *io = ccb->ccb_request;
struct mfii_raid_context *ctx = (struct mfii_raid_context *)(io + 1);
struct mfii_sge *sge = (struct mfii_sge *)(ctx + 1);
struct mfi_dcmd_frame *dcmd = ccb->ccb_mfi;
struct mfi_frame_header *hdr = &dcmd->mdf_header;
- u_int8_t *dma_buf;
+ uint8_t *dma_buf;
int rv = 0;
dma_buf = dma_alloc(len, PR_WAITOK);
if (dma_buf == NULL)
return (ENOMEM);
- mfii_scrub_ccb(ccb);
ccb->ccb_data = dma_buf;
ccb->ccb_len = len;
- switch (flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) {
- case SCSI_DATA_IN:
- ccb->ccb_direction = MFII_DATA_IN;
+ ccb->ccb_direction = dir;
+ switch (dir) {
+ case MFII_DATA_IN:
hdr->mfh_flags = htole16(MFI_FRAME_DIR_READ);
break;
- case SCSI_DATA_OUT:
- ccb->ccb_direction = MFII_DATA_OUT;
+ case MFII_DATA_OUT:
hdr->mfh_flags = htole16(MFI_FRAME_DIR_WRITE);
memcpy(dma_buf, buf, len);
break;
+ case MFII_DATA_NONE:
+ hdr->mfh_flags = htole16(MFI_FRAME_DIR_NONE);
+ break;
}
- if (mfii_load_mfa(sc, ccb, &dcmd->mdf_sgl,
- ISSET(flags, SCSI_NOSLEEP)) != 0) {
+ if (mfii_load_mfa(sc, ccb, &dcmd->mdf_sgl, 1/*cold*/) != 0) {
rv = ENOMEM;
goto done;
}
@@ -1545,22 +1686,25 @@ mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
ccb->ccb_req.scsi.flags = MFII_REQ_TYPE_SCSI;
ccb->ccb_req.scsi.smid = letoh16(ccb->ccb_smid);
- if (ISSET(flags, SCSI_NOSLEEP)) {
+#ifdef notyet
+ if (1/*cold*/) {
+#endif
/* busy-loop polling with done handler */
ccb->ccb_cookie = NULL;
ccb->ccb_done = mfii_empty_done;
mfii_poll(sc, ccb);
+#ifdef notyet
} else {
/* sleep/wakeup without done handler */
ccb->ccb_cookie = NULL;
ccb->ccb_done = NULL;
mfii_exec(sc, ccb);
}
+#endif
if (ccb->ccb_len > 0) {
bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap,
- 0, ccb->ccb_dmamap->dm_mapsize,
- (ccb->ccb_direction == MFII_DATA_IN) ?
+ 0, ccb->ccb_dmamap->dm_mapsize, (dir == MFII_DATA_IN) ?
BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE);
bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap);
@@ -1568,7 +1712,7 @@ mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
rv = hdr->mfh_cmd_status == MFI_STAT_OK ? 0 : 1;
- if (rv == 0 && ccb->ccb_direction == MFII_DATA_IN)
+ if (rv == 0 && dir == MFII_DATA_IN)
memcpy(buf, dma_buf, len);
done:
@@ -1906,6 +2050,109 @@ mfii_scsi_cmd_done(struct mfii_softc *sc, struct
mfii_ccb *ccb)
}
int
+mfii_scsi_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
+{
+ struct mfii_softc *sc = (struct mfii_softc *)link->adapter_softc;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_scsi_ioctl\n", DEVNAME(sc));
+
+ switch (cmd) {
+ case DIOCGCACHE:
+ case DIOCSCACHE:
+ return (mfii_ioctl_cache(link, cmd, (struct dk_cache *)addr));
+ break;
+
+ default:
+ if (sc->sc_ioctl)
+ return (sc->sc_ioctl(link->adapter_softc, cmd, addr));
+ break;
+ }
+
+ return (ENOTTY);
+}
+
+int
+mfii_ioctl_cache(struct scsi_link *link, u_long cmd, struct dk_cache *dc)
+{
+ struct mfii_softc *sc = (struct mfii_softc *)link->adapter_softc;
+ int rv, wrenable, rdenable;
+ struct mfi_ld_prop ldp;
+ union mfi_mbox mbox;
+
+ if (mfii_get_info(sc)) {
+ rv = EIO;
+ goto done;
+ }
+
+ if (!sc->sc_ld[link->target].ld_present) {
+ rv = EIO;
+ goto done;
+ }
+
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.b[0] = link->target;
+ if ((rv = mfii_mgmt(sc, MR_DCMD_LD_GET_PROPERTIES, MFII_DATA_IN,
+ sizeof(ldp), &ldp, &mbox)) != 0)
+ goto done;
+
+ if (sc->sc_info.mci_memory_size > 0) {
+ wrenable = ISSET(ldp.mlp_cur_cache_policy,
+ MR_LD_CACHE_ALLOW_WRITE_CACHE)? 1 : 0;
+ rdenable = ISSET(ldp.mlp_cur_cache_policy,
+ MR_LD_CACHE_ALLOW_READ_CACHE)? 1 : 0;
+ } else {
+ wrenable = ISSET(ldp.mlp_diskcache_policy,
+ MR_LD_DISK_CACHE_ENABLE)? 1 : 0;
+ rdenable = 0;
+ }
+
+ if (cmd == DIOCGCACHE) {
+ dc->wrcache = wrenable;
+ dc->rdcache = rdenable;
+ goto done;
+ } /* else DIOCSCACHE */
+
+ if (((dc->wrcache) ? 1 : 0) == wrenable &&
+ ((dc->rdcache) ? 1 : 0) == rdenable)
+ goto done;
+
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.b[0] = ldp.mlp_ld.mld_target;
+ mbox.b[1] = ldp.mlp_ld.mld_res;
+ mbox.s[1] = ldp.mlp_ld.mld_seq;
+
+ if (sc->sc_info.mci_memory_size > 0) {
+ if (dc->rdcache)
+ SET(ldp.mlp_cur_cache_policy,
+ MR_LD_CACHE_ALLOW_READ_CACHE);
+ else
+ CLR(ldp.mlp_cur_cache_policy,
+ MR_LD_CACHE_ALLOW_READ_CACHE);
+ if (dc->wrcache)
+ SET(ldp.mlp_cur_cache_policy,
+ MR_LD_CACHE_ALLOW_WRITE_CACHE);
+ else
+ CLR(ldp.mlp_cur_cache_policy,
+ MR_LD_CACHE_ALLOW_WRITE_CACHE);
+ } else {
+ if (dc->rdcache) {
+ rv = EOPNOTSUPP;
+ goto done;
+ }
+ if (dc->wrcache)
+ ldp.mlp_diskcache_policy = MR_LD_DISK_CACHE_ENABLE;
+ else
+ ldp.mlp_diskcache_policy = MR_LD_DISK_CACHE_DISABLE;
+ }
+
+ if ((rv = mfii_mgmt(sc, MR_DCMD_LD_SET_PROPERTIES, MFII_DATA_OUT,
+ sizeof(ldp), &ldp, &mbox)) != 0)
+ goto done;
+done:
+ return (rv);
+}
+
+int
mfii_scsi_cmd_io(struct mfii_softc *sc, struct scsi_xfer *xs)
{
struct scsi_link *link = xs->sc_link;
@@ -2054,7 +2301,6 @@ int
mfii_pd_scsi_probe(struct scsi_link *link)
{
struct mfii_softc *sc = link->adapter_softc;
- struct mfii_ccb *ccb;
struct mfi_pd_details mpd;
union mfi_mbox mbox;
int rv;
@@ -2065,10 +2311,8 @@ mfii_pd_scsi_probe(struct scsi_link *link)
memset(&mbox, 0, sizeof(mbox));
mbox.s[0] = htole16(link->target);
- ccb = scsi_io_get(&sc->sc_iopool, 0);
- rv = mfii_mgmt(sc, ccb, MR_DCMD_PD_GET_INFO, &mbox, &mpd, sizeof(mpd),
- SCSI_DATA_IN|SCSI_NOSLEEP);
- scsi_io_put(&sc->sc_iopool, ccb);
+ rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof(mpd), &mpd,
+ &mbox);
if (rv != 0)
return (EIO);
@@ -2403,3 +2647,1173 @@ destroy:
return (1);
}
+#if NBIO > 0
+int
+mfii_ioctl(struct device *dev, u_long cmd, caddr_t addr)
+{
+ struct mfii_softc *sc = (struct mfii_softc *)dev;
+ int error = 0;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl ", DEVNAME(sc));
+
+ rw_enter_write(&sc->sc_lock);
+
+ switch (cmd) {
+ case BIOCINQ:
+ DNPRINTF(MFII_D_IOCTL, "inq\n");
+ error = mfii_ioctl_inq(sc, (struct bioc_inq *)addr);
+ break;
+
+ case BIOCVOL:
+ DNPRINTF(MFII_D_IOCTL, "vol\n");
+ error = mfii_ioctl_vol(sc, (struct bioc_vol *)addr);
+ break;
+
+ case BIOCDISK:
+ DNPRINTF(MFII_D_IOCTL, "disk\n");
+ error = mfii_ioctl_disk(sc, (struct bioc_disk *)addr);
+ break;
+
+ case BIOCALARM:
+ DNPRINTF(MFII_D_IOCTL, "alarm\n");
+ error = mfii_ioctl_alarm(sc, (struct bioc_alarm *)addr);
+ break;
+
+ case BIOCBLINK:
+ DNPRINTF(MFII_D_IOCTL, "blink\n");
+ error = mfii_ioctl_blink(sc, (struct bioc_blink *)addr);
+ break;
+
+ case BIOCSETSTATE:
+ DNPRINTF(MFII_D_IOCTL, "setstate\n");
+ error = mfii_ioctl_setstate(sc, (struct bioc_setstate *)addr);
+ break;
+
+ case BIOCPATROL:
+ DNPRINTF(MFII_D_IOCTL, "patrol\n");
+ error = mfii_ioctl_patrol(sc, (struct bioc_patrol *)addr);
+ break;
+
+ default:
+ DNPRINTF(MFII_D_IOCTL, " invalid ioctl\n");
+ error = EINVAL;
+ }
+
+ rw_exit_write(&sc->sc_lock);
+
+ return (error);
+}
+
+int
+mfii_bio_getitall(struct mfii_softc *sc)
+{
+ int i, d, rv = EINVAL;
+ size_t size;
+ union mfi_mbox mbox;
+ struct mfi_conf *cfg = NULL;
+ struct mfi_ld_details *ld_det = NULL;
+
+ /* get info */
+ if (mfii_get_info(sc)) {
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_get_info failed\n",
+ DEVNAME(sc));
+ goto done;
+ }
+
+ /* send single element command to retrieve size for full structure */
+ cfg = malloc(sizeof *cfg, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (cfg == NULL)
+ goto done;
+ if (mfii_mgmt(sc, MR_DCMD_CONF_GET, MFII_DATA_IN, sizeof *cfg, cfg,
+ NULL)) {
+ free(cfg, M_DEVBUF, sizeof *cfg);
+ goto done;
+ }
+
+ size = cfg->mfc_size;
+ free(cfg, M_DEVBUF, sizeof *cfg);
+
+ /* memory for read config */
+ cfg = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (cfg == NULL)
+ goto done;
+ if (mfii_mgmt(sc, MR_DCMD_CONF_GET, MFII_DATA_IN, size, cfg, NULL)) {
+ free(cfg, M_DEVBUF, size);
+ goto done;
+ }
+
+ /* replace current pointer with new one */
+ if (sc->sc_cfg)
+ free(sc->sc_cfg, M_DEVBUF, 0);
+ sc->sc_cfg = cfg;
+
+ /* get all ld info */
+ if (mfii_mgmt(sc, MR_DCMD_LD_GET_LIST, MFII_DATA_IN,
+ sizeof(sc->sc_ld_list), &sc->sc_ld_list, NULL))
+ goto done;
+
+ /* get memory for all ld structures */
+ size = cfg->mfc_no_ld * sizeof(struct mfi_ld_details);
+ if (sc->sc_ld_sz != size) {
+ if (sc->sc_ld_details)
+ free(sc->sc_ld_details, M_DEVBUF, 0);
+
+ ld_det = malloc(size, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ld_det == NULL)
+ goto done;
+ sc->sc_ld_sz = size;
+ sc->sc_ld_details = ld_det;
+ }
+
+ /* find used physical disks */
+ size = sizeof(struct mfi_ld_details);
+ for (i = 0, d = 0; i < cfg->mfc_no_ld; i++) {
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.b[0] = sc->sc_ld_list.mll_list[i].mll_ld.mld_target;
+ if (mfii_mgmt(sc, MR_DCMD_LD_GET_INFO, MFII_DATA_IN, size,
+ &sc->sc_ld_details[i], &mbox))
+ goto done;
+
+ d += sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_no_drv_per_span *
+ sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_span_depth;
+ }
+ sc->sc_no_pd = d;
+
+ rv = 0;
+done:
+ return (rv);
+}
+
+int
+mfii_ioctl_inq(struct mfii_softc *sc, struct bioc_inq *bi)
+{
+ int rv = EINVAL;
+ struct mfi_conf *cfg = NULL;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_inq\n", DEVNAME(sc));
+
+ if (mfii_bio_getitall(sc)) {
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_bio_getitall failed\n",
+ DEVNAME(sc));
+ goto done;
+ }
+
+ /* count unused disks as volumes */
+ if (sc->sc_cfg == NULL)
+ goto done;
+ cfg = sc->sc_cfg;
+
+ bi->bi_nodisk = sc->sc_info.mci_pd_disks_present;
+ bi->bi_novol = cfg->mfc_no_ld + cfg->mfc_no_hs;
+#if notyet
+ bi->bi_novol = cfg->mfc_no_ld + cfg->mfc_no_hs +
+ (bi->bi_nodisk - sc->sc_no_pd);
+#endif
+ /* tell bio who we are */
+ strlcpy(bi->bi_dev, DEVNAME(sc), sizeof(bi->bi_dev));
+
+ rv = 0;
+done:
+ return (rv);
+}
+
+int
+mfii_ioctl_vol(struct mfii_softc *sc, struct bioc_vol *bv)
+{
+ int i, per, rv = EINVAL;
+ struct scsi_link *link;
+ struct device *dev;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_vol %#x\n",
+ DEVNAME(sc), bv->bv_volid);
+
+ /* we really could skip and expect that inq took care of it */
+ if (mfii_bio_getitall(sc)) {
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_bio_getitall failed\n",
+ DEVNAME(sc));
+ goto done;
+ }
+
+ if (bv->bv_volid >= sc->sc_ld_list.mll_no_ld) {
+ /* go do hotspares & unused disks */
+ rv = mfii_bio_hs(sc, bv->bv_volid, MFI_MGMT_VD, bv);
+ goto done;
+ }
+
+ i = bv->bv_volid;
+ link = scsi_get_link(sc->sc_scsibus, i, 0);
+ if (link == NULL) {
+ strlcpy(bv->bv_dev, "cache", sizeof(bv->bv_dev));
+ } else {
+ if (link->device_softc == NULL)
+ goto done;
+
+ dev = link->device_softc;
+ strlcpy(bv->bv_dev, dev->dv_xname, sizeof(bv->bv_dev));
+ }
+
+ switch(sc->sc_ld_list.mll_list[i].mll_state) {
+ case MFI_LD_OFFLINE:
+ bv->bv_status = BIOC_SVOFFLINE;
+ break;
+
+ case MFI_LD_PART_DEGRADED:
+ case MFI_LD_DEGRADED:
+ bv->bv_status = BIOC_SVDEGRADED;
+ break;
+
+ case MFI_LD_ONLINE:
+ bv->bv_status = BIOC_SVONLINE;
+ break;
+
+ default:
+ bv->bv_status = BIOC_SVINVALID;
+ DNPRINTF(MFII_D_IOCTL, "%s: invalid logical disk state %#x\n",
+ DEVNAME(sc),
+ sc->sc_ld_list.mll_list[i].mll_state);
+ }
+
+ /* additional status can modify MFI status */
+ switch (sc->sc_ld_details[i].mld_progress.mlp_in_prog) {
+ case MFI_LD_PROG_CC:
+ case MFI_LD_PROG_BGI:
+ bv->bv_status = BIOC_SVSCRUB;
+ per = (int)sc->sc_ld_details[i].mld_progress.mlp_cc.mp_progress;
+ bv->bv_percent = (per * 100) / 0xffff;
+ bv->bv_seconds =
+ sc->sc_ld_details[i].mld_progress.mlp_cc.mp_elapsed_seconds;
+ break;
+
+ case MFI_LD_PROG_FGI:
+ case MFI_LD_PROG_RECONSTRUCT:
+ /* nothing yet */
+ break;
+ }
+
+ if (sc->sc_ld_details[i].mld_cfg.mlc_prop.mlp_cur_cache_policy & 0x01)
+ bv->bv_cache = BIOC_CVWRITEBACK;
+ else
+ bv->bv_cache = BIOC_CVWRITETHROUGH;
+
+ /*
+ * The RAID levels are determined per the SNIA DDF spec, this is only
+ * a subset that is valid for the MFI controller.
+ */
+ bv->bv_level = sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_pri_raid;
+ if (sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_span_depth > 1)
+ bv->bv_level *= 10;
+
+ bv->bv_nodisk =
sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_no_drv_per_span *
+ sc->sc_ld_details[i].mld_cfg.mlc_parm.mpa_span_depth;
+
+ bv->bv_size = sc->sc_ld_details[i].mld_size * 512; /* bytes per block */
+
+ rv = 0;
+done:
+ return (rv);
+}
+
+int
+mfii_ioctl_disk(struct mfii_softc *sc, struct bioc_disk *bd)
+{
+ struct mfi_conf *cfg;
+ struct mfi_array *ar;
+ struct mfi_ld_cfg *ld;
+ struct mfi_pd_details *pd;
+ struct mfi_pd_list *pl;
+ struct mfi_pd_progress *mfp;
+ struct mfi_progress *mp;
+ struct scsi_inquiry_data *inqbuf;
+ char vend[8+16+4+1], *vendp;
+ int i, rv = EINVAL;
+ int arr, vol, disk, span;
+ union mfi_mbox mbox;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_disk %#x\n",
+ DEVNAME(sc), bd->bd_diskid);
+
+ /* we really could skip and expect that inq took care of it */
+ if (mfii_bio_getitall(sc)) {
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_bio_getitall failed\n",
+ DEVNAME(sc));
+ return (rv);
+ }
+ cfg = sc->sc_cfg;
+
+ pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+ pl = malloc(sizeof *pl, M_DEVBUF, M_WAITOK);
+
+ ar = cfg->mfc_array;
+ vol = bd->bd_volid;
+ if (vol >= cfg->mfc_no_ld) {
+ /* do hotspares */
+ rv = mfii_bio_hs(sc, bd->bd_volid, MFI_MGMT_SD, bd);
+ goto freeme;
+ }
+
+ /* calculate offset to ld structure */
+ ld = (struct mfi_ld_cfg *)(
+ ((uint8_t *)cfg) + offsetof(struct mfi_conf, mfc_array) +
+ cfg->mfc_array_size * cfg->mfc_no_array);
+
+ /* use span 0 only when raid group is not spanned */
+ if (ld[vol].mlc_parm.mpa_span_depth > 1)
+ span = bd->bd_diskid / ld[vol].mlc_parm.mpa_no_drv_per_span;
+ else
+ span = 0;
+ arr = ld[vol].mlc_span[span].mls_index;
+
+ /* offset disk into pd list */
+ disk = bd->bd_diskid % ld[vol].mlc_parm.mpa_no_drv_per_span;
+
+ if (ar[arr].pd[disk].mar_pd.mfp_id == 0xffffU) {
+ /* disk is missing but succeed command */
+ bd->bd_status = BIOC_SDFAILED;
+ rv = 0;
+
+ /* try to find an unused disk for the target to rebuild */
+ if (mfii_mgmt(sc, MR_DCMD_PD_GET_LIST, MFII_DATA_IN,
+ sizeof *pl, pl, NULL))
+ goto freeme;
+
+ for (i = 0; i < pl->mpl_no_pd; i++) {
+ if (pl->mpl_address[i].mpa_scsi_type != 0)
+ continue;
+
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+ if (mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+ sizeof *pd, pd, &mbox))
+ continue;
+
+ if (pd->mpd_fw_state == MFI_PD_UNCONFIG_GOOD ||
+ pd->mpd_fw_state == MFI_PD_UNCONFIG_BAD)
+ break;
+ }
+
+ if (i == pl->mpl_no_pd)
+ goto freeme;
+ } else {
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.s[0] = ar[arr].pd[disk].mar_pd.mfp_id;
+ if (mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+ sizeof *pd, pd, &mbox)) {
+ bd->bd_status = BIOC_SDINVALID;
+ goto freeme;
+ }
+ }
+
+ /* get the remaining fields */
+ bd->bd_channel = pd->mpd_enc_idx;
+ bd->bd_target = pd->mpd_enc_slot;
+
+ /* get status */
+ switch (pd->mpd_fw_state){
+ case MFI_PD_UNCONFIG_GOOD:
+ case MFI_PD_UNCONFIG_BAD:
+ bd->bd_status = BIOC_SDUNUSED;
+ break;
+
+ case MFI_PD_HOTSPARE: /* XXX dedicated hotspare part of array? */
+ bd->bd_status = BIOC_SDHOTSPARE;
+ break;
+
+ case MFI_PD_OFFLINE:
+ bd->bd_status = BIOC_SDOFFLINE;
+ break;
+
+ case MFI_PD_FAILED:
+ bd->bd_status = BIOC_SDFAILED;
+ break;
+
+ case MFI_PD_REBUILD:
+ bd->bd_status = BIOC_SDREBUILD;
+ break;
+
+ case MFI_PD_ONLINE:
+ bd->bd_status = BIOC_SDONLINE;
+ break;
+
+ case MFI_PD_COPYBACK:
+ case MFI_PD_SYSTEM:
+ default:
+ bd->bd_status = BIOC_SDINVALID;
+ break;
+ }
+
+ bd->bd_size = pd->mpd_size * 512; /* bytes per block */
+
+ inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data;
+ vendp = inqbuf->vendor;
+ memcpy(vend, vendp, sizeof vend - 1);
+ vend[sizeof vend - 1] = '\0';
+ strlcpy(bd->bd_vendor, vend, sizeof(bd->bd_vendor));
+
+ /* XXX find a way to retrieve serial nr from drive */
+ /* XXX find a way to get bd_procdev */
+
+ mfp = &pd->mpd_progress;
+ if (mfp->mfp_in_prog & MFI_PD_PROG_PR) {
+ mp = &mfp->mfp_patrol_read;
+ bd->bd_patrol.bdp_percent = (mp->mp_progress * 100) / 0xffff;
+ bd->bd_patrol.bdp_seconds = mp->mp_elapsed_seconds;
+ }
+
+ rv = 0;
+freeme:
+ free(pd, M_DEVBUF, sizeof *pd);
+ free(pl, M_DEVBUF, sizeof *pl);
+
+ return (rv);
+}
+
+int
+mfii_ioctl_alarm(struct mfii_softc *sc, struct bioc_alarm *ba)
+{
+ uint32_t opc, dir = MFII_DATA_NONE;
+ int rv = 0;
+ int8_t ret;
+
+ switch(ba->ba_opcode) {
+ case BIOC_SADISABLE:
+ opc = MR_DCMD_SPEAKER_DISABLE;
+ break;
+
+ case BIOC_SAENABLE:
+ opc = MR_DCMD_SPEAKER_ENABLE;
+ break;
+
+ case BIOC_SASILENCE:
+ opc = MR_DCMD_SPEAKER_SILENCE;
+ break;
+
+ case BIOC_GASTATUS:
+ opc = MR_DCMD_SPEAKER_GET;
+ dir = MFII_DATA_IN;
+ break;
+
+ case BIOC_SATEST:
+ opc = MR_DCMD_SPEAKER_TEST;
+ break;
+
+ default:
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_alarm biocalarm invalid "
+ "opcode %x\n", DEVNAME(sc), ba->ba_opcode);
+ return (EINVAL);
+ }
+
+ if (mfii_mgmt(sc, opc, dir, sizeof(ret), &ret, NULL))
+ rv = EINVAL;
+ else
+ if (ba->ba_opcode == BIOC_GASTATUS)
+ ba->ba_status = ret;
+ else
+ ba->ba_status = 0;
+
+ return (rv);
+}
+
+int
+mfii_ioctl_blink(struct mfii_softc *sc, struct bioc_blink *bb)
+{
+ int i, found, rv = EINVAL;
+ union mfi_mbox mbox;
+ uint32_t cmd;
+ struct mfi_pd_list *pd;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_blink %x\n", DEVNAME(sc),
+ bb->bb_status);
+
+ /* channel 0 means not in an enclosure so can't be blinked */
+ if (bb->bb_channel == 0)
+ return (EINVAL);
+
+ pd = malloc(sizeof(*pd), M_DEVBUF, M_WAITOK);
+
+ if (mfii_mgmt(sc, MR_DCMD_PD_GET_LIST, MFII_DATA_IN,
+ sizeof(*pd), pd, NULL))
+ goto done;
+
+ for (i = 0, found = 0; i < pd->mpl_no_pd; i++)
+ if (bb->bb_channel == pd->mpl_address[i].mpa_enc_index &&
+ bb->bb_target == pd->mpl_address[i].mpa_enc_slot) {
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ goto done;
+
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.s[0] = pd->mpl_address[i].mpa_pd_id;
+
+ switch (bb->bb_status) {
+ case BIOC_SBUNBLINK:
+ cmd = MR_DCMD_PD_UNBLINK;
+ break;
+
+ case BIOC_SBBLINK:
+ cmd = MR_DCMD_PD_BLINK;
+ break;
+
+ case BIOC_SBALARM:
+ default:
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_blink biocblink invalid "
+ "opcode %x\n", DEVNAME(sc), bb->bb_status);
+ goto done;
+ }
+
+
+ if (mfii_mgmt(sc, cmd, MFII_DATA_NONE, 0, NULL, &mbox))
+ goto done;
+
+ rv = 0;
+done:
+ free(pd, M_DEVBUF, sizeof *pd);
+ return (rv);
+}
+
+static int
+mfii_makegood(struct mfii_softc *sc, uint16_t pd_id)
+{
+ struct mfi_foreign_scan_info *fsi;
+ struct mfi_pd_details *pd;
+ union mfi_mbox mbox;
+ int rv;
+
+ fsi = malloc(sizeof *fsi, M_DEVBUF, M_WAITOK);
+ pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+
+ memset(&mbox, 0, sizeof mbox);
+ mbox.s[0] = pd_id;
+ rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof(*pd), pd,
+ &mbox);
+ if (rv != 0)
+ goto done;
+
+ if (pd->mpd_fw_state == MFI_PD_UNCONFIG_BAD) {
+ mbox.s[0] = pd_id;
+ mbox.s[1] = pd->mpd_pd.mfp_seq;
+ mbox.b[4] = MFI_PD_UNCONFIG_GOOD;
+ rv = mfii_mgmt(sc, MR_DCMD_PD_SET_STATE, MFII_DATA_NONE, 0,
+ NULL, &mbox);
+ if (rv != 0)
+ goto done;
+ }
+
+ memset(&mbox, 0, sizeof mbox);
+ mbox.s[0] = pd_id;
+ rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof(*pd), pd,
+ &mbox);
+ if (rv != 0)
+ goto done;
+
+ if (pd->mpd_ddf_state & MFI_DDF_FOREIGN) {
+ rv = mfii_mgmt(sc, MR_DCMD_CFG_FOREIGN_SCAN, MFII_DATA_IN,
+ sizeof(*fsi), fsi, NULL);
+ if (rv != 0)
+ goto done;
+
+ if (fsi->count > 0) {
+ rv = mfii_mgmt(sc, MR_DCMD_CFG_FOREIGN_CLEAR,
+ MFII_DATA_NONE, 0, NULL, NULL);
+ if (rv != 0)
+ goto done;
+ }
+ }
+
+ memset(&mbox, 0, sizeof mbox);
+ mbox.s[0] = pd_id;
+ rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof *pd, pd,
+ &mbox);
+ if (rv != 0)
+ goto done;
+
+ if (pd->mpd_fw_state != MFI_PD_UNCONFIG_GOOD ||
+ pd->mpd_ddf_state & MFI_DDF_FOREIGN)
+ rv = ENXIO;
+
+done:
+ free(fsi, M_DEVBUF, sizeof *fsi);
+ free(pd, M_DEVBUF, sizeof *pd);
+
+ return (rv);
+}
+
+static int
+mfii_makespare(struct mfii_softc *sc, uint16_t pd_id)
+{
+ struct mfi_hotspare *hs;
+ struct mfi_pd_details *pd;
+ union mfi_mbox mbox;
+ size_t size;
+ int rv = EINVAL;
+
+ /* we really could skip and expect that inq took care of it */
+ if (mfii_bio_getitall(sc)) {
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_bio_getitall failed\n",
+ DEVNAME(sc));
+ return (rv);
+ }
+ size = sizeof *hs + sizeof(uint16_t) * sc->sc_cfg->mfc_no_array;
+
+ hs = malloc(size, M_DEVBUF, M_WAITOK);
+ pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+
+ memset(&mbox, 0, sizeof mbox);
+ mbox.s[0] = pd_id;
+ rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN, sizeof *pd, pd,
+ &mbox);
+ if (rv != 0)
+ goto done;
+
+ memset(hs, 0, size);
+ hs->mhs_pd.mfp_id = pd->mpd_pd.mfp_id;
+ hs->mhs_pd.mfp_seq = pd->mpd_pd.mfp_seq;
+ rv = mfii_mgmt(sc, MR_DCMD_CFG_MAKE_SPARE, MFII_DATA_OUT, size, hs,
+ NULL);
+
+done:
+ free(hs, M_DEVBUF, size);
+ free(pd, M_DEVBUF, sizeof *pd);
+
+ return (rv);
+}
+
+int
+mfii_ioctl_setstate(struct mfii_softc *sc, struct bioc_setstate *bs)
+{
+ struct mfi_pd_details *pd;
+ struct mfi_pd_list *pl;
+ int i, found, rv = EINVAL;
+ union mfi_mbox mbox;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_setstate %x\n", DEVNAME(sc),
+ bs->bs_status);
+
+ pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+ pl = malloc(sizeof *pl, M_DEVBUF, M_WAITOK);
+
+ if (mfii_mgmt(sc, MR_DCMD_PD_GET_LIST, MFII_DATA_IN,
+ sizeof *pl, pl, NULL))
+ goto done;
+
+ for (i = 0, found = 0; i < pl->mpl_no_pd; i++)
+ if (bs->bs_channel == pl->mpl_address[i].mpa_enc_index &&
+ bs->bs_target == pl->mpl_address[i].mpa_enc_slot) {
+ found = 1;
+ break;
+ }
+
+ if (!found)
+ goto done;
+
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+
+ if (mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+ sizeof *pd, pd, &mbox))
+ goto done;
+
+ mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+ mbox.s[1] = pd->mpd_pd.mfp_seq;
+
+ switch (bs->bs_status) {
+ case BIOC_SSONLINE:
+ mbox.b[4] = MFI_PD_ONLINE;
+ break;
+
+ case BIOC_SSOFFLINE:
+ mbox.b[4] = MFI_PD_OFFLINE;
+ break;
+
+ case BIOC_SSHOTSPARE:
+ mbox.b[4] = MFI_PD_HOTSPARE;
+ break;
+
+ case BIOC_SSREBUILD:
+ if (pd->mpd_fw_state != MFI_PD_OFFLINE) {
+ if ((rv = mfii_makegood(sc,
+ pl->mpl_address[i].mpa_pd_id)))
+ goto done;
+
+ if ((rv = mfii_makespare(sc,
+ pl->mpl_address[i].mpa_pd_id)))
+ goto done;
+
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+ rv = mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+ sizeof *pd, pd, &mbox);
+ if (rv != 0)
+ goto done;
+
+ /* rebuilding might be started by mfii_makespare() */
+ if (pd->mpd_fw_state == MFI_PD_REBUILD) {
+ rv = 0;
+ goto done;
+ }
+
+ mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
+ mbox.s[1] = pd->mpd_pd.mfp_seq;
+ }
+ mbox.b[4] = MFI_PD_REBUILD;
+ break;
+
+ default:
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_setstate invalid "
+ "opcode %x\n", DEVNAME(sc), bs->bs_status);
+ goto done;
+ }
+
+
+ rv = mfii_mgmt(sc, MR_DCMD_PD_SET_STATE, MFII_DATA_NONE, 0, NULL,
+ &mbox);
+done:
+ free(pd, M_DEVBUF, sizeof *pd);
+ free(pl, M_DEVBUF, sizeof *pl);
+ return (rv);
+}
+
+int
+mfii_ioctl_patrol(struct mfii_softc *sc, struct bioc_patrol *bp)
+{
+ uint32_t opc, dir = MFII_DATA_NONE;
+ int rv = 0;
+ struct mfi_pr_properties prop;
+ struct mfi_pr_status status;
+ uint32_t time, exec_freq;
+
+ switch (bp->bp_opcode) {
+ case BIOC_SPSTOP:
+ case BIOC_SPSTART:
+ if (bp->bp_opcode == BIOC_SPSTART)
+ opc = MR_DCMD_PR_START;
+ else
+ opc = MR_DCMD_PR_STOP;
+ dir = MFII_DATA_IN;
+ if (mfii_mgmt(sc, opc, dir, 0, NULL, NULL))
+ return (EINVAL);
+ break;
+
+ case BIOC_SPMANUAL:
+ case BIOC_SPDISABLE:
+ case BIOC_SPAUTO:
+ /* Get device's time. */
+ opc = MR_DCMD_TIME_SECS_GET;
+ dir = MFII_DATA_IN;
+ if (mfii_mgmt(sc, opc, dir, sizeof(time), &time, NULL))
+ return (EINVAL);
+
+ opc = MR_DCMD_PR_GET_PROPERTIES;
+ dir = MFII_DATA_IN;
+ if (mfii_mgmt(sc, opc, dir, sizeof(prop), &prop, NULL))
+ return (EINVAL);
+
+ switch (bp->bp_opcode) {
+ case BIOC_SPMANUAL:
+ prop.op_mode = MFI_PR_OPMODE_MANUAL;
+ break;
+ case BIOC_SPDISABLE:
+ prop.op_mode = MFI_PR_OPMODE_DISABLED;
+ break;
+ case BIOC_SPAUTO:
+ if (bp->bp_autoival != 0) {
+ if (bp->bp_autoival == -1)
+ /* continuously */
+ exec_freq = 0xffffffffU;
+ else if (bp->bp_autoival > 0)
+ exec_freq = bp->bp_autoival;
+ else
+ return (EINVAL);
+ prop.exec_freq = exec_freq;
+ }
+ if (bp->bp_autonext != 0) {
+ if (bp->bp_autonext < 0)
+ return (EINVAL);
+ else
+ prop.next_exec = time + bp->bp_autonext;
+ }
+ prop.op_mode = MFI_PR_OPMODE_AUTO;
+ break;
+ }
+
+ opc = MR_DCMD_PR_SET_PROPERTIES;
+ dir = MFII_DATA_OUT;
+ if (mfii_mgmt(sc, opc, dir, sizeof(prop), &prop, NULL))
+ return (EINVAL);
+
+ break;
+
+ case BIOC_GPSTATUS:
+ opc = MR_DCMD_PR_GET_PROPERTIES;
+ dir = MFII_DATA_IN;
+ if (mfii_mgmt(sc, opc, dir, sizeof(prop), &prop, NULL))
+ return (EINVAL);
+
+ opc = MR_DCMD_PR_GET_STATUS;
+ dir = MFII_DATA_IN;
+ if (mfii_mgmt(sc, opc, dir, sizeof(status), &status, NULL))
+ return (EINVAL);
+
+ /* Get device's time. */
+ opc = MR_DCMD_TIME_SECS_GET;
+ dir = MFII_DATA_IN;
+ if (mfii_mgmt(sc, opc, dir, sizeof(time), &time, NULL))
+ return (EINVAL);
+
+ switch (prop.op_mode) {
+ case MFI_PR_OPMODE_AUTO:
+ bp->bp_mode = BIOC_SPMAUTO;
+ bp->bp_autoival = prop.exec_freq;
+ bp->bp_autonext = prop.next_exec;
+ bp->bp_autonow = time;
+ break;
+ case MFI_PR_OPMODE_MANUAL:
+ bp->bp_mode = BIOC_SPMMANUAL;
+ break;
+ case MFI_PR_OPMODE_DISABLED:
+ bp->bp_mode = BIOC_SPMDISABLED;
+ break;
+ default:
+ printf("%s: unknown patrol mode %d\n",
+ DEVNAME(sc), prop.op_mode);
+ break;
+ }
+
+ switch (status.state) {
+ case MFI_PR_STATE_STOPPED:
+ bp->bp_status = BIOC_SPSSTOPPED;
+ break;
+ case MFI_PR_STATE_READY:
+ bp->bp_status = BIOC_SPSREADY;
+ break;
+ case MFI_PR_STATE_ACTIVE:
+ bp->bp_status = BIOC_SPSACTIVE;
+ break;
+ case MFI_PR_STATE_ABORTED:
+ bp->bp_status = BIOC_SPSABORTED;
+ break;
+ default:
+ printf("%s: unknown patrol state %d\n",
+ DEVNAME(sc), status.state);
+ break;
+ }
+
+ break;
+
+ default:
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_ioctl_patrol biocpatrol invalid
"
+ "opcode %x\n", DEVNAME(sc), bp->bp_opcode);
+ return (EINVAL);
+ }
+
+ return (rv);
+}
+
+int
+mfii_bio_hs(struct mfii_softc *sc, int volid, int type, void *bio_hs)
+{
+ struct mfi_conf *cfg;
+ struct mfi_hotspare *hs;
+ struct mfi_pd_details *pd;
+ struct bioc_disk *sdhs;
+ struct bioc_vol *vdhs;
+ struct scsi_inquiry_data *inqbuf;
+ char vend[8+16+4+1], *vendp;
+ int i, rv = EINVAL;
+ uint32_t size;
+ union mfi_mbox mbox;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_vol_hs %d\n", DEVNAME(sc), volid);
+
+ if (!bio_hs)
+ return (EINVAL);
+
+ pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+
+ /* send single element command to retrieve size for full structure */
+ cfg = malloc(sizeof *cfg, M_DEVBUF, M_WAITOK);
+ if (mfii_mgmt(sc, MR_DCMD_CONF_GET, MFII_DATA_IN, sizeof *cfg, cfg,
NULL))
+ goto freeme;
+
+ size = cfg->mfc_size;
+ free(cfg, M_DEVBUF, sizeof *cfg);
+
+ /* memory for read config */
+ cfg = malloc(size, M_DEVBUF, M_WAITOK|M_ZERO);
+ if (mfii_mgmt(sc, MR_DCMD_CONF_GET, MFII_DATA_IN, size, cfg, NULL))
+ goto freeme;
+
+ /* calculate offset to hs structure */
+ hs = (struct mfi_hotspare *)(
+ ((uint8_t *)cfg) + offsetof(struct mfi_conf, mfc_array) +
+ cfg->mfc_array_size * cfg->mfc_no_array +
+ cfg->mfc_ld_size * cfg->mfc_no_ld);
+
+ if (volid < cfg->mfc_no_ld)
+ goto freeme; /* not a hotspare */
+
+ if (volid > (cfg->mfc_no_ld + cfg->mfc_no_hs))
+ goto freeme; /* not a hotspare */
+
+ /* offset into hotspare structure */
+ i = volid - cfg->mfc_no_ld;
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_vol_hs i %d volid %d no_ld %d no_hs %d
"
+ "hs %p cfg %p id %02x\n", DEVNAME(sc), i, volid, cfg->mfc_no_ld,
+ cfg->mfc_no_hs, hs, cfg, hs[i].mhs_pd.mfp_id);
+
+ /* get pd fields */
+ memset(&mbox, 0, sizeof(mbox));
+ mbox.s[0] = hs[i].mhs_pd.mfp_id;
+ if (mfii_mgmt(sc, MR_DCMD_PD_GET_INFO, MFII_DATA_IN,
+ sizeof *pd, pd, &mbox)) {
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_vol_hs illegal PD\n",
+ DEVNAME(sc));
+ goto freeme;
+ }
+
+ switch (type) {
+ case MFI_MGMT_VD:
+ vdhs = bio_hs;
+ vdhs->bv_status = BIOC_SVONLINE;
+ vdhs->bv_size = pd->mpd_size / 2 * 1024; /* XXX why? */
+ vdhs->bv_level = -1; /* hotspare */
+ vdhs->bv_nodisk = 1;
+ break;
+
+ case MFI_MGMT_SD:
+ sdhs = bio_hs;
+ sdhs->bd_status = BIOC_SDHOTSPARE;
+ sdhs->bd_size = pd->mpd_size / 2 * 1024; /* XXX why? */
+ sdhs->bd_channel = pd->mpd_enc_idx;
+ sdhs->bd_target = pd->mpd_enc_slot;
+ inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data;
+ vendp = inqbuf->vendor;
+ memcpy(vend, vendp, sizeof vend - 1);
+ vend[sizeof vend - 1] = '\0';
+ strlcpy(sdhs->bd_vendor, vend, sizeof(sdhs->bd_vendor));
+ break;
+
+ default:
+ goto freeme;
+ }
+
+ DNPRINTF(MFII_D_IOCTL, "%s: mfii_vol_hs 6\n", DEVNAME(sc));
+ rv = 0;
+freeme:
+ free(pd, M_DEVBUF, sizeof *pd);
+ free(cfg, M_DEVBUF, 0);
+
+ return (rv);
+}
+
+#ifndef SMALL_KERNEL
+
+#define MFI_BBU_SENSORS 4
+
+void
+mfii_bbu(struct mfii_softc *sc)
+{
+ struct mfi_bbu_status bbu;
+ u_int32_t status;
+ u_int32_t mask;
+ u_int32_t soh_bad;
+ int i;
+
+ if (mfii_mgmt(sc, MR_DCMD_BBU_GET_STATUS, MFII_DATA_IN,
+ sizeof(bbu), &bbu, NULL) != 0) {
+ for (i = 0; i < MFI_BBU_SENSORS; i++) {
+ sc->sc_bbu[i].value = 0;
+ sc->sc_bbu[i].status = SENSOR_S_UNKNOWN;
+ }
+ for (i = 0; i < nitems(mfi_bbu_indicators); i++) {
+ sc->sc_bbu_status[i].value = 0;
+ sc->sc_bbu_status[i].status = SENSOR_S_UNKNOWN;
+ }
+ return;
+ }
+
+ switch (bbu.battery_type) {
+ case MFI_BBU_TYPE_IBBU:
+ mask = MFI_BBU_STATE_BAD_IBBU;
+ soh_bad = 0;
+ break;
+ case MFI_BBU_TYPE_BBU:
+ mask = MFI_BBU_STATE_BAD_BBU;
+ soh_bad = (bbu.detail.bbu.is_SOH_good == 0);
+ break;
+
+ case MFI_BBU_TYPE_NONE:
+ default:
+ sc->sc_bbu[0].value = 0;
+ sc->sc_bbu[0].status = SENSOR_S_CRIT;
+ for (i = 1; i < MFI_BBU_SENSORS; i++) {
+ sc->sc_bbu[i].value = 0;
+ sc->sc_bbu[i].status = SENSOR_S_UNKNOWN;
+ }
+ for (i = 0; i < nitems(mfi_bbu_indicators); i++) {
+ sc->sc_bbu_status[i].value = 0;
+ sc->sc_bbu_status[i].status = SENSOR_S_UNKNOWN;
+ }
+ return;
+ }
+
+ status = letoh32(bbu.fw_status);
+
+ sc->sc_bbu[0].value = ((status & mask) || soh_bad) ? 0 : 1;
+ sc->sc_bbu[0].status = ((status & mask) || soh_bad) ? SENSOR_S_CRIT :
+ SENSOR_S_OK;
+
+ sc->sc_bbu[1].value = letoh16(bbu.voltage) * 1000;
+ sc->sc_bbu[2].value = (int16_t)letoh16(bbu.current) * 1000;
+ sc->sc_bbu[3].value = letoh16(bbu.temperature) * 1000000 + 273150000;
+ for (i = 1; i < MFI_BBU_SENSORS; i++)
+ sc->sc_bbu[i].status = SENSOR_S_UNSPEC;
+
+ for (i = 0; i < nitems(mfi_bbu_indicators); i++) {
+ sc->sc_bbu_status[i].value = (status & (1 << i)) ? 1 : 0;
+ sc->sc_bbu_status[i].status = SENSOR_S_UNSPEC;
+ }
+
+ return;
+}
+
+int
+mfii_create_sensors(struct mfii_softc *sc)
+{
+ struct device *dev;
+ struct scsi_link *link;
+ int i;
+
+ strlcpy(sc->sc_sensordev.xname, DEVNAME(sc),
+ sizeof(sc->sc_sensordev.xname));
+
+ if (ISSET(letoh32(sc->sc_info.mci_adapter_ops ), MFI_INFO_AOPS_BBU)) {
+ sc->sc_bbu = mallocarray(4, sizeof(*sc->sc_bbu),
+ M_DEVBUF, M_WAITOK | M_ZERO);
+
+ sc->sc_bbu[0].type = SENSOR_INDICATOR;
+ sc->sc_bbu[0].status = SENSOR_S_UNKNOWN;
+ strlcpy(sc->sc_bbu[0].desc, "bbu ok",
+ sizeof(sc->sc_bbu[0].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_bbu[0]);
+
+ sc->sc_bbu[1].type = SENSOR_VOLTS_DC;
+ sc->sc_bbu[1].status = SENSOR_S_UNSPEC;
+ sc->sc_bbu[2].type = SENSOR_AMPS;
+ sc->sc_bbu[2].status = SENSOR_S_UNSPEC;
+ sc->sc_bbu[3].type = SENSOR_TEMP;
+ sc->sc_bbu[3].status = SENSOR_S_UNSPEC;
+ for (i = 1; i < MFI_BBU_SENSORS; i++) {
+ strlcpy(sc->sc_bbu[i].desc, "bbu",
+ sizeof(sc->sc_bbu[i].desc));
+ sensor_attach(&sc->sc_sensordev, &sc->sc_bbu[i]);
+ }
+
+ sc->sc_bbu_status = malloc(sizeof(*sc->sc_bbu_status) *
+ sizeof(mfi_bbu_indicators), M_DEVBUF, M_WAITOK | M_ZERO);
+
+ for (i = 0; i < nitems(mfi_bbu_indicators); i++) {
+ sc->sc_bbu_status[i].type = SENSOR_INDICATOR;
+ sc->sc_bbu_status[i].status = SENSOR_S_UNSPEC;
+ strlcpy(sc->sc_bbu_status[i].desc,
+ mfi_bbu_indicators[i],
+ sizeof(sc->sc_bbu_status[i].desc));
+
+ sensor_attach(&sc->sc_sensordev, &sc->sc_bbu_status[i]);
+ }
+ }
+
+ sc->sc_sensors = mallocarray(sc->sc_ld_cnt, sizeof(struct ksensor),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->sc_sensors == NULL)
+ return (1);
+
+ for (i = 0; i < sc->sc_ld_cnt; i++) {
+ link = scsi_get_link(sc->sc_scsibus, i, 0);
+ if (link == NULL) {
+ strlcpy(sc->sc_sensors[i].desc, "cache",
+ sizeof(sc->sc_sensors[i].desc));
+ } else {
+ if (link->device_softc == NULL)
+ continue;
+
+ dev = link->device_softc;
+ strlcpy(sc->sc_sensors[i].desc, dev->dv_xname,
+ sizeof(sc->sc_sensors[i].desc));
+ }
+
+ sc->sc_sensors[i].type = SENSOR_DRIVE;
+ sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
+
+ sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
+ }
+
+ if (sensor_task_register(sc, mfii_refresh_sensors, 10) == NULL)
+ goto bad;
+
+ sensordev_install(&sc->sc_sensordev);
+
+ return (0);
+
+bad:
+ free(sc->sc_sensors, M_DEVBUF,
+ sc->sc_ld_cnt * sizeof(struct ksensor));
+
+ return (1);
+}
+
+void
+mfii_refresh_sensors(void *arg)
+{
+ struct mfii_softc *sc = arg;
+ int i, rv;
+ struct bioc_vol bv;
+
+ if (sc->sc_bbu != NULL)
+ mfii_bbu(sc);
+
+ for (i = 0; i < sc->sc_ld_cnt; i++) {
+ bzero(&bv, sizeof(bv));
+ bv.bv_volid = i;
+
+ rw_enter_write(&sc->sc_lock);
+ rv = mfii_ioctl_vol(sc, &bv);
+ rw_exit_write(&sc->sc_lock);
+
+ if (rv != 0) {
+ sc->sc_sensors[i].value = 0; /* unknown */
+ sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
+ continue;
+ }
+
+ switch(bv.bv_status) {
+ case BIOC_SVOFFLINE:
+ sc->sc_sensors[i].value = SENSOR_DRIVE_FAIL;
+ sc->sc_sensors[i].status = SENSOR_S_CRIT;
+ break;
+
+ case BIOC_SVDEGRADED:
+ sc->sc_sensors[i].value = SENSOR_DRIVE_PFAIL;
+ sc->sc_sensors[i].status = SENSOR_S_WARN;
+ break;
+
+ case BIOC_SVSCRUB:
+ case BIOC_SVONLINE:
+ sc->sc_sensors[i].value = SENSOR_DRIVE_ONLINE;
+ sc->sc_sensors[i].status = SENSOR_S_OK;
+ break;
+
+ case BIOC_SVINVALID:
+ /* FALLTRHOUGH */
+ default:
+ sc->sc_sensors[i].value = 0; /* unknown */
+ sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
+ break;
+ }
+ }
+}
+#endif /* SMALL_KERNEL */
+#endif /* NBIO > 0 */