.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
.\"
-.\" $FreeBSD: src/share/man/man4/twe.4,v 1.17 2006/05/20 09:39:28 brueffer Exp $
+.\" $FreeBSD: src/share/man/man4/twe.4,v 1.18 2012/11/17 01:50:48 svnexp Exp $
.\"
.Dd December 31, 2010
.Dt TWE 4
-# $FreeBSD: src/sys/modules/twe/Makefile,v 1.7 2005/08/10 04:01:21 obrien Exp $
+# $FreeBSD: src/sys/modules/twe/Makefile,v 1.8 2012/11/17 01:53:01 svnexp Exp $
KMOD= twe
SRCS= bus_if.h device_if.h pci_if.h twe.c twe_freebsd.c
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: src/sys/dev/twe/twe.c,v 1.30 2009/12/25 17:34:43 mav Exp $
+ * $FreeBSD: src/sys/dev/twe/twe.c,v 1.34 2012/11/17 01:52:19 svnexp Exp $
*/
/*
/*
* Command I/O to controller.
*/
-static void twe_done(struct twe_softc *sc);
+static void twe_done(struct twe_softc *sc, int startio);
static void twe_complete(struct twe_softc *sc);
static int twe_wait_status(struct twe_softc *sc, u_int32_t status, int timeout);
static int twe_drain_response_queue(struct twe_softc *sc);
/*
* Put command onto the freelist.
*/
+ TWE_IO_LOCK(sc);
twe_release_request(tr);
+ TWE_IO_UNLOCK(sc);
}
+ TWE_IO_LOCK(sc);
/*
* Check status register for errors, clear them.
* Wait for the controller to come ready.
*/
if (twe_wait_status(sc, TWE_STATUS_MICROCONTROLLER_READY, 60)) {
+ TWE_IO_UNLOCK(sc);
twe_printf(sc, "microcontroller not ready\n");
return(ENXIO);
}
if (!twe_soft_reset(sc))
break; /* reset process complete */
}
+ TWE_IO_UNLOCK(sc);
/* did we give up? */
if (i >= TWE_MAX_RESET_TRIES) {
twe_printf(sc, "can't initialise controller, giving up\n");
TWE_Param *drives = NULL, *param = NULL;
TWE_Array_Descriptor *ud;
+ TWE_CONFIG_ASSERT_LOCKED(sc);
if (unit < 0 || unit > TWE_MAX_UNITS)
return (EINVAL);
/*
* The controller is in a safe state, so try to find drives attached to it.
*/
+ TWE_IO_LOCK(sc);
if ((drives = twe_get_param(sc, TWE_PARAM_UNITSUMMARY, TWE_PARAM_UNITSUMMARY_Status,
TWE_MAX_UNITS, NULL)) == NULL) {
+ TWE_IO_UNLOCK(sc);
twe_printf(sc, "can't detect attached units\n");
return (EIO);
}
dr = &sc->twe_drive[unit];
/* check that the drive is online */
if (!(drives->data[unit] & TWE_PARAM_UNITSTATUS_Online)) {
+ TWE_IO_UNLOCK(sc);
error = ENXIO;
goto out;
}
table = TWE_PARAM_UNITINFO + unit;
if (twe_get_param_4(sc, table, TWE_PARAM_UNITINFO_Capacity, &dr->td_size)) {
+ TWE_IO_UNLOCK(sc);
twe_printf(sc, "error fetching capacity for unit %d\n", unit);
error = EIO;
goto out;
}
if (twe_get_param_1(sc, table, TWE_PARAM_UNITINFO_Status, &dr->td_state)) {
+ TWE_IO_UNLOCK(sc);
twe_printf(sc, "error fetching state for unit %d\n", unit);
error = EIO;
goto out;
}
if (twe_get_param_2(sc, table, TWE_PARAM_UNITINFO_DescriptorSize, &dsize)) {
+ TWE_IO_UNLOCK(sc);
twe_printf(sc, "error fetching descriptor size for unit %d\n", unit);
error = EIO;
goto out;
}
if ((param = twe_get_param(sc, table, TWE_PARAM_UNITINFO_Descriptor, dsize - 3, NULL)) == NULL) {
+ TWE_IO_UNLOCK(sc);
twe_printf(sc, "error fetching descriptor for unit %d\n", unit);
error = EIO;
goto out;
}
dr->td_cylinders = dr->td_size / (dr->td_heads * dr->td_sectors);
dr->td_twe_unit = unit;
+ TWE_IO_UNLOCK(sc);
error = twe_attach_drive(sc, dr);
{
int error;
+ TWE_CONFIG_ASSERT_LOCKED(sc);
if (unit < 0 || unit >= TWE_MAX_UNITS)
return (ENXIO);
/*
* Scan for drives
*/
+ TWE_CONFIG_LOCK(sc);
for (i = 0; i < TWE_MAX_UNITS; i++)
twe_add_unit(sc, i);
+ TWE_CONFIG_UNLOCK(sc);
/*
* Initialise connection with controller.
*/
+ TWE_IO_LOCK(sc);
twe_init_connection(sc, TWE_INIT_MESSAGE_CREDITS);
#ifdef TWE_SHUTDOWN_NOTIFICATION
* Finally enable interrupts.
*/
twe_enable_interrupts(sc);
+ TWE_IO_UNLOCK(sc);
}
/********************************************************************************
/*
* Mark the controller as shutting down, and disable any further interrupts.
*/
+ twe_lockassert(&sc->twe_io_lock);
sc->twe_state |= TWE_STATE_SHUTDOWN;
twe_disable_interrupts(sc);
if (status_reg & TWE_STATUS_COMMAND_INTERRUPT)
twe_command_intr(sc);
if (status_reg & TWE_STATUS_RESPONSE_INTERRUPT)
- twe_done(sc);
+ twe_done(sc, 1);
}
/********************************************************************************
{
struct twe_request *tr;
TWE_Command *cmd;
- twe_bio *bio;
+ struct bio *bio;
struct buf *bp;
int error;
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
if (sc->twe_state & (TWE_STATE_CTLR_BUSY | TWE_STATE_FRZN))
return;
break;
tr->tr_status = TWE_CMD_ERROR;
if (tr->tr_private != NULL) {
- bio = (twe_bio *)tr->tr_private;
+ bio = (struct bio *)(tr->tr_private);
bp = bio->bio_buf;
bp->b_error = error;
bp->b_flags |= B_ERROR;
struct twe_request *tr;
u_int8_t srid;
int error;
+ size_t tr_length;
error = 0;
switch(ioctlcmd) {
/* handle a command from userspace */
case TWEIO_COMMAND:
+ /*
+ * if there's a data buffer, allocate and copy it in.
+ * Must be in multipled of 512 bytes.
+ */
+ tr_length = roundup2(tu->tu_size, 512);
+ if (tr_length > 0) {
+ data = kmalloc(tr_length, M_DEVBUF, M_WAITOK);
+ error = copyin(tu->tu_data, data, tu->tu_size);
+ if (error) {
+ kfree(data, M_DEVBUF);
+ break;
+ }
+ } else
+ data = NULL;
+
/* get a request */
+ TWE_IO_LOCK(sc);
while (twe_get_request(sc, &tr))
- tsleep(sc, 0, "twioctl", hz);
+ lksleep(sc, &sc->twe_io_lock, 0, "twioctl", hz);
/*
* Save the command's request ID, copy the user-supplied command in,
* if there's a data buffer, allocate and copy it in.
* Must be in multipled of 512 bytes.
*/
- tr->tr_length = (tu->tu_size + 511) & ~511;
- if (tr->tr_length > 0) {
- tr->tr_data = kmalloc(tr->tr_length, M_DEVBUF, M_WAITOK);
- if ((error = copyin(tu->tu_data, tr->tr_data, tu->tu_size)) != 0)
- goto cmd_done;
+ tr->tr_length = tr_length;
+ tr->tr_data = data;
+ if (tr->tr_length > 0)
tr->tr_flags |= TWE_CMD_DATAIN | TWE_CMD_DATAOUT;
- }
/* run the command */
error = twe_wait_request(tr);
+ TWE_IO_UNLOCK(sc);
if (error)
goto cmd_done;
/* free resources */
if (tr->tr_data != NULL)
kfree(tr->tr_data, M_DEVBUF);
- if (tr != NULL)
- twe_release_request(tr);
+ TWE_IO_LOCK(sc);
+ twe_release_request(tr);
+ TWE_IO_UNLOCK(sc);
break;
case TWEQ_READY:
case TWEQ_BUSY:
case TWEQ_COMPLETE:
+ TWE_IO_LOCK(sc);
bcopy(&sc->twe_qstat[ts->ts_item], &ts->ts_qstat, sizeof(struct twe_qstat));
+ TWE_IO_UNLOCK(sc);
break;
#endif
default:
/* poll for an AEN */
case TWEIO_AEN_POLL:
+ TWE_IO_LOCK(sc);
*aen_code = twe_dequeue_aen(sc);
+ TWE_IO_UNLOCK(sc);
break;
/* wait for another AEN to show up */
case TWEIO_AEN_WAIT:
- crit_enter();
+ TWE_IO_LOCK(sc);
while ((*aen_code = twe_dequeue_aen(sc)) == TWE_AEN_QUEUE_EMPTY) {
- error = tsleep(&sc->twe_aen_queue, PCATCH, "tweaen", 0);
+ error = lksleep(&sc->twe_aen_queue, &sc->twe_io_lock, PCATCH,
+ "tweaen", 0);
if (error == EINTR)
break;
}
- crit_exit();
+ TWE_IO_UNLOCK(sc);
break;
case TWEIO_GET_PARAM:
- if ((param = twe_get_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, NULL)) == NULL) {
+ TWE_IO_LOCK(sc);
+ param = twe_get_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, NULL);
+ TWE_IO_UNLOCK(sc);
+ if (param == NULL) {
twe_printf(sc, "TWEIO_GET_PARAM failed for 0x%x/0x%x/%d\n",
tp->tp_table_id, tp->tp_param_id, tp->tp_size);
error = EINVAL;
case TWEIO_SET_PARAM:
data = kmalloc(tp->tp_size, M_DEVBUF, M_WAITOK);
error = copyin(tp->tp_data, data, tp->tp_size);
- if (error == 0)
+ if (error == 0) {
+ TWE_IO_LOCK(sc);
error = twe_set_param(sc, tp->tp_table_id, tp->tp_param_id, tp->tp_size, data);
+ TWE_IO_UNLOCK(sc);
+ }
kfree(data, M_DEVBUF);
break;
case TWEIO_RESET:
+ TWE_IO_LOCK(sc);
twe_reset(sc);
+ TWE_IO_UNLOCK(sc);
break;
case TWEIO_ADD_UNIT:
+ TWE_CONFIG_LOCK(sc);
error = twe_add_unit(sc, td->td_unit);
+ TWE_CONFIG_UNLOCK(sc);
break;
case TWEIO_DEL_UNIT:
+ TWE_CONFIG_LOCK(sc);
error = twe_del_unit(sc, td->td_unit);
+ TWE_CONFIG_UNLOCK(sc);
break;
/* XXX implement ATA PASSTHROUGH */
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
tr = NULL;
param = NULL;
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
tr = NULL;
param = NULL;
error = ENOMEM;
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
+
/* get a command */
if (twe_get_request(sc, &tr))
return(0);
{
debug_called(4);
+ twe_lockassert(&tr->tr_sc->twe_io_lock);
tr->tr_flags |= TWE_CMD_SLEEPER;
tr->tr_status = TWE_CMD_BUSY;
twe_enqueue_ready(tr);
twe_startio(tr->tr_sc);
- crit_enter();
while (tr->tr_status == TWE_CMD_BUSY)
- tsleep(tr, 0, "twewait", 0);
- crit_exit();
+ lksleep(tr, &tr->tr_sc->twe_io_lock, 0, "twewait", 0);
return(tr->tr_status != TWE_CMD_COMPLETE);
}
/* Wait up to 5 seconds for the command to complete */
while ((count++ < 5000) && (tr->tr_status == TWE_CMD_BUSY)){
DELAY(1000);
- twe_done(sc);
+ twe_done(sc, 1);
}
if (usetmp && (tr->tr_data != NULL))
bcopy(sc->twe_immediate, tr->tr_data, tr->tr_length);
{
TWE_Command *cmd = TWE_FIND_COMMAND(tr);
struct twe_softc *sc = tr->tr_sc;
- struct bio *bio = (twe_bio *)tr->tr_private;
+ struct bio *bio = tr->tr_private;
struct buf *bp = bio->bio_buf;
debug_called(4);
/*
* Sleep for a short period to allow AENs to be signalled.
*/
- tsleep(sc, 0, "twereset", hz);
+ lksleep(sc, &sc->twe_io_lock, 0, "twereset", hz);
/*
* Disable interrupts from the controller, and mask any accidental entry
*/
twe_printf(sc, "controller reset in progress...\n");
twe_disable_interrupts(sc);
- crit_enter();
/*
* Try to soft-reset the controller.
* Kick the controller to start things going again, then re-enable interrupts.
*/
twe_startio(sc);
- twe_enable_interrupts(sc);
twe_printf(sc, "controller reset done, %d commands restarted\n", i);
out:
- crit_exit();
twe_enable_interrupts(sc);
}
{
struct twe_softc *sc = tr->tr_sc;
TWE_Command *cmd;
- int i, done;
+ int i;
u_int32_t status_reg;
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
+
/* mark the command as currently being processed */
tr->tr_status = TWE_CMD_BUSY;
cmd = TWE_FIND_COMMAND(tr);
* XXX it might be more efficient to return EBUSY immediately
* and let the command be rescheduled.
*/
- for (i = 100000, done = 0; (i > 0) && !done; i--) {
- crit_enter();
+ for (i = 100000; (i > 0); i--) {
/* check to see if we can post a command */
status_reg = TWE_STATUS(sc);
twe_enqueue_busy(tr);
TWE_COMMAND_QUEUE(sc, TWE_FIND_COMMANDPHYS(tr));
- done = 1;
+
/* move command to work queue */
#ifdef TWE_DEBUG
if (tr->tr_complete != NULL) {
debug(3, "queued request %d for polling caller", cmd->generic.request_id);
}
#endif
- }
- crit_exit(); /* drop critical section to allow completion interrupts */
+ return(0);
+ } else if (!(status_reg & TWE_STATUS_RESPONSE_QUEUE_EMPTY) && i > 1)
+ twe_done(sc, 0);
}
- /* command is enqueued */
- if (done)
- return(0);
-
/*
* We couldn't get the controller to take the command; try submitting it again later.
* This should only happen if something is wrong with the controller, or if we have
* Can be called at any interrupt level, with or without interrupts enabled.
*/
static void
-twe_done(struct twe_softc *sc)
+twe_done(struct twe_softc *sc, int startio)
{
TWE_Response_Queue rq;
TWE_Command *cmd;
/* loop collecting completed commands */
found = 0;
- crit_enter();
for (;;) {
status_reg = TWE_STATUS(sc);
twe_check_bits(sc, status_reg); /* XXX should this fail? */
break; /* no response ready */
}
}
- crit_exit();
/* if we've completed any commands, try posting some more */
- if (found)
+ if (found && startio)
twe_startio(sc);
/* handle completion and timeouts */
debug_called(2);
+ twe_lockassert(&sc->twe_io_lock);
TWE_SOFT_RESET(sc);
if (twe_wait_status(sc, TWE_STATUS_ATTENTION_INTERRUPT, 30)) {
{
u_int16_t aen;
+ twe_lockassert(&sc->twe_io_lock);
for (;;) {
if (twe_get_param_2(sc, TWE_PARAM_AEN, TWE_PARAM_AEN_UnitCode, &aen))
return(1);
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
if ((msg = twe_format_aen(sc, aen)) != NULL)
twe_printf(sc, "AEN: <%s>\n", msg);
- crit_enter();
/* enqueue the AEN */
next = ((sc->twe_aen_head + 1) % TWE_Q_LENGTH);
nextnext = ((sc->twe_aen_head + 2) % TWE_Q_LENGTH);
sc->twe_wait_aen = -1;
wakeup(&sc->twe_wait_aen);
}
- crit_exit();
}
/********************************************************************************
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
if (sc->twe_aen_tail == sc->twe_aen_head) {
result = TWE_AEN_QUEUE_EMPTY;
} else {
int i, missing;
missing = 1;
- crit_enter();
for (i = sc->twe_aen_tail; (i != sc->twe_aen_head) && missing; i = (i + 1) % TWE_Q_LENGTH) {
if (sc->twe_aen_queue[i] == aen)
missing = 0;
}
- crit_exit();
return(missing);
}
expiry = time_second + timeout;
found = 0;
- crit_enter();
sc->twe_wait_aen = aen;
do {
twe_fetch_aen(sc);
- tsleep(&sc->twe_wait_aen, PZERO, "twewaen", hz);
+ lksleep(&sc->twe_wait_aen, &sc->twe_io_lock, 0, "twewaen", hz);
if (sc->twe_wait_aen == -1)
found = 1;
} while ((time_second <= expiry) && !found);
- crit_exit();
return(!found);
}
#endif
TWE_Command *cmd;
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
+
/* try to reuse an old buffer */
*tr = twe_dequeue_free(sc);
{
debug_called(4);
+ twe_lockassert(&tr->tr_sc->twe_io_lock);
if (tr->tr_private != NULL)
twe_panic(tr->tr_sc, "tr_private != NULL");
twe_enqueue_free(tr);
debug_called(2);
+ TWE_IO_LOCK(sc);
+
/* get the port count */
twe_get_param_1(sc, TWE_PARAM_CONTROLLER, TWE_PARAM_CONTROLLER_PortCount, &ports);
if (p[0])
kfree(p[0], M_DEVBUF);
}
+ TWE_IO_UNLOCK(sc);
}
/********************************************************************************
static char *
twe_format_aen(struct twe_softc *sc, u_int16_t aen)
{
- static char buf[80];
device_t child;
char *code, *msg;
case 'c':
if ((child = sc->twe_drive[TWE_AEN_UNIT(aen)].td_disk) != NULL) {
- ksprintf(buf, "twed%d: %s", device_get_unit(child), msg);
+ ksnprintf(sc->twe_aen_buf, sizeof(sc->twe_aen_buf), "twed%d: %s",
+ device_get_unit(child), msg);
} else {
- ksprintf(buf, "twe%d: %s for unknown unit %d", device_get_unit(sc->twe_dev),
- msg, TWE_AEN_UNIT(aen));
+ ksnprintf(sc->twe_aen_buf, sizeof(sc->twe_aen_buf),
+ "twe%d: %s for unknown unit %d", device_get_unit(sc->twe_dev),
+ msg, TWE_AEN_UNIT(aen));
}
- return(buf);
+ return(sc->twe_aen_buf);
case 'p':
- ksprintf(buf, "twe%d: port %d: %s", device_get_unit(sc->twe_dev), TWE_AEN_UNIT(aen),
- msg);
- return(buf);
+ ksnprintf(sc->twe_aen_buf, sizeof(sc->twe_aen_buf),
+ "twe%d: port %d: %s", device_get_unit(sc->twe_dev),
+ TWE_AEN_UNIT(aen), msg);
+ return(sc->twe_aen_buf);
case 'x':
default:
break;
}
- ksprintf(buf, "unknown AEN 0x%x", aen);
- return(buf);
+ ksnprintf(sc->twe_aen_buf, sizeof(sc->twe_aen_buf), "unknown AEN 0x%x", aen);
+ return(sc->twe_aen_buf);
}
/********************************************************************************
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: src/sys/dev/twe/twe_compat.h,v 1.14 2005/05/29 04:42:26 nyan Exp $
+ * $FreeBSD: src/sys/dev/twe/twe_compat.h,v 1.17 2012/11/17 01:52:19 svnexp Exp $
*/
/*
* Portability and compatibility interfaces.
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
+#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/buf2.h>
#include <sys/bus.h>
#include <sys/stat.h>
#include <sys/rman.h>
#include <sys/devicestat.h>
-#include <sys/thread2.h>
#include <bus/pci/pcireg.h>
#include <bus/pci/pcivar.h>
/*
* Wrappers for bus-space actions
*/
-#define TWE_CONTROL(sc, val) bus_space_write_4((sc)->twe_btag, (sc)->twe_bhandle, 0x0, (u_int32_t)val)
-#define TWE_STATUS(sc) (u_int32_t)bus_space_read_4((sc)->twe_btag, (sc)->twe_bhandle, 0x4)
-#define TWE_COMMAND_QUEUE(sc, val) bus_space_write_4((sc)->twe_btag, (sc)->twe_bhandle, 0x8, (u_int32_t)val)
-#define TWE_RESPONSE_QUEUE(sc) bus_space_read_4((sc)->twe_btag, (sc)->twe_bhandle, 0xc)
+#define TWE_CONTROL(sc, val) bus_write_4((sc)->twe_io, 0x0, (u_int32_t)val)
+#define TWE_STATUS(sc) (u_int32_t)bus_read_4((sc)->twe_io, 0x4)
+#define TWE_COMMAND_QUEUE(sc, val) bus_write_4((sc)->twe_io, 0x8, (u_int32_t)val)
+#define TWE_RESPONSE_QUEUE(sc) bus_read_4((sc)->twe_io, 0xc)
/*
* FreeBSD-specific softc elements
device_t twe_dev; /* bus device */ \
cdev_t twe_dev_t; /* control device */ \
struct resource *twe_io; /* register interface window */ \
- bus_space_handle_t twe_bhandle; /* bus space handle */ \
- bus_space_tag_t twe_btag; /* bus space tag */ \
bus_dma_tag_t twe_parent_dmat; /* parent DMA tag */ \
bus_dma_tag_t twe_buffer_dmat; /* data buffer DMA tag */ \
bus_dma_tag_t twe_cmd_dmat; /* command buffer DMA tag */ \
void *twe_immediate; /* immediate commands */ \
bus_dmamap_t twe_immediate_map; \
struct sysctl_ctx_list sysctl_ctx; \
- struct sysctl_oid *sysctl_tree;
+ struct sysctl_oid *sysctl_tree; \
+ struct lock twe_io_lock; \
+ struct lock twe_config_lock;
/*
* FreeBSD-specific request elements
#define twe_printf(sc, fmt, args...) device_printf(sc->twe_dev, fmt , ##args)
#define twed_printf(twed, fmt, args...) device_printf(twed->twed_dev, fmt , ##args)
-typedef struct bio twe_bio;
-typedef struct bio_queue_head twe_bioq;
+#define TWE_IO_LOCK(sc) lockmgr(&(sc)->twe_io_lock, LK_EXCLUSIVE)
+#define TWE_IO_UNLOCK(sc) lockmgr(&(sc)->twe_io_lock, LK_RELEASE)
+#define TWE_CONFIG_LOCK(sc) lockmgr(&(sc)->twe_config_lock, LK_EXCLUSIVE)
+#define TWE_CONFIG_UNLOCK(sc) lockmgr(&(sc)->twe_config_lock, LK_RELEASE)
+#define TWE_CONFIG_ASSERT_LOCKED(sc) KKASSERT(lockowned(&(sc)->twe_config_lock))
+
+/*
+ * XXX
+ *
+ * Mimics FreeBSD's mtx_assert() behavior.
+ * We might want a global lockassert() function in the future.
+ */
+static __inline void
+twe_lockassert(struct lock *lockp)
+{
+ if (panicstr == NULL && !dumping)
+ KKASSERT(lockstatus(lockp, curthread) != 0);
+}
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: src/sys/dev/twe/twe_freebsd.c,v 1.48 2009/12/25 17:34:43 mav Exp $
+ * $FreeBSD: src/sys/dev/twe/twe_freebsd.c,v 1.54 2012/11/17 01:52:19 svnexp Exp $
*/
/*
#include <dev/raid/twe/twevar.h>
#include <dev/raid/twe/twe_tables.h>
#include <sys/dtype.h>
+#include <sys/mplock2.h>
#include <vm/vm.h>
static d_ioctl_t twe_ioctl_wrapper;
static struct dev_ops twe_ops = {
- { "twe", 0, 0 },
+ { "twe", 0, D_MPSAFE },
.d_open = twe_open,
.d_close = twe_close,
.d_ioctl = twe_ioctl_wrapper,
cdev_t dev = ap->a_head.a_dev;
struct twe_softc *sc = (struct twe_softc *)dev->si_drv1;
+ TWE_IO_LOCK(sc);
+ if (sc->twe_state & TWE_STATE_DETACHING) {
+ TWE_IO_UNLOCK(sc);
+ return (ENXIO);
+ }
sc->twe_state |= TWE_STATE_OPEN;
+ TWE_IO_UNLOCK(sc);
return(0);
}
cdev_t dev = ap->a_head.a_dev;
struct twe_softc *sc = (struct twe_softc *)dev->si_drv1;
+ TWE_IO_LOCK(sc);
sc->twe_state &= ~TWE_STATE_OPEN;
+ TWE_IO_UNLOCK(sc);
return (0);
}
DEVMETHOD(device_suspend, twe_suspend),
DEVMETHOD(device_resume, twe_resume),
- DEVMETHOD(bus_print_child, bus_generic_print_child),
- DEVMETHOD(bus_driver_added, bus_generic_driver_added),
{ 0, 0 }
};
{
struct twe_softc *sc;
int rid, error;
- u_int32_t command;
debug_called(4);
*/
sc = device_get_softc(dev);
sc->twe_dev = dev;
+ lockinit(&sc->twe_io_lock, "twe I/O", 0, LK_CANRECURSE);
+ lockinit(&sc->twe_config_lock, "twe config", 0, LK_CANRECURSE);
sysctl_ctx_init(&sc->sysctl_ctx);
sc->sysctl_tree = SYSCTL_ADD_NODE(&sc->sysctl_ctx,
"TWE driver version");
/*
- * 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_PORTEN) == 0) {
- twe_printf(sc, "register window not available\n");
- return(ENXIO);
- }
- /*
* Force the busmaster enable bit on, in case the BIOS forgot.
*/
- command |= PCIM_CMD_BUSMASTEREN;
- pci_write_config(dev, PCIR_COMMAND, command, 2);
+ pci_enable_busmaster(dev);
/*
* Allocate the PCI register window.
twe_free(sc);
return(ENXIO);
}
- sc->twe_btag = rman_get_bustag(sc->twe_io);
- sc->twe_bhandle = rman_get_bushandle(sc->twe_io);
/*
* Allocate the parent bus DMA tag appropriate for PCI.
twe_free(sc);
return(ENXIO);
}
- if (bus_setup_intr(sc->twe_dev, sc->twe_irq, 0,
+ if (bus_setup_intr(sc->twe_dev, sc->twe_irq, INTR_MPSAFE,
twe_pci_intr, sc, &sc->twe_intr, NULL)) {
twe_printf(sc, "can't set up interrupt\n");
twe_free(sc);
dev_ops_remove_minor(&twe_ops, device_get_unit(sc->twe_dev));
sysctl_ctx_free(&sc->sysctl_ctx);
+ lockuninit(&sc->twe_config_lock);
+ lockuninit(&sc->twe_io_lock);
}
/********************************************************************************
twe_detach(device_t dev)
{
struct twe_softc *sc = device_get_softc(dev);
- int error;
debug_called(4);
- error = EBUSY;
- crit_enter();
- if (sc->twe_state & TWE_STATE_OPEN)
- goto out;
+ TWE_IO_LOCK(sc);
+ if (sc->twe_state & TWE_STATE_OPEN) {
+ TWE_IO_UNLOCK(sc);
+ return (EBUSY);
+ }
+ sc->twe_state |= TWE_STATE_DETACHING;
+ TWE_IO_UNLOCK(sc);
/*
* Shut the controller down.
*/
- if (twe_shutdown(dev))
- goto out;
+ if (twe_shutdown(dev)) {
+ TWE_IO_LOCK(sc);
+ sc->twe_state &= ~TWE_STATE_DETACHING;
+ TWE_IO_UNLOCK(sc);
+ return (EBUSY);
+ }
twe_free(sc);
- error = 0;
- out:
- crit_exit();
- return(error);
+ return(0);
}
/********************************************************************************
debug_called(4);
- crit_enter();
-
/*
* Delete all our child devices.
*/
+ TWE_CONFIG_LOCK(sc);
for (i = 0; i < TWE_MAX_UNITS; i++) {
if (sc->twe_drive[i].td_disk != 0) {
- if ((error = twe_detach_drive(sc, i)) != 0)
- goto out;
+ if ((error = twe_detach_drive(sc, i)) != 0) {
+ TWE_CONFIG_UNLOCK(sc);
+ return (error);
+ }
}
}
+ TWE_CONFIG_UNLOCK(sc);
/*
* Bring the controller down.
*/
+ TWE_IO_LOCK(sc);
twe_deinit(sc);
+ TWE_IO_UNLOCK(sc);
-out:
- crit_exit();
- return(error);
+ return(0);
}
/********************************************************************************
debug_called(4);
- crit_enter();
+ TWE_IO_LOCK(sc);
sc->twe_state |= TWE_STATE_SUSPEND;
+ TWE_IO_UNLOCK(sc);
twe_disable_interrupts(sc);
crit_exit();
debug_called(4);
+ TWE_IO_LOCK(sc);
sc->twe_state &= ~TWE_STATE_SUSPEND;
twe_enable_interrupts(sc);
+ TWE_IO_UNLOCK(sc);
return(0);
}
static void
twe_pci_intr(void *arg)
{
- twe_intr((struct twe_softc *)arg);
+ struct twe_softc *sc = arg;
+
+ TWE_IO_LOCK(sc);
+ twe_intr(sc);
+ TWE_IO_UNLOCK(sc);
}
/********************************************************************************
char buf[80];
int error;
+ get_mplock();
dr->td_disk = device_add_child(sc->twe_dev, NULL, -1);
if (dr->td_disk == NULL) {
+ rel_mplock();
twe_printf(sc, "Cannot add unit\n");
return (EIO);
}
twe_describe_code(twe_table_unitstate, dr->td_state & TWE_PARAM_UNITSTATUS_MASK));
device_set_desc_copy(dr->td_disk, buf);
- if ((error = bus_generic_attach(sc->twe_dev)) != 0) {
+ error = device_probe_and_attach(dr->td_disk);
+ rel_mplock();
+ if (error != 0) {
twe_printf(sc, "Cannot attach unit to controller. error = %d\n", error);
return (EIO);
}
{
int error = 0;
- if ((error = device_delete_child(sc->twe_dev, sc->twe_drive[unit].td_disk)) != 0) {
+ TWE_CONFIG_ASSERT_LOCKED(sc);
+ get_mplock();
+ error = device_delete_child(sc->twe_dev, sc->twe_drive[unit].td_disk);
+ rel_mplock();
+ if (error != 0) {
twe_printf(sc, "failed to delete unit %d\n", unit);
return(error);
}
static d_dump_t twed_dump;
static struct dev_ops twed_ops = {
- { "twed", 0, D_DISK },
+ { "twed", 0, D_DISK | D_MPSAFE},
.d_open = twed_open,
.d_close = twed_close,
.d_read = physread,
devstat_start_transaction(&sc->twed_stats);
/* queue the bio on the controller */
+ TWE_IO_LOCK(sc->twed_controller);
twe_enqueue_bio(sc->twed_controller, bio);
/* poke the controller to start I/O */
twe_startio(sc->twed_controller);
+ TWE_IO_UNLOCK(sc->twed_controller);
return(0);
}
debug_called(4);
/* if no error, transfer completed */
- if ((bp->b_flags & B_ERROR) == 0)
+ if (!(bp->b_flags & B_ERROR))
bp->b_resid = 0;
devstat_end_transaction_buf(&sc->twed_stats, bp);
biodone(bio);
/********************************************************************************
* Allocate a command buffer
*/
-MALLOC_DEFINE(TWE_MALLOC_CLASS, "twe_commands", "twe commands");
+static MALLOC_DEFINE(TWE_MALLOC_CLASS, "twe_commands", "twe commands");
struct twe_request *
twe_allocate_request(struct twe_softc *sc, int tag)
{
struct twe_request *tr;
- int aligned_size;
+ int aligned_size;
/*
* TWE requires requests to be 512-byte aligned. Depend on malloc()
* allocator only guarentees same-size alignment for power-of-2 requests.
*/
aligned_size = (sizeof(struct twe_request) + TWE_ALIGNMASK) &
- ~TWE_ALIGNMASK;
- tr = kmalloc(aligned_size, TWE_MALLOC_CLASS, M_INTWAIT|M_ZERO);
+ ~TWE_ALIGNMASK;
+ tr = kmalloc(aligned_size, TWE_MALLOC_CLASS, M_INTWAIT | M_ZERO);
tr->tr_sc = sc;
tr->tr_tag = tag;
if (bus_dmamap_create(sc->twe_buffer_dmat, 0, &tr->tr_dmamap)) {
debug_called(4);
+ twe_lockassert(&sc->twe_io_lock);
if (sc->twe_state & (TWE_STATE_CTLR_BUSY | TWE_STATE_FRZN)) {
twe_requeue_ready(tr);
return (EBUSY);
struct twe_softc *sc;
int i;
- crit_enter();
for (i = 0; (sc = devclass_get_softc(twe_devclass, i)) != NULL; i++)
twe_print_controller(sc);
kprintf("twed: total bio count in %u out %u\n", twed_bio_in, twed_bio_out);
- crit_exit();
}
#endif
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: src/sys/dev/twe/twe_tables.h,v 1.4 2003/12/02 07:57:20 ps Exp $
+ * $FreeBSD: src/sys/dev/twe/twe_tables.h,v 1.5 2012/11/17 01:52:19 svnexp Exp $
*/
/*
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: src/sys/dev/twe/tweio.h,v 1.5 2005/02/17 19:05:42 vkashyap Exp $
+ * $FreeBSD: src/sys/dev/twe/tweio.h,v 1.6 2012/11/17 01:52:19 svnexp Exp $
*/
#include <sys/ioccom.h>
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: src/sys/dev/twe/twereg.h,v 1.11 2004/05/12 04:10:37 vkashyap Exp $
+ * $FreeBSD: src/sys/dev/twe/twereg.h,v 1.12 2012/11/17 01:52:19 svnexp Exp $
*/
/*
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $FreeBSD: src/sys/dev/twe/twevar.h,v 1.15 2009/12/25 17:34:43 mav Exp $
+ * $FreeBSD: src/sys/dev/twe/twevar.h,v 1.19 2012/11/17 01:52:19 svnexp Exp $
*/
#define TWE_DRIVER_VERSION_STRING "1.50.01.002"
{
/* controller queues and arrays */
TAILQ_HEAD(, twe_request) twe_free; /* command structures available for reuse */
- twe_bioq twe_bioq; /* outstanding I/O operations */
+ struct bio_queue_head twe_bioq; /* outstanding I/O operations */
TAILQ_HEAD(, twe_request) twe_ready; /* requests ready for the controller */
TAILQ_HEAD(, twe_request) twe_busy; /* requests busy in the controller */
TAILQ_HEAD(, twe_request) twe_complete; /* active commands (busy or waiting for completion) */
u_int16_t twe_aen_queue[TWE_Q_LENGTH]; /* AENs queued for userland tool(s) */
int twe_aen_head, twe_aen_tail; /* ringbuffer pointers for AEN queue */
int twe_wait_aen; /* wait-for-aen notification */
+ char twe_aen_buf[80]; /* AEN format buffer */
/* controller status */
int twe_state;
#define TWE_STATE_SUSPEND (1<<3) /* controller is suspended */
#define TWE_STATE_FRZN (1<<4) /* got EINPROGRESS */
#define TWE_STATE_CTLR_BUSY (1<<5) /* controller cmd queue full */
+#define TWE_STATE_DETACHING (1<<6) /* controller is being shut down */
int twe_host_id;
struct twe_qstat twe_qstat[TWEQ_COUNT]; /* queue statistics */
int unit); /* detach drive */
extern void twe_clear_pci_parity_error(struct twe_softc *sc);
extern void twe_clear_pci_abort(struct twe_softc *sc);
-extern void twed_intr(twe_bio *bp); /* return bio from core */
+extern void twed_intr(struct bio *bp); /* return bio from core */
extern struct twe_request *twe_allocate_request(struct twe_softc *sc, int tag); /* allocate request structure */
extern void twe_free_request(struct twe_request *tr); /* free request structure */
extern int twe_map_request(struct twe_request *tr); /* make request visible to controller, do s/g */
static __inline void \
twe_enqueue_ ## name (struct twe_request *tr) \
{ \
- crit_enter(); \
TAILQ_INSERT_TAIL(&tr->tr_sc->twe_ ## name, tr, tr_link); \
TWEQ_ADD(tr->tr_sc, index); \
- crit_exit(); \
} \
static __inline void \
twe_requeue_ ## name (struct twe_request *tr) \
{ \
- crit_enter(); \
TAILQ_INSERT_HEAD(&tr->tr_sc->twe_ ## name, tr, tr_link); \
TWEQ_ADD(tr->tr_sc, index); \
- crit_exit(); \
} \
static __inline struct twe_request * \
twe_dequeue_ ## name (struct twe_softc *sc) \
{ \
struct twe_request *tr; \
\
- crit_enter(); \
if ((tr = TAILQ_FIRST(&sc->twe_ ## name)) != NULL) { \
TAILQ_REMOVE(&sc->twe_ ## name, tr, tr_link); \
TWEQ_REMOVE(sc, index); \
} \
- crit_exit(); \
return(tr); \
} \
static __inline void \
twe_remove_ ## name (struct twe_request *tr) \
{ \
- crit_enter(); \
TAILQ_REMOVE(&tr->tr_sc->twe_ ## name, tr, tr_link); \
TWEQ_REMOVE(tr->tr_sc, index); \
- crit_exit(); \
}
TWEQ_REQUEST_QUEUE(free, TWEQ_FREE)
static __inline void
twe_enqueue_bio(struct twe_softc *sc, struct bio *bio)
{
- crit_enter();
bioqdisksort(&sc->twe_bioq, bio);
/* bioq_insert_tail(&sc->twe_bioq, bio); */
TWEQ_ADD(sc, TWEQ_BIO);
- crit_exit();
}
static __inline
{
struct bio *bio;
- crit_enter();
if ((bio = bioq_first(&sc->twe_bioq)) != NULL) {
bioq_remove(&sc->twe_bioq, bio);
TWEQ_REMOVE(sc, TWEQ_BIO);
}
- crit_exit();
return(bio);
}