Hi tech@,

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.

----

Notes about implementation

mfi_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.

mfi_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, mfi_makegood() and mfi_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

Index: sys/dev/ic/mfi.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/mfi.c,v
retrieving revision 1.167
diff -u -p -r1.167 mfi.c
--- sys/dev/ic/mfi.c    2 Feb 2017 03:47:41 -0000       1.167
+++ sys/dev/ic/mfi.c    29 Jun 2017 08:05:23 -0000
@@ -1791,11 +1791,12 @@ mfi_ioctl_disk(struct mfi_softc *sc, str
        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;
 
@@ -1811,6 +1812,7 @@ mfi_ioctl_disk(struct mfi_softc *sc, str
        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;
@@ -1834,13 +1836,53 @@ mfi_ioctl_disk(struct mfi_softc *sc, str
 
        /* 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 ((rv = 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? */
@@ -1851,6 +1893,10 @@ mfi_ioctl_disk(struct mfi_softc *sc, str
                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;
@@ -1859,27 +1905,15 @@ mfi_ioctl_disk(struct mfi_softc *sc, str
                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);
@@ -1899,6 +1933,7 @@ mfi_ioctl_disk(struct mfi_softc *sc, str
        rv = 0;
 freeme:
        free(pd, M_DEVBUF, sizeof *pd);
+       free(pl, M_DEVBUF, sizeof *pl);
 
        return (rv);
 }
@@ -2009,27 +2044,136 @@ done:
        return (rv);
 }
 
+/* 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];
+};
+
+static int
+mfi_makegood(struct mfi_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;
+       if ((rv = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN, sizeof *pd, pd,
+           &mbox)))
+               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;
+               if ((rv = mfi_mgmt(sc, MR_DCMD_PD_SET_STATE, MFI_DATA_NONE, 0,
+                   NULL, &mbox)))
+                       goto done;
+       }
+
+       memset(&mbox, 0, sizeof mbox);
+       mbox.s[0] = pd_id;
+       if ((rv = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN, sizeof *pd, pd,
+           &mbox)))
+               goto done;
+
+       if (pd->mpd_ddf_state & MFI_DDF_FOREIGN) {
+               if ((rv = mfi_mgmt(sc, MR_DCMD_CFG_FOREIGN_SCAN, MFI_DATA_IN,
+                   sizeof *fsi, fsi, NULL)))
+                       goto done;
+
+               if (fsi->count > 0) {
+                       if ((rv = mfi_mgmt(sc, MR_DCMD_CFG_FOREIGN_CLEAR,
+                           MFI_DATA_NONE, 0, NULL, NULL)))
+                               goto done;
+               }
+       }
+
+       memset(&mbox, 0, sizeof mbox);
+       mbox.s[0] = pd_id;
+       if ((rv = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN, sizeof *pd, pd,
+           &mbox)))
+               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;
+       if ((rv = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN, sizeof *pd, pd,
+           &mbox)))
+               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;
                }
@@ -2038,14 +2182,14 @@ mfi_ioctl_setstate(struct mfi_softc *sc,
                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:
@@ -2061,6 +2205,30 @@ mfi_ioctl_setstate(struct mfi_softc *sc,
                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;
+                       if ((rv = mfi_mgmt(sc, MR_DCMD_PD_GET_INFO, MFI_DATA_IN,
+                           sizeof *pd, pd, &mbox)))
+                               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;
 
@@ -2078,7 +2246,7 @@ mfi_ioctl_setstate(struct mfi_softc *sc,
        rv = 0;
 done:
        free(pd, M_DEVBUF, sizeof *pd);
-       free(info, M_DEVBUF, sizeof *info);
+       free(pl, M_DEVBUF, sizeof *pl);
        return (rv);
 }
 
Index: sys/dev/ic/mfireg.h
===================================================================
RCS file: /cvs/src/sys/dev/ic/mfireg.h,v
retrieving revision 1.48
diff -u -p -r1.48 mfireg.h
--- sys/dev/ic/mfireg.h 7 Feb 2017 07:07:29 -0000       1.48
+++ sys/dev/ic/mfireg.h 29 Jun 2017 08:05:23 -0000
@@ -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

Reply via email to