I had a situation the other day where I wanted to recover some data that
was sitting on an old decommissioned raidset. Since my current kernel
doesn't have raid installed, I had to build a new kernel (GENERIC was
fine), shutdown the production box, boot up the new kernel, install the
disk drive, mount the raidset, restore files, shutdown, and reboot
normally.
What a pain in the neck. Wouldn't it have been so much simpler if I
could have just attached the drive, mounted the raidset (including
autoload of the driver module, retrieved my data, and perhaps unloaded
the no-longer-needed module? :) I had looked at doing this previously,
but backed away because it "felt like" too much complex work.
It turns out not to be such a difficult task after all!
The attached diffs (plus 2 new files) convert our existing raidframe
driver into a loadable module. Most of the changes made were needed for
enabling module unload (detaching units and releasing data structures
dynamically, rather than waiting for system shutdown time).
I've done some fairly extensive testing, on both "bare-iron" and qemu
virtual machines, and it seems to work just fine. Including scanning
for auto-configurable raidsets when the module is loaded. I've also
tested the built-in variant of the module to ensure that that still
works, too.
I'm planning to commit this later this week. I'd appreciate any review
and (hopefully constructive) comments that the community might offer.
+------------------+--------------------------+------------------------+
| Paul Goyette | PGP Key fingerprint: | E-mail addresses: |
| (Retired) | FA29 0E3B 35AF E8AE 6651 | paul at whooppee.com |
| Kernel Developer | 0786 F758 55DE 53BA 7731 | pgoyette at netbsd.org |
+------------------+--------------------------+------------------------+
# $NetBSD$
ioconf raidframe
include "conf/files"
pseudo-device raid
# $NetBSD$
.include "../Makefile.inc"
IOCONF= raid.ioconf
.PATH: ${S}/dev/raidframe
KMOD= raid
SRCS+= rf_acctrace.c rf_alloclist.c rf_aselect.c
SRCS+= rf_callback.c rf_chaindecluster.c rf_copyback.c
SRCS+= rf_cvscan.c rf_dagdegrd.c rf_dagdegwr.c
SRCS+= rf_dagffrd.c rf_dagffwr.c rf_dagfuncs.c
SRCS+= rf_dagutils.c rf_debugMem.c rf_debugprint.c
SRCS+= rf_decluster.c rf_declusterPQ.c rf_diskqueue.c
SRCS+= rf_disks.c rf_driver.c rf_engine.c
SRCS+= rf_evenodd.c rf_evenodd_dagfuncs.c rf_evenodd_dags.c
SRCS+= rf_fifo.c rf_interdecluster.c rf_invertq.c
SRCS+= rf_layout.c rf_map.c rf_mcpair.c
SRCS+= rf_netbsdkintf.c rf_nwayxor.c rf_options.c
SRCS+= rf_paritylog.c rf_paritylogDiskMgr.c rf_paritylogging.c
SRCS+= rf_parityloggingdags.c rf_paritymap.c rf_parityscan.c
SRCS+= rf_pq.c rf_pqdeg.c rf_pqdegdags.c
SRCS+= rf_psstatus.c rf_raid0.c rf_raid1.c
SRCS+= rf_raid4.c rf_raid5.c rf_raid5_rotatedspare.c
SRCS+= rf_reconbuffer.c rf_reconmap.c rf_reconstruct.c
SRCS+= rf_reconutil.c rf_revent.c rf_shutdown.c
SRCS+= rf_sstf.c rf_states.c rf_stripelocks.c
SRCS+= rf_strutils.c rf_utils.c rf_compat50.c
CPPFLAGS+= -DRAID_AUTOCONFIG=1
# Include optional raid styles
CPPFLAGS+= -DRF_INCLUDE_EVENODD=1
CPPFLAGS+= -DRF_INCLUDE_RAID5_RS=1
CPPFLAGS+= -DRF_INCLUDE_PARITYLOGGING=1
CPPFLAGS+= -DRF_INCLUDE_CHAINDECLUSTER=1
CPPFLAGS+= -DRF_INCLUDE_INTERDECLUSTER=1
CPPFLAGS+= -DRF_INCLUDE_PARITY_DECLUSTERING=1
CPPFLAGS+= -DRF_INCLUDE_PARITY_DECLUSTERING_DS=1
CPPFLAGS+= -DCOMPAT_50
.include <bsd.kmodule.mk>
Index: distrib/sets/lists/modules/mi
===================================================================
RCS file: /cvsroot/src/distrib/sets/lists/modules/mi,v
retrieving revision 1.82
diff -u -p -r1.82 mi
--- distrib/sets/lists/modules/mi 7 Dec 2015 11:38:46 -0000 1.82
+++ distrib/sets/lists/modules/mi 20 Dec 2015 04:26:50 -0000
@@ -206,6 +206,8 @@
./@MODULEDIR@/puffs/puffs.kmod base-kernel-modules kmod
./@MODULEDIR@/putter base-kernel-modules kmod
./@MODULEDIR@/putter/putter.kmod base-kernel-modules kmod
+./@MODULEDIR@/raid base-kernel-modules kmod
+./@MODULEDIR@/raid/raid.kmod base-kernel-modules kmod
./@MODULEDIR@/scsiverbose base-kernel-modules kmod
./@MODULEDIR@/scsiverbose/scsiverbose.kmod base-kernel-modules kmod
./@MODULEDIR@/sdt base-obsolete obsolete
Index: sys/modules/Makefile
===================================================================
RCS file: /cvsroot/src/sys/modules/Makefile,v
retrieving revision 1.164
diff -u -p -r1.164 Makefile
--- sys/modules/Makefile 7 Dec 2015 11:38:46 -0000 1.164
+++ sys/modules/Makefile 20 Dec 2015 04:26:54 -0000
@@ -82,6 +82,7 @@ SUBDIR+= procfs
SUBDIR+= ptyfs
SUBDIR+= puffs
SUBDIR+= putter
+SUBDIR+= raid
SUBDIR+= scsiverbose
SUBDIR+= sdtemp
SUBDIR+= secmodel_bsd44
Index: sys/dev/raidframe/rf_driver.c
===================================================================
RCS file: /cvsroot/src/sys/dev/raidframe/rf_driver.c,v
retrieving revision 1.131
diff -u -p -r1.131 rf_driver.c
--- sys/dev/raidframe/rf_driver.c 10 Dec 2012 08:36:03 -0000 1.131
+++ sys/dev/raidframe/rf_driver.c 20 Dec 2015 04:26:58 -0000
@@ -158,16 +158,21 @@ static void rf_alloc_mutex_cond(RF_Raid_
/* called at system boot time */
int
-rf_BootRaidframe(void)
+rf_BootRaidframe(bool boot)
{
- if (raidframe_booted)
- return (EBUSY);
- raidframe_booted = 1;
- rf_init_mutex2(configureMutex, IPL_NONE);
- configureCount = 0;
- isconfigged = 0;
- globalShutdown = NULL;
+ if (boot) {
+ if (raidframe_booted)
+ return (EBUSY);
+ raidframe_booted = 1;
+ rf_init_mutex2(configureMutex, IPL_NONE);
+ configureCount = 0;
+ isconfigged = 0;
+ globalShutdown = NULL;
+ } else {
+ rf_destroy_mutex2(configureMutex);
+ raidframe_booted = 0;
+ }
return (0);
}
Index: sys/dev/raidframe/rf_driver.h
===================================================================
RCS file: /cvsroot/src/sys/dev/raidframe/rf_driver.h,v
retrieving revision 1.19
diff -u -p -r1.19 rf_driver.h
--- sys/dev/raidframe/rf_driver.h 30 Apr 2011 01:44:36 -0000 1.19
+++ sys/dev/raidframe/rf_driver.h 20 Dec 2015 04:27:02 -0000
@@ -42,7 +42,7 @@
#endif
extern rf_declare_mutex2(rf_printf_mutex);
-int rf_BootRaidframe(void);
+int rf_BootRaidframe(bool);
int rf_UnbootRaidframe(void);
int rf_Shutdown(RF_Raid_t *);
int rf_Configure(RF_Raid_t *, RF_Config_t *, RF_AutoConfig_t *);
Index: sys/dev/raidframe/rf_netbsdkintf.c
===================================================================
RCS file: /cvsroot/src/sys/dev/raidframe/rf_netbsdkintf.c,v
retrieving revision 1.326
diff -u -p -r1.326 rf_netbsdkintf.c
--- sys/dev/raidframe/rf_netbsdkintf.c 8 Dec 2015 20:36:15 -0000 1.326
+++ sys/dev/raidframe/rf_netbsdkintf.c 20 Dec 2015 04:27:06 -0000
@@ -126,6 +126,7 @@ __KERNEL_RCSID(0, "$NetBSD: rf_netbsdkin
#include <sys/bufq.h>
#include <sys/reboot.h>
#include <sys/kauth.h>
+#include <sys/module.h>
#include <prop/proplib.h>
@@ -258,6 +259,14 @@ struct raid_softc {
#define raidunit(x) DISKUNIT(x)
+MODULE(MODULE_CLASS_DRIVER, raid, "dk_subr");
+
+#ifdef _MODULE
+CFDRIVER_DECL(raid, DV_DISK, NULL);
+#endif
+
+static int raid_modcmd(modcmd_t, void *);
+
extern struct cfdriver raid_cd;
CFATTACH_DECL3_NEW(raid, sizeof(struct raid_softc),
raid_match, raid_attach, raid_detach, NULL, NULL, NULL,
@@ -360,7 +369,7 @@ raiddestroy(struct raid_softc *sc) {
}
static struct raid_softc *
-raidget(int unit) {
+raidget(int unit, bool create) {
struct raid_softc *sc;
if (unit < 0) {
#ifdef DIAGNOSTIC
@@ -376,6 +385,8 @@ raidget(int unit) {
}
}
mutex_exit(&raid_lock);
+ if (!create)
+ return NULL;
if ((sc = raidcreate(unit)) == NULL)
return NULL;
mutex_enter(&raid_lock);
@@ -395,6 +406,7 @@ raidput(struct raid_softc *sc) {
void
raidattach(int num)
{
+#if 0 /* moved to raid_modcmd() */
mutex_init(&raid_lock, MUTEX_DEFAULT, IPL_NONE);
/* This is where all the initialization stuff gets done. */
@@ -423,6 +435,7 @@ raidattach(int num)
*/
if (config_finalize_register(NULL, rf_autoconfig) != 0)
aprint_error("WARNING: unable to register RAIDframe
finalizer\n");
+#endif
}
int
@@ -606,7 +619,7 @@ raidsize(dev_t dev)
int part, unit, omask, size;
unit = raidunit(dev);
- if ((rs = raidget(unit)) == NULL)
+ if ((rs = raidget(unit, false)) == NULL)
return -1;
if ((rs->sc_flags & RAIDF_INITED) == 0)
return (-1);
@@ -643,7 +656,7 @@ raiddump(dev_t dev, daddr_t blkno, void
int part, c, sparecol, j, scol, dumpto;
int error = 0;
- if ((rs = raidget(unit)) == NULL)
+ if ((rs = raidget(unit, false)) == NULL)
return ENXIO;
raidPtr = &rs->sc_r;
@@ -656,7 +669,6 @@ raiddump(dev_t dev, daddr_t blkno, void
raidPtr->Layout.numParityCol != 1)
return EINVAL;
-
if ((error = raidlock(rs)) != 0)
return error;
@@ -779,7 +791,7 @@ raidopen(dev_t dev, int flags, int fmt,
int part, pmask;
int error = 0;
- if ((rs = raidget(unit)) == NULL)
+ if ((rs = raidget(unit, true)) == NULL)
return ENXIO;
if ((error = raidlock(rs)) != 0)
return (error);
@@ -863,7 +875,7 @@ raidclose(dev_t dev, int flags, int fmt,
int error = 0;
int part;
- if ((rs = raidget(unit)) == NULL)
+ if ((rs = raidget(unit, false)) == NULL)
return ENXIO;
if ((error = raidlock(rs)) != 0)
@@ -893,15 +905,31 @@ raidclose(dev_t dev, int flags, int fmt,
rf_update_component_labels(&rs->sc_r,
RF_FINAL_COMPONENT_UPDATE);
-
- /* If the kernel is shutting down, it will detach
- * this RAID set soon enough.
+ }
+ if ((rs->sc_dkdev.dk_openmask == 0) &&
+ ((rs->sc_flags & RAIDF_SHUTDOWN) != 0)) {
+ /*
+ * Detach this raid unit
*/
+ cfdata_t cf = NULL;
+ int retcode = 0;
+
+ if (rs->sc_dev != NULL) {
+ cf = device_cfdata(rs->sc_dev);
+
+ raidunlock(rs);
+ retcode = config_detach(rs->sc_dev, DETACH_QUIET);
+ if (retcode == 0)
+ /* free the pseudo device attach bits */
+ free(cf, M_RAIDFRAME);
+ } else {
+ LIST_REMOVE(rs, sc_link);
+ }
+ return retcode;
}
raidunlock(rs);
return (0);
-
}
static void
@@ -912,7 +940,7 @@ raidstrategy(struct buf *bp)
int wlabel;
struct raid_softc *rs;
- if ((rs = raidget(unit)) == NULL) {
+ if ((rs = raidget(unit, false)) == NULL) {
bp->b_error = ENXIO;
goto done;
}
@@ -982,7 +1010,7 @@ raidread(dev_t dev, struct uio *uio, int
int unit = raidunit(dev);
struct raid_softc *rs;
- if ((rs = raidget(unit)) == NULL)
+ if ((rs = raidget(unit, false)) == NULL)
return ENXIO;
if ((rs->sc_flags & RAIDF_INITED) == 0)
@@ -999,7 +1027,7 @@ raidwrite(dev_t dev, struct uio *uio, in
int unit = raidunit(dev);
struct raid_softc *rs;
- if ((rs = raidget(unit)) == NULL)
+ if ((rs = raidget(unit, false)) == NULL)
return ENXIO;
if ((rs->sc_flags & RAIDF_INITED) == 0)
@@ -1036,6 +1064,12 @@ raid_detach_unlocked(struct raid_softc *
disk_detach(&rs->sc_dkdev);
disk_destroy(&rs->sc_dkdev);
+ /* Free the softc */
+ mutex_enter(&raid_lock);
+ LIST_REMOVE(rs, sc_link);
+ mutex_exit(&raid_lock);
+ kmem_free(rs, sizeof(*rs));
+
aprint_normal_dev(rs->sc_dev, "detached\n");
return 0;
@@ -1070,7 +1104,7 @@ raidioctl(dev_t dev, u_long cmd, void *d
struct disklabel newlabel;
#endif
- if ((rs = raidget(unit)) == NULL)
+ if ((rs = raidget(unit, false)) == NULL)
return ENXIO;
raidPtr = &rs->sc_r;
@@ -1190,23 +1224,27 @@ raidioctl(dev_t dev, u_long cmd, void *d
RF_Free(k_cfg, sizeof(RF_Config_t));
db1_printf(("rf_ioctl: retcode=%d copyin.1\n",
retcode));
- return (retcode);
+ goto no_config;
}
goto config;
config:
+ rs->sc_flags &= ~RAIDF_SHUTDOWN;
+
/* allocate a buffer for the layout-specific data, and copy it
* in */
if (k_cfg->layoutSpecificSize) {
if (k_cfg->layoutSpecificSize > 10000) {
/* sanity check */
RF_Free(k_cfg, sizeof(RF_Config_t));
- return (EINVAL);
+ retcode = EINVAL;
+ goto no_config;
}
RF_Malloc(specific_buf, k_cfg->layoutSpecificSize,
(u_char *));
if (specific_buf == NULL) {
RF_Free(k_cfg, sizeof(RF_Config_t));
- return (ENOMEM);
+ retcode = ENOMEM;
+ goto no_config;
}
retcode = copyin(k_cfg->layoutSpecific, specific_buf,
k_cfg->layoutSpecificSize);
@@ -1216,7 +1254,7 @@ raidioctl(dev_t dev, u_long cmd, void *d
k_cfg->layoutSpecificSize);
db1_printf(("rf_ioctl: retcode=%d copyin.2\n",
retcode));
- return (retcode);
+ goto no_config;
}
} else
specific_buf = NULL;
@@ -1253,6 +1291,13 @@ raidioctl(dev_t dev, u_long cmd, void *d
}
RF_Free(k_cfg, sizeof(RF_Config_t));
+ no_config:
+ /*
+ * If configuration failed, set sc_flags so that we
+ * will detach the device when we close it.
+ */
+ if (retcode != 0)
+ rs->sc_flags |= RAIDF_SHUTDOWN;
return (retcode);
/* shutdown the system */
@@ -2391,7 +2436,7 @@ raidgetdisklabel(dev_t dev)
struct cpu_disklabel *clp;
RF_Raid_t *raidPtr;
- if ((rs = raidget(unit)) == NULL)
+ if ((rs = raidget(unit, false)) == NULL)
return;
lp = rs->sc_dkdev.dk_label;
@@ -3765,7 +3810,7 @@ rf_auto_config_set(RF_ConfigSet_t *cset)
/* 1. Create a config structure */
config = malloc(sizeof(*config), M_RAIDFRAME, M_NOWAIT|M_ZERO);
if (config == NULL) {
- printf("Out of mem!?!?\n");
+ printf("%s: Out of mem - config!?!?\n", __func__);
/* XXX do something more intelligent here. */
return NULL;
}
@@ -3777,12 +3822,22 @@ rf_auto_config_set(RF_ConfigSet_t *cset)
*/
raidID = cset->ac->clabel->last_unit;
- for (sc = raidget(raidID); sc->sc_r.valid != 0; sc = raidget(++raidID))
+ for (sc = raidget(raidID, false); sc && sc->sc_r.valid != 0;
+ sc = raidget(++raidID, false))
continue;
#ifdef DEBUG
printf("Configuring raid%d:\n",raidID);
#endif
+ if (sc == NULL)
+ sc = raidget(raidID, true);
+ if (sc == NULL) {
+ printf("%s: Out of mem - softc!?!?\n", __func__);
+ /* XXX do something more intelligent here. */
+ free(config, M_RAIDFRAME);
+ return NULL;
+ }
+
raidPtr = &sc->sc_r;
/* XXX all this stuff should be done SOMEWHERE ELSE! */
@@ -3900,7 +3955,7 @@ static int
raid_detach(device_t self, int flags)
{
int error;
- struct raid_softc *rs = raidget(device_unit(self));
+ struct raid_softc *rs = raidget(device_unit(self), false);
if (rs == NULL)
return ENXIO;
@@ -3979,3 +4034,124 @@ rf_sync_component_caches(RF_Raid_t *raid
}
return error;
}
+
+/*
+ * Module interface
+ */
+
+static int
+raid_modcmd(modcmd_t cmd, void *data)
+{
+ int error;
+#ifdef _MODULE
+ int bmajor, cmajor;
+#endif
+
+ error = 0;
+ switch (cmd) {
+ case MODULE_CMD_INIT:
+ mutex_init(&raid_lock, MUTEX_DEFAULT, IPL_NONE);
+ mutex_enter(&raid_lock);
+#if (RF_INCLUDE_PARITY_DECLUSTERING_DS > 0)
+ rf_init_mutex2(rf_sparet_wait_mutex, IPL_VM);
+ rf_init_cond2(rf_sparet_wait_cv, "sparetw");
+ rf_init_cond2(rf_sparet_resp_cv, "rfgst");
+
+ rf_sparet_wait_queue = rf_sparet_resp_queue = NULL;
+#endif
+
+#ifdef _MODULE
+ bmajor = cmajor = -1;
+ error = devsw_attach("raid", &raid_bdevsw, &bmajor,
+ &raid_cdevsw, &cmajor);
+ if (error != 0) {
+ aprint_error("%s: devsw_attach failed %d\n",
+ __func__, error);
+ mutex_exit(&raid_lock);
+ break;
+ }
+ error = config_cfdriver_attach(&raid_cd);
+ if (error != 0) {
+ aprint_error("%s: config_cfdriver_attach failed %d\n",
+ __func__, error);
+ devsw_detach(&raid_bdevsw, &raid_cdevsw);
+ mutex_exit(&raid_lock);
+ break;
+ }
+#endif
+ error = config_cfattach_attach(raid_cd.cd_name, &raid_ca);
+ if (error != 0) {
+ aprint_error("%s: config_cfattach_attach failed %d\n",
+ __func__, error);
+ config_cfdriver_detach(&raid_cd);
+ devsw_detach(&raid_bdevsw, &raid_cdevsw);
+ mutex_exit(&raid_lock);
+ break;
+ }
+
+ raidautoconfigdone = false;
+
+ mutex_exit(&raid_lock);
+
+ if (error == 0) {
+ if (rf_BootRaidframe(true) == 0)
+ aprint_verbose("Kernelized RAIDframe "
+ "activated\n");
+ else
+ panic("Serious error activating RAID!!");
+ }
+
+ /*
+ * Register a finalizer which will be used to auto-config RAID
+ * sets once all real hardware devices have been found.
+ */
+ error = config_finalize_register(NULL, rf_autoconfig);
+ if (error != 0) {
+ aprint_error("WARNING: unable to register RAIDframe "
+ "finalizer\n");
+ }
+ break;
+ case MODULE_CMD_FINI:
+ mutex_enter(&raid_lock);
+
+ /* Don't allow unload if raid device(s) exist. */
+ if (!LIST_EMPTY(&raids)) {
+ mutex_exit(&raid_lock);
+ return EBUSY;
+ }
+
+ error = config_cfattach_detach(raid_cd.cd_name, &raid_ca);
+ if (error != 0) {
+ mutex_exit(&raid_lock);
+ break;
+ }
+#ifdef _MODULE
+ error = config_cfdriver_detach(&raid_cd);
+ if (error != 0) {
+ config_cfattach_attach(raid_cd.cd_name, &raid_ca);
+ mutex_exit(&raid_lock);
+ break;
+ }
+ error = devsw_detach(&raid_bdevsw, &raid_cdevsw);
+ if (error != 0) {
+ config_cfdriver_attach(&raid_cd);
+ config_cfattach_attach(raid_cd.cd_name, &raid_ca);
+ mutex_exit(&raid_lock);
+ break;
+ }
+#endif
+ rf_BootRaidframe(false);
+#if (RF_INCLUDE_PARITY_DECLUSTERING_DS > 0)
+ rf_destroy_mutex2(rf_sparet_wait_mutex);
+ rf_destroy_cond2(rf_sparet_wait_cv);
+ rf_destroy_cond2(rf_sparet_resp_cv);
+#endif
+ mutex_exit(&raid_lock);
+ mutex_destroy(&raid_lock);
+ break;
+ default:
+ error = ENOTTY;
+ break;
+ }
+ return error;
+}
Index: sys/rump/dev/lib/libraidframe/raidframe_component.c
===================================================================
RCS file: /cvsroot/src/sys/rump/dev/lib/libraidframe/raidframe_component.c,v
retrieving revision 1.2
diff -u -p -r1.2 raidframe_component.c
--- sys/rump/dev/lib/libraidframe/raidframe_component.c 20 Aug 2015 11:51:12
-0000 1.2
+++ sys/rump/dev/lib/libraidframe/raidframe_component.c 20 Dec 2015 04:27:10
-0000
@@ -39,7 +39,11 @@ __KERNEL_RCSID(0, "$NetBSD: raidframe_co
#include "ioconf.h"
-CFDRIVER_DECL(raid, DV_DISK, NULL);
+/*
+ * No longer needed - raidframe kernel module includes this declaration
+ *
+ * CFDRIVER_DECL(raid, DV_DISK, NULL);
+ */
RUMP_COMPONENT(RUMP_COMPONENT_DEV)
{