mfii(4): add bio(4) support

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
12 messages Options
Reply | Threaded
Open this post in threaded view
|

mfii(4): add bio(4) support

FUKAUMI Naoki
Hi tech@,

This patch adds bio(4) support for mfii(4).
# with "mfii(4): use MFII_FUNCTION_PASSTHRU_IO for MFI commands"

most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).

--- sys/dev/ic/mfireg.h
+++ 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 mfii_foreign_scan_cfg {
+ char data[24];
+};
+
+struct mfii_foreign_scan_info {
+ uint32_t count; /* Number of foreign configs found */
+ struct mfii_foreign_scan_cfg cfgs[8];
+};
--- sys/dev/pci/mfii.c
+++ 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,7 +311,35 @@ 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
@@ -355,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 *);
@@ -409,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 *,
@@ -445,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 *);
+int 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
@@ -575,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);
@@ -586,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);
 
@@ -716,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;
@@ -726,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);
 
@@ -739,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);
@@ -777,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;
@@ -861,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);
@@ -977,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));
@@ -1222,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);
@@ -1511,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;
  }
@@ -1569,7 +1686,7 @@ 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)) {
+ if (1/*cold*/) {
  /* busy-loop polling with done handler */
  ccb->ccb_cookie = NULL;
  ccb->ccb_done = mfii_empty_done;
@@ -1583,8 +1700,7 @@ mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
 
  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);
@@ -1592,7 +1708,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:
@@ -1930,6 +2046,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;
+ rv = mfii_mgmt(sc, MR_DCMD_LD_GET_PROPERTIES, MFII_DATA_IN, sizeof(ldp),
+    &ldp, &mbox);
+ if (rv != 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;
+ }
+
+ rv = mfii_mgmt(sc, MR_DCMD_LD_SET_PROPERTIES, MFII_DATA_OUT,
+    sizeof(ldp), &ldp, &mbox);
+done:
+ return (rv);
+}
+
+int
 mfii_scsi_cmd_io(struct mfii_softc *sc, struct scsi_xfer *xs)
 {
  struct scsi_link *link = xs->sc_link;
@@ -2078,7 +2297,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;
@@ -2089,10 +2307,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);
 
@@ -2427,3 +2643,1162 @@ 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 && link->device_softc != NULL) {
+ 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_sec_raid ==
+    MFI_DDF_SRL_SPANNED)
+ 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 mfii_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
+
+int
+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 (-1);
+ }
+
+ 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 (0);
+ }
+
+ 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 (0);
+}
+
+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)
+ goto bad;
+
+ dev = link->device_softc;
+
+ sc->sc_sensors[i].type = SENSOR_DRIVE;
+ sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
+
+ strlcpy(sc->sc_sensors[i].desc, dev->dv_xname,
+    sizeof(sc->sc_sensors[i].desc));
+
+ 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) != 0)
+ return;
+
+ 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)
+ return;
+
+ 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 */

--
FUKAUMI Naoki

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

YASUOKA Masahiko-3
Hi,

> most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).

Do I understand the rebuilding problem correctly?

- mfii_makegood() and mfii_makespare() are added before sending the
  rebuild command to fix the problem
- The problem doesn't happen with mfi(4)


On Mon, 05 Mar 2018 21:49:16 +0900 (JST)
Naoki Fukaumi <[hidden email]> wrote:

> Hi tech@,
>
> This patch adds bio(4) support for mfii(4).
> # with "mfii(4): use MFII_FUNCTION_PASSTHRU_IO for MFI commands"
>
> most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).
>
> --- sys/dev/ic/mfireg.h
> +++ 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 mfii_foreign_scan_cfg {
> + char data[24];
> +};
> +
> +struct mfii_foreign_scan_info {
> + uint32_t count; /* Number of foreign configs found */
> + struct mfii_foreign_scan_cfg cfgs[8];
> +};
> --- sys/dev/pci/mfii.c
> +++ 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,7 +311,35 @@ 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
> @@ -355,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 *);
> @@ -409,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 *,
> @@ -445,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 *);
> +int 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
> @@ -575,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);
> @@ -586,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);
>  
> @@ -716,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;
> @@ -726,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);
>  
> @@ -739,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);
> @@ -777,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;
> @@ -861,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);
> @@ -977,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));
> @@ -1222,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);
> @@ -1511,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;
>   }
> @@ -1569,7 +1686,7 @@ 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)) {
> + if (1/*cold*/) {
>   /* busy-loop polling with done handler */
>   ccb->ccb_cookie = NULL;
>   ccb->ccb_done = mfii_empty_done;
> @@ -1583,8 +1700,7 @@ mfii_mgmt(struct mfii_softc *sc, struct mfii_ccb *ccb,
>  
>   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);
> @@ -1592,7 +1708,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:
> @@ -1930,6 +2046,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;
> + rv = mfii_mgmt(sc, MR_DCMD_LD_GET_PROPERTIES, MFII_DATA_IN, sizeof(ldp),
> +    &ldp, &mbox);
> + if (rv != 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;
> + }
> +
> + rv = mfii_mgmt(sc, MR_DCMD_LD_SET_PROPERTIES, MFII_DATA_OUT,
> +    sizeof(ldp), &ldp, &mbox);
> +done:
> + return (rv);
> +}
> +
> +int
>  mfii_scsi_cmd_io(struct mfii_softc *sc, struct scsi_xfer *xs)
>  {
>   struct scsi_link *link = xs->sc_link;
> @@ -2078,7 +2297,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;
> @@ -2089,10 +2307,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);
>  
> @@ -2427,3 +2643,1162 @@ 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 && link->device_softc != NULL) {
> + 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_sec_raid ==
> +    MFI_DDF_SRL_SPANNED)
> + 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 mfii_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
> +
> +int
> +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 (-1);
> + }
> +
> + 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 (0);
> + }
> +
> + 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 (0);
> +}
> +
> +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)
> + goto bad;
> +
> + dev = link->device_softc;
> +
> + sc->sc_sensors[i].type = SENSOR_DRIVE;
> + sc->sc_sensors[i].status = SENSOR_S_UNKNOWN;
> +
> + strlcpy(sc->sc_sensors[i].desc, dev->dv_xname,
> +    sizeof(sc->sc_sensors[i].desc));
> +
> + 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) != 0)
> + return;
> +
> + 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)
> + return;
> +
> + 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 */
>
> --
> FUKAUMI Naoki

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

FUKAUMI Naoki
Hi,

From: YASUOKA Masahiko <[hidden email]>
Subject: Re: mfii(4): add bio(4) support
Date: Tue, 06 Mar 2018 08:14:44 +0900 (JST)

>> most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).
>
> Do I understand the rebuilding problem correctly?
>
> - mfii_makegood() and mfii_makespare() are added before sending the
>   rebuild command to fix the problem
> - The problem doesn't happen with mfi(4)

well, sorry for insufficient explanation.

mfi(4) has the problem. original fix for mfi(4) is here,

 https://marc.info/?t=149872412900002&r=1&w=2

to support rebuilding in current bio(4) framework, swapped(new) disk
should be exposed to userland as "unused" disk, but mfi(4) exposes
removed disk as "failed" disk which cannot be controlled any way. first
fix, in mfi(i)_ioctl_disk(), is "find an unused(new) disk and expose it
to userland".

and, disk state of unused disk need to be "unconfigured good" and not to
be "foreign". second fix, mfi(i)_makegood(), try to change disk state if
needed/possible.

then, mfi(i)_makespare() starts rebuilding.

Best regards,

--
FUKAUMI Naoki

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

Jonathan Matthew-4
In reply to this post by FUKAUMI Naoki
On Mon, Mar 05, 2018 at 09:49:16PM +0900, Naoki Fukaumi wrote:
> Hi tech@,
>
> This patch adds bio(4) support for mfii(4).
> # with "mfii(4): use MFII_FUNCTION_PASSTHRU_IO for MFI commands"
>
> most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).

Thanks for working on this, it'd be great to have this feature.

I tried this out on a dell server with a perc H710 (SAS2208) with an SSD cache
in front of the disks (dell calls this cachecade) which shows up as a logical
disk but doesn't answer scsi commands.  As a result, the sensor attach code
fails because it can't find the scsi_link for the cache disk:


> + for (i = 0; i < sc->sc_ld_cnt; i++) {
> + link = scsi_get_link(sc->sc_scsibus, i, 0);
> + if (link == NULL)
> + goto bad;
> +

I think this is the only case where we'll have NULL there, so we could just put
in 'cache' as the sensor description.

I also don't get a sensor for the battery.  I haven't looked into this yet.
Any idea why that would happen?

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

FUKAUMI Naoki
Hi,

thank you for your feedback!

From: Jonathan Matthew <[hidden email]>
Subject: Re: mfii(4): add bio(4) support
Date: Thu, 8 Mar 2018 19:18:28 +1000

> On Mon, Mar 05, 2018 at 09:49:16PM +0900, Naoki Fukaumi wrote:
>> Hi tech@,
>>
>> This patch adds bio(4) support for mfii(4).
>> # with "mfii(4): use MFII_FUNCTION_PASSTHRU_IO for MFI commands"
>>
>> most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).
>
> Thanks for working on this, it'd be great to have this feature.
>
> I tried this out on a dell server with a perc H710 (SAS2208) with an SSD cache
> in front of the disks (dell calls this cachecade) which shows up as a logical
> disk but doesn't answer scsi commands.  As a result, the sensor attach code
> fails because it can't find the scsi_link for the cache disk:
>
>
>> + for (i = 0; i < sc->sc_ld_cnt; i++) {
>> + link = scsi_get_link(sc->sc_scsibus, i, 0);
>> + if (link == NULL)
>> + goto bad;
>> +
>
> I think this is the only case where we'll have NULL there, so we could just put
> in 'cache' as the sensor description.

I didn't know about 'cachecade'. for now, I'm not sure it is better to
expose as 'cache', or ignore it.

is cachecade disk reported in bioctl(8)? (e.g. bioctl mfii0)

> I also don't get a sensor for the battery.  I haven't looked into this yet.
> Any idea why that would happen?

could you try following (only 1st hunk, only 2nd hunk, or both) patch?
# I'm not sure this is right fix...

--- sys/dev/pci/mfii.c
+++ sys/dev/pci/mfii.c
@@ -3628,28 +3628,15 @@ mfii_bbu(struct mfii_softc *sc)
  }
 
  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:
+ case MFI_BBU_TYPE_IBBU:
  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 (0);
+ mask = MFI_BBU_STATE_BAD_IBBU;
+ soh_bad = 0;
  }
 
  status = letoh32(bbu.fw_status);
@@ -3682,7 +3669,7 @@ mfii_create_sensors(struct mfii_softc *sc)
  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)) {
+ if (ISSET(letoh32(sc->sc_info.mci_hw_present), MFI_INFO_HW_BBU)) {
  sc->sc_bbu = mallocarray(4, sizeof(*sc->sc_bbu),
     M_DEVBUF, M_WAITOK | M_ZERO);
 

--
FUKAUMI Naoki

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

Jonathan Matthew-4
On Thu, Mar 08, 2018 at 06:43:51PM +0900, Naoki Fukaumi wrote:

> Hi,
>
> thank you for your feedback!
>
> From: Jonathan Matthew <[hidden email]>
> Subject: Re: mfii(4): add bio(4) support
> Date: Thu, 8 Mar 2018 19:18:28 +1000
>
> > On Mon, Mar 05, 2018 at 09:49:16PM +0900, Naoki Fukaumi wrote:
> >> Hi tech@,
> >>
> >> This patch adds bio(4) support for mfii(4).
> >> # with "mfii(4): use MFII_FUNCTION_PASSTHRU_IO for MFI commands"
> >>
> >> most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).
> >
> > Thanks for working on this, it'd be great to have this feature.
> >
> > I tried this out on a dell server with a perc H710 (SAS2208) with an SSD cache
> > in front of the disks (dell calls this cachecade) which shows up as a logical
> > disk but doesn't answer scsi commands.  As a result, the sensor attach code
> > fails because it can't find the scsi_link for the cache disk:
> >
> >
> >> + for (i = 0; i < sc->sc_ld_cnt; i++) {
> >> + link = scsi_get_link(sc->sc_scsibus, i, 0);
> >> + if (link == NULL)
> >> + goto bad;
> >> +
> >
> > I think this is the only case where we'll have NULL there, so we could just put
> > in 'cache' as the sensor description.
>
> I didn't know about 'cachecade'. for now, I'm not sure it is better to
> expose as 'cache', or ignore it.
>
> is cachecade disk reported in bioctl(8)? (e.g. bioctl mfii0)

Yes, bioctl mfii0 shows it:

    mfii0 2 Online       198910672896         RAID0 WB
          0 Online       100030242816 1:14.0  noencl <ATA     MZ-5EA1000-0D3  7D3Q>
          1 Online       100030242816 1:15.0  noencl <ATA     MZ-5EA1000-0D3  7D3Q>

If one of the cache SSDs fails, I definitely want to know about it, so I think it
should be exposed through sensors too.

>
> > I also don't get a sensor for the battery.  I haven't looked into this yet.
> > Any idea why that would happen?
>
> could you try following (only 1st hunk, only 2nd hunk, or both) patch?
> # I'm not sure this is right fix...

The 2nd hunk makes the battery sensors show up here.

>
> --- sys/dev/pci/mfii.c
> +++ sys/dev/pci/mfii.c
> @@ -3628,28 +3628,15 @@ mfii_bbu(struct mfii_softc *sc)
>   }
>  
>   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:
> + case MFI_BBU_TYPE_IBBU:
>   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 (0);
> + mask = MFI_BBU_STATE_BAD_IBBU;
> + soh_bad = 0;
>   }
>  
>   status = letoh32(bbu.fw_status);
> @@ -3682,7 +3669,7 @@ mfii_create_sensors(struct mfii_softc *sc)
>   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)) {
> + if (ISSET(letoh32(sc->sc_info.mci_hw_present), MFI_INFO_HW_BBU)) {
>   sc->sc_bbu = mallocarray(4, sizeof(*sc->sc_bbu),
>      M_DEVBUF, M_WAITOK | M_ZERO);
>  
>
> --
> FUKAUMI Naoki

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

Jonathan Matthew-4
In reply to this post by FUKAUMI Naoki
On Mon, Mar 05, 2018 at 09:49:16PM +0900, Naoki Fukaumi wrote:
> Hi tech@,
>
> This patch adds bio(4) support for mfii(4).
> # with "mfii(4): use MFII_FUNCTION_PASSTHRU_IO for MFI commands"
>
> most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).

I noticed another problem: spanned logical disks (RAID 10/50/60) don't
show the right RAID level in 'bioctl mfii0' output:

    mfii0 1 Online       876978634752 sd1     RAID1 WB
          0 Online       146815733760 1:2.0   noencl <HITACHI HUC151414CSS600 K516>
          1 Online       146815733760 1:3.0   noencl <HITACHI HUC151414CSS600 K516>
          2 Online       146815733760 1:4.0   noencl <HITACHI HUC151414CSS600 K516>
          3 Online       146815733760 1:5.0   noencl <HITACHI HUC151414CSS600 K516>
          4 Online       146815733760 1:6.0   noencl <HITACHI HUC151414CSS600 K516>
          5 Online       146815733760 1:7.0   noencl <HITACHI HUC151414CSS600 K516>
          6 Online       146815733760 1:8.0   noencl <HITACHI HUC151414CSS600 K516>
          7 Online       146815733760 1:9.0   noencl <HITACHI HUC151414CSS600 K516>
          8 Online       146815733760 1:10.0  noencl <HITACHI HUC151414CSS600 K516>
          9 Online       146815733760 1:11.0  noencl <HITACHI HUC151414CSS600 K516>
         10 Online       146815733760 1:12.0  noencl <HITACHI HUC151414CSS600 K516>
         11 Online       146815733760 1:13.0  noencl <HITACHI HUC151414CSS600 K516>

> + /*
> + * 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_sec_raid ==
> +    MFI_DDF_SRL_SPANNED)
> + bv->bv_level *= 10;

on the SAS2208 I'm testing with, it appears mpa_sec_raid is always 0.
Instead, we can check whether mpa_span_depth is greater than 1 to
determine whether it's spanned.

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

FUKAUMI Naoki
Hi,

thank you for committing (part of) fixes.

From: Jonathan Matthew <[hidden email]>
Subject: Re: mfii(4): add bio(4) support
Date: Mon, 12 Mar 2018 12:06:31 +1000

> On Mon, Mar 05, 2018 at 09:49:16PM +0900, Naoki Fukaumi wrote:
>> Hi tech@,
>>
>> This patch adds bio(4) support for mfii(4).
>> # with "mfii(4): use MFII_FUNCTION_PASSTHRU_IO for MFI commands"
>>
>> most parts are taken from mfi(4), plus fix for rebuilding (bioctl -R).
>
> I noticed another problem: spanned logical disks (RAID 10/50/60) don't
> show the right RAID level in 'bioctl mfii0' output:
>
>     mfii0 1 Online       876978634752 sd1     RAID1 WB
(snip)

I confirmed this happens on other controllers (e.g. SAS3008) and on old
controller (SAS2008) with mfi(4) too.

I'm trying to fix this (and CacheCade) issue now. I'll post new patch.

Best Regards,

--
FUKAUMI Naoki

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

FUKAUMI Naoki
In reply to this post by Jonathan Matthew-4
Hi,

about BBU detection,

From: Jonathan Matthew <[hidden email]>
Subject: Re: mfii(4): add bio(4) support
Date: Thu, 8 Mar 2018 21:56:53 +1000

>> > I also don't get a sensor for the battery.  I haven't looked into this yet.
>> > Any idea why that would happen?
>>
>> could you try following (only 1st hunk, only 2nd hunk, or both) patch?
>> # I'm not sure this is right fix...
>
> The 2nd hunk makes the battery sensors show up here.

>> @@ -3682,7 +3669,7 @@ mfii_create_sensors(struct mfii_softc *sc)
>>   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)) {
>> + if (ISSET(letoh32(sc->sc_info.mci_hw_present), MFI_INFO_HW_BBU)) {
>>   sc->sc_bbu = mallocarray(4, sizeof(*sc->sc_bbu),
>>      M_DEVBUF, M_WAITOK | M_ZERO);
>>  

that line (in mfi(4)) was introduced in mfi.c r1.142 like as

 if (MFI_INFO_HW_BBU && MFI_INFO_AOPS_BBU)

and modified(relaxed?) in r1.143 like as

 if (MFI_INFO_AOPS_BBU)


then, how about this?

 if (MFI_INFO_HW_BBU || MFI_INFO_AOPS_BBU)

Best regards,

--
FUKAUMI Naoki

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

FUKAUMI Naoki
In reply to this post by FUKAUMI Naoki
Hi Jonathan Matthew,

here is updated patch. could you review it?

----

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 */
--
2.7.4

--
FUKAUMI Naoki

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

Jonathan Matthew-4
On 15/05/18 14:13, Naoki Fukaumi wrote:
> Hi Jonathan Matthew,
>
> here is updated patch. could you review it?

I've just committed a series of changes based on your patch.
dlg and I decided that it'd be better if mfii_mgmt() took scsi flags
rather than MFII_DATA_IN/OUT, so I reworked all the callers to suit.

Thanks for all your work on this.

>
> ----
>
> 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 */
>

Reply | Threaded
Open this post in threaded view
|

Re: mfii(4): add bio(4) support

FUKAUMI Naoki
Hi,

From: Jonathan Matthew <[hidden email]>
Subject: Re: mfii(4): add bio(4) support
Date: Fri, 18 May 2018 15:30:31 +1000

> On 15/05/18 14:13, Naoki Fukaumi wrote:
>> Hi Jonathan Matthew,
>> here is updated patch. could you review it?
>
> I've just committed a series of changes based on your patch.
> dlg and I decided that it'd be better if mfii_mgmt() took scsi flags
> rather than MFII_DATA_IN/OUT, so I reworked all the callers to suit.
>
> Thanks for all your work on this.

Thank you very much!

I will submit following fixes for mfi(4) too. if you can, please
review them :)

>> * make "bioctl -R" work after hot swapping

>> * don't return on error while updating sensors

>> * fix RAID level of spanned logical disk

>> * report cachecade disk in ioctl/sensor

--
FUKAUMI Naoki