Hi Jonathan Matthew,

From: YASUOKA Masahiko <yasu...@openbsd.org>
Subject: Re: mfi(4): Make "bioctl -R" work after hot swapping
Date: Tue, 24 Apr 2018 20:20:40 +0900 (JST)

> On Thu, 29 Jun 2017 17:14:41 +0900 (JST)
> FUKAUMI Naoki <fuka...@soum.co.jp> wrote:
>> 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.
> 
> Let me update the diff.
> 
> ok? comments?

here is updated patch which includes following fixes from mfii(4),

* don't return on error while updating sensors
* fix BBU detection
* fix RAID level of spanned logical disk
* report cachecade disk in ioctl/sensor

could you review it?

diff --git a/sys/dev/ic/mfi.c b/sys/dev/ic/mfi.c
index 57fe533..af39b52 100644
--- a/sys/dev/ic/mfi.c
+++ b/sys/dev/ic/mfi.c
@@ -121,7 +121,7 @@ int         mfi_bio_hs(struct mfi_softc *, int, int, void 
*);
 #ifndef SMALL_KERNEL
 int            mfi_create_sensors(struct mfi_softc *);
 void           mfi_refresh_sensors(void *);
-int            mfi_bbu(struct mfi_softc *);
+void           mfi_bbu(struct mfi_softc *);
 #endif /* SMALL_KERNEL */
 #endif /* NBIO > 0 */
 
@@ -1582,7 +1582,8 @@ mfi_ioctl(struct device *dev, u_long cmd, caddr_t addr)
 int
 mfi_bio_getitall(struct mfi_softc *sc)
 {
-       int                     i, d, size, rv = EINVAL;
+       int                     i, d, rv = EINVAL;
+       size_t                  size;
        union mfi_mbox          mbox;
        struct mfi_conf         *cfg = NULL;
        struct mfi_ld_details   *ld_det = NULL;
@@ -1716,8 +1717,13 @@ mfi_ioctl_vol(struct mfi_softc *sc, struct bioc_vol *bv)
 
        i = bv->bv_volid;
        link = scsi_get_link(sc->sc_scsibus, i, 0);
-       if (link != NULL && link->device_softc != NULL) {
+       if (link == NULL) {
+               strlcpy(bv->bv_dev, "cache", sizeof(bv->bv_dev));
+       } else {
                dev = link->device_softc;
+               if (dev == NULL)
+                       goto done;
+
                strlcpy(bv->bv_dev, dev->dv_xname, sizeof(bv->bv_dev));
        }
 
@@ -1769,8 +1775,7 @@ mfi_ioctl_vol(struct mfi_softc *sc, struct bioc_vol *bv)
         * 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)
+       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 *
@@ -1790,11 +1795,12 @@ mfi_ioctl_disk(struct mfi_softc *sc, struct bioc_disk 
*bd)
        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                     rv = EINVAL;
+       int                     i, rv = EINVAL;
        int                     arr, vol, disk, span;
        union mfi_mbox          mbox;
 
@@ -1810,6 +1816,7 @@ mfi_ioctl_disk(struct mfi_softc *sc, struct bioc_disk *bd)
        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;
@@ -1833,13 +1840,53 @@ mfi_ioctl_disk(struct mfi_softc *sc, struct bioc_disk 
*bd)
 
        /* offset disk into pd list */
        disk = bd->bd_diskid % ld[vol].mlc_parm.mpa_no_drv_per_span;
-       bd->bd_target = ar[arr].pd[disk].mar_enc_slot;
+
+       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 (mfi_mgmt(sc, MR_DCMD_PD_GET_LIST, MFI_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 (mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_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 (mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_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 (ar[arr].pd[disk].mar_pd_state){
+       switch (pd->mpd_fw_state){
        case MFI_PD_UNCONFIG_GOOD:
-       case MFI_PD_FAILED:
-               bd->bd_status = BIOC_SDFAILED;
+       case MFI_PD_UNCONFIG_BAD:
+               bd->bd_status = BIOC_SDUNUSED;
                break;
 
        case MFI_PD_HOTSPARE: /* XXX dedicated hotspare part of array? */
@@ -1850,6 +1897,10 @@ mfi_ioctl_disk(struct mfi_softc *sc, struct bioc_disk 
*bd)
                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;
@@ -1858,27 +1909,15 @@ mfi_ioctl_disk(struct mfi_softc *sc, struct bioc_disk 
*bd)
                bd->bd_status = BIOC_SDONLINE;
                break;
 
-       case MFI_PD_UNCONFIG_BAD: /* XXX define new state in bio */
+       case MFI_PD_COPYBACK:
+       case MFI_PD_SYSTEM:
        default:
                bd->bd_status = BIOC_SDINVALID;
                break;
        }
 
-       /* get the remaining fields */
-       memset(&mbox, 0, sizeof(mbox));
-       mbox.s[0] = ar[arr].pd[disk].mar_pd.mfp_id;
-       if (mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN,
-           sizeof *pd, pd, &mbox)) {
-               /* disk is missing but succeed command */
-               rv = 0;
-               goto freeme;
-       }
-
        bd->bd_size = pd->mpd_size * 512; /* bytes per block */
 
-       /* if pd->mpd_enc_idx is 0 then it is not in an enclosure */
-       bd->bd_channel = pd->mpd_enc_idx;
-
        inqbuf = (struct scsi_inquiry_data *)&pd->mpd_inq_data;
        vendp = inqbuf->vendor;
        memcpy(vend, vendp, sizeof vend - 1);
@@ -1898,6 +1937,7 @@ mfi_ioctl_disk(struct mfi_softc *sc, struct bioc_disk *bd)
        rv = 0;
 freeme:
        free(pd, M_DEVBUF, sizeof *pd);
+       free(pl, M_DEVBUF, sizeof *pl);
 
        return (rv);
 }
@@ -2008,27 +2048,133 @@ done:
        return (rv);
 }
 
+static int
+mfi_makegood(struct mfi_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 = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_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 = mfi_mgmt(sc, MR_DCMD_PD_SET_STATE, MFI_DATA_NONE, 0,
+                   NULL, &mbox);
+               if (rv != 0)
+                       goto done;
+       }
+
+       memset(&mbox, 0, sizeof mbox);
+       mbox.s[0] = pd_id;
+       rv = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN, sizeof(*pd), pd,
+           &mbox);
+       if (rv != 0)
+               goto done;
+
+       if (pd->mpd_ddf_state & MFI_DDF_FOREIGN) {
+               rv = mfi_mgmt(sc, MR_DCMD_CFG_FOREIGN_SCAN, MFI_DATA_IN,
+                   sizeof(*fsi), fsi, NULL);
+               if (rv != 0)
+                       goto done;
+
+               if (fsi->count > 0) {
+                       rv = mfi_mgmt(sc, MR_DCMD_CFG_FOREIGN_CLEAR,
+                           MFI_DATA_NONE, 0, NULL, NULL);
+                       if (rv != 0)
+                               goto done;
+               }
+       }
+
+       memset(&mbox, 0, sizeof mbox);
+       mbox.s[0] = pd_id;
+       rv = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_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
+mfi_makespare(struct mfi_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 (mfi_bio_getitall(sc)) {
+               DNPRINTF(MFI_D_IOCTL, "%s: mfi_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 = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_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 = mfi_mgmt(sc, MR_DCMD_CFG_MAKE_SPARE, MFI_DATA_OUT, size, hs, NULL);
+
+done:
+       free(hs, M_DEVBUF, size);
+       free(pd, M_DEVBUF, sizeof *pd);
+
+       return (rv);
+}
+
 int
 mfi_ioctl_setstate(struct mfi_softc *sc, struct bioc_setstate *bs)
 {
-       struct mfi_pd_list      *pd;
-       struct mfi_pd_details   *info;
+       struct mfi_pd_details   *pd;
+       struct mfi_pd_list      *pl;
        int                     i, found, rv = EINVAL;
        union mfi_mbox          mbox;
 
        DNPRINTF(MFI_D_IOCTL, "%s: mfi_ioctl_setstate %x\n", DEVNAME(sc),
            bs->bs_status);
 
-       pd = malloc(sizeof(*pd), M_DEVBUF, M_WAITOK);
-       info = malloc(sizeof *info, M_DEVBUF, M_WAITOK);
+       pd = malloc(sizeof *pd, M_DEVBUF, M_WAITOK);
+       pl = malloc(sizeof *pl, M_DEVBUF, M_WAITOK);
 
        if (mfi_mgmt(sc, MR_DCMD_PD_GET_LIST, MFI_DATA_IN,
-           sizeof(*pd), pd, NULL))
+           sizeof *pl, pl, NULL))
                goto done;
 
-       for (i = 0, found = 0; i < pd->mpl_no_pd; i++)
-               if (bs->bs_channel == pd->mpl_address[i].mpa_enc_index &&
-                   bs->bs_target == pd->mpl_address[i].mpa_enc_slot) {
+       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;
                }
@@ -2037,14 +2183,14 @@ mfi_ioctl_setstate(struct mfi_softc *sc, struct 
bioc_setstate *bs)
                goto done;
 
        memset(&mbox, 0, sizeof(mbox));
-       mbox.s[0] = pd->mpl_address[i].mpa_pd_id;
+       mbox.s[0] = pl->mpl_address[i].mpa_pd_id;
 
        if (mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN,
-           sizeof *info, info, &mbox))
+           sizeof *pd, pd, &mbox))
                goto done;
 
-       mbox.s[0] = pd->mpl_address[i].mpa_pd_id;
-       mbox.s[1] = info->mpd_pd.mfp_seq;
+       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:
@@ -2060,6 +2206,31 @@ mfi_ioctl_setstate(struct mfi_softc *sc, struct 
bioc_setstate *bs)
                break;
 
        case BIOC_SSREBUILD:
+               if (pd->mpd_fw_state != MFI_PD_OFFLINE) {
+                       if ((rv = mfi_makegood(sc,
+                           pl->mpl_address[i].mpa_pd_id)))
+                               goto done;
+
+                       if ((rv = mfi_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 = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN,
+                           sizeof *pd, pd, &mbox);
+                       if (rv != 0)
+                               goto done;
+
+                       /* rebuilding might be started by mfi_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;
 
@@ -2070,14 +2241,10 @@ mfi_ioctl_setstate(struct mfi_softc *sc, struct 
bioc_setstate *bs)
        }
 
 
-       if ((rv = mfi_mgmt(sc, MR_DCMD_PD_SET_STATE, MFI_DATA_NONE, 0, NULL,
-           &mbox)))
-               goto done;
-
-       rv = 0;
+       rv = mfi_mgmt(sc, MR_DCMD_PD_SET_STATE, MFI_DATA_NONE, 0, NULL, &mbox);
 done:
        free(pd, M_DEVBUF, sizeof *pd);
-       free(info, M_DEVBUF, sizeof *info);
+       free(pl, M_DEVBUF, sizeof *pl);
        return (rv);
 }
 
@@ -2335,7 +2502,7 @@ static const char *mfi_bbu_indicators[] = {
 
 #define MFI_BBU_SENSORS 4
 
-int
+void
 mfi_bbu(struct mfi_softc *sc)
 {
        struct mfi_bbu_status bbu;
@@ -2354,7 +2521,7 @@ mfi_bbu(struct mfi_softc *sc)
                        sc->sc_bbu_status[i].value = 0;
                        sc->sc_bbu_status[i].status = SENSOR_S_UNKNOWN;
                }
-               return (-1);
+               return;
        }
 
        switch (bbu.battery_type) {
@@ -2379,7 +2546,7 @@ mfi_bbu(struct mfi_softc *sc)
                        sc->sc_bbu_status[i].value = 0;
                        sc->sc_bbu_status[i].status = SENSOR_S_UNKNOWN;
                }
-               return (0);
+               return;
        }
 
        status = letoh32(bbu.fw_status);
@@ -2398,8 +2565,6 @@ mfi_bbu(struct mfi_softc *sc)
                sc->sc_bbu_status[i].value = (status & (1 << i)) ? 1 : 0;
                sc->sc_bbu_status[i].status = SENSOR_S_UNSPEC;
        }
-
-       return (0);
 }
 
 int
@@ -2412,7 +2577,7 @@ mfi_create_sensors(struct mfi_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);
 
@@ -2455,17 +2620,21 @@ mfi_create_sensors(struct mfi_softc *sc)
 
        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;
+               if (link == NULL) {
+                       strlcpy(sc->sc_sensors[i].desc, "cache",
+                           sizeof(sc->sc_sensors[i].desc));
+               } else {
+                       dev = link->device_softc;
+                       if (dev == NULL)
+                               continue;
+
+                       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;
-
-               strlcpy(sc->sc_sensors[i].desc, dev->dv_xname,
-                   sizeof(sc->sc_sensors[i].desc));
-
                sensor_attach(&sc->sc_sensordev, &sc->sc_sensors[i]);
        }
 
@@ -2490,8 +2659,8 @@ mfi_refresh_sensors(void *arg)
        int                     i, rv;
        struct bioc_vol         bv;
 
-       if (sc->sc_bbu != NULL && mfi_bbu(sc) != 0)
-               return;
+       if (sc->sc_bbu != NULL)
+               mfi_bbu(sc);
 
        for (i = 0; i < sc->sc_ld_cnt; i++) {
                bzero(&bv, sizeof(bv));
@@ -2501,8 +2670,11 @@ mfi_refresh_sensors(void *arg)
                rv = mfi_ioctl_vol(sc, &bv);
                rw_exit_write(&sc->sc_lock);
 
-               if (rv != 0)
-                       return;
+               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:
-- 
2.7.4

Reply via email to