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)
This commit is contained in:
John Baldwin 2012-09-17 15:27:30 +00:00
parent 8b3daf895a
commit 0fca6f8bf5
5 changed files with 299 additions and 245 deletions

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,9 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/sx.h>
#include <sys/bus.h>
#include <sys/conf.h>
@ -84,10 +86,17 @@ mlxd_open(struct disk *dp)
return (ENXIO);
/* controller not active? */
if (sc->mlxd_controller->mlx_state & MLX_STATE_SHUTDOWN)
MLX_CONFIG_LOCK(sc->mlxd_controller);
MLX_IO_LOCK(sc->mlxd_controller);
if (sc->mlxd_controller->mlx_state & MLX_STATE_SHUTDOWN) {
MLX_IO_UNLOCK(sc->mlxd_controller);
MLX_CONFIG_UNLOCK(sc->mlxd_controller);
return(ENXIO);
}
sc->mlxd_flags |= MLXD_OPEN;
MLX_IO_UNLOCK(sc->mlxd_controller);
MLX_CONFIG_UNLOCK(sc->mlxd_controller);
return (0);
}
@ -97,10 +106,14 @@ mlxd_close(struct disk *dp)
struct mlxd_softc *sc = (struct mlxd_softc *)dp->d_drv1;
debug_called(1);
if (sc == NULL)
return (ENXIO);
MLX_CONFIG_LOCK(sc->mlxd_controller);
MLX_IO_LOCK(sc->mlxd_controller);
sc->mlxd_flags &= ~MLXD_OPEN;
MLX_IO_UNLOCK(sc->mlxd_controller);
MLX_CONFIG_UNLOCK(sc->mlxd_controller);
return (0);
}
@ -142,13 +155,16 @@ mlxd_strategy(mlx_bio *bp)
}
/* XXX may only be temporarily offline - sleep? */
MLX_IO_LOCK(sc->mlxd_controller);
if (sc->mlxd_drive->ms_state == MLX_SYSD_OFFLINE) {
MLX_IO_UNLOCK(sc->mlxd_controller);
MLX_BIO_SET_ERROR(bp, ENXIO);
goto bad;
}
MLX_BIO_STATS_START(bp);
mlx_submit_buf(sc->mlxd_controller, bp);
MLX_IO_UNLOCK(sc->mlxd_controller);
return;
bad:
@ -232,7 +248,6 @@ mlxd_attach(device_t dev)
sc->mlxd_disk->d_mediasize = MLX_BLKSIZE * (off_t)sc->mlxd_drive->ms_size;
sc->mlxd_disk->d_fwsectors = sc->mlxd_drive->ms_sectors;
sc->mlxd_disk->d_fwheads = sc->mlxd_drive->ms_heads;
sc->mlxd_disk->d_flags = DISKFLAG_NEEDSGIANT;
/*
* Set maximum I/O size to the lesser of the recommended maximum and the practical

View File

@ -30,7 +30,10 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/module.h>
#include <sys/mutex.h>
#include <sys/sx.h>
#include <sys/bus.h>
#include <sys/conf.h>
@ -88,6 +91,21 @@ struct mlx_ident
{0, 0, 0, 0, 0, 0}
};
static struct mlx_ident *
mlx_pci_match(device_t dev)
{
struct mlx_ident *m;
for (m = mlx_identifiers; m->vendor != 0; m++) {
if ((m->vendor == pci_get_vendor(dev)) &&
(m->device == pci_get_device(dev)) &&
((m->subvendor == 0) || ((m->subvendor == pci_get_subvendor(dev)) &&
(m->subdevice == pci_get_subdevice(dev)))))
return (m);
}
return (NULL);
}
static int
mlx_pci_probe(device_t dev)
{
@ -95,15 +113,10 @@ mlx_pci_probe(device_t dev)
debug_called(1);
for (m = mlx_identifiers; m->vendor != 0; m++) {
if ((m->vendor == pci_get_vendor(dev)) &&
(m->device == pci_get_device(dev)) &&
((m->subvendor == 0) || ((m->subvendor == pci_get_subvendor(dev)) &&
(m->subdevice == pci_get_subdevice(dev))))) {
device_set_desc(dev, m->desc);
return(BUS_PROBE_DEFAULT);
}
m = mlx_pci_match(dev);
if (m != NULL) {
device_set_desc(dev, m->desc);
return(BUS_PROBE_DEFAULT);
}
return(ENXIO);
}
@ -112,45 +125,29 @@ static int
mlx_pci_attach(device_t dev)
{
struct mlx_softc *sc;
int i, error;
u_int32_t command;
struct mlx_ident *m;
int error;
debug_called(1);
/*
* Make sure we are going to be able to talk to this board.
*/
command = pci_read_config(dev, PCIR_COMMAND, 2);
if ((command & PCIM_CMD_MEMEN) == 0) {
device_printf(dev, "memory window not available\n");
return(ENXIO);
}
/* force the busmaster enable bit on */
command |= PCIM_CMD_BUSMASTEREN;
pci_write_config(dev, PCIR_COMMAND, command, 2);
pci_enable_busmaster(dev);
/*
* Initialise softc.
*/
sc = device_get_softc(dev);
bzero(sc, sizeof(*sc));
sc->mlx_dev = dev;
/*
* Work out what sort of adapter this is (we need to know this in order
* to map the appropriate interface resources).
*/
sc->mlx_iftype = 0;
for (i = 0; mlx_identifiers[i].vendor != 0; i++) {
if ((mlx_identifiers[i].vendor == pci_get_vendor(dev)) &&
(mlx_identifiers[i].device == pci_get_device(dev))) {
sc->mlx_iftype = mlx_identifiers[i].iftype;
break;
}
}
if (sc->mlx_iftype == 0) /* shouldn't happen */
m = mlx_pci_match(dev);
if (m == NULL) /* shouldn't happen */
return(ENXIO);
sc->mlx_iftype = m->iftype;
mtx_init(&sc->mlx_io_lock, "mlx I/O", NULL, MTX_DEF);
sx_init(&sc->mlx_config_lock, "mlx config");
callout_init_mtx(&sc->mlx_timeout, &sc->mlx_io_lock, 0);
/*
* Allocate the PCI register window.
*/
@ -183,8 +180,6 @@ mlx_pci_attach(device_t dev)
mlx_free(sc);
return(ENXIO);
}
sc->mlx_btag = rman_get_bustag(sc->mlx_mem);
sc->mlx_bhandle = rman_get_bushandle(sc->mlx_mem);
/*
* Allocate the parent bus DMA tag appropriate for PCI.

View File

@ -78,18 +78,18 @@
#define MLX_V3_FWERROR_PARAM1 0x00
#define MLX_V3_FWERROR_PARAM2 0x01
#define MLX_V3_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_MAILBOX + idx, val)
#define MLX_V3_GET_STATUS_IDENT(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_STATUS_IDENT)
#define MLX_V3_GET_STATUS(sc) bus_space_read_2 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_STATUS)
#define MLX_V3_GET_IDBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_IDBR)
#define MLX_V3_PUT_IDBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_IDBR, val)
#define MLX_V3_GET_ODBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_ODBR)
#define MLX_V3_PUT_ODBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_ODBR, val)
#define MLX_V3_PUT_IER(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_IER, val)
#define MLX_V3_GET_FWERROR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_FWERROR)
#define MLX_V3_PUT_FWERROR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V3_FWERROR, val)
#define MLX_V3_GET_FWERROR_PARAM1(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_FWERROR_PARAM1)
#define MLX_V3_GET_FWERROR_PARAM2(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V3_FWERROR_PARAM2)
#define MLX_V3_PUT_MAILBOX(sc, idx, val) bus_write_1(sc->mlx_mem, MLX_V3_MAILBOX + idx, val)
#define MLX_V3_GET_STATUS_IDENT(sc) bus_read_1 (sc->mlx_mem, MLX_V3_STATUS_IDENT)
#define MLX_V3_GET_STATUS(sc) bus_read_2 (sc->mlx_mem, MLX_V3_STATUS)
#define MLX_V3_GET_IDBR(sc) bus_read_1 (sc->mlx_mem, MLX_V3_IDBR)
#define MLX_V3_PUT_IDBR(sc, val) bus_write_1(sc->mlx_mem, MLX_V3_IDBR, val)
#define MLX_V3_GET_ODBR(sc) bus_read_1 (sc->mlx_mem, MLX_V3_ODBR)
#define MLX_V3_PUT_ODBR(sc, val) bus_write_1(sc->mlx_mem, MLX_V3_ODBR, val)
#define MLX_V3_PUT_IER(sc, val) bus_write_1(sc->mlx_mem, MLX_V3_IER, val)
#define MLX_V3_GET_FWERROR(sc) bus_read_1 (sc->mlx_mem, MLX_V3_FWERROR)
#define MLX_V3_PUT_FWERROR(sc, val) bus_write_1(sc->mlx_mem, MLX_V3_FWERROR, val)
#define MLX_V3_GET_FWERROR_PARAM1(sc) bus_read_1 (sc->mlx_mem, MLX_V3_FWERROR_PARAM1)
#define MLX_V3_GET_FWERROR_PARAM2(sc) bus_read_1 (sc->mlx_mem, MLX_V3_FWERROR_PARAM2)
#define MLX_V3_IDB_FULL (1<<0) /* mailbox is full */
#define MLX_V3_IDB_INIT_BUSY (1<<1) /* initialisation in progress */
@ -115,18 +115,18 @@
#define MLX_V4_FWERROR_PARAM2 0x1001
/* use longword access? */
#define MLX_V4_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_MAILBOX + idx, val)
#define MLX_V4_GET_STATUS_IDENT(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_STATUS_IDENT)
#define MLX_V4_GET_STATUS(sc) bus_space_read_2 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_STATUS)
#define MLX_V4_GET_IDBR(sc) bus_space_read_4 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_IDBR)
#define MLX_V4_PUT_IDBR(sc, val) bus_space_write_4(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_IDBR, val)
#define MLX_V4_GET_ODBR(sc) bus_space_read_4 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_ODBR)
#define MLX_V4_PUT_ODBR(sc, val) bus_space_write_4(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_ODBR, val)
#define MLX_V4_PUT_IER(sc, val) bus_space_write_4(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_IER, val)
#define MLX_V4_GET_FWERROR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_FWERROR)
#define MLX_V4_PUT_FWERROR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V4_FWERROR, val)
#define MLX_V4_GET_FWERROR_PARAM1(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_FWERROR_PARAM1)
#define MLX_V4_GET_FWERROR_PARAM2(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V4_FWERROR_PARAM2)
#define MLX_V4_PUT_MAILBOX(sc, idx, val) bus_write_1(sc->mlx_mem, MLX_V4_MAILBOX + idx, val)
#define MLX_V4_GET_STATUS_IDENT(sc) bus_read_1 (sc->mlx_mem, MLX_V4_STATUS_IDENT)
#define MLX_V4_GET_STATUS(sc) bus_read_2 (sc->mlx_mem, MLX_V4_STATUS)
#define MLX_V4_GET_IDBR(sc) bus_read_4 (sc->mlx_mem, MLX_V4_IDBR)
#define MLX_V4_PUT_IDBR(sc, val) bus_write_4(sc->mlx_mem, MLX_V4_IDBR, val)
#define MLX_V4_GET_ODBR(sc) bus_read_4 (sc->mlx_mem, MLX_V4_ODBR)
#define MLX_V4_PUT_ODBR(sc, val) bus_write_4(sc->mlx_mem, MLX_V4_ODBR, val)
#define MLX_V4_PUT_IER(sc, val) bus_write_4(sc->mlx_mem, MLX_V4_IER, val)
#define MLX_V4_GET_FWERROR(sc) bus_read_1 (sc->mlx_mem, MLX_V4_FWERROR)
#define MLX_V4_PUT_FWERROR(sc, val) bus_write_1(sc->mlx_mem, MLX_V4_FWERROR, val)
#define MLX_V4_GET_FWERROR_PARAM1(sc) bus_read_1 (sc->mlx_mem, MLX_V4_FWERROR_PARAM1)
#define MLX_V4_GET_FWERROR_PARAM2(sc) bus_read_1 (sc->mlx_mem, MLX_V4_FWERROR_PARAM2)
#define MLX_V4_IDB_FULL (1<<0) /* mailbox is full */
#define MLX_V4_IDB_INIT_BUSY (1<<1) /* initialisation in progress */
@ -160,18 +160,18 @@
#define MLX_V5_FWERROR_PARAM1 0x50
#define MLX_V5_FWERROR_PARAM2 0x51
#define MLX_V5_PUT_MAILBOX(sc, idx, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_MAILBOX + idx, val)
#define MLX_V5_GET_STATUS_IDENT(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_STATUS_IDENT)
#define MLX_V5_GET_STATUS(sc) bus_space_read_2 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_STATUS)
#define MLX_V5_GET_IDBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_IDBR)
#define MLX_V5_PUT_IDBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_IDBR, val)
#define MLX_V5_GET_ODBR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_ODBR)
#define MLX_V5_PUT_ODBR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_ODBR, val)
#define MLX_V5_PUT_IER(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_IER, val)
#define MLX_V5_GET_FWERROR(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_FWERROR)
#define MLX_V5_PUT_FWERROR(sc, val) bus_space_write_1(sc->mlx_btag, sc->mlx_bhandle, MLX_V5_FWERROR, val)
#define MLX_V5_GET_FWERROR_PARAM1(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_FWERROR_PARAM1)
#define MLX_V5_GET_FWERROR_PARAM2(sc) bus_space_read_1 (sc->mlx_btag, sc->mlx_bhandle, MLX_V5_FWERROR_PARAM2)
#define MLX_V5_PUT_MAILBOX(sc, idx, val) bus_write_1(sc->mlx_mem, MLX_V5_MAILBOX + idx, val)
#define MLX_V5_GET_STATUS_IDENT(sc) bus_read_1 (sc->mlx_mem, MLX_V5_STATUS_IDENT)
#define MLX_V5_GET_STATUS(sc) bus_read_2 (sc->mlx_mem, MLX_V5_STATUS)
#define MLX_V5_GET_IDBR(sc) bus_read_1 (sc->mlx_mem, MLX_V5_IDBR)
#define MLX_V5_PUT_IDBR(sc, val) bus_write_1(sc->mlx_mem, MLX_V5_IDBR, val)
#define MLX_V5_GET_ODBR(sc) bus_read_1 (sc->mlx_mem, MLX_V5_ODBR)
#define MLX_V5_PUT_ODBR(sc, val) bus_write_1(sc->mlx_mem, MLX_V5_ODBR, val)
#define MLX_V5_PUT_IER(sc, val) bus_write_1(sc->mlx_mem, MLX_V5_IER, val)
#define MLX_V5_GET_FWERROR(sc) bus_read_1 (sc->mlx_mem, MLX_V5_FWERROR)
#define MLX_V5_PUT_FWERROR(sc, val) bus_write_1(sc->mlx_mem, MLX_V5_FWERROR, val)
#define MLX_V5_GET_FWERROR_PARAM1(sc) bus_read_1 (sc->mlx_mem, MLX_V5_FWERROR_PARAM1)
#define MLX_V5_GET_FWERROR_PARAM2(sc) bus_read_1 (sc->mlx_mem, MLX_V5_FWERROR_PARAM2)
#define MLX_V5_IDB_EMPTY (1<<0) /* mailbox is empty */
#define MLX_V5_IDB_INIT_DONE (1<<1) /* initialisation has completed */

View File

@ -113,8 +113,6 @@ struct mlx_softc
struct resource *mlx_mem; /* mailbox interface window */
int mlx_mem_rid;
int mlx_mem_type;
bus_space_handle_t mlx_bhandle; /* bus space handle */
bus_space_tag_t mlx_btag; /* bus space tag */
bus_dma_tag_t mlx_parent_dmat;/* parent DMA tag */
bus_dma_tag_t mlx_buffer_dmat;/* data buffer DMA tag */
struct resource *mlx_irq; /* interrupt */
@ -149,7 +147,10 @@ struct mlx_softc
#define MLX_STATE_SHUTDOWN (1<<1) /* controller is shut down */
#define MLX_STATE_OPEN (1<<2) /* control device is open */
#define MLX_STATE_SUSPEND (1<<3) /* controller is suspended */
struct callout_handle mlx_timeout; /* periodic status monitor */
#define MLX_STATE_QFROZEN (1<<4) /* bio queue frozen */
struct mtx mlx_io_lock;
struct sx mlx_config_lock;
struct callout mlx_timeout; /* periodic status monitor */
time_t mlx_lastpoll; /* last time_second we polled for status */
u_int16_t mlx_lastevent; /* sequence number of the last event we recorded */
int mlx_currevent; /* sequence number last time we looked */
@ -160,7 +161,6 @@ struct mlx_softc
struct mlx_rebuild_status mlx_rebuildstat; /* last rebuild status */
struct mlx_pause mlx_pause; /* pending pause operation details */
int mlx_locks; /* reentrancy avoidance */
int mlx_flags;
#define MLX_SPINUP_REPORTED (1<<0) /* "spinning up drives" message displayed */
#define MLX_EVENTLOG_BUSY (1<<1) /* currently reading event log */
@ -174,34 +174,17 @@ struct mlx_softc
int (* mlx_tryqueue)(struct mlx_softc *sc, struct mlx_command *mc);
int (* mlx_findcomplete)(struct mlx_softc *sc, u_int8_t *slot, u_int16_t *status);
void (* mlx_intaction)(struct mlx_softc *sc, int action);
int (* mlx_fw_handshake)(struct mlx_softc *sc, int *error, int *param1, int *param2);
int (* mlx_fw_handshake)(struct mlx_softc *sc, int *error, int *param1, int *param2, int first);
#define MLX_INTACTION_DISABLE 0
#define MLX_INTACTION_ENABLE 1
};
/*
* Simple (stupid) locks.
*
* Note that these are designed to avoid reentrancy, not concurrency, and will
* need to be replaced with something better.
*/
#define MLX_LOCK_COMPLETING (1<<0)
#define MLX_LOCK_STARTING (1<<1)
static __inline int
mlx_lock_tas(struct mlx_softc *sc, int lock)
{
if ((sc)->mlx_locks & (lock))
return(1);
atomic_set_int(&sc->mlx_locks, lock);
return(0);
}
static __inline void
mlx_lock_clr(struct mlx_softc *sc, int lock)
{
atomic_clear_int(&sc->mlx_locks, lock);
}
#define MLX_IO_LOCK(sc) mtx_lock(&(sc)->mlx_io_lock)
#define MLX_IO_UNLOCK(sc) mtx_unlock(&(sc)->mlx_io_lock)
#define MLX_IO_ASSERT_LOCKED(sc) mtx_assert(&(sc)->mlx_io_lock, MA_OWNED)
#define MLX_CONFIG_LOCK(sc) sx_xlock(&(sc)->mlx_config_lock)
#define MLX_CONFIG_UNLOCK(sc) sx_xunlock(&(sc)->mlx_config_lock)
#define MLX_CONFIG_ASSERT_LOCKED(sc) sx_assert(&(sc)->mlx_config_lock, SA_XLOCKED)
/*
* Interface between bus connections and driver core.