Deleted Added
full compact
2c2
< * Copyright (c) 2000 Michael Smith
---
> * Copyright (c) 2000, 2001 Michael Smith
27c27
< * $FreeBSD: head/sys/dev/mly/mly.c 68877 2000-11-18 15:21:22Z dwmalone $
---
> * $FreeBSD: head/sys/dev/mly/mly.c 73050 2001-02-25 22:48:34Z msmith $
36a37,38
> #include <sys/ioccom.h>
> #include <sys/stat.h>
45a48
> #include <dev/mly/mlyio.h>
68,70c71,72
< 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);
82a85
> void mly_print_controller(int controller);
83a87,111
> 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
> };
>
103,106c131,134
< 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);
127c155
< * Initialise the slot allocator so that we can issue commands.
---
> * Allocate command buffers
129,130c157,158
< sc->mly_max_commands = MLY_SLOT_MAX;
< sc->mly_last_slot = MLY_SLOT_START;
---
> if ((error = mly_alloc_commands(sc)))
> return(error);
139,143d166
< * Update the slot allocator limit based on the controller inquiry.
< */
< sc->mly_max_commands = imin(sc->mly_controllerinfo->maximum_parallel_commands, MLY_SLOT_MAX);
<
< /*
179a203,209
> /*
> * 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;
>
372a403,404
> sc->mly_btl[bus][target].mb_speed = pdi->speed;
> sc->mly_btl[bus][target].mb_width = pdi->width;
441,443c473,478
< 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);
454c489,490
< mci.param.setmemorymailbox.health_buffer_physaddr, mci.param.setmemorymailbox.health_buffer_size);
---
> mci.param.setmemorymailbox.health_buffer_physaddr,
> mci.param.setmemorymailbox.health_buffer_size);
809c845
< while(MLY_CMD_STATE(mc) != MLY_CMD_COMPLETE) {
---
> while(!(mc->mc_flags & MLY_CMD_COMPLETE)) {
814c850
< while(MLY_CMD_STATE(mc) != MLY_CMD_COMPLETE)
---
> while(!(mc->mc_flags & MLY_CMD_COMPLETE)) {
815a852
> }
867,868c904
< * 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.
870,871d905
< if (mly_get_slot(mc))
< return(EBUSY);
872a907
> mc->mc_packet->generic.command_id = mc->mc_slot;
874a910
>
886c922,923
<
---
> mc->mc_flags |= MLY_CMD_BUSY;
>
897c934
< /* check to see if the next slot is free yet */
---
> /* check to see if the next index is free yet */
901a939
> mc->mc_flags |= MLY_CMD_BUSY;
916a955
> mly_enqueue_busy(mc);
939,949c978,985
< 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;
971,981c1007,1014
< 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;
984c1017,1018
< 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);
987c1021
< /* clear and move to next slot */
---
> /* clear and move to next index */
1022c1056
< while ((mc = mly_dequeue_completed(sc)) != NULL) {
---
> while ((mc = mly_dequeue_complete(sc)) != NULL) {
1034c1068
< MLY_CMD_SETSTATE(mc, MLY_CMD_COMPLETE);
---
> mc->mc_flags |= MLY_CMD_COMPLETE;
1065a1100,1102
>
> /* wake up anyone that might be interested in this */
> wakeup(&sc->mly_event_change);
1078,1125d1114
< * 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);
< }
<
< /********************************************************************************
1135,1142c1124
< 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)
1145d1126
< MLY_CMD_SETSTATE(mc, MLY_CMD_SETUP);
1162d1142
< MLY_CMD_SETSTATE(mc, MLY_CMD_FREE);
1179,1183c1159
< * 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.
1186c1162
< 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)
1188c1164
< struct mly_command_cluster *mcc = (struct mly_command_cluster *)arg;
---
> struct mly_softc *sc = (struct mly_softc *)arg
1192c1168
< mcc->mcc_packetphys = segs[0].ds_addr;
---
> sc->mly_packetphys = segs[0].ds_addr;
1196c1172
< * Allocate and initialise a cluster of commands.
---
> * Allocate and initialise command and packet structures.
1198,1199c1174,1175
< static void
< mly_alloc_command_cluster(struct mly_softc *sc)
---
> static int
> mly_alloc_commands(struct mly_softc *sc)
1201d1176
< struct mly_command_cluster *mcc;
1205c1180,1190
< debug_called(1);
---
> /*
> * 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);
1207,1232c1192,1200
< 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);
< }
---
> 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);
1233a1202
> return(0);
1237,1238c1206,1207
< * 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.
1256c1225
< tabofs = (mc->mc_slot * MLY_MAXSGENTRIES);
---
> tabofs = ((mc->mc_slot - MLY_SLOT_START) * MLY_MAXSGENTRIES);
1523d1491
< mly_printf(sc, " state %d\n", MLY_CMD_STATE(mc));
1531c1499
< 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");
1708a1677,1860
>
> /********************************************************************************
> * 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);
> }