summaryrefslogtreecommitdiffstats
path: root/sys/dev/mly/mly.c
diff options
context:
space:
mode:
authormsmith <msmith@FreeBSD.org>2001-02-25 22:48:34 +0000
committermsmith <msmith@FreeBSD.org>2001-02-25 22:48:34 +0000
commite5b2c99d71956a1a73efdfbefb3ed19bd7d00d3b (patch)
tree92c016de15d655b019e93776c4543e7d28f396b8 /sys/dev/mly/mly.c
parent4e1aac88bdd20610856392647e751f1cd18eef78 (diff)
downloadFreeBSD-src-e5b2c99d71956a1a73efdfbefb3ed19bd7d00d3b.zip
FreeBSD-src-e5b2c99d71956a1a73efdfbefb3ed19bd7d00d3b.tar.gz
Major update and bugfix for the 'mly' driver.
- Convert to a more efficient queueing implementation. - Don't allocate command buffers on the fly; simply work from a static pool. - Add a control device interface, for later use. - Handle controller overload better as a consequence of the improved queue implementation. - Add support for the XPT_GET_TRAN_SETTINGS ccb, and correctly set the virtual SCSI channels up for multiple outstanding I/Os. - Update copyrights for 2001. - Some whitespace fixes to improve readability. Due to a misunderstanding on my part, previous versions of the driver were limited to a single outstanding I/O per virtual drive. Needless to say, this update improves performance substantially.
Diffstat (limited to 'sys/dev/mly/mly.c')
-rw-r--r--sys/dev/mly/mly.c466
1 files changed, 309 insertions, 157 deletions
diff --git a/sys/dev/mly/mly.c b/sys/dev/mly/mly.c
index 71ad591..fd3d357 100644
--- a/sys/dev/mly/mly.c
+++ b/sys/dev/mly/mly.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2000 Michael Smith
+ * Copyright (c) 2000, 2001 Michael Smith
* Copyright (c) 2000 BSDi
* All rights reserved.
*
@@ -34,6 +34,8 @@
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/ctype.h>
+#include <sys/ioccom.h>
+#include <sys/stat.h>
#include <machine/bus_memio.h>
#include <machine/bus.h>
@@ -43,6 +45,7 @@
#include <cam/scsi/scsi_all.h>
#include <dev/mly/mlyreg.h>
+#include <dev/mly/mlyio.h>
#include <dev/mly/mlyvar.h>
#define MLY_DEFINE_TABLES
#include <dev/mly/mly_tables.h>
@@ -65,9 +68,8 @@ static int mly_immediate_command(struct mly_command *mc);
static int mly_start(struct mly_command *mc);
static void mly_complete(void *context, int pending);
-static int mly_get_slot(struct mly_command *mc);
-static void mly_alloc_command_cluster_map(void *arg, bus_dma_segment_t *segs, int nseg, int error);
-static void mly_alloc_command_cluster(struct mly_softc *sc);
+static void mly_alloc_commands_map(void *arg, bus_dma_segment_t *segs, int nseg, int error);
+static int mly_alloc_commands(struct mly_softc *sc);
static void mly_map_command(struct mly_command *mc);
static void mly_unmap_command(struct mly_command *mc);
@@ -80,6 +82,32 @@ static void mly_print_command(struct mly_command *mc);
static void mly_print_packet(struct mly_command *mc);
static void mly_panic(struct mly_softc *sc, char *reason);
#endif
+void mly_print_controller(int controller);
+
+static d_open_t mly_user_open;
+static d_close_t mly_user_close;
+static d_ioctl_t mly_user_ioctl;
+static int mly_user_command(struct mly_softc *sc, struct mly_user_command *uc);
+static int mly_user_health(struct mly_softc *sc, struct mly_user_health *uh);
+
+#define MLY_CDEV_MAJOR 158
+
+static struct cdevsw mly_cdevsw = {
+ mly_user_open,
+ mly_user_close,
+ noread,
+ nowrite,
+ mly_user_ioctl,
+ nopoll,
+ nommap,
+ nostrategy,
+ "mly",
+ MLY_CDEV_MAJOR,
+ nodump,
+ nopsize,
+ 0,
+ -1
+};
/********************************************************************************
********************************************************************************
@@ -100,10 +128,10 @@ mly_attach(struct mly_softc *sc)
/*
* Initialise per-controller queues.
*/
- TAILQ_INIT(&sc->mly_freecmds);
- TAILQ_INIT(&sc->mly_ready);
- TAILQ_INIT(&sc->mly_completed);
- TAILQ_INIT(&sc->mly_clusters);
+ mly_initq_free(sc);
+ mly_initq_ready(sc);
+ mly_initq_busy(sc);
+ mly_initq_complete(sc);
#if __FreeBSD_version >= 500005
/*
@@ -124,10 +152,10 @@ mly_attach(struct mly_softc *sc)
return(error);
/*
- * Initialise the slot allocator so that we can issue commands.
+ * Allocate command buffers
*/
- sc->mly_max_commands = MLY_SLOT_MAX;
- sc->mly_last_slot = MLY_SLOT_START;
+ if ((error = mly_alloc_commands(sc)))
+ return(error);
/*
* Obtain controller feature information
@@ -136,11 +164,6 @@ mly_attach(struct mly_softc *sc)
return(error);
/*
- * Update the slot allocator limit based on the controller inquiry.
- */
- sc->mly_max_commands = imin(sc->mly_controllerinfo->maximum_parallel_commands, MLY_SLOT_MAX);
-
- /*
* Get the current event counter for health purposes, populate the initial
* health status buffer.
*/
@@ -177,6 +200,13 @@ mly_attach(struct mly_softc *sc)
*/
mly_periodic((void *)sc);
+ /*
+ * Create the control device.
+ */
+ sc->mly_dev_t = make_dev(&mly_cdevsw, device_get_unit(sc->mly_dev), UID_ROOT, GID_OPERATOR,
+ S_IRUSR | S_IWUSR, "mly%d", device_get_unit(sc->mly_dev));
+ sc->mly_dev_t->si_drv1 = sc;
+
/* enable interrupts now */
MLY_UNMASK_INTERRUPTS(sc);
@@ -370,6 +400,8 @@ mly_complete_rescan(struct mly_command *mc)
sc->mly_btl[bus][target].mb_flags = MLY_BTL_PHYSICAL; /* clears all other flags */
sc->mly_btl[bus][target].mb_type = MLY_DEVICE_TYPE_PHYSICAL;
sc->mly_btl[bus][target].mb_state = pdi->state;
+ sc->mly_btl[bus][target].mb_speed = pdi->speed;
+ sc->mly_btl[bus][target].mb_width = pdi->width;
if (pdi->state != MLY_DEVICE_STATE_UNCONFIGURED)
sc->mly_btl[bus][target].mb_flags |= MLY_BTL_PROTECTED;
debug(2, "BTL rescan for %d:%d returns %s", bus, target,
@@ -438,9 +470,12 @@ mly_enable_mmbox(struct mly_softc *sc)
bzero(&mci, sizeof(mci));
mci.sub_ioctl = MDACIOCTL_SETMEMORYMAILBOX;
/* set buffer addresses */
- mci.param.setmemorymailbox.command_mailbox_physaddr = sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_command);
- mci.param.setmemorymailbox.status_mailbox_physaddr = sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_status);
- mci.param.setmemorymailbox.health_buffer_physaddr = sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_health);
+ mci.param.setmemorymailbox.command_mailbox_physaddr =
+ sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_command);
+ mci.param.setmemorymailbox.status_mailbox_physaddr =
+ sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_status);
+ mci.param.setmemorymailbox.health_buffer_physaddr =
+ sc->mly_mmbox_busaddr + offsetof(struct mly_mmbox, mmm_health);
/* set buffer sizes - abuse of data_size field is revolting */
sp = (u_int8_t *)&mci.data_size;
@@ -451,7 +486,8 @@ mly_enable_mmbox(struct mly_softc *sc)
debug(1, "memory mailbox at %p (0x%llx/%d 0x%llx/%d 0x%llx/%d", sc->mly_mmbox,
mci.param.setmemorymailbox.command_mailbox_physaddr, sp[0],
mci.param.setmemorymailbox.status_mailbox_physaddr, sp[1],
- mci.param.setmemorymailbox.health_buffer_physaddr, mci.param.setmemorymailbox.health_buffer_size);
+ mci.param.setmemorymailbox.health_buffer_physaddr,
+ mci.param.setmemorymailbox.health_buffer_size);
if ((error = mly_ioctl(sc, &mci, NULL, 0, &status, NULL, NULL)))
return(error);
@@ -806,13 +842,14 @@ mly_immediate_command(struct mly_command *mc)
if (sc->mly_state & MLY_STATE_INTERRUPTS_ON) {
/* sleep on the command */
- while(MLY_CMD_STATE(mc) != MLY_CMD_COMPLETE) {
+ while(!(mc->mc_flags & MLY_CMD_COMPLETE)) {
tsleep(mc, PRIBIO, "mlywait", 0);
}
} else {
/* spin and collect status while we do */
- while(MLY_CMD_STATE(mc) != MLY_CMD_COMPLETE)
+ while(!(mc->mc_flags & MLY_CMD_COMPLETE)) {
mly_done(mc->mc_sc);
+ }
}
splx(s);
return(0);
@@ -864,14 +901,13 @@ mly_start(struct mly_command *mc)
debug_called(2);
/*
- * Set the command up for delivery to the controller. This may fail
- * due to resource shortages.
+ * Set the command up for delivery to the controller.
*/
- if (mly_get_slot(mc))
- return(EBUSY);
mly_map_command(mc);
+ mc->mc_packet->generic.command_id = mc->mc_slot;
s = splcam();
+
/*
* Do we have to use the hardware mailbox?
*/
@@ -883,7 +919,8 @@ mly_start(struct mly_command *mc)
splx(s);
return(EBUSY);
}
-
+ mc->mc_flags |= MLY_CMD_BUSY;
+
/*
* It's ready, send the command.
*/
@@ -894,11 +931,12 @@ mly_start(struct mly_command *mc)
pkt = &sc->mly_mmbox->mmm_command[sc->mly_mmbox_command_index];
- /* check to see if the next slot is free yet */
+ /* check to see if the next index is free yet */
if (pkt->mmbox.flag != 0) {
splx(s);
return(EBUSY);
}
+ mc->mc_flags |= MLY_CMD_BUSY;
/* copy in new command */
bcopy(mc->mc_packet->mmbox.data, pkt->mmbox.data, sizeof(pkt->mmbox.data));
@@ -914,6 +952,7 @@ mly_start(struct mly_command *mc)
sc->mly_mmbox_command_index = (sc->mly_mmbox_command_index + 1) % MLY_MMBOX_COMMANDS;
}
+ mly_enqueue_busy(mc);
splx(s);
return(0);
}
@@ -936,17 +975,14 @@ mly_done(struct mly_softc *sc)
if (MLY_ODBR_TRUE(sc, MLY_HM_STSREADY)) {
slot = MLY_GET_REG2(sc, sc->mly_status_mailbox);
if (slot < MLY_SLOT_MAX) {
- mc = sc->mly_busycmds[slot];
- if (mc != NULL) {
- mc->mc_status = MLY_GET_REG(sc, sc->mly_status_mailbox + 2);
- mc->mc_sense = MLY_GET_REG(sc, sc->mly_status_mailbox + 3);
- mc->mc_resid = MLY_GET_REG4(sc, sc->mly_status_mailbox + 4);
- mly_enqueue_completed(mc);
- sc->mly_busycmds[slot] = NULL;
- worked = 1;
- } else {
- mly_printf(sc, "got HM completion for nonbusy slot %u\n", slot);
- }
+ mc = &sc->mly_command[slot - MLY_SLOT_START];
+ mc->mc_status = MLY_GET_REG(sc, sc->mly_status_mailbox + 2);
+ mc->mc_sense = MLY_GET_REG(sc, sc->mly_status_mailbox + 3);
+ mc->mc_resid = MLY_GET_REG4(sc, sc->mly_status_mailbox + 4);
+ mly_remove_busy(mc);
+ mc->mc_flags &= ~MLY_CMD_BUSY;
+ mly_enqueue_complete(mc);
+ worked = 1;
} else {
/* slot 0xffff may mean "extremely bogus command" */
mly_printf(sc, "got HM completion for illegal slot %u\n", slot);
@@ -968,23 +1004,21 @@ mly_done(struct mly_softc *sc)
/* get slot number */
slot = sp->status.command_id;
if (slot < MLY_SLOT_MAX) {
- mc = sc->mly_busycmds[slot];
- if (mc != NULL) {
- mc->mc_status = sp->status.status;
- mc->mc_sense = sp->status.sense_length;
- mc->mc_resid = sp->status.residue;
- mly_enqueue_completed(mc);
- sc->mly_busycmds[slot] = NULL;
- worked = 1;
- } else {
- mly_printf(sc, "got AM completion for nonbusy slot %u\n", slot);
- }
+ mc = &sc->mly_command[slot - MLY_SLOT_START];
+ mc->mc_status = sp->status.status;
+ mc->mc_sense = sp->status.sense_length;
+ mc->mc_resid = sp->status.residue;
+ mly_remove_busy(mc);
+ mc->mc_flags &= ~MLY_CMD_BUSY;
+ mly_enqueue_complete(mc);
+ worked = 1;
} else {
/* slot 0xffff may mean "extremely bogus command" */
- mly_printf(sc, "got AM completion for illegal slot %u at %d\n", slot, sc->mly_mmbox_status_index);
+ mly_printf(sc, "got AM completion for illegal slot %u at %d\n",
+ slot, sc->mly_mmbox_status_index);
}
- /* clear and move to next slot */
+ /* clear and move to next index */
sp->mmbox.flag = 0;
sc->mly_mmbox_status_index = (sc->mly_mmbox_status_index + 1) % MLY_MMBOX_STATUS;
}
@@ -1019,7 +1053,7 @@ mly_complete(void *context, int pending)
/*
* Spin pulling commands off the completed queue and processing them.
*/
- while ((mc = mly_dequeue_completed(sc)) != NULL) {
+ while ((mc = mly_dequeue_complete(sc)) != NULL) {
/*
* Free controller resources, mark command complete.
@@ -1031,7 +1065,7 @@ mly_complete(void *context, int pending)
*/
mly_unmap_command(mc);
mc_complete = mc->mc_complete;
- MLY_CMD_SETSTATE(mc, MLY_CMD_COMPLETE);
+ mc->mc_flags |= MLY_CMD_COMPLETE;
/*
* Call completion handler or wake up sleeping consumer.
@@ -1063,6 +1097,9 @@ mly_complete(void *context, int pending)
debug(1, "event change %d, event status update, %d -> %d", sc->mly_event_change,
sc->mly_event_waiting, sc->mly_mmbox->mmm_health.status.next_event);
sc->mly_event_waiting = sc->mly_mmbox->mmm_health.status.next_event;
+
+ /* wake up anyone that might be interested in this */
+ wakeup(&sc->mly_event_change);
}
if (sc->mly_event_counter != sc->mly_event_waiting)
mly_fetch_event(sc);
@@ -1075,54 +1112,6 @@ mly_complete(void *context, int pending)
********************************************************************************/
/********************************************************************************
- * Give a command a slot in our lookup table, so that we can recover it when
- * the controller returns the slot number.
- *
- * Slots are freed in mly_done().
- */
-static int
-mly_get_slot(struct mly_command *mc)
-{
- struct mly_softc *sc = mc->mc_sc;
- u_int16_t slot;
- int tries;
-
- debug_called(3);
-
- if (mc->mc_flags & MLY_CMD_SLOTTED)
- return(0);
-
- /*
- * Optimisation for the controller-busy case - check to see whether
- * we are already over the limit and stop immediately.
- */
- if (sc->mly_busy_count >= sc->mly_max_commands)
- return(EBUSY);
-
- /*
- * Scan forward from the last slot that we assigned looking for a free
- * slot. Don't scan more than the maximum number of commands that we
- * support (we should never reach the limit here due to the optimisation
- * above)
- */
- slot = sc->mly_last_slot;
- for (tries = sc->mly_max_commands; tries > 0; tries--) {
- if (sc->mly_busycmds[slot] == NULL) {
- sc->mly_busycmds[slot] = mc;
- mc->mc_slot = slot;
- mc->mc_packet->generic.command_id = slot;
- mc->mc_flags |= MLY_CMD_SLOTTED;
- sc->mly_last_slot = slot;
- return(0);
- }
- slot++;
- if (slot >= MLY_SLOT_MAX)
- slot = MLY_SLOT_START;
- }
- return(EBUSY);
-}
-
-/********************************************************************************
* Allocate a command.
*/
int
@@ -1132,17 +1121,9 @@ mly_alloc_command(struct mly_softc *sc, struct mly_command **mcp)
debug_called(3);
- if ((mc = mly_dequeue_free(sc)) == NULL) {
- mly_alloc_command_cluster(sc);
- mc = mly_dequeue_free(sc);
- }
- if (mc != NULL)
- TAILQ_REMOVE(&sc->mly_freecmds, mc, mc_link);
-
- if (mc == NULL)
+ if ((mc = mly_dequeue_free(sc)) == NULL)
return(ENOMEM);
- MLY_CMD_SETSTATE(mc, MLY_CMD_SETUP);
*mcp = mc;
return(0);
}
@@ -1159,7 +1140,6 @@ mly_release_command(struct mly_command *mc)
* Fill in parts of the command that may cause confusion if
* a consumer doesn't when we are later allocated.
*/
- MLY_CMD_SETSTATE(mc, MLY_CMD_FREE);
mc->mc_data = NULL;
mc->mc_flags = 0;
mc->mc_complete = NULL;
@@ -1176,66 +1156,55 @@ mly_release_command(struct mly_command *mc)
}
/********************************************************************************
- * Map helper for command cluster allocation.
- *
- * Note that there are never more command packets in a cluster than will fit in
- * a page, so there is no need to look at anything other than the base of the
- * allocation (which will be page-aligned).
+ * Map helper for command allocation.
*/
static void
-mly_alloc_command_cluster_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
+mly_alloc_commands_map(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
- struct mly_command_cluster *mcc = (struct mly_command_cluster *)arg;
+ struct mly_softc *sc = (struct mly_softc *)arg
debug_called(2);
- mcc->mcc_packetphys = segs[0].ds_addr;
+ sc->mly_packetphys = segs[0].ds_addr;
}
/********************************************************************************
- * Allocate and initialise a cluster of commands.
+ * Allocate and initialise command and packet structures.
*/
-static void
-mly_alloc_command_cluster(struct mly_softc *sc)
+static int
+mly_alloc_commands(struct mly_softc *sc)
{
- struct mly_command_cluster *mcc;
struct mly_command *mc;
int i;
- debug_called(1);
-
- mcc = malloc(sizeof(struct mly_command_cluster), M_DEVBUF, M_NOWAIT);
- if (mcc != NULL) {
-
- /*
- * Allocate enough space for all the command packets for this cluster and
- * map them permanently into controller-visible space.
- */
- if (bus_dmamem_alloc(sc->mly_packet_dmat, (void **)&mcc->mcc_packet,
- BUS_DMA_NOWAIT, &mcc->mcc_packetmap)) {
- free(mcc, M_DEVBUF);
- return;
- }
- bus_dmamap_load(sc->mly_packet_dmat, mcc->mcc_packetmap, mcc->mcc_packet,
- MLY_CMD_CLUSTERCOUNT * sizeof(union mly_command_packet),
- mly_alloc_command_cluster_map, mcc, 0);
-
- mly_enqueue_cluster(sc, mcc);
- for (i = 0; i < MLY_CMD_CLUSTERCOUNT; i++) {
- mc = &mcc->mcc_command[i];
- bzero(mc, sizeof(*mc));
- mc->mc_sc = sc;
- mc->mc_packet = mcc->mcc_packet + i;
- mc->mc_packetphys = mcc->mcc_packetphys + (i * sizeof(union mly_command_packet));
- if (!bus_dmamap_create(sc->mly_buffer_dmat, 0, &mc->mc_datamap))
- mly_release_command(mc);
- }
+ /*
+ * Allocate enough space for all the command packets in one chunk and
+ * map them permanently into controller-visible space.
+ */
+ if (bus_dmamem_alloc(sc->mly_packet_dmat, (void **)&sc->mly_packet,
+ BUS_DMA_NOWAIT, &sc->mly_packetmap)) {
+ return(ENOMEM);
}
+ bus_dmamap_load(sc->mly_packet_dmat, sc->mly_packetmap, sc->mly_packet,
+ MLY_MAXCOMMANDS * sizeof(union mly_command_packet),
+ mly_alloc_commands_map, sc, 0);
+
+ for (i = 0; i < MLY_MAXCOMMANDS; i++) {
+ mc = &sc->mly_command[i];
+ bzero(mc, sizeof(*mc));
+ mc->mc_sc = sc;
+ mc->mc_slot = MLY_SLOT_START + i;
+ mc->mc_packet = sc->mly_packet + i;
+ mc->mc_packetphys = sc->mly_packetphys + (i * sizeof(union mly_command_packet));
+ if (!bus_dmamap_create(sc->mly_buffer_dmat, 0, &mc->mc_datamap))
+ mly_release_command(mc);
+ }
+ return(0);
}
/********************************************************************************
- * Command-mapping helper function - populate this command slot's s/g table
- * with the s/g entries for this command.
+ * Command-mapping helper function - populate this command's s/g table
+ * with the s/g entries for its data.
*/
static void
mly_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
@@ -1253,7 +1222,7 @@ mly_map_command_sg(void *arg, bus_dma_segment_t *segs, int nseg, int error)
sg = &gen->transfer.direct.sg[0];
gen->command_control.extended_sg_table = 0;
} else {
- tabofs = (mc->mc_slot * MLY_MAXSGENTRIES);
+ tabofs = ((mc->mc_slot - MLY_SLOT_START) * MLY_MAXSGENTRIES);
sg = sc->mly_sg_table + tabofs;
gen->transfer.indirect.entries[0] = nseg;
gen->transfer.indirect.table_physaddr[0] = sc->mly_sg_busaddr + (tabofs * sizeof(struct mly_sg_entry));
@@ -1520,7 +1489,6 @@ mly_print_command(struct mly_command *mc)
mly_printf(sc, "COMMAND @ %p\n", mc);
mly_printf(sc, " slot %d\n", mc->mc_slot);
- mly_printf(sc, " state %d\n", MLY_CMD_STATE(mc));
mly_printf(sc, " status 0x%x\n", mc->mc_status);
mly_printf(sc, " sense len %d\n", mc->mc_sense);
mly_printf(sc, " resid %d\n", mc->mc_resid);
@@ -1528,7 +1496,7 @@ mly_print_command(struct mly_command *mc)
if (mc->mc_packet != NULL)
mly_print_packet(mc);
mly_printf(sc, " data %p/%d\n", mc->mc_data, mc->mc_length);
- mly_printf(sc, " flags %b\n", mc->mc_flags, "\20\11slotted\12mapped\13priority\14datain\15dataout\n");
+ mly_printf(sc, " flags %b\n", mc->mc_flags, "\20\1busy\2complete\3slotted\4mapped\5datain\6dataout\n");
mly_printf(sc, " complete %p\n", mc->mc_complete);
mly_printf(sc, " private %p\n", mc->mc_private);
}
@@ -1706,3 +1674,187 @@ mly_panic(struct mly_softc *sc, char *reason)
panic(reason);
}
#endif
+
+/********************************************************************************
+ * Print queue statistics, callable from DDB.
+ */
+void
+mly_print_controller(int controller)
+{
+ struct mly_softc *sc;
+
+ if ((sc = devclass_get_softc(devclass_find("mly"), controller)) == NULL) {
+ printf("mly: controller %d invalid\n", controller);
+ } else {
+ device_printf(sc->mly_dev, "queue curr max\n");
+ device_printf(sc->mly_dev, "free %04d/%04d\n",
+ sc->mly_qstat[MLYQ_FREE].q_length, sc->mly_qstat[MLYQ_FREE].q_max);
+ device_printf(sc->mly_dev, "ready %04d/%04d\n",
+ sc->mly_qstat[MLYQ_READY].q_length, sc->mly_qstat[MLYQ_READY].q_max);
+ device_printf(sc->mly_dev, "busy %04d/%04d\n",
+ sc->mly_qstat[MLYQ_BUSY].q_length, sc->mly_qstat[MLYQ_BUSY].q_max);
+ device_printf(sc->mly_dev, "complete %04d/%04d\n",
+ sc->mly_qstat[MLYQ_COMPLETE].q_length, sc->mly_qstat[MLYQ_COMPLETE].q_max);
+ }
+}
+
+
+/********************************************************************************
+ ********************************************************************************
+ Control device interface
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Accept an open operation on the control device.
+ */
+static int
+mly_user_open(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int unit = minor(dev);
+ struct mly_softc *sc = devclass_get_softc(devclass_find("mly"), unit);
+
+ sc->mly_state |= MLY_STATE_OPEN;
+ return(0);
+}
+
+/********************************************************************************
+ * Accept the last close on the control device.
+ */
+static int
+mly_user_close(dev_t dev, int flags, int fmt, struct proc *p)
+{
+ int unit = minor(dev);
+ struct mly_softc *sc = devclass_get_softc(devclass_find("mly"), unit);
+
+ sc->mly_state &= ~MLY_STATE_OPEN;
+ return (0);
+}
+
+/********************************************************************************
+ * Handle controller-specific control operations.
+ */
+static int
+mly_user_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
+{
+ struct mly_softc *sc = (struct mly_softc *)dev->si_drv1;
+ struct mly_user_command *uc = (struct mly_user_command *)addr;
+ struct mly_user_health *uh = (struct mly_user_health *)addr;
+
+ switch(cmd) {
+ case MLYIO_COMMAND:
+ return(mly_user_command(sc, uc));
+ case MLYIO_HEALTH:
+ return(mly_user_health(sc, uh));
+ default:
+ return(ENOIOCTL);
+ }
+}
+
+/********************************************************************************
+ * Execute a command passed in from userspace.
+ *
+ * The control structure contains the actual command for the controller, as well
+ * as the user-space data pointer and data size, and an optional sense buffer
+ * size/pointer. On completion, the data size is adjusted to the command
+ * residual, and the sense buffer size to the size of the returned sense data.
+ *
+ */
+static int
+mly_user_command(struct mly_softc *sc, struct mly_user_command *uc)
+{
+ struct mly_command *mc;
+ int error, s;
+
+ /* allocate a command */
+ if (mly_alloc_command(sc, &mc)) {
+ error = ENOMEM;
+ goto out; /* XXX Linux version will wait for a command */
+ }
+
+ /* handle data size/direction */
+ mc->mc_length = (uc->DataTransferLength >= 0) ? uc->DataTransferLength : -uc->DataTransferLength;
+ if (mc->mc_length > 0) {
+ if ((mc->mc_data = malloc(mc->mc_length, M_DEVBUF, M_NOWAIT)) == NULL) {
+ error = ENOMEM;
+ goto out;
+ }
+ }
+ if (uc->DataTransferLength > 0) {
+ mc->mc_flags |= MLY_CMD_DATAIN;
+ bzero(mc->mc_data, mc->mc_length);
+ }
+ if (uc->DataTransferLength < 0) {
+ mc->mc_flags |= MLY_CMD_DATAOUT;
+ if ((error = copyin(uc->DataTransferBuffer, mc->mc_data, mc->mc_length)) != 0)
+ goto out;
+ }
+
+ /* copy the controller command */
+ bcopy(&uc->CommandMailbox, mc->mc_packet, sizeof(uc->CommandMailbox));
+
+ /* clear command completion handler so that we get woken up */
+ mc->mc_complete = NULL;
+
+ /* execute the command */
+ s = splcam();
+ mly_requeue_ready(mc);
+ mly_startio(sc);
+ while (!(mc->mc_flags & MLY_CMD_COMPLETE))
+ tsleep(mc, PRIBIO, "mlyioctl", 0);
+ splx(s);
+
+ /* return the data to userspace */
+ if (uc->DataTransferLength > 0)
+ if ((error = copyout(mc->mc_data, uc->DataTransferBuffer, mc->mc_length)) != 0)
+ goto out;
+
+ /* return the sense buffer to userspace */
+ if ((uc->RequestSenseLength > 0) && (mc->mc_sense > 0)) {
+ if ((error = copyout(mc->mc_packet, uc->RequestSenseBuffer,
+ min(uc->RequestSenseLength, mc->mc_sense))) != 0)
+ goto out;
+ }
+
+ /* return command results to userspace (caller will copy out) */
+ uc->DataTransferLength = mc->mc_resid;
+ uc->RequestSenseLength = min(uc->RequestSenseLength, mc->mc_sense);
+ uc->CommandStatus = mc->mc_status;
+ error = 0;
+
+ out:
+ if (mc->mc_data != NULL)
+ free(mc->mc_data, M_DEVBUF);
+ if (mc != NULL)
+ mly_release_command(mc);
+ return(error);
+}
+
+/********************************************************************************
+ * Return health status to userspace. If the health change index in the user
+ * structure does not match that currently exported by the controller, we
+ * return the current status immediately. Otherwise, we block until either
+ * interrupted or new status is delivered.
+ */
+static int
+mly_user_health(struct mly_softc *sc, struct mly_user_health *uh)
+{
+ struct mly_health_status mh;
+ int error, s;
+
+ /* fetch the current health status from userspace */
+ if ((error = copyin(uh->HealthStatusBuffer, &mh, sizeof(mh))) != 0)
+ return(error);
+
+ /* spin waiting for a status update */
+ s = splcam();
+ error = EWOULDBLOCK;
+ while ((error != 0) && (sc->mly_event_change == mh.change_counter))
+ error = tsleep(&sc->mly_event_change, PRIBIO | PCATCH, "mlyhealth", 0);
+ splx(s);
+
+ /* copy the controller's health status buffer out (there is a race here if it changes again) */
+ error = copyout(&sc->mly_mmbox->mmm_health.status, uh->HealthStatusBuffer,
+ sizeof(uh->HealthStatusBuffer));
+ return(error);
+}
OpenPOWER on IntegriCloud