Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 27 Aug 2012 17:24:07 +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: r239740 - head/sys/dev/ida
Message-ID:  <201208271724.q7RHO7R0030652@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: jhb
Date: Mon Aug 27 17:24:07 2012
New Revision: 239740
URL: http://svn.freebsd.org/changeset/base/239740

Log:
  Rework the DMA handling in ida(4) and add locking to make this driver
  MPSAFE.
  - Preallocate a full set of QCBs during attach rather than allocating new
    ones on demand to avoid allocations in the I/O path.
  - Remove the explicit bus space tag/handle and use bus_*() on the
    relevant 'struct resource' instead.
  - Defer logical drive probing to an intrhook.
  - Fix ida_detach() to detach and delete child devices (logical drives).
  - Update the DMA handling to support EINPROGRESS by moving the work to
    submit a mapped request into the bus_dma callback routine as well as
    add support for freezing the queue when EINPROGRESS is encountered.
  
  Tested by:	Marco Steinbach  coco executive-computing de

Modified:
  head/sys/dev/ida/ida.c
  head/sys/dev/ida/ida_disk.c
  head/sys/dev/ida/ida_eisa.c
  head/sys/dev/ida/ida_pci.c
  head/sys/dev/ida/idavar.h

Modified: head/sys/dev/ida/ida.c
==============================================================================
--- head/sys/dev/ida/ida.c	Mon Aug 27 17:15:14 2012	(r239739)
+++ head/sys/dev/ida/ida.c	Mon Aug 27 17:24:07 2012	(r239740)
@@ -38,7 +38,9 @@ __FBSDID("$FreeBSD$");
 #include <sys/param.h>
 #include <sys/kernel.h>
 #include <sys/systm.h>
+#include <sys/lock.h>
 #include <sys/malloc.h>
+#include <sys/mutex.h>
 #include <sys/stat.h>
 
 #include <sys/bio.h>
@@ -56,17 +58,17 @@ __FBSDID("$FreeBSD$");
 #include <dev/ida/idaio.h>
 
 /* prototypes */
-static void ida_alloc_qcb(struct ida_softc *ida);
-static void ida_construct_qcb(struct ida_softc *ida);
-static void ida_start(struct ida_softc *ida);
+static int ida_alloc_qcbs(struct ida_softc *ida);
 static void ida_done(struct ida_softc *ida, struct ida_qcb *qcb);
+static void ida_start(struct ida_softc *ida);
+static void ida_startio(struct ida_softc *ida);
+static void ida_startup(void *arg);
+static void ida_timeout(void *arg);
 static int ida_wait(struct ida_softc *ida, struct ida_qcb *qcb);
-static void ida_timeout (void *arg);
 
 static d_ioctl_t ida_ioctl;
 static struct cdevsw ida_cdevsw = {
 	.d_version =	D_VERSION,
-	.d_flags =	D_NEEDGIANT,
 	.d_ioctl =	ida_ioctl,
 	.d_name =	"ida",
 };
@@ -76,10 +78,16 @@ ida_free(struct ida_softc *ida)
 {
 	int i;
 
+	if (ida->ih != NULL)
+		bus_teardown_intr(ida->dev, ida->irq, ida->ih);
+
+	mtx_lock(&ida->lock);
 	callout_stop(&ida->ch);
+	mtx_unlock(&ida->lock);
+	callout_drain(&ida->ch);
 
 	if (ida->buffer_dmat) {
-		for (i = 0; i < ida->num_qcbs; i++)
+		for (i = 0; i < IDA_QCB_MAX; i++)
 			bus_dmamap_destroy(ida->buffer_dmat, ida->qcbs[i].dmamap);
 		bus_dma_tag_destroy(ida->buffer_dmat);
 	}
@@ -96,9 +104,6 @@ ida_free(struct ida_softc *ida)
 	if (ida->qcbs != NULL)
 		free(ida->qcbs, M_DEVBUF);
 
-	if (ida->ih != NULL)
-		bus_teardown_intr(ida->dev, ida->irq, ida->ih);
-
 	if (ida->irq != NULL)
 		bus_release_resource(ida->dev, ida->irq_res_type,
 		    0, ida->irq);
@@ -109,6 +114,8 @@ ida_free(struct ida_softc *ida)
 	if (ida->regs != NULL)
 		bus_release_resource(ida->dev, ida->regs_res_type,
 		    ida->regs_res_id, ida->regs);
+
+	mtx_destroy(&ida->lock);
 }
 
 /*
@@ -130,14 +137,21 @@ ida_get_qcb(struct ida_softc *ida)
 
 	if ((qcb = SLIST_FIRST(&ida->free_qcbs)) != NULL) {
 		SLIST_REMOVE_HEAD(&ida->free_qcbs, link.sle);
-	} else {
-		ida_alloc_qcb(ida);
-		if ((qcb = SLIST_FIRST(&ida->free_qcbs)) != NULL)
-			SLIST_REMOVE_HEAD(&ida->free_qcbs, link.sle);
+		bzero(qcb->hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req));
 	}
 	return (qcb);
 }
 
+static __inline void
+ida_free_qcb(struct ida_softc *ida, struct ida_qcb *qcb)
+{
+
+	qcb->state = QCB_FREE;
+	qcb->buf = NULL;
+	qcb->error = 0;
+	SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle);
+}
+
 static __inline bus_addr_t
 idahwqcbvtop(struct ida_softc *ida, struct ida_hardware_qcb *hwqcb)
 {
@@ -155,42 +169,35 @@ idahwqcbptov(struct ida_softc *ida, bus_
 	return (hwqcb->qcb);
 }
 
-/*
- * XXX
- * since we allocate all QCB space up front during initialization, then
- * why bother with this routine?
- */
-static void
-ida_alloc_qcb(struct ida_softc *ida)
+static int
+ida_alloc_qcbs(struct ida_softc *ida)
 {
 	struct ida_qcb *qcb;
-	int error;
-
-	if (ida->num_qcbs >= IDA_QCB_MAX)
-		return;
-
-	qcb = &ida->qcbs[ida->num_qcbs];
+	int error, i;
 
-	error = bus_dmamap_create(ida->buffer_dmat, /*flags*/0, &qcb->dmamap);
-	if (error != 0)
-		return;
+	for (i = 0; i < IDA_QCB_MAX; i++) {
+		qcb = &ida->qcbs[i];
 
-	qcb->flags = QCB_FREE;
-	qcb->hwqcb = &ida->hwqcbs[ida->num_qcbs];
-	qcb->hwqcb->qcb = qcb;
-	qcb->hwqcb_busaddr = idahwqcbvtop(ida, qcb->hwqcb);
-	SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle);
-	ida->num_qcbs++;
+		error = bus_dmamap_create(ida->buffer_dmat, /*flags*/0, &qcb->dmamap);
+		if (error != 0)
+			return (error);
+
+		qcb->ida = ida;
+		qcb->flags = QCB_FREE;
+		qcb->hwqcb = &ida->hwqcbs[i];
+		qcb->hwqcb->qcb = qcb;
+		qcb->hwqcb_busaddr = idahwqcbvtop(ida, qcb->hwqcb);
+		SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle);
+	}
+	return (0);
 }
 
 int
 ida_init(struct ida_softc *ida)
 {
-	int error;
-
-	ida->unit = device_get_unit(ida->dev);
-	ida->tag = rman_get_bustag(ida->regs);
-	ida->bsh = rman_get_bushandle(ida->regs);
+	struct ida_controller_info cinfo;
+	device_t child;
+	int error, i, unit;
 
 	SLIST_INIT(&ida->free_qcbs);
 	STAILQ_INIT(&ida->qcb_queue);
@@ -219,8 +226,8 @@ ida_init(struct ida_softc *ida)
 		/* nsegments	*/ 1,
 		/* maxsegsz	*/ BUS_SPACE_MAXSIZE_32BIT,
 		/* flags	*/ 0,
-		/* lockfunc	*/ busdma_lock_mutex,
-		/* lockarg	*/ &Giant,
+		/* lockfunc	*/ NULL,
+		/* lockarg	*/ NULL,
 		&ida->hwqcb_dmat);
 	if (error)
 		return (ENOMEM);
@@ -258,26 +265,19 @@ ida_init(struct ida_softc *ida)
 
 	bzero(ida->hwqcbs, IDA_QCB_MAX * sizeof(struct ida_hardware_qcb));
 
-	ida_alloc_qcb(ida);		/* allocate an initial qcb */
-
-	callout_init(&ida->ch, CALLOUT_MPSAFE);
-
-	return (0);
-}
-
-void
-ida_attach(struct ida_softc *ida)
-{
-	struct ida_controller_info cinfo;
-	int error, i;
+	error = ida_alloc_qcbs(ida);
+	if (error)
+		return (error);
 
+	mtx_lock(&ida->lock);
 	ida->cmd.int_enable(ida, 0);
 
 	error = ida_command(ida, CMD_GET_CTRL_INFO, &cinfo, sizeof(cinfo),
 	    IDA_CONTROLLER, 0, DMA_DATA_IN);
 	if (error) {
+		mtx_unlock(&ida->lock);
 		device_printf(ida->dev, "CMD_GET_CTRL_INFO failed.\n");
-		return;
+		return (error);
 	}
 
 	device_printf(ida->dev, "drives=%d firm_rev=%c%c%c%c\n",
@@ -290,33 +290,68 @@ ida_attach(struct ida_softc *ida)
 		error = ida_command(ida, CMD_START_FIRMWARE,
 		    &data, sizeof(data), IDA_CONTROLLER, 0, DMA_DATA_IN);
 		if (error) {
+			mtx_unlock(&ida->lock);
 			device_printf(ida->dev, "CMD_START_FIRMWARE failed.\n");
-			return;
+			return (error);
 		}
 	}
+	
+	ida->cmd.int_enable(ida, 1);
+	ida->flags |= IDA_ATTACHED;
+	mtx_unlock(&ida->lock);
+
+	for (i = 0; i < cinfo.num_drvs; i++) {
+		child = device_add_child(ida->dev, /*"idad"*/NULL, -1);
+		if (child != NULL)
+			device_set_ivars(child, (void *)(intptr_t)i);
+	}
 
-	ida->ida_dev_t = make_dev(&ida_cdevsw, ida->unit,
+	ida->ich.ich_func = ida_startup;
+	ida->ich.ich_arg = ida;
+	if (config_intrhook_establish(&ida->ich) != 0) {
+		device_delete_children(ida->dev);
+		device_printf(ida->dev, "Cannot establish configuration hook\n");
+		return (error);
+	}
+
+	unit = device_get_unit(ida->dev);
+	ida->ida_dev_t = make_dev(&ida_cdevsw, unit,
 				 UID_ROOT, GID_OPERATOR, S_IRUSR | S_IWUSR,
-				 "ida%d", ida->unit);
+				 "ida%d", unit);
 	ida->ida_dev_t->si_drv1 = ida;
 
-	ida->num_drives = 0;
-	for (i = 0; i < cinfo.num_drvs; i++)
-		device_add_child(ida->dev, /*"idad"*/NULL, -1);
+	return (0);
+}
+
+static void
+ida_startup(void *arg)
+{
+	struct ida_softc *ida;
 
-	bus_generic_attach(ida->dev);
+	ida = arg;
 
-	ida->cmd.int_enable(ida, 1);
+	config_intrhook_disestablish(&ida->ich);
+
+	mtx_lock(&Giant);
+	bus_generic_attach(ida->dev);
+	mtx_unlock(&Giant);
 }
 
 int
 ida_detach(device_t dev)
 {
 	struct ida_softc *ida;
-	int error = 0;
+	int error;
 
 	ida = (struct ida_softc *)device_get_softc(dev);
 
+	error = bus_generic_detach(dev);
+	if (error)
+		return (error);
+	error = device_delete_children(dev);
+	if (error)
+		return (error);
+
 	/*
 	 * XXX
 	 * before detaching, we must make sure that the system is
@@ -335,11 +370,25 @@ ida_detach(device_t dev)
 }
 
 static void
-ida_setup_dmamap(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
+ida_data_cb(void *arg, bus_dma_segment_t *segs, int nsegments, int error)
 {
-	struct ida_hardware_qcb *hwqcb = (struct ida_hardware_qcb *)arg;
+	struct ida_hardware_qcb *hwqcb;
+	struct ida_softc *ida;
+	struct ida_qcb *qcb;
+	bus_dmasync_op_t op;
 	int i;
 
+	qcb = arg;
+	ida = qcb->ida;
+	if (!dumping)
+		mtx_assert(&ida->lock, MA_OWNED);
+	if (error) {
+		qcb->error = error;
+		ida_done(ida, qcb);
+		return;
+	}
+
+	hwqcb = qcb->hwqcb;
 	hwqcb->hdr.size = htole16((sizeof(struct ida_req) +
 	    sizeof(struct ida_sgb) * IDA_NSEG) >> 2);
 
@@ -348,6 +397,47 @@ ida_setup_dmamap(void *arg, bus_dma_segm
 		hwqcb->seg[i].length = htole32(segs[i].ds_len);
 	}
 	hwqcb->req.sgcount = nsegments;
+	if (qcb->flags & DMA_DATA_TRANSFER) {
+		switch (qcb->flags & DMA_DATA_TRANSFER) {
+		case DMA_DATA_TRANSFER:
+			op = BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE;
+			break;
+		case DMA_DATA_IN:
+			op = BUS_DMASYNC_PREREAD;
+			break;
+		default:
+			KASSERT((qcb->flags & DMA_DATA_TRANSFER) ==
+			    DMA_DATA_OUT, ("bad DMA data flags"));
+			op = BUS_DMASYNC_PREWRITE;
+			break;
+		}
+		bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
+	}
+	bus_dmamap_sync(ida->hwqcb_dmat, ida->hwqcb_dmamap,
+	    BUS_DMASYNC_PREWRITE | BUS_DMASYNC_PREREAD);
+
+	STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe);
+	ida_start(ida);
+	ida->flags &= ~IDA_QFROZEN;
+}
+
+static int
+ida_map_qcb(struct ida_softc *ida, struct ida_qcb *qcb, void *data,
+    bus_size_t datasize)
+{
+	int error, flags;
+
+	if (ida->flags & IDA_INTERRUPTS)
+		flags = BUS_DMA_WAITOK;
+	else
+		flags = BUS_DMA_NOWAIT;
+	error = bus_dmamap_load(ida->buffer_dmat, qcb->dmamap, data, datasize,
+	    ida_data_cb, qcb, flags);
+	if (error == EINPROGRESS) {
+		ida->flags |= IDA_QFROZEN;
+		error = 0;
+	}
+	return (error);
 }
 
 int
@@ -356,105 +446,96 @@ ida_command(struct ida_softc *ida, int c
 {
 	struct ida_hardware_qcb *hwqcb;
 	struct ida_qcb *qcb;
-	bus_dmasync_op_t op;
-	int s, error;
+	int error;
 
-	s = splbio();
+	if (!dumping)
+		mtx_assert(&ida->lock, MA_OWNED);
 	qcb = ida_get_qcb(ida);
-	splx(s);
 
 	if (qcb == NULL) {
-		printf("ida_command: out of QCBs");
+		device_printf(ida->dev, "out of QCBs\n");
 		return (EAGAIN);
 	}
 
+	qcb->flags = flags | IDA_COMMAND;
 	hwqcb = qcb->hwqcb;
-	bzero(hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req));
-
-	bus_dmamap_load(ida->buffer_dmat, qcb->dmamap,
-	    (void *)data, datasize, ida_setup_dmamap, hwqcb, 0);
-	op = qcb->flags & DMA_DATA_IN ?
-	    BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE;
-	bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
-
 	hwqcb->hdr.drive = drive;
 	hwqcb->req.blkno = htole32(pblkno);
 	hwqcb->req.bcount = htole16(howmany(datasize, DEV_BSIZE));
 	hwqcb->req.command = command;
 
-	qcb->flags = flags | IDA_COMMAND;
-
-	s = splbio();
-	STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe);
-	ida_start(ida);
-	error = ida_wait(ida, qcb);
-	splx(s);
+	error = ida_map_qcb(ida, qcb, data, datasize);
+	if (error == 0) {
+		error = ida_wait(ida, qcb);
+		/* Don't free QCB on a timeout in case it later completes. */
+		if (error)
+			return (error);
+		error = qcb->error;
+	}
 
 	/* XXX should have status returned here? */
 	/* XXX have "status pointer" area in QCB? */
 
+	ida_free_qcb(ida, qcb);
 	return (error);
 }
 
 void
 ida_submit_buf(struct ida_softc *ida, struct bio *bp)
 {
+	mtx_lock(&ida->lock);
 	bioq_insert_tail(&ida->bio_queue, bp);
-	ida_construct_qcb(ida);
-	ida_start(ida);
+	ida_startio(ida);
+	mtx_unlock(&ida->lock);
 }
 
 static void
-ida_construct_qcb(struct ida_softc *ida)
+ida_startio(struct ida_softc *ida)
 {
 	struct ida_hardware_qcb *hwqcb;
 	struct ida_qcb *qcb;
-	bus_dmasync_op_t op;
+	struct idad_softc *drv;
 	struct bio *bp;
+	int error;
 
-	bp = bioq_first(&ida->bio_queue);
-	if (bp == NULL)
-		return;				/* no more buffers */
-
-	qcb = ida_get_qcb(ida);
-	if (qcb == NULL)
-		return;				/* out of resources */
-
-	bioq_remove(&ida->bio_queue, bp);
-	qcb->buf = bp;
-	qcb->flags = bp->bio_cmd == BIO_READ ? DMA_DATA_IN : DMA_DATA_OUT;
-
-	hwqcb = qcb->hwqcb;
-	bzero(hwqcb, sizeof(struct ida_hdr) + sizeof(struct ida_req));
-
-	bus_dmamap_load(ida->buffer_dmat, qcb->dmamap,
-	    (void *)bp->bio_data, bp->bio_bcount, ida_setup_dmamap, hwqcb, 0);
-	op = qcb->flags & DMA_DATA_IN ?
-	    BUS_DMASYNC_PREREAD : BUS_DMASYNC_PREWRITE;
-	bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
+	mtx_assert(&ida->lock, MA_OWNED);
+	for (;;) {
+		if (ida->flags & IDA_QFROZEN)
+			return;
+		bp = bioq_first(&ida->bio_queue);
+		if (bp == NULL)
+			return;				/* no more buffers */
+
+		qcb = ida_get_qcb(ida);
+		if (qcb == NULL)
+			return;				/* out of resources */
+
+		bioq_remove(&ida->bio_queue, bp);
+		qcb->buf = bp;
+		qcb->flags = bp->bio_cmd == BIO_READ ? DMA_DATA_IN : DMA_DATA_OUT;
 
-	{
-		struct idad_softc *drv = (struct idad_softc *)bp->bio_driver1;
+		hwqcb = qcb->hwqcb;
+		drv = bp->bio_driver1;
 		hwqcb->hdr.drive = drv->drive;
-	}
+		hwqcb->req.blkno = bp->bio_pblkno;
+		hwqcb->req.bcount = howmany(bp->bio_bcount, DEV_BSIZE);
+		hwqcb->req.command = bp->bio_cmd == BIO_READ ? CMD_READ : CMD_WRITE;
 
-	hwqcb->req.blkno = bp->bio_pblkno;
-	hwqcb->req.bcount = howmany(bp->bio_bcount, DEV_BSIZE);
-	hwqcb->req.command = bp->bio_cmd == BIO_READ ? CMD_READ : CMD_WRITE;
-
-	STAILQ_INSERT_TAIL(&ida->qcb_queue, qcb, link.stqe);
+		error = ida_map_qcb(ida, qcb, bp->bio_data, bp->bio_bcount);
+		if (error) {
+			qcb->error = error;
+			ida_done(ida, qcb);
+		}
+	}
 }
 
-/*
- * This routine will be called from ida_intr in order to queue up more
- * I/O, meaning that we may be in an interrupt context.  Hence, we should
- * not muck around with spl() in this routine.
- */
 static void
 ida_start(struct ida_softc *ida)
 {
 	struct ida_qcb *qcb;
 
+	if (!dumping)
+		mtx_assert(&ida->lock, MA_OWNED);
 	while ((qcb = STAILQ_FIRST(&ida->qcb_queue)) != NULL) {
 		if (ida->cmd.fifo_full(ida))
 			break;
@@ -465,7 +546,7 @@ ida_start(struct ida_softc *ida)
 		 */
 
 		/* Set a timeout. */
-		if (!ida->qactive)
+		if (!ida->qactive && !dumping)
 			callout_reset(&ida->ch, hz * 5, ida_timeout, ida);
 		ida->qactive++;
 
@@ -481,17 +562,23 @@ ida_wait(struct ida_softc *ida, struct i
 	bus_addr_t completed;
 	int delay;
 
+	if (!dumping)
+		mtx_assert(&ida->lock, MA_OWNED);
 	if (ida->flags & IDA_INTERRUPTS) {
-		if (tsleep(qcb, PRIBIO, "idacmd", 5 * hz))
+		if (mtx_sleep(qcb, &ida->lock, PRIBIO, "idacmd", 5 * hz)) {
+			qcb->state = QCB_TIMEDOUT;
 			return (ETIMEDOUT);
+		}
 		return (0);
 	}
 
 again:
 	delay = 5 * 1000 * 100;			/* 5 sec delay */
 	while ((completed = ida->cmd.done(ida)) == 0) {
-		if (delay-- == 0)
+		if (delay-- == 0) {
+			qcb->state = QCB_TIMEDOUT;
 			return (ETIMEDOUT);
+		}
 		DELAY(10);
 	}
 
@@ -511,8 +598,11 @@ ida_intr(void *data)
 
 	ida = (struct ida_softc *)data;
 
-	if (ida->cmd.int_pending(ida) == 0)
+	mtx_lock(&ida->lock);
+	if (ida->cmd.int_pending(ida) == 0) {
+		mtx_unlock(&ida->lock);
 		return;				/* not our interrupt */
+	}
 
 	while ((completed = ida->cmd.done(ida)) != 0) {
 		qcb = idahwqcbptov(ida, completed & ~3);
@@ -527,7 +617,8 @@ ida_intr(void *data)
 			qcb->hwqcb->req.error = CMD_REJECTED;
 		ida_done(ida, qcb);
 	}
-	ida_start(ida);
+	ida_startio(ida);
+	mtx_unlock(&ida->lock);
 }
 
 /*
@@ -536,19 +627,35 @@ ida_intr(void *data)
 static void
 ida_done(struct ida_softc *ida, struct ida_qcb *qcb)
 {
-	int error = 0;
+	bus_dmasync_op_t op;
+	int active, error = 0;
 
 	/*
 	 * finish up command
 	 */
-	if (qcb->flags & DMA_DATA_TRANSFER) {
-		bus_dmasync_op_t op;
-
-		op = qcb->flags & DMA_DATA_IN ?
-		    BUS_DMASYNC_POSTREAD : BUS_DMASYNC_POSTWRITE;
+	if (!dumping)
+		mtx_assert(&ida->lock, MA_OWNED);
+	active = (qcb->state != QCB_FREE);
+	if (qcb->flags & DMA_DATA_TRANSFER && active) {
+		switch (qcb->flags & DMA_DATA_TRANSFER) {
+		case DMA_DATA_TRANSFER:
+			op = BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE;
+			break;
+		case DMA_DATA_IN:
+			op = BUS_DMASYNC_POSTREAD;
+			break;
+		default:
+			KASSERT((qcb->flags & DMA_DATA_TRANSFER) ==
+			    DMA_DATA_OUT, ("bad DMA data flags"));
+			op = BUS_DMASYNC_POSTWRITE;
+			break;
+		}
 		bus_dmamap_sync(ida->buffer_dmat, qcb->dmamap, op);
 		bus_dmamap_unload(ida->buffer_dmat, qcb->dmamap);
 	}
+	if (active)
+		bus_dmamap_sync(ida->hwqcb_dmat, ida->hwqcb_dmamap,
+		    BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
 
 	if (qcb->hwqcb->req.error & SOFT_ERROR) {
 		if (qcb->buf)
@@ -571,32 +678,37 @@ ida_done(struct ida_softc *ida, struct i
 		error = 1;
 		device_printf(ida->dev, "invalid request\n");
 	}
+	if (qcb->error) {
+		error = 1;
+		device_printf(ida->dev, "request failed to map: %d\n", qcb->error);
+	}
 
 	if (qcb->flags & IDA_COMMAND) {
 		if (ida->flags & IDA_INTERRUPTS)
 			wakeup(qcb);
+		if (qcb->state == QCB_TIMEDOUT)
+			ida_free_qcb(ida, qcb);
 	} else {
 		KASSERT(qcb->buf != NULL, ("ida_done(): qcb->buf is NULL!"));
 		if (error)
 			qcb->buf->bio_flags |= BIO_ERROR;
 		idad_intr(qcb->buf);
+		ida_free_qcb(ida, qcb);
 	}
 
+	if (!active)
+		return;
+
 	ida->qactive--;
 	/* Reschedule or cancel timeout */
 	if (ida->qactive)
 		callout_reset(&ida->ch, hz * 5, ida_timeout, ida);
 	else
 		callout_stop(&ida->ch);
-
-	qcb->state = QCB_FREE;
-	qcb->buf = NULL;
-	SLIST_INSERT_HEAD(&ida->free_qcbs, qcb, link.sle);
-	ida_construct_qcb(ida);
 }
 
 static void
-ida_timeout (void *arg)
+ida_timeout(void *arg)
 {
 	struct ida_softc *ida;
 
@@ -661,8 +773,10 @@ ida_ioctl (struct cdev *dev, u_long cmd,
 			daddr = &data;
 			len = sizeof(data);
 		}
+		mtx_lock(&sc->lock);
 		error = ida_command(sc, uc->command, daddr, len,
 				    uc->drive, uc->blkno, flags);
+		mtx_unlock(&sc->lock);
 		break;
 	default:
 		error = ENOIOCTL;

Modified: head/sys/dev/ida/ida_disk.c
==============================================================================
--- head/sys/dev/ida/ida_disk.c	Mon Aug 27 17:15:14 2012	(r239739)
+++ head/sys/dev/ida/ida_disk.c	Mon Aug 27 17:24:07 2012	(r239740)
@@ -87,7 +87,6 @@ static void
 idad_strategy(struct bio *bp)
 {
 	struct idad_softc *drv;
-	int s;
 
 	drv = bp->bio_disk->d_drv1;
 	if (drv == NULL) {
@@ -104,9 +103,7 @@ idad_strategy(struct bio *bp)
 	}
 
 	bp->bio_driver1 = drv;
-	s = splbio();
 	ida_submit_buf(drv->controller, bp);
-	splx(s);
 	return;
 
 bad:
@@ -179,11 +176,12 @@ idad_attach(device_t dev)
 	drv->dev = dev;
 	drv->controller = (struct ida_softc *)device_get_softc(parent);
 	drv->unit = device_get_unit(dev);
-	drv->drive = drv->controller->num_drives;
-	drv->controller->num_drives++;
+	drv->drive = (intptr_t)device_get_ivars(dev);
 
+	mtx_lock(&drv->controller->lock);
 	error = ida_command(drv->controller, CMD_GET_LOG_DRV_INFO,
 	    &dinfo, sizeof(dinfo), drv->drive, 0, DMA_DATA_IN);
+	mtx_unlock(&drv->controller->lock);
 	if (error) {
 		device_printf(dev, "CMD_GET_LOG_DRV_INFO failed\n");
 		return (ENXIO);
@@ -213,7 +211,6 @@ idad_attach(device_t dev)
 	drv->disk->d_drv1 = drv;
 	drv->disk->d_maxsize = DFLTPHYS;		/* XXX guess? */
 	drv->disk->d_unit = drv->unit;
-	drv->disk->d_flags = DISKFLAG_NEEDSGIANT;
 	disk_create(drv->disk, DISK_VERSION);
 
 	return (0);

Modified: head/sys/dev/ida/ida_eisa.c
==============================================================================
--- head/sys/dev/ida/ida_eisa.c	Mon Aug 27 17:15:14 2012	(r239739)
+++ head/sys/dev/ida/ida_eisa.c	Mon Aug 27 17:24:07 2012	(r239740)
@@ -282,6 +282,8 @@ ida_eisa_attach(device_t dev)
 	board = ida_eisa_match(eisa_get_id(dev));
 	ida->cmd = *board->accessor;
 	ida->flags = board->flags;
+	mtx_init(&ida->lock, "ida", NULL, MTX_DEF);
+	callout_init_mtx(&ida->ch, &ida->lock, 0);
 
 	ida->regs_res_type = SYS_RES_IOPORT;
 	ida->regs_res_id = 0;
@@ -293,7 +295,7 @@ ida_eisa_attach(device_t dev)
 	}
 
 	error = bus_dma_tag_create(
-		/* parent	*/	NULL,
+		/* parent	*/	bus_get_dma_tag(dev),
 		/* alignment	*/	0,
 		/* boundary	*/	0,
 		/* lowaddr	*/	BUS_SPACE_MAXADDR_32BIT,
@@ -323,7 +325,7 @@ ida_eisa_attach(device_t dev)
 		return (ENOMEM);
 	}
 
-	error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY,
+	error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY | INTR_MPSAFE,
 	    NULL, ida_intr, ida, &ida->ih);
 	if (error) {
 		device_printf(dev, "can't setup interrupt\n");
@@ -337,9 +339,6 @@ ida_eisa_attach(device_t dev)
 		return (error);
 	}
 
-	ida_attach(ida);
-	ida->flags |= IDA_ATTACHED;
-
 	return (0);
 }
 

Modified: head/sys/dev/ida/ida_pci.c
==============================================================================
--- head/sys/dev/ida/ida_pci.c	Mon Aug 27 17:15:14 2012	(r239739)
+++ head/sys/dev/ida/ida_pci.c	Mon Aug 27 17:24:07 2012	(r239740)
@@ -236,23 +236,14 @@ ida_pci_attach(device_t dev)
 	struct ida_board *board = ida_pci_match(dev);
 	u_int32_t id = pci_get_devid(dev);
 	struct ida_softc *ida;
-	u_int command;
 	int error, rid;
 
-	command = pci_read_config(dev, PCIR_COMMAND, 1);
-
-	/*
-	 * it appears that this board only does MEMIO access.
-	 */
-	if ((command & PCIM_CMD_MEMEN) == 0) {
-	        device_printf(dev, "Only memory mapped I/O is supported\n");
-		return (ENXIO);
-	}
-
 	ida = (struct ida_softc *)device_get_softc(dev);
 	ida->dev = dev;
 	ida->cmd = *board->accessor;
 	ida->flags = board->flags;
+	mtx_init(&ida->lock, "ida", NULL, MTX_DEF);
+	callout_init_mtx(&ida->ch, &ida->lock, 0);
 
 	ida->regs_res_type = SYS_RES_MEMORY;
 	ida->regs_res_id = IDA_PCI_MEMADDR;
@@ -295,7 +286,7 @@ ida_pci_attach(device_t dev)
 	        ida_free(ida);
 	        return (ENOMEM);
 	}
-	error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY,
+	error = bus_setup_intr(dev, ida->irq, INTR_TYPE_BIO | INTR_ENTROPY | INTR_MPSAFE,
 	    NULL, ida_intr, ida, &ida->ih);
 	if (error) {
 		device_printf(dev, "can't setup interrupt\n");
@@ -308,8 +299,6 @@ ida_pci_attach(device_t dev)
 	        ida_free(ida);
 	        return (error);
 	}
-	ida_attach(ida);
-	ida->flags |= IDA_ATTACHED;
 
 	return (0);
 }

Modified: head/sys/dev/ida/idavar.h
==============================================================================
--- head/sys/dev/ida/idavar.h	Mon Aug 27 17:15:14 2012	(r239739)
+++ head/sys/dev/ida/idavar.h	Mon Aug 27 17:24:07 2012	(r239740)
@@ -34,18 +34,18 @@
 #define	_IDAVAR_H
 
 #define	ida_inb(ida, port) \
-	bus_space_read_1((ida)->tag, (ida)->bsh, port)
+	bus_read_1((ida)->regs, port)
 #define	ida_inw(ida, port) \
-	bus_space_read_2((ida)->tag, (ida)->bsh, port)
+	bus_read_2((ida)->regs, port)
 #define	ida_inl(ida, port) \
-	bus_space_read_4((ida)->tag, (ida)->bsh, port)
+	bus_read_4((ida)->regs, port)
 
 #define	ida_outb(ida, port, val) \
-	bus_space_write_1((ida)->tag, (ida)->bsh, port, val)
+	bus_write_1((ida)->regs, port, val)
 #define	ida_outw(ida, port, val) \
-	bus_space_write_2((ida)->tag, (ida)->bsh, port, val)
+	bus_write_2((ida)->regs, port, val)
 #define	ida_outl(ida, port, val) \
-	bus_space_write_4((ida)->tag, (ida)->bsh, port, val)
+	bus_write_4((ida)->regs, port, val)
 
 struct ida_hdr {
 	u_int8_t	drive;		/* logical drive */
@@ -83,6 +83,7 @@ struct ida_hardware_qcb {
 typedef enum {
 	QCB_FREE		= 0x0000,
 	QCB_ACTIVE		= 0x0001,	/* waiting for completion */
+	QCB_TIMEDOUT		= 0x0002,
 } qcb_state;
 
 #define	DMA_DATA_IN	0x0001
@@ -93,8 +94,11 @@ typedef enum {
 #define	IDA_QCB_MAX	256
 #define	IDA_CONTROLLER	0		/* drive "number" for controller */
 
+struct ida_softc;
+
 struct ida_qcb {
 	struct		ida_hardware_qcb *hwqcb;
+	struct		ida_softc *ida;
 	qcb_state	state;
 	short		flags;
 	union {
@@ -104,10 +108,9 @@ struct ida_qcb {
 	bus_dmamap_t	dmamap;
 	bus_addr_t	hwqcb_busaddr;
 	struct		bio *buf;		/* bio associated with qcb */
+	int		error;
 };
 
-struct ida_softc;
-
 struct ida_access {
 	int		(*fifo_full)(struct ida_softc *);
 	void		(*submit)(struct ida_softc *, struct ida_qcb *);
@@ -122,10 +125,10 @@ struct ida_access {
 #define	IDA_ATTACHED	0x01		/* attached */
 #define	IDA_FIRMWARE	0x02		/* firmware must be started */
 #define	IDA_INTERRUPTS	0x04		/* interrupts enabled */
+#define	IDA_QFROZEN	0x08		/* request queue frozen */
 
 struct ida_softc {
 	device_t	dev;
-	int		unit;
 
 	struct callout	ch;
 	struct cdev *ida_dev_t;
@@ -138,8 +141,8 @@ struct ida_softc {
 	struct		resource *irq;
 	void		*ih;
 
-	bus_space_tag_t		tag;
-	bus_space_handle_t	bsh;
+	struct mtx	lock;
+	struct intr_config_hook ich;
 
 	/* various DMA tags */
 	bus_dma_tag_t	parent_dmat;
@@ -151,8 +154,6 @@ struct ida_softc {
 
 	bus_dma_tag_t	sg_dmat;
 
-	int		num_drives;
-	int		num_qcbs;
 	int		flags;
 
 	int		qactive;
@@ -197,7 +198,6 @@ extern struct ida_softc *ida_alloc(devic
 	int regs_type, int regs_id, bus_dma_tag_t parent_dmat);
 extern void ida_free(struct ida_softc *ida);
 extern int ida_init(struct ida_softc *ida);
-extern void ida_attach(struct ida_softc *ida);
 extern int ida_command(struct ida_softc *ida, int command, void *data,
 	int datasize, int drive, u_int32_t pblkno, int flags);
 extern void ida_submit_buf(struct ida_softc *ida, struct bio *bp);



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201208271724.q7RHO7R0030652>