summaryrefslogtreecommitdiffstats
path: root/sys/dev/amr
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/amr')
-rw-r--r--sys/dev/amr/amr.c257
-rw-r--r--sys/dev/amr/amr_disk.c49
-rw-r--r--sys/dev/amr/amr_pci.c7
-rw-r--r--sys/dev/amr/amrvar.h31
4 files changed, 248 insertions, 96 deletions
diff --git a/sys/dev/amr/amr.c b/sys/dev/amr/amr.c
index 3145e08..c38f1d1 100644
--- a/sys/dev/amr/amr.c
+++ b/sys/dev/amr/amr.c
@@ -108,6 +108,11 @@ static void amr_releasecmd(struct amr_command *ac);
static void amr_freecmd(struct amr_command *ac);
/*
+ * Status monitoring
+ */
+static void amr_periodic(void *data);
+
+/*
* Interface-specific shims
*/
static void amr_quartz_submit_command(struct amr_softc *sc);
@@ -142,6 +147,8 @@ amr_free(struct amr_softc *sc)
debug("called");
+ /* cancel status timeout */
+ untimeout(amr_periodic, sc, sc->amr_timeout);
/* throw away any command buffers */
while ((ac = TAILQ_FIRST(&sc->amr_freecmds)) != NULL) {
@@ -405,6 +412,11 @@ amr_attach(struct amr_softc *sc)
if (amr_sglist_map(sc))
return(ENXIO);
+ /*
+ * Start the timeout routine.
+ */
+ sc->amr_timeout = timeout(amr_periodic, sc, hz);
+
return(0);
}
@@ -536,7 +548,6 @@ amr_shutdown(device_t dev)
sc->amr_drive[i].al_disk = 0;
}
}
- bus_generic_detach(sc->amr_dev);
out:
splx(s);
@@ -585,16 +596,11 @@ void
amr_intr(void *arg)
{
struct amr_softc *sc = (struct amr_softc *)arg;
- int worked;
- debug("called on %p", sc);
+ debug("called");
- /* spin collecting finished commands, process them if we find anything */
- worked = 0;
- while (amr_done(sc))
- worked = 1;
- if (worked)
- amr_complete(sc);
+ /* collect finished commands, queue anything waiting */
+ amr_done(sc);
};
/*******************************************************************************
@@ -667,6 +673,52 @@ amr_submit_ioctl(struct amr_softc *sc, struct amr_logdrive *drive, u_long cmd,
/********************************************************************************
********************************************************************************
+ Status Monitoring
+ ********************************************************************************
+ ********************************************************************************/
+
+/********************************************************************************
+ * Perform a periodic check of the controller status
+ */
+static void
+amr_periodic(void *data)
+{
+ struct amr_softc *sc = (struct amr_softc *)data;
+ int s, i;
+
+ debug("called");
+
+
+ /*
+ * Check for commands that are massively late. This will need to be
+ * revisited if/when we deal with eg. device format commands.
+ * The 30 second value is entirely arbitrary.
+ */
+ s = splbio();
+ if (sc->amr_busycmdcount > 0) {
+ for (i = 0; i < AMR_MAXCMD; i++) {
+ /*
+ * If the command has been busy for more than 30 seconds, declare it
+ * wedged and retire it with an error.
+ */
+ if ((sc->amr_busycmd[i] != NULL) &&
+ (sc->amr_busycmd[i]->ac_status == AMR_STATUS_BUSY) &&
+ ((sc->amr_busycmd[i]->ac_stamp + 30) < time_second)) {
+ device_printf(sc->amr_dev, "command %d wedged after 30 seconds\n", i);
+ sc->amr_busycmd[i]->ac_status = AMR_STATUS_WEDGED;
+ amr_completeio(sc->amr_busycmd[i]);
+ }
+ }
+ }
+ splx(s);
+
+ /* reschedule */
+ sc->amr_timeout = timeout(amr_periodic, sc, hz);
+}
+
+
+/********************************************************************************
+ ********************************************************************************
Command Wrappers
********************************************************************************
********************************************************************************/
@@ -698,7 +750,13 @@ amr_query_controller(struct amr_softc *sc)
ae->ae_adapter.aa_memorysize);
}
sc->amr_maxdrives = 8;
- sc->amr_maxio = ae->ae_adapter.aa_maxio;
+
+ /*
+ * Cap the maximum number of outstanding I/Os. AMI's Linux driver doesn't trust
+ * the controller's reported value, and lockups have been seen when we do.
+ */
+ sc->amr_maxio = imin(ae->ae_adapter.aa_maxio, AMR_LIMITCMD);
+
for (i = 0; i < ae->ae_ldrv.al_numdrives; i++) {
sc->amr_drive[i].al_size = ae->ae_ldrv.al_size[i];
sc->amr_drive[i].al_state = ae->ae_ldrv.al_state[i];
@@ -710,6 +768,10 @@ amr_query_controller(struct amr_softc *sc)
sc->amr_drive[i].al_size = 0xffffffff;
free(ae, M_DEVBUF);
} else {
+ /*
+ * The "40LD" (40 logical drive support) firmware is mentioned in the Linux
+ * driver, but no adapters from AMI appear to support it.
+ */
free(buf, M_DEVBUF);
sc->amr_maxdrives = 40;
@@ -764,8 +826,8 @@ amr_enquiry(struct amr_softc *sc, size_t bufsize, u_int8_t cmd, u_int8_t cmdsub,
mbox[3] = cmdqual;
ac->ac_mailbox.mb_physaddr = ac->ac_dataphys;
- /* run the command in polled/wait mode as suits the current mode */
- if ((sc->amr_state & AMR_STATE_INTEN) ? amr_wait_command(ac) : amr_poll_command(ac))
+ /* can't assume that interrupts are going to work here, so play it safe */
+ if (amr_poll_command(ac))
goto out;
error = ac->ac_status;
@@ -800,8 +862,8 @@ amr_flush(struct amr_softc *sc)
/* build the command proper */
ac->ac_mailbox.mb_command = AMR_CMD_FLUSH;
- /* run the command in polled/wait mode as suits the current mode */
- if ((sc->amr_state & AMR_STATE_INTEN) ? amr_wait_command(ac) : amr_poll_command(ac))
+ /* we have to poll, as the system may be going down or otherwise damaged */
+ if (amr_poll_command(ac))
goto out;
error = ac->ac_status;
@@ -828,6 +890,10 @@ amr_startio(struct amr_softc *sc)
int cmd;
int s;
+ /* avoid reentrancy */
+ if (amr_lock_tas(sc, AMR_LOCK_STARTING))
+ return;
+
/* spin until something prevents us from doing any work */
s = splbio();
for (;;) {
@@ -865,9 +931,9 @@ amr_startio(struct amr_softc *sc)
amr_mapcmd(ac);
/* build a suitable I/O command (assumes 512-byte rounded transfers) */
- amrd = (struct amrd_softc *)bp->b_driver1;
- driveno = amrd->amrd_drive - &sc->amr_drive[0];
- blkcount = bp->b_bcount / AMR_BLKSIZE;
+ amrd = (struct amrd_softc *)bp->b_dev->si_drv1;
+ driveno = amrd->amrd_drive - sc->amr_drive;
+ blkcount = (bp->b_bcount + AMR_BLKSIZE - 1) / AMR_BLKSIZE;
if ((bp->b_pblkno + blkcount) > sc->amr_drive[driveno].al_size)
device_printf(sc->amr_dev, "I/O beyond end of unit (%u,%d > %u)\n",
@@ -892,6 +958,7 @@ amr_startio(struct amr_softc *sc)
s = splbio();
}
splx(s);
+ amr_lock_clr(sc, AMR_LOCK_STARTING);
}
/********************************************************************************
@@ -902,6 +969,10 @@ amr_completeio(struct amr_command *ac)
{
struct amr_softc *sc = ac->ac_sc;
struct buf *bp = (struct buf *)ac->ac_private;
+ int notify, release;
+
+ notify = 1;
+ release = 1;
if (ac->ac_status != AMR_STATUS_SUCCESS) { /* could be more verbose here? */
bp->b_error = EIO;
@@ -909,14 +980,24 @@ amr_completeio(struct amr_command *ac)
switch(ac->ac_status) {
/* XXX need more information on I/O error reasons */
+ case AMR_STATUS_LATE:
+ notify = 0; /* we've already notified the parent */
+ break;
+
+ case AMR_STATUS_WEDGED:
+ release = 0; /* the command is still outstanding, we can't release */
+ break;
+
default:
device_printf(sc->amr_dev, "I/O error - %x\n", ac->ac_status);
amr_printcommand(ac);
break;
}
}
- amr_releasecmd(ac);
- amrd_intr(bp);
+ if (release)
+ amr_releasecmd(ac);
+ if (notify)
+ amrd_intr(bp);
}
/********************************************************************************
@@ -1101,14 +1182,13 @@ amr_unmapcmd(struct amr_command *ac)
}
/********************************************************************************
- * Take a command and give it to the controller. Take care of any completed
- * commands we encouter while waiting.
+ * Take a command and give it to the controller.
*/
static int
amr_start(struct amr_command *ac)
{
struct amr_softc *sc = ac->ac_sc;
- int worked, done, s, i;
+ int done, s, i;
debug("called");
@@ -1124,9 +1204,11 @@ amr_start(struct amr_command *ac)
/* set impossible status so that a woken sleeper can tell the command is busy */
ac->ac_status = AMR_STATUS_BUSY;
- /* spin waiting for the mailbox */
+ /*
+ * Spin waiting for the mailbox, give up after ~1 second.
+ */
debug("wait for mailbox");
- for (i = 10000, done = 0, worked = 0; (i > 0) && !done; i--) {
+ for (i = 10000, done = 0; (i > 0) && !done; i--) {
s = splbio();
/* is the mailbox free? */
@@ -1142,20 +1224,12 @@ amr_start(struct amr_command *ac)
/* not free, try to clean up while we wait */
} else {
debug("busy flag %x\n", sc->amr_mailbox->mb_busy);
- /* try to kill some time being useful */
- if (amr_done(sc)) {
- worked = 1;
- } else {
- DELAY(100);
- }
+ /* this is somewhat ugly */
+ DELAY(100);
}
- splx(s);
+ splx(s); /* drop spl to allow completion interrupts */
}
- /* do completion processing if we picked anything up */
- if (worked)
- amr_complete(sc);
-
/* command is enqueued? */
if (done) {
ac->ac_stamp = time_second;
@@ -1170,6 +1244,7 @@ amr_start(struct amr_command *ac)
sc->amr_busycmd[ac->ac_slot] = NULL;
device_printf(sc->amr_dev, "controller wedged (not taking commands)\n");
ac->ac_status = AMR_STATUS_WEDGED;
+ amr_complete(sc);
return(EIO);
}
@@ -1183,43 +1258,63 @@ amr_done(struct amr_softc *sc)
{
struct amr_command *ac;
struct amr_mailbox mbox;
- int i, idx, result;
+ int i, idx, s, result;
debug("called");
/* See if there's anything for us to do */
result = 0;
- if (sc->amr_get_work(sc, &mbox)) {
- /* iterate over completed commands */
- for (i = 0; i < mbox.mb_nstatus; i++) {
- /* get pointer to busy command */
- idx = mbox.mb_completed[i] - 1;
- ac = sc->amr_busycmd[idx];
-
- /* really a busy command? */
- if (ac != NULL) {
-
- /* pull the command from the busy index */
- sc->amr_busycmd[idx] = NULL;
- sc->amr_busycmdcount--;
+
+ /* loop collecting completed commands */
+ s = splbio();
+ for (;;) {
+ /* poll for a completed command's identifier and status */
+ if (sc->amr_get_work(sc, &mbox)) {
+ result = 1;
+
+ /* iterate over completed commands in this result */
+ for (i = 0; i < mbox.mb_nstatus; i++) {
+ /* get pointer to busy command */
+ idx = mbox.mb_completed[i] - 1;
+ ac = sc->amr_busycmd[idx];
+
+ /* really a busy command? */
+ if (ac != NULL) {
+
+ /* pull the command from the busy index */
+ sc->amr_busycmd[idx] = NULL;
+ sc->amr_busycmdcount--;
- /* unmap data buffer */
- amr_unmapcmd(ac);
-
- /* aborted command? */
- if (ac == (struct amr_command *)sc) {
- device_printf(sc->amr_dev, "aborted command completed (%d)\n", idx);
- ac = NULL;
-
- /* completed normally, save status */
- } else {
- ac->ac_status = mbox.mb_status;
- debug("completed command with status %x", mbox.mb_status);
+ /* aborted command? */
+ if (ac == (struct amr_command *)sc) {
+ device_printf(sc->amr_dev, "aborted command completed (%d)\n", idx);
+ sc->amr_busycmd[idx] = NULL; /* free the slot again */
+ ac = NULL;
+
+ /* wedged command? */
+ } else if (ac->ac_status == AMR_STATUS_WEDGED) {
+ device_printf(sc->amr_dev, "wedged command completed (%d)\n", idx);
+ ac->ac_status = AMR_STATUS_LATE;
+
+ /* completed normally, save status */
+ } else {
+ ac->ac_status = mbox.mb_status;
+ debug("completed command with status %x", mbox.mb_status);
+ }
}
- result = 1;
}
+ } else {
+ break;
}
}
+
+ /* if we've completed any commands, try posting some more */
+ if (result)
+ amr_startio(sc);
+
+ /* handle completion and timeouts */
+ amr_complete(sc);
+
return(result);
}
@@ -1234,15 +1329,22 @@ amr_complete(struct amr_softc *sc)
debug("called");
- count = 0;
+ if (amr_lock_tas(sc, AMR_LOCK_COMPLETING))
+ return;
s = splbio();
+ count = 0;
+
+ /* scan the list of busy/done commands */
ac = TAILQ_FIRST(&sc->amr_work);
while (ac != NULL) {
nc = TAILQ_NEXT(ac, ac_link);
- /* Skip if command is still active */
+ /* Command has been completed in some fashion */
if (ac->ac_status != AMR_STATUS_BUSY) {
+
+ /* unmap the command's data buffer */
+ amr_unmapcmd(ac);
/*
* Is there a completion handler?
@@ -1273,9 +1375,8 @@ amr_complete(struct amr_softc *sc)
ac = nc;
}
splx(s);
-
- /* queue more work if we can */
- amr_startio(sc);
+
+ amr_lock_clr(sc, AMR_LOCK_COMPLETING);
}
/********************************************************************************
@@ -1377,7 +1478,6 @@ amr_quartz_submit_command(struct amr_softc *sc)
sc->amr_mailbox->mb_poll = 0;
sc->amr_mailbox->mb_ack = 0;
- /* XXX write barrier? */
while(AMR_QGET_IDB(sc) & AMR_QIDB_SUBMIT)
; /* XXX aiee! what if it dies? */
AMR_QPUT_IDB(sc, sc->amr_mailboxphys | AMR_QIDB_SUBMIT);
@@ -1404,7 +1504,7 @@ amr_quartz_get_work(struct amr_softc *sc, struct amr_mailbox *mbsave)
int s, worked;
u_int32_t outd;
- debug("called");
+/* debug("called"); */
worked = 0;
s = splbio();
@@ -1502,7 +1602,7 @@ amr_printcommand(struct amr_command *ac)
device_printf(sc->amr_dev, "blkcount %d lba %d\n",
ac->ac_mailbox.mb_blkcount, ac->ac_mailbox.mb_lba);
device_printf(sc->amr_dev, "virtaddr %p length %lu\n", ac->ac_data, (unsigned long)ac->ac_length);
- device_printf(sc->amr_dev, "physaddr %08x nsg %d\n",
+ device_printf(sc->amr_dev, "sg physaddr %08x nsg %d\n",
ac->ac_mailbox.mb_physaddr, ac->ac_mailbox.mb_nsgelem);
/* get base address of s/g table */
@@ -1510,3 +1610,20 @@ amr_printcommand(struct amr_command *ac)
for (i = 0; i < ac->ac_mailbox.mb_nsgelem; i++, sg++)
device_printf(sc->amr_dev, " %x/%d\n", sg->sg_addr, sg->sg_count);
}
+
+/********************************************************************************
+ * Print information on all the controllers in the system, useful mostly
+ * for calling from DDB.
+ */
+void
+amr_report(void)
+{
+ struct amr_softc *sc;
+ int i, s;
+
+ s = splbio();
+ for (i = 0; (sc = devclass_get_softc(amr_devclass, i)) != NULL; i++) {
+ device_printf(sc->amr_dev, "amr_waitbufs %d amr_busycmdcount %d amr_workcount %d\n",
+ sc->amr_waitbufs, sc->amr_busycmdcount, sc->amr_workcount);
+ }
+}
diff --git a/sys/dev/amr/amr_disk.c b/sys/dev/amr/amr_disk.c
index e9c0567..6ce523d 100644
--- a/sys/dev/amr/amr_disk.c
+++ b/sys/dev/amr/amr_disk.c
@@ -105,19 +105,10 @@ static driver_t amrd_driver = {
DRIVER_MODULE(amrd, amr, amrd_driver, amrd_devclass, 0, 0);
-static __inline struct amrd_softc *
-amrd_getsoftc(dev_t dev)
-{
- int unit;
-
- unit = dkunit(dev);
- return ((struct amrd_softc *)devclass_get_softc(amrd_devclass, unit));
-}
-
static int
amrd_open(dev_t dev, int flags, int fmt, struct proc *p)
{
- struct amrd_softc *sc = amrd_getsoftc(dev);
+ struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1;
struct disklabel *label;
debug("called");
@@ -146,7 +137,7 @@ amrd_open(dev_t dev, int flags, int fmt, struct proc *p)
static int
amrd_close(dev_t dev, int flags, int fmt, struct proc *p)
{
- struct amrd_softc *sc = amrd_getsoftc(dev);
+ struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1;
debug("called");
@@ -159,7 +150,7 @@ amrd_close(dev_t dev, int flags, int fmt, struct proc *p)
static int
amrd_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
{
- struct amrd_softc *sc = amrd_getsoftc(dev);
+ struct amrd_softc *sc = (struct amrd_softc *)dev->si_drv1;
int error;
debug("called");
@@ -183,9 +174,10 @@ amrd_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct proc *p)
static void
amrd_strategy(struct buf *bp)
{
- struct amrd_softc *sc = amrd_getsoftc(bp->b_dev);
+ struct amrd_softc *sc = (struct amrd_softc *)bp->b_dev->si_drv1;
- debug("called");
+ debug("called to %s %d bytes at b_blkno 0x%x b_pblkno 0x%x",
+ (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, bp->b_blkno, bp->b_pblkno);
/* bogus disk? */
if (sc == NULL) {
@@ -205,8 +197,6 @@ amrd_strategy(struct buf *bp)
if (bp->b_bcount == 0)
goto done;
- /* pass reference to us */
- bp->b_driver1 = sc;
devstat_start_transaction(&sc->amrd_stats);
amr_submit_buf(sc->amrd_controller, bp);
return;
@@ -227,14 +217,21 @@ void
amrd_intr(void *data)
{
struct buf *bp = (struct buf *)data;
- struct amrd_softc *sc = (struct amrd_softc *)bp->b_driver1;
+ struct amrd_softc *sc = (struct amrd_softc *)bp->b_dev->si_drv1;
debug("called");
-
- if (bp->b_flags & B_ERROR)
+
+ if (bp->b_flags & B_ERROR) {
bp->b_error = EIO;
- else
+ debug("i/o error\n");
+ } else {
+#if 0
+ int i;
+ for (i = 0; i < 512; i += 16)
+ debug(" %04x %16D", i, bp->b_data + i, " ");
+#endif
bp->b_resid = 0;
+ }
devstat_end_transaction_buf(&sc->amrd_stats, bp);
biodone(bp);
@@ -291,9 +288,13 @@ amrd_attach(device_t dev)
DEVSTAT_TYPE_STORARRAY | DEVSTAT_TYPE_IF_OTHER,
DEVSTAT_PRIORITY_ARRAY);
- disk_create(sc->amrd_unit, &sc->amrd_disk, 0, &amrd_cdevsw, &amrddisk_cdevsw);
+ sc->amrd_dev_t = disk_create(sc->amrd_unit, &sc->amrd_disk, 0, &amrd_cdevsw, &amrddisk_cdevsw);
+ sc->amrd_dev_t->si_drv1 = sc;
disks_registered++;
+ /* set maximum I/O size */
+ /* dsk->si_iosize_max = ??? */;
+
return (0);
}
@@ -305,11 +306,7 @@ amrd_detach(device_t dev)
debug("called");
devstat_remove_entry(&sc->amrd_stats);
-
- /* hack to handle lack of destroy_disk() */
- if (--disks_registered == 0)
- cdevsw_remove(&amrddisk_cdevsw);
-
+ disk_destroy(sc->amrd_dev_t);
return(0);
}
diff --git a/sys/dev/amr/amr_pci.c b/sys/dev/amr/amr_pci.c
index 7968a32..382a4a8 100644
--- a/sys/dev/amr/amr_pci.c
+++ b/sys/dev/amr/amr_pci.c
@@ -159,6 +159,13 @@ amr_pci_attach(device_t dev)
}
}
+ /* force the busmaster enable bit on */
+ if (!(command & PCIM_CMD_BUSMASTEREN)) {
+ device_printf(dev, "busmaster bit not set, enabling\n");
+ command |= PCIM_CMD_BUSMASTEREN;
+ pci_write_config(dev, PCIR_COMMAND, command, 2);
+ }
+
/*
* Allocate the PCI register window.
*/
diff --git a/sys/dev/amr/amrvar.h b/sys/dev/amr/amrvar.h
index 13e0f46..84777b5 100644
--- a/sys/dev/amr/amrvar.h
+++ b/sys/dev/amr/amrvar.h
@@ -38,6 +38,7 @@
#define AMR_SIGNATURE 0x3344
#define AMR_MAXCMD 255 /* ident = 0 not allowed */
+#define AMR_LIMITCMD 120 /* maximum count of outstanding commands */
#define AMR_MAXLD 40
#define AMR_BLKSIZE 512
@@ -75,6 +76,7 @@ struct amr_command
int ac_status;
#define AMR_STATUS_BUSY 0xffff
#define AMR_STATUS_WEDGED 0xdead
+#define AMR_STATUS_LATE 0xdeed
struct amr_mailbox ac_mailbox;
u_int32_t ac_sgphys;
int ac_nsgent;
@@ -131,6 +133,7 @@ struct amr_softc
#define AMR_STATE_SUSPEND (1<<1)
#define AMR_STATE_INTEN (1<<2)
#define AMR_STATE_SHUTDOWN (1<<3)
+ struct callout_handle amr_timeout; /* periodic status check */
/* per-controller queues */
struct buf_queue_head amr_bufq; /* pending I/O */
@@ -141,6 +144,8 @@ struct amr_softc
int amr_workcount;
TAILQ_HEAD(,amr_command) amr_freecmds;
+ int amr_locks; /* reentrancy avoidance */
+
/* controller type-specific support */
int amr_type;
#define AMR_TYPE_STD 0
@@ -151,6 +156,30 @@ struct amr_softc
};
/*
+ * Simple (stupid) locks.
+ *
+ * Note that these are designed to avoid reentrancy, not concurrency, and will
+ * need to be replaced with something better.
+ */
+#define AMR_LOCK_COMPLETING (1<<0)
+#define AMR_LOCK_STARTING (1<<1)
+
+static __inline int
+amr_lock_tas(struct amr_softc *sc, int lock)
+{
+ if ((sc)->amr_locks & (lock))
+ return(1);
+ atomic_set_int(&sc->amr_locks, lock);
+ return(0);
+}
+
+static __inline void
+amr_lock_clr(struct amr_softc *sc, int lock)
+{
+ atomic_clear_int(&sc->amr_locks, lock);
+}
+
+/*
* I/O primitives
*/
/* Quartz */
@@ -196,6 +225,7 @@ extern devclass_t amr_devclass;
struct amrd_softc
{
device_t amrd_dev;
+ dev_t amrd_dev_t;
struct amr_softc *amrd_controller;
struct amr_logdrive *amrd_drive;
struct disk amrd_disk;
@@ -214,3 +244,4 @@ extern int amr_submit_ioctl(struct amr_softc *sc, struct amr_logdrive *drive, u_
caddr_t addr, int32_t flag, struct proc *p);
extern void amrd_intr(void *data);
+extern void amr_report(void);
OpenPOWER on IntegriCloud