Date: Mon, 17 Sep 2012 15:27:31 +0000 (UTC) From: John Baldwin <jhb@FreeBSD.org> To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r240608 - head/sys/dev/mlx Message-ID: <201209171527.q8HFRVjF043807@svn.freebsd.org>
next in thread | raw e-mail | index | archive | help
Author: jhb Date: Mon Sep 17 15:27:30 2012 New Revision: 240608 URL: http://svn.freebsd.org/changeset/base/240608 Log: Add locking to mlx(4) to make it MPSAFE along with some other fixes: - Use callout(9) rather than timeout(9). - Add a mutex as an I/O lock that protects the adapter and is used for the I/O path. - Add an sx lock as a configuration lock that protects the relationship of configured volumes. - Freeze the request queue when a DMA load is deferred with EINPROGRESS and unfreeze the queue when the DMA callback is invoked. - Explicitly poll the hardware while waiting to submit a command to allow completed commands to free up slots in the command ring. - Remove driver-wide 'initted' variable from mlx_*_fw_handshake() routines. That state should be per-controller instead. Add it as an argument since the first caller knows when it is the first caller. - Remove explicit bus_space tag/handle and use bus_*() rather than bus_space_*(). - Move duplicated PCI device ID probing into a mlx_pci_match() routine. - Don't check for PCIM_CMD_MEMEN (the PCI bus will enable that when allocating the resource) and use pci_enable_busmaster() rather than manipulating the register directly. Tested by: no one despite multiple requests (hope it works) Modified: head/sys/dev/mlx/mlx.c head/sys/dev/mlx/mlx_disk.c head/sys/dev/mlx/mlx_pci.c head/sys/dev/mlx/mlxreg.h head/sys/dev/mlx/mlxvar.h Modified: head/sys/dev/mlx/mlx.c ============================================================================== --- head/sys/dev/mlx/mlx.c Mon Sep 17 14:54:58 2012 (r240607) +++ head/sys/dev/mlx/mlx.c Mon Sep 17 15:27:30 2012 (r240608) @@ -32,8 +32,11 @@ #include <sys/param.h> #include <sys/systm.h> +#include <sys/lock.h> #include <sys/malloc.h> +#include <sys/mutex.h> #include <sys/kernel.h> +#include <sys/sx.h> #include <sys/bus.h> #include <sys/conf.h> @@ -53,7 +56,6 @@ static struct cdevsw mlx_cdevsw = { .d_version = D_VERSION, - .d_flags = D_NEEDGIANT, .d_open = mlx_open, .d_close = mlx_close, .d_ioctl = mlx_ioctl, @@ -68,17 +70,17 @@ devclass_t mlx_devclass; static int mlx_v3_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); static int mlx_v3_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); static void mlx_v3_intaction(struct mlx_softc *sc, int action); -static int mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); +static int mlx_v3_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first); static int mlx_v4_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); static int mlx_v4_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); static void mlx_v4_intaction(struct mlx_softc *sc, int action); -static int mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); +static int mlx_v4_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first); static int mlx_v5_tryqueue(struct mlx_softc *sc, struct mlx_command *mc); static int mlx_v5_findcomplete(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status); static void mlx_v5_intaction(struct mlx_softc *sc, int action); -static int mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2); +static int mlx_v5_fw_handshake(struct mlx_softc *sc, int *error, int *param1, int *param2, int first); /* * Status monitoring @@ -130,8 +132,9 @@ static void mlx_setup_dmamap(struct ml bus_dma_segment_t *segs, int nsegments, int error); static void mlx_unmapcmd(struct mlx_command *mc); +static int mlx_shutdown_locked(struct mlx_softc *sc); static int mlx_start(struct mlx_command *mc); -static int mlx_done(struct mlx_softc *sc); +static int mlx_done(struct mlx_softc *sc, int startio); static void mlx_complete(struct mlx_softc *sc); /* @@ -164,14 +167,24 @@ mlx_free(struct mlx_softc *sc) debug_called(1); + /* destroy control device */ + if (sc->mlx_dev_t != NULL) + destroy_dev(sc->mlx_dev_t); + + if (sc->mlx_intr) + bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr); + /* cancel status timeout */ - untimeout(mlx_periodic, sc, sc->mlx_timeout); + MLX_IO_LOCK(sc); + callout_stop(&sc->mlx_timeout); /* throw away any command buffers */ while ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) { TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); mlx_freecmd(mc); } + MLX_IO_UNLOCK(sc); + callout_drain(&sc->mlx_timeout); /* destroy data-transfer DMA tag */ if (sc->mlx_buffer_dmat) @@ -184,8 +197,6 @@ mlx_free(struct mlx_softc *sc) bus_dma_tag_destroy(sc->mlx_sg_dmat); /* disconnect the interrupt handler */ - if (sc->mlx_intr) - bus_teardown_intr(sc->mlx_dev, sc->mlx_irq, sc->mlx_intr); if (sc->mlx_irq != NULL) bus_release_resource(sc->mlx_dev, SYS_RES_IRQ, 0, sc->mlx_irq); @@ -201,9 +212,8 @@ mlx_free(struct mlx_softc *sc) if (sc->mlx_enq2 != NULL) free(sc->mlx_enq2, M_DEVBUF); - /* destroy control device */ - if (sc->mlx_dev_t != (struct cdev *)NULL) - destroy_dev(sc->mlx_dev_t); + sx_destroy(&sc->mlx_config_lock); + mtx_destroy(&sc->mlx_io_lock); } /******************************************************************************** @@ -327,7 +337,9 @@ mlx_attach(struct mlx_softc *sc) } /* disable interrupts before we start talking to the controller */ + MLX_IO_LOCK(sc); sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); + MLX_IO_UNLOCK(sc); /* * Wait for the controller to come ready, handshake with the firmware if required. @@ -336,7 +348,8 @@ mlx_attach(struct mlx_softc *sc) */ hsmsg = 0; DELAY(1000); - while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2)) != 0) { + while ((hscode = sc->mlx_fw_handshake(sc, &hserror, &hsparam1, &hsparam2, + hsmsg == 0)) != 0) { /* report first time around... */ if (hsmsg == 0) { device_printf(sc->mlx_dev, "controller initialisation in progress...\n"); @@ -364,7 +377,8 @@ mlx_attach(struct mlx_softc *sc) device_printf(sc->mlx_dev, "can't allocate interrupt\n"); return(ENXIO); } - error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO | INTR_ENTROPY, NULL, mlx_intr, sc, &sc->mlx_intr); + error = bus_setup_intr(sc->mlx_dev, sc->mlx_irq, INTR_TYPE_BIO | + INTR_ENTROPY | INTR_MPSAFE, NULL, mlx_intr, sc, &sc->mlx_intr); if (error) { device_printf(sc->mlx_dev, "can't set up interrupt\n"); return(ENXIO); @@ -382,7 +396,7 @@ mlx_attach(struct mlx_softc *sc) BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ 0, /* flags */ busdma_lock_mutex, /* lockfunc */ - &Giant, /* lockarg */ + &sc->mlx_io_lock, /* lockarg */ &sc->mlx_buffer_dmat); if (error != 0) { device_printf(sc->mlx_dev, "can't allocate buffer DMA tag\n"); @@ -407,7 +421,9 @@ mlx_attach(struct mlx_softc *sc) /* * Obtain controller feature information */ + MLX_IO_LOCK(sc); if ((sc->mlx_enq2 = mlx_enquire(sc, MLX_CMD_ENQUIRY2, sizeof(struct mlx_enquiry2), NULL)) == NULL) { + MLX_IO_UNLOCK(sc); device_printf(sc->mlx_dev, "ENQUIRY2 failed\n"); return(ENXIO); } @@ -420,6 +436,7 @@ mlx_attach(struct mlx_softc *sc) case MLX_IFTYPE_2: /* These controllers don't report the firmware version in the ENQUIRY2 response */ if ((meo = mlx_enquire(sc, MLX_CMD_ENQUIRY_OLD, sizeof(struct mlx_enquiry_old), NULL)) == NULL) { + MLX_IO_UNLOCK(sc); device_printf(sc->mlx_dev, "ENQUIRY_OLD failed\n"); return(ENXIO); } @@ -453,8 +470,10 @@ mlx_attach(struct mlx_softc *sc) } break; default: + MLX_IO_UNLOCK(sc); return(ENXIO); /* should never happen */ } + MLX_IO_UNLOCK(sc); /* * Create the final scatter/gather mappings now that we have characterised the controller. @@ -481,7 +500,7 @@ mlx_attach(struct mlx_softc *sc) /* * Start the timeout routine. */ - sc->mlx_timeout = timeout(mlx_periodic, sc, hz); + callout_reset(&sc->mlx_timeout, hz, mlx_periodic, sc); /* print a little information about the controller */ mlx_describe_controller(sc); @@ -505,13 +524,16 @@ mlx_startup(struct mlx_softc *sc) * Scan all the system drives and attach children for those that * don't currently have them. */ + MLX_IO_LOCK(sc); mes = mlx_enquire(sc, MLX_CMD_ENQSYSDRIVE, sizeof(*mes) * MLX_MAXDRIVES, NULL); + MLX_IO_UNLOCK(sc); if (mes == NULL) { device_printf(sc->mlx_dev, "error fetching drive status\n"); return; } /* iterate over drives returned */ + MLX_CONFIG_LOCK(sc); for (i = 0, dr = &sc->mlx_sysdrive[0]; (i < MLX_MAXDRIVES) && (mes[i].sd_size != 0xffffffff); i++, dr++) { @@ -543,10 +565,13 @@ mlx_startup(struct mlx_softc *sc) device_printf(sc->mlx_dev, "bus_generic_attach returned %d", error); /* mark controller back up */ + MLX_IO_LOCK(sc); sc->mlx_state &= ~MLX_STATE_SHUTDOWN; /* enable interrupts */ sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); + MLX_IO_UNLOCK(sc); + MLX_CONFIG_UNLOCK(sc); } /******************************************************************************** @@ -557,12 +582,12 @@ mlx_detach(device_t dev) { struct mlx_softc *sc = device_get_softc(dev); struct mlxd_softc *mlxd; - int i, s, error; + int i, error; debug_called(1); error = EBUSY; - s = splbio(); + MLX_CONFIG_LOCK(sc); if (sc->mlx_state & MLX_STATE_OPEN) goto out; @@ -577,12 +602,13 @@ mlx_detach(device_t dev) } if ((error = mlx_shutdown(dev))) goto out; + MLX_CONFIG_UNLOCK(sc); mlx_free(sc); - error = 0; + return (0); out: - splx(s); + MLX_CONFIG_UNLOCK(sc); return(error); } @@ -600,13 +626,24 @@ int mlx_shutdown(device_t dev) { struct mlx_softc *sc = device_get_softc(dev); - int i, s, error; + int error; + + MLX_CONFIG_LOCK(sc); + error = mlx_shutdown_locked(sc); + MLX_CONFIG_UNLOCK(sc); + return (error); +} + +static int +mlx_shutdown_locked(struct mlx_softc *sc) +{ + int i, error; debug_called(1); - s = splbio(); - error = 0; + MLX_CONFIG_ASSERT_LOCKED(sc); + MLX_IO_LOCK(sc); sc->mlx_state |= MLX_STATE_SHUTDOWN; sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); @@ -617,19 +654,18 @@ mlx_shutdown(device_t dev) } else { printf("done\n"); } + MLX_IO_UNLOCK(sc); /* delete all our child devices */ for (i = 0; i < MLX_MAXDRIVES; i++) { if (sc->mlx_sysdrive[i].ms_disk != 0) { if ((error = device_delete_child(sc->mlx_dev, sc->mlx_sysdrive[i].ms_disk)) != 0) - goto out; + return (error); sc->mlx_sysdrive[i].ms_disk = 0; } } - out: - splx(s); - return(error); + return (0); } /******************************************************************************** @@ -639,11 +675,10 @@ int mlx_suspend(device_t dev) { struct mlx_softc *sc = device_get_softc(dev); - int s; debug_called(1); - s = splbio(); + MLX_IO_LOCK(sc); sc->mlx_state |= MLX_STATE_SUSPEND; /* flush controller */ @@ -651,7 +686,7 @@ mlx_suspend(device_t dev) printf("%s\n", mlx_flush(sc) ? "failed" : "done"); sc->mlx_intaction(sc, MLX_INTACTION_DISABLE); - splx(s); + MLX_IO_UNLOCK(sc); return(0); } @@ -666,8 +701,10 @@ mlx_resume(device_t dev) debug_called(1); + MLX_IO_LOCK(sc); sc->mlx_state &= ~MLX_STATE_SUSPEND; sc->mlx_intaction(sc, MLX_INTACTION_ENABLE); + MLX_IO_UNLOCK(sc); return(0); } @@ -684,7 +721,9 @@ mlx_intr(void *arg) debug_called(1); /* collect finished commands, queue anything waiting */ - mlx_done(sc); + MLX_IO_LOCK(sc); + mlx_done(sc, 1); + MLX_IO_UNLOCK(sc); }; /******************************************************************************* @@ -694,14 +733,12 @@ mlx_intr(void *arg) int mlx_submit_buf(struct mlx_softc *sc, mlx_bio *bp) { - int s; debug_called(1); - s = splbio(); + MLX_IO_ASSERT_LOCKED(sc); MLX_BIO_QINSERT(sc->mlx_bioq, bp); sc->mlx_waitbufs++; - splx(s); mlx_startio(sc); return(0); } @@ -714,7 +751,11 @@ mlx_open(struct cdev *dev, int flags, in { struct mlx_softc *sc = dev->si_drv1; + MLX_CONFIG_LOCK(sc); + MLX_IO_LOCK(sc); sc->mlx_state |= MLX_STATE_OPEN; + MLX_IO_UNLOCK(sc); + MLX_CONFIG_UNLOCK(sc); return(0); } @@ -726,7 +767,11 @@ mlx_close(struct cdev *dev, int flags, i { struct mlx_softc *sc = dev->si_drv1; + MLX_CONFIG_LOCK(sc); + MLX_IO_LOCK(sc); sc->mlx_state &= ~MLX_STATE_OPEN; + MLX_IO_UNLOCK(sc); + MLX_CONFIG_UNLOCK(sc); return (0); } @@ -753,12 +798,14 @@ mlx_ioctl(struct cdev *dev, u_long cmd, */ case MLX_NEXT_CHILD: /* search system drives */ + MLX_CONFIG_LOCK(sc); for (i = 0; i < MLX_MAXDRIVES; i++) { /* is this one attached? */ 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); + MLX_CONFIG_UNLOCK(sc); return(0); } /* we want the one after this one */ @@ -766,13 +813,16 @@ mlx_ioctl(struct cdev *dev, u_long cmd, *arg = -1; } } + MLX_CONFIG_UNLOCK(sc); return(ENOENT); /* * Scan the controller to see whether new drives have appeared. */ case MLX_RESCAN_DRIVES: + mtx_lock(&Giant); mlx_startup(sc); + mtx_unlock(&Giant); return(0); /* @@ -780,10 +830,12 @@ mlx_ioctl(struct cdev *dev, u_long cmd, * away. */ case MLX_DETACH_DRIVE: /* detach one drive */ - + MLX_CONFIG_LOCK(sc); if (((dr = mlx_findunit(sc, *arg)) == NULL) || - ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) + ((mlxd = device_get_softc(dr->ms_disk)) == NULL)) { + MLX_CONFIG_UNLOCK(sc); return(ENOENT); + } device_printf(dr->ms_disk, "detaching..."); error = 0; @@ -793,10 +845,13 @@ mlx_ioctl(struct cdev *dev, u_long cmd, } /* flush controller */ + MLX_IO_LOCK(sc); if (mlx_flush(sc)) { + MLX_IO_UNLOCK(sc); error = EBUSY; goto detach_out; } + MLX_IO_UNLOCK(sc); /* nuke drive */ if ((error = device_delete_child(sc->mlx_dev, dr->ms_disk)) != 0) @@ -804,6 +859,7 @@ mlx_ioctl(struct cdev *dev, u_long cmd, dr->ms_disk = 0; detach_out: + MLX_CONFIG_UNLOCK(sc); if (error) { printf("failed\n"); } else { @@ -823,28 +879,33 @@ mlx_ioctl(struct cdev *dev, u_long cmd, if (!(sc->mlx_feature & MLX_FEAT_PAUSEWORKS)) return(EOPNOTSUPP); + /* check time values */ mp = (struct mlx_pause *)addr; + if ((mp->mp_when < 0) || (mp->mp_when > 3600)) + return(EINVAL); + if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30))) + return(EINVAL); + + MLX_IO_LOCK(sc); if ((mp->mp_which == MLX_PAUSE_CANCEL) && (sc->mlx_pause.mp_when != 0)) { /* cancel a pending pause operation */ sc->mlx_pause.mp_which = 0; } else { /* fix for legal channels */ mp->mp_which &= ((1 << sc->mlx_enq2->me_actual_channels) -1); - /* check time values */ - if ((mp->mp_when < 0) || (mp->mp_when > 3600)) - return(EINVAL); - if ((mp->mp_howlong < 1) || (mp->mp_howlong > (0xf * 30))) - return(EINVAL); /* check for a pause currently running */ - if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) + if ((sc->mlx_pause.mp_which != 0) && (sc->mlx_pause.mp_when == 0)) { + MLX_IO_UNLOCK(sc); return(EBUSY); + } /* looks ok, go with it */ sc->mlx_pause.mp_which = mp->mp_which; sc->mlx_pause.mp_when = time_second + mp->mp_when; sc->mlx_pause.mp_howlong = sc->mlx_pause.mp_when + mp->mp_howlong; } + MLX_IO_UNLOCK(sc); return(0); /* @@ -857,7 +918,9 @@ mlx_ioctl(struct cdev *dev, u_long cmd, * Start a rebuild on a given SCSI disk */ case MLX_REBUILDASYNC: + MLX_IO_LOCK(sc); if (sc->mlx_background != 0) { + MLX_IO_UNLOCK(sc); rb->rr_status = 0x0106; return(EBUSY); } @@ -887,13 +950,16 @@ mlx_ioctl(struct cdev *dev, u_long cmd, } if (error == 0) sc->mlx_background = MLX_BACKGROUND_REBUILD; + MLX_IO_UNLOCK(sc); return(error); /* * Get the status of the current rebuild or consistency check. */ case MLX_REBUILDSTAT: + MLX_IO_LOCK(sc); *rs = sc->mlx_rebuildstat; + MLX_IO_UNLOCK(sc); return(0); /* @@ -902,12 +968,16 @@ mlx_ioctl(struct cdev *dev, u_long cmd, */ case MLX_GET_SYSDRIVE: error = ENOENT; + MLX_CONFIG_LOCK(sc); + mtx_lock(&Giant); mlxd = (struct mlxd_softc *)devclass_get_softc(mlxd_devclass, *arg); + mtx_unlock(&Giant); 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; } + MLX_CONFIG_UNLOCK(sc); return(error); default: @@ -930,14 +1000,18 @@ mlx_submit_ioctl(struct mlx_softc *sc, s * Return the current status of this drive. */ case MLXD_STATUS: + MLX_IO_LOCK(sc); *arg = drive->ms_state; + MLX_IO_UNLOCK(sc); return(0); /* * Start a background consistency check on this drive. */ case MLXD_CHECKASYNC: /* start a background consistency check */ + MLX_IO_LOCK(sc); if (sc->mlx_background != 0) { + MLX_IO_UNLOCK(sc); *arg = 0x0106; return(EBUSY); } @@ -964,6 +1038,7 @@ mlx_submit_ioctl(struct mlx_softc *sc, s } if (error == 0) sc->mlx_background = MLX_BACKGROUND_CHECK; + MLX_IO_UNLOCK(sc); *arg = result; return(error); @@ -987,6 +1062,7 @@ mlx_periodic(void *data) struct mlx_softc *sc = (struct mlx_softc *)data; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); /* * Run a bus pause? @@ -1045,10 +1121,10 @@ mlx_periodic(void *data) 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); + mlx_done(sc, 1); /* reschedule another poll next second or so */ - sc->mlx_timeout = timeout(mlx_periodic, sc, hz); + callout_reset(&sc->mlx_timeout, hz, mlx_periodic, sc); } /******************************************************************************** @@ -1060,6 +1136,7 @@ mlx_periodic_enquiry(struct mlx_command struct mlx_softc *sc = mc->mc_sc; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); /* Command completed OK? */ if (mc->mc_status != 0) { @@ -1124,7 +1201,7 @@ mlx_periodic_enquiry(struct mlx_command debug(1, "event log pointer was %d, now %d\n", sc->mlx_lastevent, sc->mlx_currevent); /* mark the event log as busy */ - atomic_set_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY); + sc->mlx_flags |= MLX_EVENTLOG_BUSY; /* drain new eventlog entries */ mlx_periodic_eventlog_poll(sc); @@ -1205,6 +1282,7 @@ mlx_periodic_eventlog_poll(struct mlx_so int error = 0; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); /* get ourselves a command buffer */ error = 1; @@ -1263,6 +1341,7 @@ mlx_periodic_eventlog_respond(struct mlx char *reason; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); sc->mlx_lastevent++; /* next message... */ if (mc->mc_status == 0) { @@ -1321,7 +1400,7 @@ mlx_periodic_eventlog_respond(struct mlx mlx_periodic_eventlog_poll(sc); } else { /* clear log-busy status */ - atomic_clear_int(&sc->mlx_flags, MLX_EVENTLOG_BUSY); + sc->mlx_flags &= ~MLX_EVENTLOG_BUSY; } } @@ -1334,6 +1413,7 @@ mlx_periodic_rebuild(struct mlx_command struct mlx_softc *sc = mc->mc_sc; struct mlx_rebuild_status *mr = (struct mlx_rebuild_status *)mc->mc_data; + MLX_IO_ASSERT_LOCKED(sc); switch(mc->mc_status) { case 0: /* operation running, update stats */ sc->mlx_rebuildstat = *mr; @@ -1384,6 +1464,8 @@ mlx_pause_action(struct mlx_softc *sc) struct mlx_command *mc; int failsafe, i, command; + MLX_IO_ASSERT_LOCKED(sc); + /* What are we doing here? */ if (sc->mlx_pause.mp_when == 0) { command = MLX_CMD_STARTCHANNEL; @@ -1440,7 +1522,8 @@ mlx_pause_done(struct mlx_command *mc) struct mlx_softc *sc = mc->mc_sc; int command = mc->mc_mailbox[0]; int channel = mc->mc_mailbox[2] & 0xf; - + + MLX_IO_ASSERT_LOCKED(sc); if (mc->mc_status != 0) { device_printf(sc->mlx_dev, "%s command failed - %s\n", command == MLX_CMD_STOPCHANNEL ? "pause" : "resume", mlx_diagnose_command(mc)); @@ -1509,6 +1592,7 @@ mlx_enquire(struct mlx_softc *sc, int co int error; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); /* get ourselves a command buffer */ error = 1; @@ -1562,6 +1646,7 @@ mlx_flush(struct mlx_softc *sc) int error; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); /* get ourselves a command buffer */ error = 1; @@ -1604,6 +1689,7 @@ mlx_check(struct mlx_softc *sc, int driv int error; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); /* get ourselves a command buffer */ error = 0x10000; @@ -1647,6 +1733,7 @@ mlx_rebuild(struct mlx_softc *sc, int ch int error; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); /* get ourselves a command buffer */ error = 0x10000; @@ -1689,6 +1776,7 @@ mlx_wait_command(struct mlx_command *mc) int error, count; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); mc->mc_complete = NULL; mc->mc_private = mc; /* wake us when you're done */ @@ -1698,7 +1786,7 @@ mlx_wait_command(struct mlx_command *mc) count = 0; /* XXX better timeout? */ while ((mc->mc_status == MLX_STATUS_BUSY) && (count < 30)) { - tsleep(mc->mc_private, PRIBIO | PCATCH, "mlxwcmd", hz); + mtx_sleep(mc->mc_private, &sc->mlx_io_lock, PRIBIO | PCATCH, "mlxwcmd", hz); } if (mc->mc_status != 0) { @@ -1720,9 +1808,10 @@ static int mlx_poll_command(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; - int error, count, s; + int error, count; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); mc->mc_complete = NULL; mc->mc_private = NULL; /* we will poll for it */ @@ -1732,13 +1821,11 @@ mlx_poll_command(struct mlx_command *mc) count = 0; do { /* poll for completion */ - mlx_done(mc->mc_sc); + mlx_done(mc->mc_sc, 1); } while ((mc->mc_status == MLX_STATUS_BUSY) && (count++ < 15000000)); if (mc->mc_status != MLX_STATUS_BUSY) { - s = splbio(); TAILQ_REMOVE(&sc->mlx_work, mc, mc_link); - splx(s); return(0); } device_printf(sc->mlx_dev, "command failed - %s\n", mlx_diagnose_command(mc)); @@ -1809,30 +1896,27 @@ mlx_startio_cb(void *arg, bus_dma_segmen mc->mc_status = MLX_STATUS_WEDGED; mlx_completeio(mc); } + + sc->mlx_state &= ~MLX_STATE_QFROZEN; } /******************************************************************************** * Pull as much work off the softc's work queue as possible and give it to the * controller. Leave a couple of slots free for emergencies. - * - * Must be called at splbio or in an equivalent fashion that prevents - * reentry or activity on the bioq. */ static void mlx_startio(struct mlx_softc *sc) { struct mlx_command *mc; mlx_bio *bp; - int s; int error; - /* avoid reentrancy */ - if (mlx_lock_tas(sc, MLX_LOCK_STARTING)) - return; + MLX_IO_ASSERT_LOCKED(sc); /* spin until something prevents us from doing any work */ - s = splbio(); for (;;) { + if (sc->mlx_state & MLX_STATE_QFROZEN) + break; /* see if there's work to be done */ if ((bp = MLX_BIO_QFIRST(sc->mlx_bioq)) == NULL) @@ -1848,7 +1932,6 @@ mlx_startio(struct mlx_softc *sc) /* get the buf containing our work */ MLX_BIO_QREMOVE(sc->mlx_bioq, bp); sc->mlx_waitbufs--; - splx(s); /* connect the buf to the command */ mc->mc_complete = mlx_completeio; @@ -1859,14 +1942,11 @@ mlx_startio(struct mlx_softc *sc) /* map the command so the controller can work with it */ error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length, mlx_startio_cb, mc, 0); - if (error == EINPROGRESS) { - break; + if (error == EINPROGRESS) { + sc->mlx_state |= MLX_STATE_QFROZEN; + break; } - - s = splbio(); } - splx(s); - mlx_lock_clr(sc, MLX_LOCK_STARTING); } /******************************************************************************** @@ -1878,7 +1958,8 @@ mlx_completeio(struct mlx_command *mc) struct mlx_softc *sc = mc->mc_sc; mlx_bio *bp = (mlx_bio *)mc->mc_private; struct mlxd_softc *mlxd = (struct mlxd_softc *)MLX_BIO_SOFTC(bp); - + + MLX_IO_ASSERT_LOCKED(sc); if (mc->mc_status != MLX_STATUS_OK) { /* could be more verbose here? */ MLX_BIO_SET_ERROR(bp, EIO); @@ -1969,8 +2050,11 @@ mlx_user_command(struct mlx_softc *sc, s error = ENOMEM; /* get ourselves a command and copy in from user space */ - if ((mc = mlx_alloccmd(sc)) == NULL) + MLX_IO_LOCK(sc); + if ((mc = mlx_alloccmd(sc)) == NULL) { + MLX_IO_UNLOCK(sc); return(error); + } bcopy(mu->mu_command, mc->mc_mailbox, sizeof(mc->mc_mailbox)); debug(0, "got command buffer"); @@ -1983,9 +2067,13 @@ mlx_user_command(struct mlx_softc *sc, s error = EINVAL; goto out; } + MLX_IO_UNLOCK(sc); if (((kbuf = malloc(mu->mu_datasize, M_DEVBUF, M_WAITOK)) == NULL) || - (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) + (error = copyin(mu->mu_buf, kbuf, mu->mu_datasize))) { + MLX_IO_LOCK(sc); goto out; + } + MLX_IO_LOCK(sc); debug(0, "got kernel buffer"); } @@ -2010,17 +2098,20 @@ mlx_user_command(struct mlx_softc *sc, s mc->mc_private = mu; error = bus_dmamap_load(sc->mlx_buffer_dmat, mc->mc_dmamap, mc->mc_data, mc->mc_length, mlx_user_cb, mc, BUS_DMA_NOWAIT); + if (error) + goto out; /* copy out status and data */ mu->mu_status = mc->mc_status; - if ((mu->mu_datasize > 0) && - ((error = copyout(kbuf, mu->mu_buf, mu->mu_datasize)))) - goto out; - - error = 0; + if (mu->mu_datasize > 0) { + MLX_IO_UNLOCK(sc); + error = copyout(kbuf, mu->mu_buf, mu->mu_datasize); + MLX_IO_LOCK(sc); + } out: mlx_releasecmd(mc); + MLX_IO_UNLOCK(sc); if (kbuf != NULL) free(kbuf, M_DEVBUF); return(error); @@ -2042,10 +2133,12 @@ static int mlx_getslot(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; - int s, slot, limit; + int slot, limit; debug_called(1); + MLX_IO_ASSERT_LOCKED(sc); + /* * Enforce slot-usage limit, if we have the required information. */ @@ -2062,7 +2155,6 @@ mlx_getslot(struct mlx_command *mc) * * XXX linear search is slow */ - s = splbio(); for (slot = 0; slot < limit; slot++) { debug(2, "try slot %d", slot); if (sc->mlx_busycmd[slot] == NULL) @@ -2072,7 +2164,6 @@ mlx_getslot(struct mlx_command *mc) sc->mlx_busycmd[slot] = mc; sc->mlx_busycmds++; } - splx(s); /* out of slots? */ if (slot >= limit) @@ -2153,7 +2244,7 @@ static int mlx_start(struct mlx_command *mc) { struct mlx_softc *sc = mc->mc_sc; - int i, s, done; + int i; debug_called(1); @@ -2165,22 +2256,17 @@ mlx_start(struct mlx_command *mc) /* set a default 60-second timeout XXX tunable? XXX not currently used */ mc->mc_timeout = time_second + 60; - + /* spin waiting for the mailbox */ - for (i = 100000, done = 0; (i > 0) && !done; i--) { - s = splbio(); + for (i = 100000; i > 0; i--) { if (sc->mlx_tryqueue(sc, mc)) { - done = 1; /* move command to work queue */ TAILQ_INSERT_TAIL(&sc->mlx_work, mc, mc_link); - } - splx(s); /* drop spl to allow completion interrupts */ + return (0); + } else if (i > 1) + mlx_done(sc, 0); } - /* command is enqueued */ - if (done) - return(0); - /* * We couldn't get the controller to take the command. Revoke the slot * that the command was given and return it with a bad status. @@ -2200,19 +2286,19 @@ mlx_start(struct mlx_command *mc) * Returns nonzero if one or more commands were completed. */ static int -mlx_done(struct mlx_softc *sc) +mlx_done(struct mlx_softc *sc, int startio) { struct mlx_command *mc; - int s, result; + int result; u_int8_t slot; u_int16_t status; debug_called(2); + MLX_IO_ASSERT_LOCKED(sc); result = 0; /* loop collecting completed commands */ - s = splbio(); for (;;) { /* poll for a completed command's identifier and status */ if (sc->mlx_findcomplete(sc, &slot, &status)) { @@ -2235,10 +2321,9 @@ mlx_done(struct mlx_softc *sc) break; } } - splx(s); /* if we've completed any commands, try posting some more */ - if (result) + if (result && startio) mlx_startio(sc); /* handle completion and timeouts */ @@ -2254,15 +2339,9 @@ static void mlx_complete(struct mlx_softc *sc) { struct mlx_command *mc, *nc; - int s; debug_called(2); - - /* avoid reentrancy XXX might want to signal and request a restart */ - if (mlx_lock_tas(sc, MLX_LOCK_COMPLETING)) - return; - - s = splbio(); + MLX_IO_ASSERT_LOCKED(sc); /* scan the list of busy/done commands */ mc = TAILQ_FIRST(&sc->mlx_work); @@ -2299,9 +2378,6 @@ mlx_complete(struct mlx_softc *sc) } mc = nc; } - splx(s); - - mlx_lock_clr(sc, MLX_LOCK_COMPLETING); } /******************************************************************************** @@ -2329,14 +2405,12 @@ mlx_alloccmd(struct mlx_softc *sc) { struct mlx_command *mc; int error; - int s; debug_called(1); - s = splbio(); + MLX_IO_ASSERT_LOCKED(sc); if ((mc = TAILQ_FIRST(&sc->mlx_freecmds)) != NULL) TAILQ_REMOVE(&sc->mlx_freecmds, mc, mc_link); - splx(s); /* allocate a new command buffer? */ if (mc == NULL) { @@ -2362,13 +2436,11 @@ mlx_alloccmd(struct mlx_softc *sc) static void mlx_releasecmd(struct mlx_command *mc) { - int s; debug_called(1); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201209171527.q8HFRVjF043807>