Major bugfix and minor update. This should resolve the current issues
with the driver locking up under load. - Restructure so that we use a static pool of commands/FIBs, rather than allocating them in clusters. The cluster allocation just made things more complicated, and allowed us to waste more memory in peak load situations. - Make queueing macros more like my other drivers. This adds queue stats for free. Add some debugging to take advantage of this. - Reimplement the periodic timeout scan. Kick the interrupt handler and the start routine every scan as well, just to be safe. Track busy commands properly. - Bring resource cleanup into line with resource allocation. We should now clean up correctly after a failed probe/unload/etc. - Try to start new commands when old ones are completed. We weren't doing this before, which could lead to deadlock when the controller was full. - Don't try to build a new command if we have found a deferred command. This could cause us to lose the deferred command. - Use diskerr() to report I/O errors. - Don't bail if the AdapterInfo structure is the wrong size. Some variation seems to be normal. We need to improve our handing of 2.x firmware sets. - Improve some comments in an attempt to try to make things clearer. - Restructure to avoid some warnings.
This commit is contained in:
parent
826737698c
commit
0b94a66e3f
@ -44,15 +44,16 @@
|
||||
#include <sys/disk.h>
|
||||
#include <sys/file.h>
|
||||
#include <sys/signalvar.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#include <machine/bus_memio.h>
|
||||
#include <machine/bus.h>
|
||||
#include <machine/resource.h>
|
||||
|
||||
#include <dev/aac/aacreg.h>
|
||||
#include <dev/aac/aac_ioctl.h>
|
||||
#include <dev/aac/aacvar.h>
|
||||
#include <dev/aac/aac_tables.h>
|
||||
#include <dev/aac/aac_ioctl.h>
|
||||
|
||||
devclass_t aac_devclass;
|
||||
|
||||
@ -60,7 +61,7 @@ static void aac_startup(void *arg);
|
||||
|
||||
/* Command Processing */
|
||||
static void aac_startio(struct aac_softc *sc);
|
||||
static void aac_timeout(struct aac_command *cm);
|
||||
static void aac_timeout(struct aac_softc *sc);
|
||||
static int aac_start(struct aac_command *cm);
|
||||
static void aac_complete(void *context, int pending);
|
||||
static int aac_bio_command(struct aac_softc *sc, struct aac_command **cmp);
|
||||
@ -72,9 +73,9 @@ static void aac_host_response(struct aac_softc *sc);
|
||||
/* Command Buffer Management */
|
||||
static int aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp);
|
||||
static void aac_release_command(struct aac_command *cm);
|
||||
static void aac_map_command_cluster(void *arg, bus_dma_segment_t *segs, int nseg, int error);
|
||||
static void aac_alloc_command_cluster(struct aac_softc *sc);
|
||||
static void aac_free_command_cluster(struct aac_command_cluster *cmc);
|
||||
static void aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error);
|
||||
static int aac_alloc_commands(struct aac_softc *sc);
|
||||
static void aac_free_commands(struct aac_softc *sc);
|
||||
static void aac_map_command(struct aac_command *cm);
|
||||
static void aac_unmap_command(struct aac_command *cm);
|
||||
|
||||
@ -140,10 +141,10 @@ static d_close_t aac_close;
|
||||
static d_ioctl_t aac_ioctl;
|
||||
static int aac_ioctl_sendfib(struct aac_softc *sc, caddr_t ufib);
|
||||
static void aac_handle_aif(struct aac_softc *sc, struct aac_aif_command *aif);
|
||||
static int aac_return_aif(struct aac_softc *sc, caddr_t uptr);
|
||||
#ifdef AAC_COMPAT_LINUX
|
||||
static int aac_linux_rev_check(struct aac_softc *sc, caddr_t udata);
|
||||
static int aac_linux_getnext_aif(struct aac_softc *sc, caddr_t arg);
|
||||
static int aac_linux_return_aif(struct aac_softc *sc, caddr_t uptr);
|
||||
#endif
|
||||
|
||||
#define AAC_CDEV_MAJOR 150
|
||||
@ -165,11 +166,6 @@ static struct cdevsw aac_cdevsw = {
|
||||
-1, /* bmaj */
|
||||
};
|
||||
|
||||
/* Timeout for giving up on a command sent to the controller */
|
||||
#ifndef AAC_CMD_TIMEOUT
|
||||
#define AAC_CMD_TIMEOUT 15
|
||||
#endif
|
||||
|
||||
/********************************************************************************
|
||||
********************************************************************************
|
||||
Device Interface
|
||||
@ -189,11 +185,11 @@ aac_attach(struct aac_softc *sc)
|
||||
/*
|
||||
* Initialise per-controller queues.
|
||||
*/
|
||||
TAILQ_INIT(&sc->aac_freecmds);
|
||||
TAILQ_INIT(&sc->aac_ready);
|
||||
TAILQ_INIT(&sc->aac_completed);
|
||||
TAILQ_INIT(&sc->aac_clusters);
|
||||
bioq_init(&sc->aac_bioq);
|
||||
aac_initq_free(sc);
|
||||
aac_initq_ready(sc);
|
||||
aac_initq_busy(sc);
|
||||
aac_initq_complete(sc);
|
||||
aac_initq_bio(sc);
|
||||
|
||||
#if __FreeBSD_version >= 500005
|
||||
/*
|
||||
@ -208,10 +204,16 @@ aac_attach(struct aac_softc *sc)
|
||||
/* mark controller as suspended until we get ourselves organised */
|
||||
sc->aac_state |= AAC_STATE_SUSPEND;
|
||||
|
||||
/*
|
||||
* Allocate command structures.
|
||||
*/
|
||||
if ((error = aac_alloc_commands(sc)) != 0)
|
||||
return(error);
|
||||
|
||||
/*
|
||||
* Initialise the adapter.
|
||||
*/
|
||||
if ((error = aac_init(sc)))
|
||||
if ((error = aac_init(sc)) != 0)
|
||||
return(error);
|
||||
|
||||
/*
|
||||
@ -222,7 +224,6 @@ aac_attach(struct aac_softc *sc)
|
||||
/*
|
||||
* Register to probe our containers later.
|
||||
*/
|
||||
bzero(&sc->aac_ich, sizeof(struct intr_config_hook));
|
||||
sc->aac_ich.ich_func = aac_startup;
|
||||
sc->aac_ich.ich_arg = sc;
|
||||
if (config_intrhook_establish(&sc->aac_ich) != 0) {
|
||||
@ -303,36 +304,38 @@ aac_startup(void *arg)
|
||||
|
||||
/* enable interrupts now */
|
||||
AAC_UNMASK_INTERRUPTS(sc);
|
||||
|
||||
/* enable the timeout watchdog */
|
||||
timeout((timeout_t*)aac_timeout, sc, AAC_PERIODIC_INTERVAL * hz);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Free all of the resources associated with (sc)
|
||||
*
|
||||
* Should not be called if the controller is active.
|
||||
*
|
||||
* XXX verify that we are freeing all our resources here...
|
||||
*/
|
||||
void
|
||||
aac_free(struct aac_softc *sc)
|
||||
{
|
||||
struct aac_command_cluster *cmc;
|
||||
|
||||
debug_called(1);
|
||||
|
||||
/* remove the control device */
|
||||
if (sc->aac_dev_t != NULL)
|
||||
destroy_dev(sc->aac_dev_t);
|
||||
|
||||
/* throw away any command buffers */
|
||||
while ((cmc = aac_dequeue_cluster(sc)) != NULL)
|
||||
aac_free_command_cluster(cmc);
|
||||
/* throw away any FIB buffers, discard the FIB DMA tag */
|
||||
if (sc->aac_fibs != NULL)
|
||||
aac_free_commands(sc);
|
||||
if (sc->aac_fib_dmat)
|
||||
bus_dma_tag_destroy(sc->aac_fib_dmat);
|
||||
|
||||
/* destroy the common area */
|
||||
if (sc->aac_common) {
|
||||
bus_dmamap_unload(sc->aac_common_dmat, sc->aac_common_dmamap);
|
||||
bus_dmamem_free(sc->aac_common_dmat, sc->aac_common, sc->aac_common_dmamap);
|
||||
bus_dma_tag_destroy(sc->aac_common_dmat);
|
||||
}
|
||||
if (sc->aac_common_dmat)
|
||||
bus_dma_tag_destroy(sc->aac_common_dmat);
|
||||
|
||||
/* disconnect the interrupt handler */
|
||||
if (sc->aac_intr)
|
||||
@ -344,10 +347,6 @@ aac_free(struct aac_softc *sc)
|
||||
if (sc->aac_buffer_dmat)
|
||||
bus_dma_tag_destroy(sc->aac_buffer_dmat);
|
||||
|
||||
/* destroy FIB DMA tag */
|
||||
if (sc->aac_buffer_dmat)
|
||||
bus_dma_tag_destroy(sc->aac_fib_dmat);
|
||||
|
||||
/* destroy the parent DMA tag */
|
||||
if (sc->aac_parent_dmat)
|
||||
bus_dma_tag_destroy(sc->aac_parent_dmat);
|
||||
@ -384,7 +383,7 @@ aac_detach(device_t dev)
|
||||
*
|
||||
* This function is called before detach or system shutdown.
|
||||
*
|
||||
* Note that we can assume that the camq on the controller is empty, as we won't
|
||||
* Note that we can assume that the bioq on the controller is empty, as we won't
|
||||
* allow shutdown if any device is open.
|
||||
*/
|
||||
int
|
||||
@ -522,18 +521,13 @@ aac_startio(struct aac_softc *sc)
|
||||
cm = aac_dequeue_ready(sc);
|
||||
|
||||
/* try to build a command off the bio queue (ignore error return) */
|
||||
aac_bio_command(sc, &cm);
|
||||
if (cm == NULL)
|
||||
aac_bio_command(sc, &cm);
|
||||
|
||||
/* nothing to do? */
|
||||
if (cm == NULL)
|
||||
break;
|
||||
|
||||
/* Set a timeout for this command to be completed by the controller */
|
||||
/* Disable this for now until the timeout queue is fixed or the driver
|
||||
* can watch timeouts itself
|
||||
* cm->timeout_handle = timeout((timeout_t*)aac_timeout, cm, AAC_CMD_TIMEOUT * hz);
|
||||
*/
|
||||
|
||||
/* try to give the command to the controller */
|
||||
if (aac_start(cm) == EBUSY) {
|
||||
/* put it on the ready queue for later */
|
||||
@ -543,27 +537,6 @@ aac_startio(struct aac_softc *sc)
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
aac_timeout(struct aac_command *cm)
|
||||
{
|
||||
struct aac_softc *sc;
|
||||
struct bio *bp;
|
||||
struct aac_disk *ad;
|
||||
|
||||
sc = cm->cm_sc;
|
||||
bp = (struct bio*)cm->cm_private;
|
||||
ad = (struct aac_disk *)bp->bio_dev->si_drv1;
|
||||
|
||||
device_printf(sc->aac_dev, "Timeout waiting for controller to respond to command\n");
|
||||
|
||||
/* Should try to requeue the command... is it possible? Bail for now */
|
||||
bp->bio_error = EIO;
|
||||
bp->bio_flags |= BIO_ERROR;
|
||||
devstat_end_transaction_bio(&ad->ad_stats, bp);
|
||||
biodone(bp);
|
||||
aac_release_command(cm);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Deliver a command to the controller; allocate controller resources at the
|
||||
* last moment when possible.
|
||||
@ -572,25 +545,30 @@ static int
|
||||
aac_start(struct aac_command *cm)
|
||||
{
|
||||
struct aac_softc *sc = cm->cm_sc;
|
||||
int s, error;
|
||||
|
||||
debug_called(2);
|
||||
|
||||
/* get the command mapped */
|
||||
aac_map_command(cm);
|
||||
|
||||
/* fix up the address values */
|
||||
/* fix up the address values in the FIB */
|
||||
cm->cm_fib->Header.SenderFibAddress = (u_int32_t)cm->cm_fib;
|
||||
cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
|
||||
|
||||
/* save a pointer to the command for speedy reverse-lookup */
|
||||
cm->cm_fib->Header.SenderData = (u_int32_t)cm; /* XXX ack, sizing */
|
||||
cm->cm_fib->Header.SenderData = (u_int32_t)cm; /* XXX 64-bit physical address issue */
|
||||
|
||||
/* put the FIB on the outbound queue */
|
||||
s = splbio();
|
||||
if (aac_enqueue_fib(sc, AAC_ADAP_NORM_CMD_QUEUE, cm->cm_fib->Header.Size,
|
||||
cm->cm_fib->Header.ReceiverFibAddress))
|
||||
return(EBUSY);
|
||||
|
||||
return(0);
|
||||
cm->cm_fib->Header.ReceiverFibAddress)) {
|
||||
error = EBUSY;
|
||||
} else {
|
||||
aac_enqueue_busy(cm);
|
||||
error = 0;
|
||||
}
|
||||
return(error);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
@ -645,8 +623,9 @@ aac_host_response(struct aac_softc *sc)
|
||||
if (cm == NULL) {
|
||||
AAC_PRINT_FIB(sc, fib);
|
||||
} else {
|
||||
aac_remove_busy(cm);
|
||||
aac_unmap_command(cm); /* XXX defer? */
|
||||
aac_enqueue_completed(cm);
|
||||
aac_enqueue_complete(cm);
|
||||
}
|
||||
}
|
||||
|
||||
@ -671,9 +650,9 @@ aac_complete(void *context, int pending)
|
||||
|
||||
/* pull completed commands off the queue */
|
||||
for (;;) {
|
||||
cm = aac_dequeue_completed(sc);
|
||||
cm = aac_dequeue_complete(sc);
|
||||
if (cm == NULL)
|
||||
return;
|
||||
break;
|
||||
cm->cm_flags |= AAC_CMD_COMPLETED;
|
||||
|
||||
/* is there a completion handler? */
|
||||
@ -684,6 +663,9 @@ aac_complete(void *context, int pending)
|
||||
wakeup(cm);
|
||||
}
|
||||
}
|
||||
|
||||
/* see if we can start some more I/O */
|
||||
aac_startio(sc);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
@ -698,7 +680,7 @@ aac_submit_bio(struct bio *bp)
|
||||
debug_called(2);
|
||||
|
||||
/* queue the BIO and try to get some work done */
|
||||
bioq_insert_tail(&sc->aac_bioq, bp);
|
||||
aac_enqueue_bio(sc, bp);
|
||||
aac_startio(sc);
|
||||
}
|
||||
|
||||
@ -714,23 +696,22 @@ aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
|
||||
struct aac_blockwrite *bw;
|
||||
struct aac_disk *ad;
|
||||
struct bio *bp;
|
||||
int s;
|
||||
|
||||
debug_called(2);
|
||||
|
||||
/* get the resources we will need */
|
||||
cm = NULL;
|
||||
s = splbio();
|
||||
if ((bp = bioq_first(&sc->aac_bioq)))
|
||||
bioq_remove(&sc->aac_bioq, bp);
|
||||
splx(s);
|
||||
if (bp == NULL) /* no work? */
|
||||
if ((bp = aac_dequeue_bio(sc)) == NULL)
|
||||
goto fail;
|
||||
if (aac_alloc_command(sc, &cm)) /* get a command */
|
||||
goto fail;
|
||||
|
||||
/* fill out the command */
|
||||
cm->cm_data = (void *)bp->bio_data;
|
||||
cm->cm_datalen = bp->bio_bcount;
|
||||
cm->cm_complete = aac_bio_complete;
|
||||
cm->cm_private = bp;
|
||||
cm->cm_timestamp = time_second;
|
||||
|
||||
/* build the FIB */
|
||||
fib = cm->cm_fib;
|
||||
@ -745,9 +726,6 @@ aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
|
||||
|
||||
/* build the read/write request */
|
||||
ad = (struct aac_disk *)bp->bio_dev->si_drv1;
|
||||
cm->cm_data = (void *)bp->bio_data;
|
||||
cm->cm_datalen = bp->bio_bcount;
|
||||
cm->cm_complete = aac_bio_complete;
|
||||
if (BIO_IS_READ(bp)) {
|
||||
br = (struct aac_blockread *)&fib->data[0];
|
||||
br->Command = VM_CtBlockRead;
|
||||
@ -774,7 +752,7 @@ aac_bio_command(struct aac_softc *sc, struct aac_command **cmp)
|
||||
|
||||
fail:
|
||||
if (bp != NULL)
|
||||
bioq_insert_tail(&sc->aac_bioq, bp);
|
||||
aac_enqueue_bio(sc, bp);
|
||||
if (cm != NULL)
|
||||
aac_release_command(cm);
|
||||
return(ENOMEM);
|
||||
@ -786,18 +764,11 @@ fail:
|
||||
static void
|
||||
aac_bio_complete(struct aac_command *cm)
|
||||
{
|
||||
struct aac_softc *sc = cm->cm_sc;
|
||||
struct aac_blockread_response *brr;
|
||||
struct aac_blockwrite_response *bwr;
|
||||
struct bio *bp;
|
||||
AAC_FSAStatus status;
|
||||
|
||||
/* kill the timeout timer */
|
||||
/* Disable this for now until the timeout queue is fixed or the driver
|
||||
* can watch timeouts itself
|
||||
* untimeout((timeout_t *)aac_timeout, cm, cm->timeout_handle);
|
||||
*/
|
||||
|
||||
/* fetch relevant status and then release the command */
|
||||
bp = (struct bio *)cm->cm_private;
|
||||
if (BIO_IS_READ(bp)) {
|
||||
@ -815,11 +786,10 @@ aac_bio_complete(struct aac_command *cm)
|
||||
} else {
|
||||
bp->bio_error = EIO;
|
||||
bp->bio_flags |= BIO_ERROR;
|
||||
|
||||
/* XXX be more verbose? */
|
||||
device_printf(sc->aac_dev, "I/O error %d (%s)\n", status, AAC_COMMAND_STATUS(status));
|
||||
/* pass an error string out to the disk layer */
|
||||
bp->bio_driver1 = aac_describe_code(aac_command_status_table, status);
|
||||
}
|
||||
aac_complete_bio(bp); /* XXX rename one of these functions! */
|
||||
aac_biodone(bp);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
@ -859,15 +829,22 @@ aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
|
||||
|
||||
debug_called(3);
|
||||
|
||||
cm = aac_dequeue_free(sc);
|
||||
if (cm == NULL) {
|
||||
aac_alloc_command_cluster(sc);
|
||||
cm = aac_dequeue_free(sc);
|
||||
}
|
||||
if (cm == NULL)
|
||||
if ((cm = aac_dequeue_free(sc)) == NULL)
|
||||
return(ENOMEM);
|
||||
|
||||
/* initialise the command/FIB */
|
||||
*cmp = cm;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Release a command back to the freelist.
|
||||
*/
|
||||
static void
|
||||
aac_release_command(struct aac_command *cm)
|
||||
{
|
||||
debug_called(3);
|
||||
|
||||
/* (re)initialise the command/FIB */
|
||||
cm->cm_sgtable = NULL;
|
||||
cm->cm_flags = 0;
|
||||
cm->cm_complete = NULL;
|
||||
@ -885,90 +862,67 @@ aac_alloc_command(struct aac_softc *sc, struct aac_command **cmp)
|
||||
cm->cm_fib->Header.SenderFibAddress = (u_int32_t)cm->cm_fib;
|
||||
cm->cm_fib->Header.ReceiverFibAddress = cm->cm_fibphys;
|
||||
|
||||
*cmp = cm;
|
||||
return(0);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Release a command back to the freelist.
|
||||
*/
|
||||
static void
|
||||
aac_release_command(struct aac_command *cm)
|
||||
{
|
||||
debug_called(3);
|
||||
|
||||
aac_enqueue_free(cm);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Map helper for command cluster allocation. Tell each of the FIBs what its
|
||||
* address in the adapter's space is, fill in a few other fields.
|
||||
* Map helper for command/FIB allocation.
|
||||
*/
|
||||
static void
|
||||
aac_map_command_cluster(void *arg, bus_dma_segment_t *segs, int nseg, int error)
|
||||
aac_map_command_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error)
|
||||
{
|
||||
struct aac_command_cluster *cmc = (struct aac_command_cluster *)arg;
|
||||
struct aac_softc *sc = (struct aac_softc *)arg;
|
||||
|
||||
debug_called(3);
|
||||
|
||||
cmc->cmc_fibphys = segs[0].ds_addr;
|
||||
sc->aac_fibphys = segs[0].ds_addr;
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Allocate and initialise a cluster of commands.
|
||||
* Allocate and initialise commands/FIBs for this adapter.
|
||||
*/
|
||||
static void
|
||||
aac_alloc_command_cluster(struct aac_softc *sc)
|
||||
static int
|
||||
aac_alloc_commands(struct aac_softc *sc)
|
||||
{
|
||||
struct aac_command_cluster *cmc;
|
||||
struct aac_command *cm;
|
||||
int i;
|
||||
|
||||
debug_called(1);
|
||||
|
||||
cmc = malloc(sizeof(struct aac_command_cluster), M_DEVBUF,
|
||||
M_NOWAIT | M_ZERO);
|
||||
if (cmc != NULL) {
|
||||
/* allocate the FIB cluster in DMAable memory and load it */
|
||||
if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&cmc->cmc_fibs, BUS_DMA_NOWAIT, &cmc->cmc_fibmap)) {
|
||||
free(cmc, M_DEVBUF);
|
||||
return;
|
||||
}
|
||||
bus_dmamap_load(sc->aac_fib_dmat, cmc->cmc_fibmap, cmc->cmc_fibs,
|
||||
AAC_CLUSTER_COUNT * sizeof(struct aac_fib), aac_map_command_cluster, cmc, 0);
|
||||
|
||||
aac_enqueue_cluster(sc, cmc);
|
||||
for (i = 0; i < AAC_CLUSTER_COUNT; i++) {
|
||||
cm = &cmc->cmc_command[i];
|
||||
cm->cm_sc = sc;
|
||||
cm->cm_fib = cmc->cmc_fibs + i;
|
||||
cm->cm_fibphys = cmc->cmc_fibphys + (i * sizeof(struct aac_fib));
|
||||
|
||||
if (!bus_dmamap_create(sc->aac_buffer_dmat, 0, &cm->cm_datamap))
|
||||
aac_release_command(cm);
|
||||
}
|
||||
} else {
|
||||
debug(2, "can't allocate memeory for command cluster");
|
||||
/* allocate the FIBs in DMAable memory and load them */
|
||||
if (bus_dmamem_alloc(sc->aac_fib_dmat, (void **)&sc->aac_fibs, BUS_DMA_NOWAIT, &sc->aac_fibmap)) {
|
||||
return(ENOMEM);
|
||||
}
|
||||
bus_dmamap_load(sc->aac_fib_dmat, sc->aac_fibmap, sc->aac_fibs,
|
||||
AAC_FIB_COUNT * sizeof(struct aac_fib), aac_map_command_helper, sc, 0);
|
||||
|
||||
/* initialise constant fields in the command structure */
|
||||
for (i = 0; i < AAC_FIB_COUNT; i++) {
|
||||
cm = &sc->aac_command[i];
|
||||
cm->cm_sc = sc;
|
||||
cm->cm_fib = sc->aac_fibs + i;
|
||||
cm->cm_fibphys = sc->aac_fibphys + (i * sizeof(struct aac_fib));
|
||||
|
||||
if (!bus_dmamap_create(sc->aac_buffer_dmat, 0, &cm->cm_datamap))
|
||||
aac_release_command(cm);
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Free a command cluster.
|
||||
* Free FIBs owned by this adapter.
|
||||
*/
|
||||
static void
|
||||
aac_free_command_cluster(struct aac_command_cluster *cmc)
|
||||
aac_free_commands(struct aac_softc *sc)
|
||||
{
|
||||
struct aac_softc *sc = cmc->cmc_command[0].cm_sc;
|
||||
int i;
|
||||
|
||||
debug_called(1);
|
||||
|
||||
for (i = 0; i < AAC_CLUSTER_COUNT; i++)
|
||||
bus_dmamap_destroy(sc->aac_buffer_dmat, cmc->cmc_command[i].cm_datamap);
|
||||
bus_dmamap_unload(sc->aac_fib_dmat, cmc->cmc_fibmap);
|
||||
bus_dmamem_free(sc->aac_fib_dmat, cmc->cmc_fibs, cmc->cmc_fibmap);
|
||||
|
||||
free(cmc, M_DEVBUF);
|
||||
for (i = 0; i < AAC_FIB_COUNT; i++)
|
||||
bus_dmamap_destroy(sc->aac_buffer_dmat, sc->aac_command[i].cm_datamap);
|
||||
bus_dmamap_unload(sc->aac_fib_dmat, sc->aac_fibmap);
|
||||
bus_dmamem_free(sc->aac_fib_dmat, sc->aac_fibs, sc->aac_fibmap);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
@ -1142,11 +1096,13 @@ aac_init(struct aac_softc *sc)
|
||||
|
||||
/*
|
||||
* Initialise FIB queues. Note that it appears that the layout of the indexes
|
||||
* and the segmentation of the entries is mandated by the adapter, which is
|
||||
* and the segmentation of the entries may be mandated by the adapter, which is
|
||||
* only told about the base of the queue index fields.
|
||||
*
|
||||
* The initial values of the indices are assumed to inform the adapter
|
||||
* of the sizes of the respective queues.
|
||||
* of the sizes of the respective queues, and theoretically it could work out
|
||||
* the entire layout of the queue structures from this. We take the easy
|
||||
* route and just lay this area out like everyone else does.
|
||||
*
|
||||
* The Linux driver uses a much more complex scheme whereby several header
|
||||
* records are kept for each queue. We use a couple of generic list manipulation
|
||||
@ -1196,8 +1152,8 @@ aac_init(struct aac_softc *sc)
|
||||
* Give the init structure to the controller.
|
||||
*/
|
||||
if (aac_sync_command(sc, AAC_MONKER_INITSTRUCT,
|
||||
sc->aac_common_busaddr + offsetof(struct aac_common, ac_init),
|
||||
0, 0, 0, NULL)) {
|
||||
sc->aac_common_busaddr + offsetof(struct aac_common, ac_init),
|
||||
0, 0, 0, NULL)) {
|
||||
device_printf(sc->aac_dev, "error establishing init structure\n");
|
||||
return(EIO);
|
||||
}
|
||||
@ -1210,8 +1166,8 @@ aac_init(struct aac_softc *sc)
|
||||
*/
|
||||
static int
|
||||
aac_sync_command(struct aac_softc *sc, u_int32_t command,
|
||||
u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
|
||||
u_int32_t *sp)
|
||||
u_int32_t arg0, u_int32_t arg1, u_int32_t arg2, u_int32_t arg3,
|
||||
u_int32_t *sp)
|
||||
{
|
||||
time_t then;
|
||||
u_int32_t status;
|
||||
@ -1243,7 +1199,7 @@ aac_sync_command(struct aac_softc *sc, u_int32_t command,
|
||||
status = AAC_GET_MAILBOXSTATUS(sc);
|
||||
if (sp != NULL)
|
||||
*sp = status;
|
||||
return(0); /* check command return status? */
|
||||
return(0);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
@ -1285,7 +1241,7 @@ aac_sync_fib(struct aac_softc *sc, u_int32_t command, u_int32_t xferstate,
|
||||
* Give the FIB to the controller, wait for a response.
|
||||
*/
|
||||
if (aac_sync_command(sc, AAC_MONKER_SYNCFIB, fib->Header.ReceiverFibAddress,
|
||||
0, 0, 0, NULL)) {
|
||||
0, 0, 0, NULL)) {
|
||||
debug(2, "IO error");
|
||||
return(EIO);
|
||||
}
|
||||
@ -1324,9 +1280,10 @@ static struct {
|
||||
* Atomically insert an entry into the nominated queue, returns 0 on success or EBUSY
|
||||
* if the queue is full.
|
||||
*
|
||||
* XXX note that it would be more efficient to defer notifying the controller in
|
||||
* the case where we may be inserting several entries in rapid succession, but
|
||||
* implementing this usefully is difficult.
|
||||
* Note: it would be more efficient to defer notifying the controller in
|
||||
* the case where we may be inserting several entries in rapid succession, but
|
||||
* implementing this usefully may be difficult (it would involve a separate
|
||||
* queue/notify interface).
|
||||
*/
|
||||
static int
|
||||
aac_enqueue_fib(struct aac_softc *sc, int queue, u_int32_t fib_size, u_int32_t fib_addr)
|
||||
@ -1415,6 +1372,41 @@ out:
|
||||
return(error);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Check for commands that have been outstanding for a suspiciously long time,
|
||||
* and complain about them.
|
||||
*/
|
||||
static void
|
||||
aac_timeout(struct aac_softc *sc)
|
||||
{
|
||||
int s;
|
||||
struct aac_command *cm;
|
||||
time_t deadline;
|
||||
|
||||
/* simulate an interrupt to handle possibly-missed interrupts */
|
||||
aac_intr(sc);
|
||||
|
||||
/* kick the I/O queue to restart it in the case of deadlock */
|
||||
aac_startio(sc);
|
||||
|
||||
/* traverse the busy command list, bitch about late commands once only */
|
||||
deadline = time_second - AAC_CMD_TIMEOUT;
|
||||
s = splbio();
|
||||
TAILQ_FOREACH(cm, &sc->aac_busy, cm_link) {
|
||||
if ((cm->cm_timestamp < deadline) && !(cm->cm_flags & AAC_CMD_TIMEDOUT)) {
|
||||
cm->cm_flags |= AAC_CMD_TIMEDOUT;
|
||||
device_printf(sc->aac_dev, "COMMAND TIMED OUT AFTER %d SECONDS\n",
|
||||
(int)(time_second - cm->cm_timestamp));
|
||||
AAC_PRINT_FIB(sc, cm->cm_fib);
|
||||
}
|
||||
}
|
||||
splx(s);
|
||||
|
||||
/* reset the timer for next time */
|
||||
timeout((timeout_t*)aac_timeout, sc, AAC_PERIODIC_INTERVAL * hz);
|
||||
return;
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
********************************************************************************
|
||||
Interface Function Vectors
|
||||
@ -1600,7 +1592,7 @@ aac_describe_controller(struct aac_softc *sc)
|
||||
if (bufsize != sizeof(*info)) {
|
||||
device_printf(sc->aac_dev, "RequestAdapterInfo returned wrong data size (%d != %d)\n",
|
||||
bufsize, sizeof(*info));
|
||||
return;
|
||||
/*return;*/
|
||||
}
|
||||
info = (struct aac_adapter_info *)&buf[0];
|
||||
|
||||
@ -1630,7 +1622,7 @@ aac_describe_code(struct aac_code_lookup *table, u_int32_t code)
|
||||
for (i = 0; table[i].string != NULL; i++)
|
||||
if (table[i].code == code)
|
||||
return(table[i].string);
|
||||
return(table[i+1].string);
|
||||
return(table[i + 1].string);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
@ -1671,43 +1663,65 @@ aac_close(dev_t dev, int flags, int fmt, struct proc *p)
|
||||
static int
|
||||
aac_ioctl(dev_t dev, u_long cmd, caddr_t arg, int flag, struct proc *p)
|
||||
{
|
||||
struct aac_softc *sc = dev->si_drv1;
|
||||
int error = 0, i;
|
||||
union aac_statrequest *as = (union aac_statrequest *)arg;
|
||||
struct aac_softc *sc = dev->si_drv1;
|
||||
int error = 0;
|
||||
#ifdef AAC_COMPAT_LINUX
|
||||
int i;
|
||||
#endif
|
||||
|
||||
debug_called(2);
|
||||
|
||||
switch (cmd) {
|
||||
case AACIO_STATS:
|
||||
switch (as->as_item) {
|
||||
case AACQ_FREE:
|
||||
case AACQ_BIO:
|
||||
case AACQ_READY:
|
||||
case AACQ_BUSY:
|
||||
case AACQ_COMPLETE:
|
||||
bcopy(&sc->aac_qstat[as->as_item], &as->as_qstat, sizeof(struct aac_qstat));
|
||||
break;
|
||||
default:
|
||||
error = ENOENT;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
#ifdef AAC_COMPAT_LINUX
|
||||
case FSACTL_SENDFIB:
|
||||
debug(0, "FSACTL_SENDFIB");
|
||||
debug(1, "FSACTL_SENDFIB");
|
||||
error = aac_ioctl_sendfib(sc, arg);
|
||||
break;
|
||||
case FSACTL_AIF_THREAD:
|
||||
debug(0, "FSACTL_AIF_THREAD");
|
||||
debug(1, "FSACTL_AIF_THREAD");
|
||||
error = EINVAL;
|
||||
break;
|
||||
case FSACTL_OPEN_GET_ADAPTER_FIB:
|
||||
debug(0, "FSACTL_OPEN_GET_ADAPTER_FIB");
|
||||
debug(1, "FSACTL_OPEN_GET_ADAPTER_FIB");
|
||||
/*
|
||||
* Pass the caller out an AdapterFibContext.
|
||||
*
|
||||
* Note that because we only support one opener, we
|
||||
* basically ignore this. Set the caller's context to a magic
|
||||
* number just in case.
|
||||
*
|
||||
* The Linux code hands the driver a pointer into kernel space,
|
||||
* and then trusts it when the caller hands it back. Aiee!
|
||||
*/
|
||||
i = AAC_AIF_SILLYMAGIC;
|
||||
error = copyout(&i, arg, sizeof(i));
|
||||
break;
|
||||
case FSACTL_GET_NEXT_ADAPTER_FIB:
|
||||
debug(0, "FSACTL_GET_NEXT_ADAPTER_FIB");
|
||||
debug(1, "FSACTL_GET_NEXT_ADAPTER_FIB");
|
||||
error = aac_linux_getnext_aif(sc, arg);
|
||||
break;
|
||||
case FSACTL_CLOSE_GET_ADAPTER_FIB:
|
||||
debug(0, "FSACTL_CLOSE_GET_ADAPTER_FIB");
|
||||
debug(1, "FSACTL_CLOSE_GET_ADAPTER_FIB");
|
||||
/* don't do anything here */
|
||||
break;
|
||||
case FSACTL_MINIPORT_REV_CHECK:
|
||||
debug(0, "FSACTL_MINIPORT_REV_CHECK");
|
||||
debug(1, "FSACTL_MINIPORT_REV_CHECK");
|
||||
error = aac_linux_rev_check(sc, arg);
|
||||
break;
|
||||
#endif
|
||||
@ -1801,28 +1815,6 @@ aac_handle_aif(struct aac_softc *sc, struct aac_aif_command *aif)
|
||||
aac_print_aif(sc, aif);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Hand the next AIF off the top of the queue out to userspace.
|
||||
*/
|
||||
static int
|
||||
aac_return_aif(struct aac_softc *sc, caddr_t uptr)
|
||||
{
|
||||
int error, s;
|
||||
|
||||
debug_called(2);
|
||||
|
||||
s = splbio();
|
||||
if (sc->aac_aifq_tail == sc->aac_aifq_head) {
|
||||
error = EAGAIN;
|
||||
} else {
|
||||
error = copyout(&sc->aac_aifq[sc->aac_aifq_tail], uptr, sizeof(struct aac_aif_command));
|
||||
if (!error)
|
||||
sc->aac_aifq_tail = (sc->aac_aifq_tail + 1) % AAC_AIFQ_LENGTH;
|
||||
}
|
||||
splx(s);
|
||||
return(error);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
********************************************************************************
|
||||
Linux Management Interface
|
||||
@ -1860,7 +1852,7 @@ aac_linux_ioctl(struct proc *p, struct linux_ioctl_args *args)
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Return the Revision of the driver to the userspace and check to see if the
|
||||
* Return the Revision of the driver to userspace and check to see if the
|
||||
* userspace app is possibly compatible. This is extremely bogus right now
|
||||
* because I have no idea how to handle the versioning of this driver. It is
|
||||
* needed, though, to get aaccli working.
|
||||
@ -1914,14 +1906,14 @@ aac_linux_getnext_aif(struct aac_softc *sc, caddr_t arg)
|
||||
} else {
|
||||
|
||||
s = splbio();
|
||||
error = aac_return_aif(sc, agf.AifFib);
|
||||
error = aac_linux_return_aif(sc, agf.AifFib);
|
||||
|
||||
if ((error == EAGAIN) && (agf.Wait)) {
|
||||
sc->aac_state |= AAC_STATE_AIF_SLEEPER;
|
||||
while (error == EAGAIN) {
|
||||
error = tsleep(sc->aac_aifq, PRIBIO | PCATCH, "aacaif", 0);
|
||||
if (error == 0)
|
||||
error = aac_return_aif(sc, agf.AifFib);
|
||||
error = aac_linux_return_aif(sc, agf.AifFib);
|
||||
}
|
||||
sc->aac_state &= ~AAC_STATE_AIF_SLEEPER;
|
||||
}
|
||||
@ -1931,4 +1923,27 @@ aac_linux_getnext_aif(struct aac_softc *sc, caddr_t arg)
|
||||
return(error);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
* Hand the next AIF off the top of the queue out to userspace.
|
||||
*/
|
||||
static int
|
||||
aac_linux_return_aif(struct aac_softc *sc, caddr_t uptr)
|
||||
{
|
||||
int error, s;
|
||||
|
||||
debug_called(2);
|
||||
|
||||
s = splbio();
|
||||
if (sc->aac_aifq_tail == sc->aac_aifq_head) {
|
||||
error = EAGAIN;
|
||||
} else {
|
||||
error = copyout(&sc->aac_aifq[sc->aac_aifq_tail], uptr, sizeof(struct aac_aif_command));
|
||||
if (!error)
|
||||
sc->aac_aifq_tail = (sc->aac_aifq_tail + 1) % AAC_AIFQ_LENGTH;
|
||||
}
|
||||
splx(s);
|
||||
return(error);
|
||||
}
|
||||
|
||||
|
||||
#endif /* AAC_COMPAT_LINUX */
|
||||
|
@ -60,5 +60,5 @@
|
||||
|
||||
#else /* new bio style */
|
||||
# include <sys/bio.h>
|
||||
#define BIO_IS_READ(x) ((x)->bio_cmd == BIO_READ)
|
||||
# define BIO_IS_READ(x) ((x)->bio_cmd == BIO_READ)
|
||||
#endif
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include <machine/bus.h>
|
||||
|
||||
#include <dev/aac/aacreg.h>
|
||||
#include <dev/aac/aac_ioctl.h>
|
||||
#include <dev/aac/aacvar.h>
|
||||
|
||||
void aac_printstate0(void);
|
||||
@ -90,7 +91,16 @@ aac_print_queues(struct aac_softc *sc)
|
||||
sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_PRODUCER_INDEX],
|
||||
sc->aac_queues->qt_qindex[AAC_ADAP_HIGH_RESP_QUEUE][AAC_CONSUMER_INDEX],
|
||||
AAC_ADAP_HIGH_RESP_ENTRIES);
|
||||
|
||||
device_printf(sc->aac_dev, "AACQ_FREE %d/%d\n",
|
||||
sc->aac_qstat[AACQ_FREE].q_length, sc->aac_qstat[AACQ_FREE].q_max);
|
||||
device_printf(sc->aac_dev, "AACQ_BIO %d/%d\n",
|
||||
sc->aac_qstat[AACQ_BIO].q_length, sc->aac_qstat[AACQ_BIO].q_max);
|
||||
device_printf(sc->aac_dev, "AACQ_READY %d/%d\n",
|
||||
sc->aac_qstat[AACQ_READY].q_length, sc->aac_qstat[AACQ_READY].q_max);
|
||||
device_printf(sc->aac_dev, "AACQ_BUSY %d/%d\n",
|
||||
sc->aac_qstat[AACQ_BUSY].q_length, sc->aac_qstat[AACQ_BUSY].q_max);
|
||||
device_printf(sc->aac_dev, "AACQ_COMPLETE %d/%d\n",
|
||||
sc->aac_qstat[AACQ_COMPLETE].q_length, sc->aac_qstat[AACQ_COMPLETE].q_max);
|
||||
}
|
||||
|
||||
/********************************************************************************
|
||||
|
@ -41,6 +41,7 @@
|
||||
#include <sys/rman.h>
|
||||
|
||||
#include <dev/aac/aacreg.h>
|
||||
#include <dev/aac/aac_ioctl.h>
|
||||
#include <dev/aac/aacvar.h>
|
||||
|
||||
/*
|
||||
@ -187,13 +188,15 @@ aac_disk_strategy(struct bio *bp)
|
||||
* Handle completion of an I/O request.
|
||||
*/
|
||||
void
|
||||
aac_complete_bio(struct bio *bp)
|
||||
aac_biodone(struct bio *bp)
|
||||
{
|
||||
struct aac_disk *sc = (struct aac_disk *)bp->bio_dev->si_drv1;
|
||||
|
||||
debug_called(4);
|
||||
|
||||
devstat_end_transaction_bio(&sc->ad_stats, bp);
|
||||
if (bp->bio_flags & BIO_ERROR)
|
||||
diskerr(bp, (char *)bp->bio_driver1, 0, &sc->ad_label);
|
||||
biodone(bp);
|
||||
}
|
||||
|
||||
@ -226,7 +229,7 @@ aac_disk_attach(device_t dev)
|
||||
sc->ad_container = device_get_ivars(dev);
|
||||
sc->ad_dev = dev;
|
||||
|
||||
/* require that extended translation be enabled XXX document! */
|
||||
/* require that extended translation be enabled - other drivers read the disk! */
|
||||
sc->ad_size = sc->ad_container->co_mntobj.Capacity;
|
||||
if (sc->ad_size >= (2 * 1024 * 1024)) { /* 2GB */
|
||||
sc->ad_heads = 255;
|
||||
|
@ -28,6 +28,31 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Command queue statistics
|
||||
*/
|
||||
#define AACQ_FREE 0
|
||||
#define AACQ_BIO 1
|
||||
#define AACQ_READY 2
|
||||
#define AACQ_BUSY 3
|
||||
#define AACQ_COMPLETE 4
|
||||
#define AACQ_COUNT 5 /* total number of queues */
|
||||
|
||||
struct aac_qstat {
|
||||
u_int32_t q_length;
|
||||
u_int32_t q_max;
|
||||
};
|
||||
|
||||
/*
|
||||
* Statistics request
|
||||
*/
|
||||
union aac_statrequest {
|
||||
u_int32_t as_item;
|
||||
struct aac_qstat as_qstat;
|
||||
};
|
||||
|
||||
#define AACIO_STATS _IOWR('T', 101, union aac_statrequest)
|
||||
|
||||
#ifdef AAC_COMPAT_LINUX
|
||||
|
||||
/*
|
||||
|
@ -50,6 +50,7 @@
|
||||
#include <pci/pcivar.h>
|
||||
|
||||
#include <dev/aac/aacreg.h>
|
||||
#include <dev/aac/aac_ioctl.h>
|
||||
#include <dev/aac/aacvar.h>
|
||||
|
||||
static int aac_pci_probe(device_t dev);
|
||||
@ -230,7 +231,7 @@ aac_pci_attach(device_t dev)
|
||||
BUS_SPACE_MAXADDR, /* lowaddr */
|
||||
BUS_SPACE_MAXADDR, /* highaddr */
|
||||
NULL, NULL, /* filter, filterarg */
|
||||
AAC_CLUSTER_COUNT * sizeof(struct aac_fib), 1,/* maxsize, nsegments */
|
||||
AAC_FIB_COUNT * sizeof(struct aac_fib), 1, /* maxsize, nsegments */
|
||||
BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */
|
||||
0, /* flags */
|
||||
&sc->aac_fib_dmat)) {
|
||||
@ -241,6 +242,7 @@ aac_pci_attach(device_t dev)
|
||||
/*
|
||||
* Detect the hardware interface version, set up the bus interface indirection.
|
||||
*/
|
||||
sc->aac_hwif = AAC_HWIF_UNKNOWN;
|
||||
for (i = 0; aac_identifiers[i].vendor != 0; i++) {
|
||||
if ((aac_identifiers[i].vendor == pci_get_vendor(dev)) &&
|
||||
(aac_identifiers[i].device == pci_get_device(dev))) {
|
||||
@ -259,6 +261,11 @@ aac_pci_attach(device_t dev)
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sc->aac_hwif == AAC_HWIF_UNKNOWN) {
|
||||
device_printf(sc->aac_dev, "unknown hardware type\n");
|
||||
error = ENXIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Do bus-independent initialisation.
|
||||
|
@ -489,7 +489,7 @@ struct aac_adapter_info {
|
||||
u_int32_t TotalMem; /* adapter Total Memory */
|
||||
struct FsaRevision KernelRevision; /* adapter Kernel Software Revision */
|
||||
struct FsaRevision MonitorRevision; /* adapter Monitor/Diagnostic Software Revision */
|
||||
struct FsaRevision HardwareRevision;/* TDB */
|
||||
struct FsaRevision HardwareRevision;/* TBD */
|
||||
struct FsaRevision BIOSRevision; /* adapter BIOS Revision */
|
||||
u_int32_t ClusteringEnabled;
|
||||
u_int32_t ClusterChannelMask;
|
||||
|
@ -45,13 +45,12 @@
|
||||
#define AAC_ADAPTER_FIBS 8
|
||||
|
||||
/*
|
||||
* FIBs are allocated in clusters as we need them; each cluster must be physically
|
||||
* contiguous. Set the number of FIBs to try to allocate in a cluster.
|
||||
* Setting this value too high may result in FIBs not being available in conditions
|
||||
* of high load with fragmented physical memory. The value must be a multiple of
|
||||
* (PAGE_SIZE / 512).
|
||||
* FIBs are allocated up-front, and the pool isn't grown. We should allocate
|
||||
* enough here to let us keep the adapter busy without wasting large amounts
|
||||
* of kernel memory. The current interface implementation limits us to 512
|
||||
* FIBs queued for the adapter at any one time.
|
||||
*/
|
||||
#define AAC_CLUSTER_COUNT 64
|
||||
#define AAC_FIB_COUNT 128
|
||||
|
||||
/*
|
||||
* The controller reports status events in AIFs. We hang on to a number of these
|
||||
@ -72,7 +71,18 @@
|
||||
/*
|
||||
* Timeout for immediate commands.
|
||||
*/
|
||||
#define AAC_IMMEDIATE_TIMEOUT 30
|
||||
#define AAC_IMMEDIATE_TIMEOUT 30 /* seconds */
|
||||
|
||||
/*
|
||||
* Timeout for normal commands
|
||||
*/
|
||||
#define AAC_CMD_TIMEOUT 30 /* seconds */
|
||||
|
||||
/*
|
||||
* Rate at which we periodically check for timed out commands and kick the
|
||||
* controller.
|
||||
*/
|
||||
#define AAC_PERIODIC_INTERVAL 10 /* seconds */
|
||||
|
||||
/*
|
||||
* Character device major numbers.
|
||||
@ -139,26 +149,11 @@ struct aac_command
|
||||
#define AAC_CMD_DATAIN (1<<1) /* command involves data moving from controller to host */
|
||||
#define AAC_CMD_DATAOUT (1<<2) /* command involves data moving from host to controller */
|
||||
#define AAC_CMD_COMPLETED (1<<3) /* command has been completed */
|
||||
#define AAC_CMD_TIMEDOUT (1<<4) /* command taken too long */
|
||||
|
||||
void (* cm_complete)(struct aac_command *cm);
|
||||
void *cm_private;
|
||||
struct callout_handle timeout_handle; /* timeout handle */
|
||||
};
|
||||
|
||||
/*
|
||||
* Command/command packet cluster.
|
||||
*
|
||||
* Due to the difficulty of using the zone allocator to create a new
|
||||
* zone from within a module, we use our own clustering to reduce
|
||||
* memory wastage due to allocating lots of these small structures.
|
||||
*/
|
||||
struct aac_command_cluster
|
||||
{
|
||||
TAILQ_ENTRY(aac_command_cluster) cmc_link;
|
||||
struct aac_fib *cmc_fibs;
|
||||
bus_dmamap_t cmc_fibmap;
|
||||
u_int32_t cmc_fibphys;
|
||||
struct aac_command cmc_command[AAC_CLUSTER_COUNT];
|
||||
time_t cm_timestamp; /* command creation time */
|
||||
};
|
||||
|
||||
/*
|
||||
@ -231,29 +226,30 @@ extern struct aac_interface aac_sa_interface;
|
||||
struct aac_softc
|
||||
{
|
||||
/* bus connections */
|
||||
device_t aac_dev;
|
||||
struct resource *aac_regs_resource; /* register interface window */
|
||||
int aac_regs_rid; /* resource ID */
|
||||
bus_space_handle_t aac_bhandle; /* bus space handle */
|
||||
bus_space_tag_t aac_btag; /* bus space tag */
|
||||
bus_dma_tag_t aac_parent_dmat; /* parent DMA tag */
|
||||
bus_dma_tag_t aac_buffer_dmat; /* data buffer/command DMA tag */
|
||||
struct resource *aac_irq; /* interrupt */
|
||||
int aac_irq_rid;
|
||||
void *aac_intr; /* interrupt handle */
|
||||
device_t aac_dev;
|
||||
struct resource *aac_regs_resource; /* register interface window */
|
||||
int aac_regs_rid; /* resource ID */
|
||||
bus_space_handle_t aac_bhandle; /* bus space handle */
|
||||
bus_space_tag_t aac_btag; /* bus space tag */
|
||||
bus_dma_tag_t aac_parent_dmat; /* parent DMA tag */
|
||||
bus_dma_tag_t aac_buffer_dmat; /* data buffer/command DMA tag */
|
||||
struct resource *aac_irq; /* interrupt */
|
||||
int aac_irq_rid;
|
||||
void *aac_intr; /* interrupt handle */
|
||||
|
||||
/* controller features, limits and status */
|
||||
int aac_state;
|
||||
int aac_state;
|
||||
#define AAC_STATE_SUSPEND (1<<0)
|
||||
#define AAC_STATE_OPEN (1<<1)
|
||||
#define AAC_STATE_INTERRUPTS_ON (1<<2)
|
||||
#define AAC_STATE_AIF_SLEEPER (1<<3)
|
||||
struct FsaRevision aac_revision;
|
||||
struct FsaRevision aac_revision;
|
||||
|
||||
/* controller hardware interface */
|
||||
int aac_hwif;
|
||||
#define AAC_HWIF_I960RX 0
|
||||
#define AAC_HWIF_STRONGARM 1
|
||||
#define AAC_HWIF_UNKNOWN -1
|
||||
bus_dma_tag_t aac_common_dmat; /* common structure DMA tag */
|
||||
bus_dmamap_t aac_common_dmamap; /* common structure DMA map */
|
||||
struct aac_common *aac_common;
|
||||
@ -261,17 +257,23 @@ struct aac_softc
|
||||
struct aac_interface aac_if;
|
||||
|
||||
/* command/fib resources */
|
||||
TAILQ_HEAD(,aac_command_cluster) aac_clusters; /* command memory blocks */
|
||||
bus_dma_tag_t aac_fib_dmat; /* DMA tag for allocating FIBs */
|
||||
bus_dma_tag_t aac_fib_dmat; /* DMA tag for allocating FIBs */
|
||||
struct aac_fib *aac_fibs;
|
||||
bus_dmamap_t aac_fibmap;
|
||||
u_int32_t aac_fibphys;
|
||||
struct aac_command aac_command[AAC_FIB_COUNT];
|
||||
|
||||
/* command management */
|
||||
TAILQ_HEAD(,aac_command) aac_freecmds; /* command structures available for reuse */
|
||||
TAILQ_HEAD(,aac_command) aac_free; /* command structures available for reuse */
|
||||
TAILQ_HEAD(,aac_command) aac_ready; /* commands on hold for controller resources */
|
||||
TAILQ_HEAD(,aac_command) aac_completed; /* commands which have been returned by the controller */
|
||||
TAILQ_HEAD(,aac_command) aac_busy;
|
||||
TAILQ_HEAD(,aac_command) aac_complete; /* commands which have been returned by the controller */
|
||||
struct bio_queue_head aac_bioq;
|
||||
struct aac_queue_table *aac_queues;
|
||||
struct aac_queue_entry *aac_qentries[AAC_QUEUE_COUNT];
|
||||
|
||||
struct aac_qstat aac_qstat[AACQ_COUNT]; /* queue statistics */
|
||||
|
||||
/* connected containters */
|
||||
struct aac_container aac_container[AAC_MAX_CONTAINERS];
|
||||
|
||||
@ -301,7 +303,7 @@ extern int aac_resume(device_t dev);
|
||||
extern void aac_intr(void *arg);
|
||||
extern devclass_t aac_devclass;
|
||||
extern void aac_submit_bio(struct bio *bp);
|
||||
extern void aac_complete_bio(struct bio *bp);
|
||||
extern void aac_biodone(struct bio *bp);
|
||||
|
||||
/*
|
||||
* Debugging levels:
|
||||
@ -310,25 +312,31 @@ extern void aac_complete_bio(struct bio *bp);
|
||||
* 2 - extremely noisy, emit trace items in loops, etc.
|
||||
*/
|
||||
#ifdef AAC_DEBUG
|
||||
#define debug(level, fmt, args...) do { if (level <= AAC_DEBUG) printf("%s: " fmt "\n", __FUNCTION__ , ##args); } while(0)
|
||||
#define debug_called(level) do { if (level <= AAC_DEBUG) printf(__FUNCTION__ ": called\n"); } while(0)
|
||||
# define debug(level, fmt, args...) \
|
||||
do { \
|
||||
if (level <= AAC_DEBUG) printf("%s: " fmt "\n", __FUNCTION__ , ##args); \
|
||||
} while(0)
|
||||
# define debug_called(level) \
|
||||
do { \
|
||||
if (level <= AAC_DEBUG) printf(__FUNCTION__ ": called\n"); \
|
||||
} while(0)
|
||||
|
||||
extern void aac_print_queues(struct aac_softc *sc);
|
||||
extern void aac_panic(struct aac_softc *sc, char *reason);
|
||||
extern void aac_print_fib(struct aac_softc *sc, struct aac_fib *fib, char *caller);
|
||||
extern void aac_print_aif(struct aac_softc *sc, struct aac_aif_command *aif);
|
||||
|
||||
#define AAC_PRINT_FIB(sc, fib) aac_print_fib(sc, fib, __FUNCTION__)
|
||||
# define AAC_PRINT_FIB(sc, fib) aac_print_fib(sc, fib, __FUNCTION__)
|
||||
|
||||
#else
|
||||
#define debug(level, fmt, args...)
|
||||
#define debug_called(level)
|
||||
# define debug(level, fmt, args...)
|
||||
# define debug_called(level)
|
||||
|
||||
#define aac_print_queues(sc)
|
||||
#define aac_panic(sc, reason)
|
||||
#define aac_print_aif(sc, aif)
|
||||
# define aac_print_queues(sc)
|
||||
# define aac_panic(sc, reason)
|
||||
# define aac_print_aif(sc, aif)
|
||||
|
||||
#define AAC_PRINT_FIB(sc, fib)
|
||||
# define AAC_PRINT_FIB(sc, fib)
|
||||
#endif
|
||||
|
||||
struct aac_code_lookup {
|
||||
@ -337,109 +345,115 @@ struct aac_code_lookup {
|
||||
};
|
||||
|
||||
/********************************************************************************
|
||||
* Queue primitives
|
||||
*
|
||||
* These are broken out individually to make statistics gathering easier.
|
||||
* Queue primitives for driver queues.
|
||||
*/
|
||||
#define AACQ_ADD(sc, qname) \
|
||||
do { \
|
||||
struct aac_qstat *qs = &(sc)->aac_qstat[qname]; \
|
||||
\
|
||||
qs->q_length++; \
|
||||
if (qs->q_length > qs->q_max) \
|
||||
qs->q_max = qs->q_length; \
|
||||
} while(0)
|
||||
|
||||
#define AACQ_REMOVE(sc, qname) (sc)->aac_qstat[qname].q_length--
|
||||
#define AACQ_INIT(sc, qname) \
|
||||
do { \
|
||||
sc->aac_qstat[qname].q_length = 0; \
|
||||
sc->aac_qstat[qname].q_max = 0; \
|
||||
} while(0)
|
||||
|
||||
|
||||
#define AACQ_COMMAND_QUEUE(name, index) \
|
||||
static __inline void \
|
||||
aac_initq_ ## name (struct aac_softc *sc) \
|
||||
{ \
|
||||
TAILQ_INIT(&sc->aac_ ## name); \
|
||||
AACQ_INIT(sc, index); \
|
||||
} \
|
||||
static __inline void \
|
||||
aac_enqueue_ ## name (struct aac_command *cm) \
|
||||
{ \
|
||||
int s; \
|
||||
\
|
||||
s = splbio(); \
|
||||
TAILQ_INSERT_TAIL(&cm->cm_sc->aac_ ## name, cm, cm_link); \
|
||||
AACQ_ADD(cm->cm_sc, index); \
|
||||
splx(s); \
|
||||
} \
|
||||
static __inline void \
|
||||
aac_requeue_ ## name (struct aac_command *cm) \
|
||||
{ \
|
||||
int s; \
|
||||
\
|
||||
s = splbio(); \
|
||||
TAILQ_INSERT_HEAD(&cm->cm_sc->aac_ ## name, cm, cm_link); \
|
||||
AACQ_ADD(cm->cm_sc, index); \
|
||||
splx(s); \
|
||||
} \
|
||||
static __inline struct aac_command * \
|
||||
aac_dequeue_ ## name (struct aac_softc *sc) \
|
||||
{ \
|
||||
struct aac_command *cm; \
|
||||
int s; \
|
||||
\
|
||||
s = splbio(); \
|
||||
if ((cm = TAILQ_FIRST(&sc->aac_ ## name)) != NULL) { \
|
||||
TAILQ_REMOVE(&sc->aac_ ## name, cm, cm_link); \
|
||||
AACQ_REMOVE(sc, index); \
|
||||
} \
|
||||
splx(s); \
|
||||
return(cm); \
|
||||
} \
|
||||
static __inline void \
|
||||
aac_remove_ ## name (struct aac_command *cm) \
|
||||
{ \
|
||||
int s; \
|
||||
\
|
||||
s = splbio(); \
|
||||
TAILQ_REMOVE(&cm->cm_sc->aac_ ## name, cm, cm_link); \
|
||||
AACQ_REMOVE(cm->cm_sc, index); \
|
||||
splx(s); \
|
||||
} \
|
||||
struct hack
|
||||
|
||||
AACQ_COMMAND_QUEUE(free, AACQ_FREE);
|
||||
AACQ_COMMAND_QUEUE(ready, AACQ_READY);
|
||||
AACQ_COMMAND_QUEUE(busy, AACQ_BUSY);
|
||||
AACQ_COMMAND_QUEUE(complete, AACQ_COMPLETE);
|
||||
|
||||
/*
|
||||
* outstanding bio queue
|
||||
*/
|
||||
static __inline void
|
||||
aac_initq_bio(struct aac_softc *sc)
|
||||
{
|
||||
bioq_init(&sc->aac_bioq);
|
||||
AACQ_INIT(sc, AACQ_BIO);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
aac_enqueue_ready(struct aac_command *cm)
|
||||
aac_enqueue_bio(struct aac_softc *sc, struct bio *bp)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
TAILQ_INSERT_TAIL(&cm->cm_sc->aac_ready, cm, cm_link);
|
||||
bioq_insert_tail(&sc->aac_bioq, bp);
|
||||
AACQ_ADD(sc, AACQ_BIO);
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
aac_requeue_ready(struct aac_command *cm)
|
||||
static __inline struct bio *
|
||||
aac_dequeue_bio(struct aac_softc *sc)
|
||||
{
|
||||
int s;
|
||||
struct bio *bp;
|
||||
|
||||
s = splbio();
|
||||
TAILQ_INSERT_HEAD(&cm->cm_sc->aac_ready, cm, cm_link);
|
||||
if ((bp = bioq_first(&sc->aac_bioq)) != NULL) {
|
||||
bioq_remove(&sc->aac_bioq, bp);
|
||||
AACQ_REMOVE(sc, AACQ_BIO);
|
||||
}
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static __inline struct aac_command *
|
||||
aac_dequeue_ready(struct aac_softc *sc)
|
||||
{
|
||||
struct aac_command *cm;
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
if ((cm = TAILQ_FIRST(&sc->aac_ready)) != NULL)
|
||||
TAILQ_REMOVE(&sc->aac_ready, cm, cm_link);
|
||||
splx(s);
|
||||
return(cm);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
aac_enqueue_completed(struct aac_command *cm)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
TAILQ_INSERT_TAIL(&cm->cm_sc->aac_completed, cm, cm_link);
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static __inline struct aac_command *
|
||||
aac_dequeue_completed(struct aac_softc *sc)
|
||||
{
|
||||
struct aac_command *cm;
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
if ((cm = TAILQ_FIRST(&sc->aac_completed)) != NULL)
|
||||
TAILQ_REMOVE(&sc->aac_completed, cm, cm_link);
|
||||
splx(s);
|
||||
return(cm);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
aac_enqueue_free(struct aac_command *cm)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
TAILQ_INSERT_HEAD(&cm->cm_sc->aac_freecmds, cm, cm_link);
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static __inline struct aac_command *
|
||||
aac_dequeue_free(struct aac_softc *sc)
|
||||
{
|
||||
struct aac_command *cm;
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
if ((cm = TAILQ_FIRST(&sc->aac_freecmds)) != NULL)
|
||||
TAILQ_REMOVE(&sc->aac_freecmds, cm, cm_link);
|
||||
splx(s);
|
||||
return(cm);
|
||||
}
|
||||
|
||||
static __inline void
|
||||
aac_enqueue_cluster(struct aac_softc *sc, struct aac_command_cluster *cmc)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
TAILQ_INSERT_HEAD(&sc->aac_clusters, cmc, cmc_link);
|
||||
splx(s);
|
||||
}
|
||||
|
||||
static __inline struct aac_command_cluster *
|
||||
aac_dequeue_cluster(struct aac_softc *sc)
|
||||
{
|
||||
struct aac_command_cluster *cmc;
|
||||
int s;
|
||||
|
||||
s = splbio();
|
||||
if ((cmc = TAILQ_FIRST(&sc->aac_clusters)) != NULL)
|
||||
TAILQ_REMOVE(&sc->aac_clusters, cmc, cmc_link);
|
||||
splx(s);
|
||||
return(cmc);
|
||||
return(bp);
|
||||
}
|
||||
|
@ -28,6 +28,31 @@
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* Command queue statistics
|
||||
*/
|
||||
#define AACQ_FREE 0
|
||||
#define AACQ_BIO 1
|
||||
#define AACQ_READY 2
|
||||
#define AACQ_BUSY 3
|
||||
#define AACQ_COMPLETE 4
|
||||
#define AACQ_COUNT 5 /* total number of queues */
|
||||
|
||||
struct aac_qstat {
|
||||
u_int32_t q_length;
|
||||
u_int32_t q_max;
|
||||
};
|
||||
|
||||
/*
|
||||
* Statistics request
|
||||
*/
|
||||
union aac_statrequest {
|
||||
u_int32_t as_item;
|
||||
struct aac_qstat as_qstat;
|
||||
};
|
||||
|
||||
#define AACIO_STATS _IOWR('T', 101, union aac_statrequest)
|
||||
|
||||
#ifdef AAC_COMPAT_LINUX
|
||||
|
||||
/*
|
||||
|
Loading…
x
Reference in New Issue
Block a user