aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Baldwin <jhb@FreeBSD.org>2014-11-18 22:12:51 +0000
committerJohn Baldwin <jhb@FreeBSD.org>2014-11-18 22:12:51 +0000
commitc1dffb2f082f9e49e1968b3b61bf4fdedcdf1e2d (patch)
tree34cc3119a34f44f70a688d0ef0e452b8ef311c45
parent42e8c47b7861e51f07a88a304708148ed0a84dea (diff)
downloadsrc-c1dffb2f082f9e49e1968b3b61bf4fdedcdf1e2d.tar.gz
src-c1dffb2f082f9e49e1968b3b61bf4fdedcdf1e2d.zip
Add locking to wds(4) and mark MPSAFE.
- Add per-softc mutex. - Use mutex for CAM SIM lock. - Use bus_*() instead of inb() and outb(). - Use bus_alloc_resource_any() when reasonable. Tested by: no one
Notes
Notes: svn path=/head/; revision=274680
-rw-r--r--sys/dev/wds/wd7000.c174
1 files changed, 84 insertions, 90 deletions
diff --git a/sys/dev/wds/wd7000.c b/sys/dev/wds/wd7000.c
index 3ea5b29b3a6c..4a0caf4f0f7b 100644
--- a/sys/dev/wds/wd7000.c
+++ b/sys/dev/wds/wd7000.c
@@ -159,8 +159,8 @@ __FBSDID("$FreeBSD$");
#include <isa/isavar.h>
#include <isa/pnpvar.h>
-#define WDSTOPHYS(wp, a) ( ((u_long)a) - ((u_long)wp->dx) + ((u_long)wp->dx_p) )
-#define WDSTOVIRT(wp, a) ( ((char *)a) - ((char*)wp->dx_p) + ((char *)wp->dx) )
+#define WDSTOPHYS(wp, a) ( ((uintptr_t)a) - ((uintptr_t)wp->dx) + (wp->dx_p) )
+#define WDSTOVIRT(wp, a) ( ((a) - (wp->dx_p)) + ((char *)wp->dx) )
/* 0x10000 (64k) should be enough. But just to be sure... */
#define BUFSIZ 0x12000
@@ -298,8 +298,8 @@ struct wdsdx {
struct wds {
device_t dev;
+ struct mtx lock;
int unit;
- int addr;
int drq;
struct cam_sim *sim; /* SIM descriptor for this card */
struct cam_path *path; /* wildcard path for this card */
@@ -307,7 +307,7 @@ struct wds {
u_int32_t data_free;
u_int32_t wdsr_free;
struct wdsdx *dx;
- struct wdsdx *dx_p; /* physical address */
+ bus_addr_t dx_p; /* physical address */
struct resource *port_r;
int port_rid;
struct resource *drq_r;
@@ -323,7 +323,8 @@ struct wds {
static int wds_probe(device_t dev);
static int wds_attach(device_t dev);
-static void wds_intr(struct wds *wp);
+static void wds_intr(void *arg);
+static void wds_intr_locked(struct wds *wp);
static void wds_action(struct cam_sim * sim, union ccb * ccb);
static void wds_poll(struct cam_sim * sim);
@@ -345,8 +346,8 @@ static void wds_done(struct wds *wp, struct wds_req *r, u_int8_t stat);
static int wds_runsense(struct wds *wp, struct wds_req *r);
static int wds_getvers(struct wds *wp);
-static int wds_cmd(int base, u_int8_t * p, int l);
-static void wds_wait(int reg, int mask, int val);
+static int wds_cmd(struct wds *wp, u_int8_t * p, int l);
+static void wds_wait(struct wds *wp, int reg, int mask, int val);
static struct wds_req *cmdtovirt(struct wds *wp, u_int32_t phys);
@@ -455,6 +456,7 @@ static int
wds_probe(device_t dev)
{
struct wds *wp;
+ unsigned long addr;
int error = 0;
int irq;
@@ -466,14 +468,13 @@ wds_probe(device_t dev)
wp->unit = device_get_unit(dev);
wp->dev = dev;
- wp->addr = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
- if (wp->addr == 0 || wp->addr <0x300
- || wp->addr > 0x3f8 || wp->addr & 0x7) {
- device_printf(dev, "invalid port address 0x%x\n", wp->addr);
+ addr = bus_get_resource_start(dev, SYS_RES_IOPORT, 0 /*rid*/);
+ if (addr == 0 || addr <0x300 || addr > 0x3f8 || addr & 0x7) {
+ device_printf(dev, "invalid port address 0x%lx\n", addr);
return (ENXIO);
}
- if (bus_set_resource(dev, SYS_RES_IOPORT, 0, wp->addr, WDS_NPORTS) < 0)
+ if (bus_set_resource(dev, SYS_RES_IOPORT, 0, addr, WDS_NPORTS) < 0)
return (ENXIO);
/* get the DRQ */
@@ -491,9 +492,8 @@ wds_probe(device_t dev)
}
wp->port_rid = 0;
- wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &wp->port_rid,
- /*start*/ 0, /*end*/ ~0,
- /*count*/ 0, RF_ACTIVE);
+ wp->port_r = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &wp->port_rid,
+ RF_ACTIVE);
if (wp->port_r == NULL)
return (ENXIO);
@@ -519,32 +519,29 @@ wds_attach(device_t dev)
int error = 0;
wp = (struct wds *)device_get_softc(dev);
+ mtx_init(&wp->lock, "wds", NULL, MTX_DEF);
wp->port_rid = 0;
- wp->port_r = bus_alloc_resource(dev, SYS_RES_IOPORT, &wp->port_rid,
- /*start*/ 0, /*end*/ ~0,
- /*count*/ 0, RF_ACTIVE);
+ wp->port_r = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &wp->port_rid,
+ RF_ACTIVE);
if (wp->port_r == NULL)
- return (ENXIO);
+ goto bad;
/* We must now release resources on error. */
wp->drq_rid = 0;
- wp->drq_r = bus_alloc_resource(dev, SYS_RES_DRQ, &wp->drq_rid,
- /*start*/ 0, /*end*/ ~0,
- /*count*/ 0, RF_ACTIVE);
+ wp->drq_r = bus_alloc_resource_any(dev, SYS_RES_DRQ, &wp->drq_rid,
+ RF_ACTIVE);
if (wp->drq_r == NULL)
goto bad;
wp->intr_rid = 0;
- wp->intr_r = bus_alloc_resource(dev, SYS_RES_IRQ, &wp->intr_rid,
- /*start*/ 0, /*end*/ ~0,
- /*count*/ 0, RF_ACTIVE);
+ wp->intr_r = bus_alloc_resource_any(dev, SYS_RES_IRQ, &wp->intr_rid,
+ RF_ACTIVE);
if (wp->intr_r == NULL)
goto bad;
- error = bus_setup_intr(dev, wp->intr_r, INTR_TYPE_CAM | INTR_ENTROPY,
- NULL, (driver_intr_t *)wds_intr, (void *)wp,
- &wp->intr_cookie);
+ error = bus_setup_intr(dev, wp->intr_r, INTR_TYPE_CAM | INTR_ENTROPY |
+ INTR_MPSAFE, NULL, wds_intr, wp, &wp->intr_cookie);
if (error)
goto bad;
@@ -557,8 +554,8 @@ wds_attach(device_t dev)
/*maxsize*/ sizeof(* wp->dx),
/*nsegments*/ 1,
/*maxsegsz*/ sizeof(* wp->dx), /*flags*/ 0,
- /*lockfunc*/busdma_lock_mutex,
- /*lockarg*/&Giant,
+ /*lockfunc*/NULL,
+ /*lockarg*/NULL,
&wp->bustag);
if (error)
goto bad;
@@ -607,15 +604,17 @@ wds_attach(device_t dev)
goto bad;
sim = cam_sim_alloc(wds_action, wds_poll, "wds", (void *) wp,
- wp->unit, &Giant, 1, 1, devq);
+ wp->unit, &wp->lock, 1, 1, devq);
if (sim == NULL) {
cam_simq_free(devq);
goto bad;
}
wp->sim = sim;
+ mtx_lock(&wp->lock);
if (xpt_bus_register(sim, dev, 0) != CAM_SUCCESS) {
cam_sim_free(sim, /* free_devq */ TRUE);
+ mtx_unlock(&wp->lock);
goto bad;
}
if (xpt_create_path(&pathp, /* periph */ NULL,
@@ -623,14 +622,17 @@ wds_attach(device_t dev)
CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
xpt_bus_deregister(cam_sim_path(sim));
cam_sim_free(sim, /* free_devq */ TRUE);
+ mtx_unlock(&wp->lock);
goto bad;
}
+ mtx_unlock(&wp->lock);
wp->path = pathp;
return (0);
bad:
wds_free_resources(wp);
+ mtx_destroy(&wp->lock);
if (error)
return (error);
else /* exact error is unknown */
@@ -759,19 +761,29 @@ wdsr_alloc(struct wds *wp)
}
static void
-wds_intr(struct wds *wp)
+wds_intr(void *arg)
+{
+ struct wds *wp;
+
+ wp = arg;
+ mtx_lock(&wp->lock);
+ wds_intr_locked(wp);
+ mtx_unlock(&wp->lock);
+}
+
+static void
+wds_intr_locked(struct wds *wp)
{
struct wds_req *rp;
struct wds_mb *in;
u_int8_t stat;
u_int8_t c;
- int addr = wp->addr;
DBG(DBX "wds%d: interrupt [\n", wp->unit);
smallog('[');
- if (inb(addr + WDS_STAT) & WDS_IRQ) {
- c = inb(addr + WDS_IRQSTAT);
+ if (bus_read_1(wp->port_r, WDS_STAT) & WDS_IRQ) {
+ c = bus_read_1(wp->port_r, WDS_IRQSTAT);
if ((c & WDSI_MASK) == WDSI_MSVC) {
c = c & ~WDSI_MASK;
in = &wp->dx->imbs[c];
@@ -790,7 +802,7 @@ wds_intr(struct wds *wp)
} else
device_printf(wp->dev,
"weird interrupt, irqstat=0x%x\n", c);
- outb(addr + WDS_IRQACK, 0);
+ bus_write_1(wp->port_r, WDS_IRQACK, 0);
} else {
smallog('?');
}
@@ -934,12 +946,12 @@ wds_runsense(struct wds *wp, struct wds_req *r)
scsi_ulto3b(sizeof(struct scsi_sense_data), r->cmd.len);
r->cmd.write = 0x80;
- outb(wp->addr + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
+ bus_write_1(wp->port_r, WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
wp->dx->ombs[r->ombn].stat = 1;
c = WDSC_MSTART(r->ombn);
- if (wds_cmd(wp->addr, &c, sizeof c) != 0) {
+ if (wds_cmd(wp, &c, sizeof c) != 0) {
device_printf(wp->dev, "unable to start outgoing sense mbox\n");
wp->dx->ombs[r->ombn].stat = 0;
wdsr_ccb_done(wp, r, r->ccb, CAM_AUTOSENSE_FAIL);
@@ -958,12 +970,9 @@ static int
wds_getvers(struct wds *wp)
{
struct wds_req *r;
- int base;
u_int8_t c;
int i;
- base = wp->addr;
-
r = wdsr_alloc(wp);
if (!r) {
device_printf(wp->dev, "no request slot available!\n");
@@ -978,10 +987,10 @@ wds_getvers(struct wds *wp)
bzero(&r->cmd, sizeof r->cmd);
r->cmd.cmd = WDSX_GETFIRMREV;
- outb(base + WDS_HCR, WDSH_DRQEN);
+ bus_write_1(wp->port_r, WDS_HCR, WDSH_DRQEN);
c = WDSC_MSTART(r->ombn);
- if (wds_cmd(base, (u_int8_t *) & c, sizeof c)) {
+ if (wds_cmd(wp, (u_int8_t *) & c, sizeof c)) {
device_printf(wp->dev, "version request failed\n");
wp->wdsr_free |= (1 << r->id);
wp->dx->ombs[r->ombn].stat = 0;
@@ -989,14 +998,14 @@ wds_getvers(struct wds *wp)
}
while (1) {
i = 0;
- while ((inb(base + WDS_STAT) & WDS_IRQ) == 0) {
+ while ((bus_read_1(wp->port_r, WDS_STAT) & WDS_IRQ) == 0) {
DELAY(9000);
if (++i == 100) {
device_printf(wp->dev, "getvers timeout\n");
return (-1);
}
}
- wds_intr(wp);
+ wds_intr_locked(wp);
if (r->flags & WR_DONE) {
device_printf(wp->dev, "firmware version %d.%02d\n",
r->cmd.targ, r->cmd.scb[0]);
@@ -1016,9 +1025,8 @@ wdsr_ccb_done(struct wds *wp, struct wds_req *r,
/* To implement timeouts we would need to know how to abort the
* command on controller, and this is a great mystery.
* So for now we just pass the responsibility for timeouts
- * to the controlles itself, it does that reasonably good.
+ * to the controller itself, it does that reasonably good.
*/
- /* untimeout(_timeout, (caddr_t) hcb, ccb->ccb_h.timeout_ch); */
/* we're about to free a hcb, so the shortage has ended */
frag_free(wp, r->mask);
if (wp->want_wdsr && status != CAM_REQUEUE_REQ) {
@@ -1040,7 +1048,6 @@ wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio)
struct wds *wp;
struct ccb_hdr *ccb_h;
struct wds_req *r;
- int base;
u_int8_t c;
int error;
int n;
@@ -1072,7 +1079,6 @@ wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio)
xpt_done((union ccb *) csio);
return;
}
- base = wp->addr;
/*
* this check is mostly for debugging purposes,
@@ -1150,11 +1156,11 @@ wds_scsi_io(struct cam_sim * sim, struct ccb_scsiio * csio)
scsi_ulto3b(0, r->cmd.next);
- outb(base + WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
+ bus_write_1(wp->port_r, WDS_HCR, WDSH_IRQEN | WDSH_DRQEN);
c = WDSC_MSTART(r->ombn);
- if (wds_cmd(base, &c, sizeof c) != 0) {
+ if (wds_cmd(wp, &c, sizeof c) != 0) {
device_printf(wp->dev, "unable to start outgoing mbox\n");
wp->dx->ombs[r->ombn].stat = 0;
wdsr_ccb_done(wp, r, r->ccb, CAM_RESRC_UNAVAIL);
@@ -1170,16 +1176,13 @@ static void
wds_action(struct cam_sim * sim, union ccb * ccb)
{
int unit = cam_sim_unit(sim);
- int s;
DBG(DBX "wds%d: action 0x%x\n", unit, ccb->ccb_h.func_code);
switch (ccb->ccb_h.func_code) {
case XPT_SCSI_IO:
- s = splcam();
DBG(DBX "wds%d: SCSI IO entered\n", unit);
wds_scsi_io(sim, &ccb->csio);
DBG(DBX "wds%d: SCSI IO returned\n", unit);
- splx(s);
break;
case XPT_RESET_BUS:
/* how to do it right ? */
@@ -1242,7 +1245,7 @@ wds_action(struct cam_sim * sim, union ccb * ccb)
static void
wds_poll(struct cam_sim * sim)
{
- wds_intr((struct wds *)cam_sim_softc(sim));
+ wds_intr_locked(cam_sim_softc(sim));
}
/* part of initialization done in probe() */
@@ -1251,32 +1254,29 @@ wds_poll(struct cam_sim * sim)
static int
wds_preinit(struct wds *wp)
{
- int base;
int i;
- base = wp->addr;
-
/*
* Sending a command causes the CMDRDY bit to clear.
*/
- outb(base + WDS_CMD, WDSC_NOOP);
- if (inb(base + WDS_STAT) & WDS_RDY)
+ bus_write_1(wp->port_r, WDS_CMD, WDSC_NOOP);
+ if (bus_read_1(wp->port_r, WDS_STAT) & WDS_RDY)
return (ENXIO);
/*
* the controller exists. reset and init.
*/
- outb(base + WDS_HCR, WDSH_ASCRESET | WDSH_SCSIRESET);
+ bus_write_1(wp->port_r, WDS_HCR, WDSH_ASCRESET | WDSH_SCSIRESET);
DELAY(30);
- outb(base + WDS_HCR, 0);
+ bus_write_1(wp->port_r, WDS_HCR, 0);
- if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
+ if ((bus_read_1(wp->port_r, WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
for (i = 0; i < 10; i++) {
- if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
+ if ((bus_read_1(wp->port_r, WDS_STAT) & (WDS_RDY)) == WDS_RDY)
break;
DELAY(40000);
}
- if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
+ if ((bus_read_1(wp->port_r, WDS_STAT) & (WDS_RDY)) != WDS_RDY)
/* probe timeout */
return (ENXIO);
}
@@ -1291,23 +1291,20 @@ static int
wds_init(struct wds *wp)
{
struct wds_setup init;
- int base;
int i;
struct wds_cmd wc;
- base = wp->addr;
-
- outb(base + WDS_HCR, WDSH_DRQEN);
+ bus_write_1(wp->port_r, WDS_HCR, WDSH_DRQEN);
isa_dmacascade(wp->drq);
- if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
+ if ((bus_read_1(wp->port_r, WDS_STAT) & (WDS_RDY)) != WDS_RDY) {
for (i = 0; i < 10; i++) {
- if ((inb(base + WDS_STAT) & (WDS_RDY)) == WDS_RDY)
+ if ((bus_read_1(wp->port_r, WDS_STAT) & (WDS_RDY)) == WDS_RDY)
break;
DELAY(40000);
}
- if ((inb(base + WDS_STAT) & (WDS_RDY)) != WDS_RDY)
+ if ((bus_read_1(wp->port_r, WDS_STAT) & (WDS_RDY)) != WDS_RDY)
/* probe timeout */
return (1);
}
@@ -1321,18 +1318,18 @@ wds_init(struct wds *wp)
init.nomb = WDS_NOMB;
init.nimb = WDS_NIMB;
- wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
- if (wds_cmd(base, (u_int8_t *) & init, sizeof init) != 0) {
+ wds_wait(wp, WDS_STAT, WDS_RDY, WDS_RDY);
+ if (wds_cmd(wp, (u_int8_t *) & init, sizeof init) != 0) {
device_printf(wp->dev, "wds_cmd init failed\n");
return (1);
}
- wds_wait(base + WDS_STAT, WDS_INIT, WDS_INIT);
+ wds_wait(wp, WDS_STAT, WDS_INIT, WDS_INIT);
- wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
+ wds_wait(wp, WDS_STAT, WDS_RDY, WDS_RDY);
bzero(&wc, sizeof wc);
wc.cmd = WDSC_DISUNSOL;
- if (wds_cmd(base, (char *) &wc, sizeof wc) != 0) {
+ if (wds_cmd(wp, (char *) &wc, sizeof wc) != 0) {
device_printf(wp->dev, "wds_cmd init2 failed\n");
return (1);
}
@@ -1340,29 +1337,26 @@ wds_init(struct wds *wp)
}
static int
-wds_cmd(int base, u_int8_t * p, int l)
+wds_cmd(struct wds *wp, u_int8_t * p, int l)
{
- int s = splcam();
while (l--) {
do {
- outb(base + WDS_CMD, *p);
- wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
- } while (inb(base + WDS_STAT) & WDS_REJ);
+ bus_write_1(wp->port_r, WDS_CMD, *p);
+ wds_wait(wp, WDS_STAT, WDS_RDY, WDS_RDY);
+ } while (bus_read_1(wp->port_r, WDS_STAT) & WDS_REJ);
p++;
}
- wds_wait(base + WDS_STAT, WDS_RDY, WDS_RDY);
-
- splx(s);
+ wds_wait(wp, WDS_STAT, WDS_RDY, WDS_RDY);
return (0);
}
static void
-wds_wait(int reg, int mask, int val)
+wds_wait(struct wds *wp, int reg, int mask, int val)
{
- while ((inb(reg) & mask) != val)
+ while ((bus_read_1(wp->port_r, reg) & mask) != val)
;
}
@@ -1394,9 +1388,9 @@ wds_print(void)
if (wp == NULL)
continue;
printf("wds%d: want_wdsr=0x%x stat=0x%x irq=%s irqstat=0x%x\n",
- unit, wp->want_wdsr, inb(wp->addr + WDS_STAT) & 0xff,
- (inb(wp->addr + WDS_STAT) & WDS_IRQ) ? "ready" : "no",
- inb(wp->addr + WDS_IRQSTAT) & 0xff);
+ unit, wp->want_wdsr, bus_read_1(wp->port_r, WDS_STAT) & 0xff,
+ (bus_read_1(wp->port_r, WDS_STAT) & WDS_IRQ) ? "ready" : "no",
+ bus_read_1(wp->port_r, WDS_IRQSTAT) & 0xff);
for (i = 0; i < MAXSIMUL; i++) {
r = &wp->dx->req[i];
if( wp->wdsr_free & (1 << r->id) ) {