diff options
-rw-r--r-- | sys/dev/mlx/mlx.c | 289 | ||||
-rw-r--r-- | sys/dev/mlx/mlx_disk.c | 10 | ||||
-rw-r--r-- | sys/dev/mlx/mlxio.h | 48 | ||||
-rw-r--r-- | sys/dev/mlx/mlxreg.h | 11 | ||||
-rw-r--r-- | sys/dev/mlx/mlxvar.h | 12 |
5 files changed, 235 insertions, 135 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); } diff --git a/sys/dev/mlx/mlx_disk.c b/sys/dev/mlx/mlx_disk.c index 22328ba..5420326 100644 --- a/sys/dev/mlx/mlx_disk.c +++ b/sys/dev/mlx/mlx_disk.c @@ -80,9 +80,8 @@ static struct cdevsw mlxd_cdevsw = { /* bmaj */ MLXD_BDEV_MAJOR }; -static devclass_t mlxd_devclass; +devclass_t mlxd_devclass; static struct cdevsw mlxddisk_cdevsw; -static int disks_registered = 0; static device_method_t mlxd_methods[] = { DEVMETHOD(device_probe, mlxd_probe), @@ -272,7 +271,7 @@ mlxd_attach(device_t dev) dsk = disk_create(sc->mlxd_unit, &sc->mlxd_disk, 0, &mlxd_cdevsw, &mlxddisk_cdevsw); dsk->si_drv1 = sc; - disks_registered++; + sc->mlxd_dev_t = dsk; /* set maximum I/O size */ dsk->si_iosize_max = sc->mlxd_controller->mlx_enq2->me_maxblk * MLX_BLKSIZE; @@ -288,10 +287,7 @@ mlxd_detach(device_t dev) debug_called(1); devstat_remove_entry(&sc->mlxd_stats); - - /* hack to handle lack of destroy_disk() */ - if (--disks_registered == 0) - cdevsw_remove(&mlxddisk_cdevsw); + disk_destroy(sc->mlxd_dev_t); return(0); } diff --git a/sys/dev/mlx/mlxio.h b/sys/dev/mlx/mlxio.h index 984b223..f091bef 100644 --- a/sys/dev/mlx/mlxio.h +++ b/sys/dev/mlx/mlxio.h @@ -31,32 +31,15 @@ /* * System Disk ioctls */ -struct mlxd_rebuild -{ - int rb_channel; - int rb_target; -}; -struct mlxd_rebuild_status -{ - int rs_drive; - int rs_size; - int rs_remaining; -}; - -#define MLXD_STATUS _IOR ('M', 100, int) -#define MLXD_REBUILDASYNC _IOW ('M', 101, struct mlxd_rebuild) -#define MLXD_CHECKASYNC _IOW ('M', 102, int) -#define MLXD_REBUILDSTAT _IOR ('M', 103, struct mlxd_rebuild_status) - -/* - * System Disk status values - */ +/* system disk status values */ #define MLX_SYSD_ONLINE 0x03 #define MLX_SYSD_CRITICAL 0x04 -#define MLX_SYSD_REBUILD 0xfe #define MLX_SYSD_OFFLINE 0xff +#define MLXD_STATUS _IOR ('M', 100, int) +#define MLXD_CHECKASYNC _IOR ('M', 101, int) /* command result returned in argument */ + /* * Controller ioctls */ @@ -85,9 +68,30 @@ struct mlx_usercommand }; +struct mlx_rebuild_request +{ + int rr_channel; + int rr_target; + int rr_status; +}; + +struct mlx_rebuild_status +{ + u_int16_t rs_code; +#define MLX_REBUILDSTAT_REBUILDCHECK 0x0000 +#define MLX_REBUILDSTAT_ADDCAPACITY 0x0400 +#define MLX_REBUILDSTAT_ADDCAPACITYINIT 0x0500 +#define MLX_REBUILDSTAT_IDLE 0xffff + u_int16_t rs_drive; + int rs_size; + int rs_remaining; +}; + #define MLX_NEXT_CHILD _IOWR('M', 0, int) #define MLX_RESCAN_DRIVES _IO ('M', 1) #define MLX_DETACH_DRIVE _IOW ('M', 2, int) #define MLX_PAUSE_CHANNEL _IOW ('M', 3, struct mlx_pause) #define MLX_COMMAND _IOWR('M', 4, struct mlx_usercommand) - +#define MLX_REBUILDASYNC _IOWR('M', 5, struct mlx_rebuild_request) +#define MLX_REBUILDSTAT _IOR ('M', 6, struct mlx_rebuild_status) +#define MLX_GET_SYSDRIVE _IOWR('M', 7, int) diff --git a/sys/dev/mlx/mlxreg.h b/sys/dev/mlx/mlxreg.h index c5382bf..382a2fd 100644 --- a/sys/dev/mlx/mlxreg.h +++ b/sys/dev/mlx/mlxreg.h @@ -48,6 +48,7 @@ #define MLX_CMD_STARTCHANNEL 0x12 #define MLX_CMD_READ_CONFIG 0x4e #define MLX_CMD_DIRECT_CDB 0x04 +#define MLX_CMD_DEVICE_STATE 0x50 #ifdef _KERNEL @@ -483,6 +484,16 @@ struct mlx_dcdb u_int8_t res1; } __attribute__ ((packed)); +struct mlx_bbtable_entry +{ + u_int32_t bbt_block_number; + u_int8_t bbt_extent; + u_int8_t res1; + u_int8_t bbt_entry_type; + u_int8_t bbt_system_drive:5; + u_int8_t res2:3; +} __attribute__ ((packed)); + #ifdef _KERNEL /* * Inlines to build various command structures diff --git a/sys/dev/mlx/mlxvar.h b/sys/dev/mlx/mlxvar.h index 7847ca2..cd0cd90 100644 --- a/sys/dev/mlx/mlxvar.h +++ b/sys/dev/mlx/mlxvar.h @@ -148,11 +148,13 @@ struct mlx_softc #define MLX_STATE_SUSPEND (1<<3) /* controller is suspended */ struct callout_handle mlx_timeout; /* periodic status monitor */ time_t mlx_lastpoll; /* last time_second we polled for status */ - int mlx_lastevent; /* sequence number of the last event we recorded */ + u_int16_t mlx_lastevent; /* sequence number of the last event we recorded */ int mlx_currevent; /* sequence number last time we looked */ - int mlx_rebuild; /* if >= 0, drive is being rebuilt */ - u_int32_t mlx_rebuildstat;/* blocks left to rebuild if active */ - int mlx_check; /* if >= 0, drive is being checked */ + int mlx_background; /* if != 0 rebuild or check is in progress */ +#define MLX_BACKGROUND_CHECK 1 /* we started a check */ +#define MLX_BACKGROUND_REBUILD 2 /* we started a rebuild */ +#define MLX_BACKGROUND_SPONTANEOUS 3 /* it just happened somehow */ + struct mlx_rebuild_status mlx_rebuildstat; /* last rebuild status */ struct mlx_pause mlx_pause; /* pending pause operation details */ int mlx_locks; /* reentrancy avoidance */ @@ -213,6 +215,7 @@ extern d_close_t mlx_close; extern d_ioctl_t mlx_ioctl; extern devclass_t mlx_devclass; +extern devclass_t mlxd_devclass; /* * Mylex System Disk driver @@ -220,6 +223,7 @@ extern devclass_t mlx_devclass; struct mlxd_softc { device_t mlxd_dev; + dev_t mlxd_dev_t; struct mlx_softc *mlxd_controller; struct mlx_sysdrive *mlxd_drive; struct disk mlxd_disk; |