summaryrefslogtreecommitdiffstats
path: root/sys/dev/mlx/mlx.c
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>2000-04-11 02:52:46 +0000
committermsmith <msmith@FreeBSD.org>2000-04-11 02:52:46 +0000
commit612fc8c5158ae5c2f3017445553eefe095fbbfc1 (patch)
treef329a647fd19231f32b45be5d1325231c626e853 /sys/dev/mlx/mlx.c
parent1f9a3b6ffeeb06ce3881cbb6016adb4f15ef9d81 (diff)
downloadFreeBSD-src-612fc8c5158ae5c2f3017445553eefe095fbbfc1.zip
FreeBSD-src-612fc8c5158ae5c2f3017445553eefe095fbbfc1.tar.gz
Add features required for basic userland management utility:
- implement user-initiated background drive rebuild - implement user-initiated background consistency check - log controller-initiated background rebuild/check operations Try to fix the elusive "invalid log operation" bug, and panic if we do hit this one in the hopes of getting better information. Tidy up diagnostic messages. Try to use disk_create/disk_destroy correctly. This isn't working properly yet, but it's not clear whose fault that is.
Diffstat (limited to 'sys/dev/mlx/mlx.c')
-rw-r--r--sys/dev/mlx/mlx.c289
1 files changed, 187 insertions, 102 deletions
diff --git a/sys/dev/mlx/mlx.c b/sys/dev/mlx/mlx.c
index 7ec3da4..4bfc8b5 100644
--- a/sys/dev/mlx/mlx.c
+++ b/sys/dev/mlx/mlx.c
@@ -111,6 +111,7 @@ static void mlx_pause_done(struct mlx_command *mc);
static void *mlx_enquire(struct mlx_softc *sc, int command, size_t bufsize,
void (*complete)(struct mlx_command *mc));
static int mlx_flush(struct mlx_softc *sc);
+static int mlx_check(struct mlx_softc *sc, int drive);
static int mlx_rebuild(struct mlx_softc *sc, int channel, int target);
static int mlx_wait_command(struct mlx_command *mc);
static int mlx_poll_command(struct mlx_command *mc);
@@ -317,7 +318,6 @@ mlx_attach(struct mlx_softc *sc)
sc->mlx_sg_nseg = MLX_NSEG_NEW;
break;
default:
- device_printf(sc->mlx_dev, "attaching unsupported interface version %d\n", sc->mlx_iftype);
return(ENXIO); /* should never happen */
}
@@ -356,13 +356,13 @@ mlx_attach(struct mlx_softc *sc)
rid = 0;
sc->mlx_irq = bus_alloc_resource(sc->mlx_dev, SYS_RES_IRQ, &rid, 0, ~0, 1, RF_SHAREABLE | RF_ACTIVE);
if (sc->mlx_irq == NULL) {
- device_printf(sc->mlx_dev, "couldn't allocate interrupt\n");
+ device_printf(sc->mlx_dev, "can't allocate interrupt\n");
mlx_free(sc);
return(ENXIO);
}
error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO, mlx_intr, sc, &sc->mlx_intr);
if (error) {
- device_printf(sc->mlx_dev, "couldn't set up interrupt\n");
+ device_printf(sc->mlx_dev, "can't set up interrupt\n");
mlx_free(sc);
return(ENXIO);
}
@@ -390,7 +390,7 @@ mlx_attach(struct mlx_softc *sc)
sc->mlx_maxiop = 8;
error = mlx_sglist_map(sc);
if (error != 0) {
- device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n");
+ device_printf(sc->mlx_dev, "can't make initial s/g list mapping\n");
return(error);
}
@@ -403,7 +403,7 @@ mlx_attach(struct mlx_softc *sc)
/*
* We don't (yet) know where the event log is up to.
*/
- sc->mlx_lastevent = -1;
+ sc->mlx_currevent = -1;
/*
* Do quirk/feature related things.
@@ -446,7 +446,6 @@ mlx_attach(struct mlx_softc *sc)
}
break;
default:
- device_printf(sc->mlx_dev, "interface version corrupted to %d\n", sc->mlx_iftype);
return(ENXIO); /* should never happen */
}
@@ -457,15 +456,15 @@ mlx_attach(struct mlx_softc *sc)
sc->mlx_maxiop = sc->mlx_enq2->me_max_commands;
error = mlx_sglist_map(sc);
if (error != 0) {
- device_printf(sc->mlx_dev, "couldn't make initial s/g list mapping\n");
+ device_printf(sc->mlx_dev, "can't make permanent s/g list mapping\n");
return(error);
}
/*
- * No rebuild or check is in progress.
+ * No user-requested background operation is in progress.
*/
- sc->mlx_rebuild = -1;
- sc->mlx_check = -1;
+ sc->mlx_background = 0;
+ sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
/*
* Create the control device.
@@ -733,13 +732,15 @@ mlx_close(dev_t dev, int flags, int fmt, struct proc *p)
int
mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
{
- int unit = minor(dev);
- struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit);
- int *arg = (int *)addr;
- struct mlx_pause *mp;
- struct mlx_sysdrive *dr;
- struct mlxd_softc *mlxd;
- int i, error;
+ int unit = minor(dev);
+ struct mlx_softc *sc = devclass_get_softc(mlx_devclass, unit);
+ struct mlx_rebuild_request *rb = (struct mlx_rebuild_request *)addr;
+ struct mlx_rebuild_status *rs = (struct mlx_rebuild_status *)addr;
+ int *arg = (int *)addr;
+ struct mlx_pause *mp;
+ struct mlx_sysdrive *dr;
+ struct mlxd_softc *mlxd;
+ int i, error;
switch(cmd) {
/*
@@ -754,7 +755,7 @@ mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
if (sc->mlx_sysdrive[i].ms_disk != 0) {
/* looking for the next one we come across? */
if (*arg == -1) {
- *arg = device_get_unit(sc->mlx_sysdrive[i].ms_disk);
+ *arg = device_get_unit(sc->mlx_sysdrive[0].ms_disk);
return(0);
}
/* we want the one after this one */
@@ -849,6 +850,63 @@ mlx_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
case MLX_COMMAND:
return(mlx_user_command(sc, (struct mlx_usercommand *)addr));
+ /*
+ * Start a rebuild on a given SCSI disk
+ */
+ case MLX_REBUILDASYNC:
+ if (sc->mlx_background != 0) {
+ rb->rr_status = 0x0106;
+ return(EBUSY);
+ }
+ rb->rr_status = mlx_rebuild(sc, rb->rr_channel, rb->rr_target);
+ switch (rb->rr_status) {
+ case 0:
+ error = 0;
+ break;
+ case 0x10000:
+ error = ENOMEM; /* couldn't set up the command */
+ break;
+ case 0x0002:
+ error = EBUSY;
+ break;
+ case 0x0104:
+ error = EIO;
+ break;
+ case 0x0105:
+ error = ERANGE;
+ break;
+ case 0x0106:
+ error = EBUSY;
+ break;
+ default:
+ error = EINVAL;
+ break;
+ }
+ if (error == 0)
+ sc->mlx_background = MLX_BACKGROUND_REBUILD;
+ return(error);
+
+ /*
+ * Get the status of the current rebuild or consistency check.
+ */
+ case MLX_REBUILDSTAT:
+ *rs = sc->mlx_rebuildstat;
+ return(0);
+
+ /*
+ * Return the per-controller system drive number matching the
+ * disk device number in (arg), if it happens to belong to us.
+ */
+ case MLX_GET_SYSDRIVE:
+ error = ENOENT;
+ mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg);
+ if ((mlxd != NULL) && (mlxd->mlxd_drive >= sc->mlx_sysdrive) &&
+ (mlxd->mlxd_drive < (sc->mlx_sysdrive + MLX_MAXDRIVES))) {
+ error = 0;
+ *arg = mlxd->mlxd_drive - sc->mlx_sysdrive;
+ }
+ return(error);
+
default:
return(ENOTTY);
}
@@ -861,10 +919,8 @@ int
mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
caddr_t addr, int32_t flag, struct proc *p)
{
- struct mlxd_rebuild *mr = (struct mlxd_rebuild *)addr;
- struct mlxd_rebuild_status *mp = (struct mlxd_rebuild_status *)addr;
int *arg = (int *)addr;
- int error;
+ int error, result;
switch(cmd) {
/*
@@ -875,64 +931,39 @@ mlx_submit_ioctl(struct mlx_softc *sc, struct mlx_sysdrive *drive, u_long cmd,
return(0);
/*
- * Start a background rebuild on this drive.
+ * Start a background consistency check on this drive.
*/
- case MLXD_REBUILDASYNC:
- /* XXX lock? */
- if (sc->mlx_rebuild >= 0)
+ case MLXD_CHECKASYNC: /* start a background consistency check */
+ if (sc->mlx_background != 0) {
+ *arg = 0x0106;
return(EBUSY);
- sc->mlx_rebuild = drive - &sc->mlx_sysdrive[0];
-
- switch (mlx_rebuild(sc, mr->rb_channel, mr->rb_target)) {
+ }
+ result = mlx_check(sc, drive - &sc->mlx_sysdrive[0]);
+ switch (result) {
case 0:
- drive->ms_state = MLX_SYSD_REBUILD;
error = 0;
break;
case 0x10000:
error = ENOMEM; /* couldn't set up the command */
break;
- case 0x0002:
- case 0x0106:
- error = EBUSY;
- break;
- case 0x0004:
+ case 0x0002:
error = EIO;
break;
case 0x0105:
error = ERANGE;
break;
+ case 0x0106:
+ error = EBUSY;
+ break;
default:
error = EINVAL;
break;
}
- if (error != 0)
- sc->mlx_rebuild = -1;
+ if (error == 0)
+ sc->mlx_background = MLX_BACKGROUND_CHECK;
+ *arg = result;
return(error);
- /*
- * Start a background consistency check on this drive.
- */
- case MLXD_CHECKASYNC: /* start a background consistency check */
- /* XXX implement */
- break;
-
- /*
- * Get the status of the current rebuild or consistency check.
- */
- case MLXD_REBUILDSTAT:
-
- if (sc->mlx_rebuild >= 0) { /* may be a second or so out of date */
- mp->rs_drive = sc->mlx_rebuild;
- mp->rs_size = sc->mlx_sysdrive[sc->mlx_rebuild].ms_size;
- mp->rs_remaining = sc->mlx_rebuildstat;
- return(0);
- } else if (sc->mlx_check >= 0) {
- /* XXX implement */
- } else {
- /* XXX should return status of last completed operation? */
- return(EINVAL);
- }
-
}
return(ENOIOCTL);
}
@@ -1004,13 +1035,12 @@ mlx_periodic(void *data)
mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(struct mlx_enq_sys_drive) * MLX_MAXDRIVES,
mlx_periodic_enquiry);
- /*
- * Get drive rebuild/check status
- */
- if (sc->mlx_rebuild >= 0)
- mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
}
+ /* get drive rebuild/check status */
+ /* XXX should check sc->mlx_background if this is only valid while in progress */
+ mlx_enquire(sc, MLX_CMD_REBUILDSTAT, sizeof(struct mlx_rebuild_stat), mlx_periodic_rebuild);
+
/* deal with possibly-missed interrupts and timed-out commands */
mlx_done(sc);
@@ -1082,7 +1112,7 @@ mlx_periodic_enquiry(struct mlx_command *mc)
{
struct mlx_enquiry *me = (struct mlx_enquiry *)mc->mc_data;
- if (sc->mlx_lastevent == -1) {
+ if (sc->mlx_currevent == -1) {
/* initialise our view of the event log */
sc->mlx_currevent = sc->mlx_lastevent = me->me_event_log_seq_num;
} else if (me->me_event_log_seq_num != sc->mlx_lastevent) {
@@ -1105,24 +1135,21 @@ mlx_periodic_enquiry(struct mlx_command *mc)
(i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff);
i++) {
- /* if disk is being rebuilt, we should not check it */
- if (dr->ms_state == MLX_SYSD_REBUILD) {
- /* has state been changed by controller? */
- if (dr->ms_state != mes[i].sd_state) {
- switch(mes[i].sd_state) {
- case MLX_SYSD_OFFLINE:
- device_printf(dr->ms_disk, "drive offline\n");
- break;
- case MLX_SYSD_ONLINE:
- device_printf(dr->ms_disk, "drive online\n");
- break;
- case MLX_SYSD_CRITICAL:
- device_printf(dr->ms_disk, "drive critical\n");
- break;
- }
- /* save new state */
- dr->ms_state = mes[i].sd_state;
+ /* has state been changed by controller? */
+ if (dr->ms_state != mes[i].sd_state) {
+ switch(mes[i].sd_state) {
+ case MLX_SYSD_OFFLINE:
+ device_printf(dr->ms_disk, "drive offline\n");
+ break;
+ case MLX_SYSD_ONLINE:
+ device_printf(dr->ms_disk, "drive online\n");
+ break;
+ case MLX_SYSD_CRITICAL:
+ device_printf(dr->ms_disk, "drive critical\n");
+ break;
}
+ /* save new state */
+ dr->ms_state = mes[i].sd_state;
}
}
break;
@@ -1258,6 +1285,8 @@ mlx_periodic_eventlog_respond(struct mlx_command *mc)
}
} else {
device_printf(sc->mlx_dev, "error reading message log - %s\n", mlx_diagnose_command(mc));
+ panic("log operation failed: lastevent = %d, currevent = %d",
+ sc->mlx_lastevent, sc->mlx_currevent);
}
/* dispose of command and data */
@@ -1270,29 +1299,42 @@ mlx_periodic_eventlog_respond(struct mlx_command *mc)
}
/********************************************************************************
- * Handle the completion of a rebuild operation.
+ * Handle check/rebuild operations in progress.
*/
static void
mlx_periodic_rebuild(struct mlx_command *mc)
{
struct mlx_softc *sc = mc->mc_sc;
- struct mlx_rebuild_stat *mr = (struct mlx_rebuild_stat *)mc->mc_private;
+ struct mlx_rebuild_status *mr = (struct mlx_rebuild_status *)mc->mc_data;
switch(mc->mc_status) {
- case 0: /* all OK, rebuild still running */
- sc->mlx_rebuildstat = mr->rb_remaining;
+ case 0: /* operation running, update stats */
+ sc->mlx_rebuildstat = *mr;
+
+ /* spontaneous rebuild/check? */
+ if (sc->mlx_background == 0) {
+ sc->mlx_background = MLX_BACKGROUND_SPONTANEOUS;
+ device_printf(sc->mlx_dev, "background check/rebuild operation started\n");
+ }
break;
- case 0x0105: /* rebuild/check finished */
- if (sc->mlx_rebuild >= 0) {
- device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild completed\n");
- sc->mlx_rebuild = -1;
- } else if (sc->mlx_check >= 0) {
- device_printf(sc->mlx_sysdrive[sc->mlx_check].ms_disk, "consistency check completed\n");
- sc->mlx_check = -1;
- } else {
- device_printf(sc->mlx_dev, "consistency check completed\n");
+ case 0x0105: /* nothing running, finalise stats and report */
+ switch(sc->mlx_background) {
+ case MLX_BACKGROUND_CHECK:
+ device_printf(sc->mlx_dev, "consistency check completed\n"); /* XXX print drive? */
+ break;
+ case MLX_BACKGROUND_REBUILD:
+ device_printf(sc->mlx_dev, "drive rebuild completed\n"); /* XXX print channel/target? */
+ break;
+ case MLX_BACKGROUND_SPONTANEOUS:
+ default:
+ /* if we have previously been non-idle, report the transition */
+ if (sc->mlx_rebuildstat.rs_code != MLX_REBUILDSTAT_IDLE) {
+ device_printf(sc->mlx_dev, "background check/rebuild operation completed\n");
+ }
}
+ sc->mlx_background = 0;
+ sc->mlx_rebuildstat.rs_code = MLX_REBUILDSTAT_IDLE;
break;
}
free(mc->mc_data, M_DEVBUF);
@@ -1501,7 +1543,50 @@ mlx_flush(struct mlx_softc *sc)
}
/********************************************************************************
- * Start a background rebuild on the nominated controller/channel/target.
+ * Start a background consistency check on (drive).
+ *
+ * May be called with interrupts enabled or disabled; will return as soon as the
+ * operation has started or been refused.
+ */
+static int
+mlx_check(struct mlx_softc *sc, int drive)
+{
+ struct mlx_command *mc;
+ int error;
+
+ debug_called(1);
+
+ /* get ourselves a command buffer */
+ error = 0x10000;
+ if ((mc = mlx_alloccmd(sc)) == NULL)
+ goto out;
+ /* get a command slot */
+ if (mlx_getslot(mc))
+ goto out;
+
+ /* build a checkasync command, set the "fix it" flag */
+ mlx_make_type2(mc, MLX_CMD_CHECKASYNC, 0, 0, 0, 0, 0, drive | 0x80, 0, 0);
+
+ /* start the command and wait for it to be returned */
+ if (mlx_wait_command(mc))
+ goto out;
+
+ /* command completed OK? */
+ if (mc->mc_status != 0) {
+ device_printf(sc->mlx_dev, "CHECK ASYNC failed - %s\n", mlx_diagnose_command(mc));
+ } else {
+ device_printf(sc->mlx_sysdrive[drive].ms_disk, "consistency check started");
+ }
+ error = mc->mc_status;
+
+ out:
+ if (mc != NULL)
+ mlx_releasecmd(mc);
+ return(error);
+}
+
+/********************************************************************************
+ * Start a background rebuild of the physical drive at (channel),(target).
*
* May be called with interrupts enabled or disabled; will return as soon as the
* operation has started or been refused.
@@ -1522,18 +1607,18 @@ mlx_rebuild(struct mlx_softc *sc, int channel, int target)
if (mlx_getslot(mc))
goto out;
- /* build a rebuild command */
+ /* build a checkasync command, set the "fix it" flag */
mlx_make_type2(mc, MLX_CMD_REBUILDASYNC, channel, target, 0, 0, 0, 0, 0, 0);
- /* run the command in either polled or wait mode */
- if ((sc->mlx_state & MLX_STATE_INTEN) ? mlx_wait_command(mc) : mlx_poll_command(mc))
+ /* start the command and wait for it to be returned */
+ if (mlx_wait_command(mc))
goto out;
/* command completed OK? */
if (mc->mc_status != 0) {
device_printf(sc->mlx_dev, "REBUILD ASYNC failed - %s\n", mlx_diagnose_command(mc));
} else {
- device_printf(sc->mlx_sysdrive[sc->mlx_rebuild].ms_disk, "rebuild started");
+ device_printf(sc->mlx_dev, "drive rebuild started for %d:%d\n", channel, target);
}
error = mc->mc_status;
@@ -1607,7 +1692,7 @@ mlx_poll_command(struct mlx_command *mc)
splx(s);
return(0);
}
- device_printf(sc->mlx_dev, "I/O error 0x%x\n", mc->mc_status);
+ device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc));
return(EIO);
}
OpenPOWER on IntegriCloud