Overhaul of the timeout/reinit framework. This should clear up most

of the leftovers from the old version that really doesn't work anymore.

Add a reset function for host-end of the ATA channel. This is needed
for the SiI3112 in order to whack it back to reality if a device
locks up the SATA interface (thereby preventing that we can reset the
device). The result is that ATA now recovers from the timeouts that
happens with the SiI3112A and more or less all disks based on old
PATA electronics with a Marvell PATA->SATA converter. This includes
lots of the popular SATA dongles and the WDC Raptor disks..
This commit is contained in:
sos 2004-01-11 22:08:34 +00:00
parent f20180c215
commit d6c0154728
15 changed files with 358 additions and 220 deletions

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -40,7 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/bus.h>
#include <sys/bio.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/sema.h>
#include <sys/sysctl.h>
#include <sys/taskqueue.h>
#include <machine/stdarg.h>
@ -60,13 +60,13 @@ static d_ioctl_t ata_ioctl;
static struct cdevsw ata_cdevsw = {
.d_ioctl = ata_ioctl,
.d_name = "ata",
.d_maj = 159,
};
/* prototypes */
static void ata_shutdown(void *, int);
static int ata_getparam(struct ata_device *, u_int8_t);
static void ata_identify_devices(struct ata_channel *);
static void ata_fail_requests(struct ata_channel *ch,struct ata_device *device);
static void ata_boot_attach(void);
static void bswap(int8_t *, int);
static void btrim(int8_t *, int);
@ -175,7 +175,6 @@ int
ata_detach(device_t dev)
{
struct ata_channel *ch;
struct ata_request *request;
if (!dev || !(ch = device_get_softc(dev)) || !ch->r_irq)
return ENXIO;
@ -190,16 +189,9 @@ ata_detach(device_t dev)
#endif
/* fail outstanding requests on this channel */
mtx_lock(&ch->queue_mtx);
while ((request = TAILQ_FIRST(&ch->ata_queue))) {
TAILQ_REMOVE(&ch->ata_queue, request, chain);
request->status = ATA_S_ERROR;
mtx_unlock(&ch->queue_mtx);
ata_finish(request);
mtx_lock(&ch->queue_mtx);
}
mtx_unlock(&ch->queue_mtx);
ata_fail_requests(ch, NULL);
/* flush cache and powerdown device */
if (ch->device[MASTER].param) {
if (ch->device[MASTER].param->support.command2 & ATA_SUPPORT_FLUSHCACHE)
ata_controlcmd(&ch->device[MASTER], ATA_FLUSHCACHE, 0, 0, 0);
@ -237,42 +229,51 @@ ata_reinit(struct ata_channel *ch)
return ENXIO;
/* reset the HW */
ata_printf(ch, -1, "resetting devices ..\n");
if (bootverbose)
ata_printf(ch, -1, "reiniting channel ..\n");
ATA_FORCELOCK_CH(ch, ATA_CONTROL);
ch->flags |= ATA_IMMEDIATE_MODE;
ch->running = NULL;
devices = ch->devices;
ch->hw.reset(ch);
ATA_UNLOCK_CH(ch);
if (bootverbose)
ata_printf(ch, -1, "resetting done ..\n");
/* detach what left the channel during reset */
if ((misdev = devices & ~ch->devices)) {
if ((misdev & (ATA_ATA_MASTER | ATA_ATAPI_MASTER)) &&
ch->device[MASTER].detach) {
if (request && (request->device == &ch->device[MASTER])) {
request->result = ENXIO;
request->flags |= ATA_R_DONE;
if (request->callback)
(request->callback)(request);
else
wakeup(request);
sema_post(&request->done);
}
ch->device[MASTER].detach(&ch->device[MASTER]);
ata_fail_requests(ch, &ch->device[MASTER]);
free(ch->device[MASTER].param, M_ATA);
ch->device[MASTER].param = NULL;
}
if ((misdev & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE)) &&
ch->device[SLAVE].detach) {
if (request && (request->device == &ch->device[SLAVE])) {
request->result = ENXIO;
request->flags |= ATA_R_DONE;
if (request->callback)
(request->callback)(request);
else
wakeup(request);
sema_post(&request->done);
}
ch->device[SLAVE].detach(&ch->device[SLAVE]);
ata_fail_requests(ch, &ch->device[SLAVE]);
free(ch->device[SLAVE].param, M_ATA);
ch->device[SLAVE].param = NULL;
}
}
/* identify whats present on this channel now */
/* identify what is present on the channel now */
ata_identify_devices(ch);
/* attach new devices that appeared during reset */
@ -285,17 +286,26 @@ ata_reinit(struct ata_channel *ch)
ch->device[SLAVE].attach(&ch->device[SLAVE]);
}
/* restore transfermode on devices */
if (ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER))
/* restore device config and transfermode on devices */
if (ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER)) {
if (ch->device[MASTER].config)
ch->device[MASTER].config(&ch->device[MASTER]);
ch->device[MASTER].setmode(&ch->device[MASTER],ch->device[MASTER].mode);
if (ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE))
}
if (ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE)) {
if (ch->device[SLAVE].config)
ch->device[SLAVE].config(&ch->device[SLAVE]);
ch->device[SLAVE].setmode(&ch->device[SLAVE], ch->device[SLAVE].mode);
}
#ifdef DEV_ATAPICAM
atapi_cam_reinit_bus(ch);
#endif
printf("done\n");
if (bootverbose)
ata_printf(ch, -1, "device config done ..\n");
ch->flags &= ~ATA_IMMEDIATE_MODE;
ata_start(ch);
return 0;
}
@ -543,21 +553,18 @@ ata_ioctl(dev_t dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td)
static int
ata_getparam(struct ata_device *atadev, u_int8_t command)
{
struct ata_params *atacap;
struct ata_request *request;
int error = ENOMEM;
if (atadev->param)
atacap = atadev->param;
else
atacap = malloc(sizeof(struct ata_params), M_ATA, M_NOWAIT);
if (atacap) {
if (!atadev->param)
atadev->param = malloc(sizeof(struct ata_params), M_ATA, M_NOWAIT);
if (atadev->param) {
request = ata_alloc_request();
if (request) {
request->device = atadev;
request->u.ata.command = command;
request->flags = (ATA_R_READ | ATA_R_QUIET);
request->data = (caddr_t)atacap;
request->flags = (ATA_R_READ | ATA_R_AT_HEAD);
request->data = (caddr_t)atadev->param;
request->timeout = 2;
request->retries = 3;
request->bytecount = sizeof(struct ata_params);
@ -566,18 +573,19 @@ ata_getparam(struct ata_device *atadev, u_int8_t command)
ata_queue_request(request);
if (!(error = request->result))
break;
request->flags &= ~ATA_R_QUIET;
request->retries--;
}
ata_free_request(request);
}
if (!isprint(atacap->model[0]) || !isprint(atacap->model[1]))
if (!isprint(atadev->param->model[0]) ||
!isprint(atadev->param->model[1]))
error = ENXIO;
if (error) {
free(atadev->param, M_ATA);
atadev->param = NULL;
free(atacap, M_ATA);
}
else {
struct ata_params *atacap = atadev->param;
#if BYTE_ORDER == BIG_ENDIAN
int16_t *ptr;
@ -598,7 +606,6 @@ ata_getparam(struct ata_device *atadev, u_int8_t command)
bswap(atacap->serial, sizeof(atacap->serial));
btrim(atacap->serial, sizeof(atacap->serial));
bpack(atacap->serial, atacap->serial, sizeof(atacap->serial));
atadev->param = atacap;
if (bootverbose)
ata_prtdev(atadev,
"pio=0x%02x wdma=0x%02x udma=0x%02x cable=%spin\n",
@ -694,6 +701,24 @@ ata_identify_devices(struct ata_channel *ch)
}
}
static void
ata_fail_requests(struct ata_channel *ch, struct ata_device *device)
{
struct ata_request *request;
mtx_lock(&ch->queue_mtx);
while ((request = TAILQ_FIRST(&ch->ata_queue))) {
if (device == NULL || request->device == device) {
TAILQ_REMOVE(&ch->ata_queue, request, chain);
request->result = ENXIO;
mtx_unlock(&ch->queue_mtx);
ata_finish(request);
mtx_lock(&ch->queue_mtx);
}
}
mtx_unlock(&ch->queue_mtx);
}
static void
ata_boot_attach(void)
{
@ -964,12 +989,12 @@ ata_init(void)
printf("ata: malloc of delayed attach hook failed\n");
return;
}
ata_delayed_attach->ich_func = (void*)ata_boot_attach;
if (config_intrhook_establish(ata_delayed_attach) != 0) {
printf("ata: config_intrhook_establish failed\n");
free(ata_delayed_attach, M_TEMP);
}
/* Register a handler to flush write caches on shutdown */
if ((EVENTHANDLER_REGISTER(shutdown_post_sync, ata_shutdown,
NULL, SHUTDOWN_PRI_DEFAULT)) == NULL)

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -182,21 +182,25 @@ struct ata_request {
u_int32_t donecount; /* bytes transferred */
caddr_t data; /* pointer to data buf */
int flags;
#define ATA_R_DONE 0x0001
#define ATA_R_CONTROL 0x0002
#define ATA_R_READ 0x0004
#define ATA_R_WRITE 0x0008
#define ATA_R_CONTROL 0x0001
#define ATA_R_READ 0x0002
#define ATA_R_WRITE 0x0004
#define ATA_R_DMA 0x0008
#define ATA_R_ATAPI 0x0010
#define ATA_R_QUIET 0x0020
#define ATA_R_DMA 0x0040
#define ATA_R_INTR_SEEN 0x0040
#define ATA_R_TIMEOUT 0x0080
#define ATA_R_ORDERED 0x0100
#define ATA_R_AT_HEAD 0x0200
#define ATA_R_REQUEUE 0x0400
#define ATA_R_SKIPSTART 0x0800
#define ATA_R_DEBUG 0x1000
void (*callback)(struct ata_request *request);
struct sema done; /* request done sema */
int retries; /* retry count */
int timeout; /* timeout for this cmd */
struct callout_handle timeout_handle; /* handle for untimeout */
@ -206,6 +210,19 @@ struct ata_request {
TAILQ_ENTRY(ata_request) chain; /* list management */
};
/* define this for debugging request processing */
#if 0
#define ATA_DEBUG_RQ(request, string) \
{ \
if (request->flags & ATA_R_DEBUG) \
ata_prtdev(request->device, "req=%08x %s " string "\n", \
(u_int)request, ata_cmd2str(request)); \
}
#else
#define ATA_DEBUG_RQ(request, string)
#endif
/* structure describing an ATA/ATAPI device */
struct ata_device {
struct ata_channel *channel;
@ -218,6 +235,7 @@ struct ata_device {
void *softc; /* ptr to softc for device */
void (*attach)(struct ata_device *atadev);
void (*detach)(struct ata_device *atadev);
void (*config)(struct ata_device *atadev);
void (*start)(struct ata_device *atadev);
int flags;
#define ATA_D_USE_CHS 0x0001
@ -289,6 +307,7 @@ struct ata_channel {
#define ATA_USE_PC98GEOM 0x04
#define ATA_ATAPI_DMA_RO 0x08
#define ATA_48BIT_ACTIVE 0x10
#define ATA_IMMEDIATE_MODE 0x20
struct ata_device device[2]; /* devices on this channel */
#define MASTER 0x00
@ -305,11 +324,12 @@ struct ata_channel {
#define ATA_ACTIVE 0x0001
#define ATA_CONTROL 0x0002
void (*reset)(struct ata_channel *);
void (*locking)(struct ata_channel *, int);
#define ATA_LF_LOCK 0x0001
#define ATA_LF_UNLOCK 0x0002
struct mtx queue_mtx;
struct mtx queue_mtx; /* queue lock */
TAILQ_HEAD(, ata_request) ata_queue; /* head of ATA queue */
void *running; /* currently running request */
};

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/stdarg.h>
#include <machine/resource.h>

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2002, 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 2002 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/ata.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/resource.h>
#include <machine/bus.h>
@ -144,7 +145,7 @@ ata_cbus_attach(device_t dev)
return ENXIO;
}
if ((bus_setup_intr(dev, ctlr->irq, INTR_TYPE_BIO | INTR_ENTROPY,
if ((bus_setup_intr(dev, ctlr->irq, ATA_INTR_FLAGS,
ata_cbus_intr, ctlr, &ctlr->ih))) {
device_printf(dev, "unable to setup interrupt\n");
bus_release_resource(dev, SYS_RES_IOPORT, ATA_IOADDR_RID, ctlr->io);

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
#include <sys/ata.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/stdarg.h>
#include <machine/resource.h>
@ -99,6 +100,7 @@ static int ata_serverworks_chipinit(device_t);
static void ata_serverworks_setmode(struct ata_device *, int);
static int ata_sii_chipinit(device_t);
static int ata_sii_mio_allocate(device_t, struct ata_channel *);
static void ata_sii_reset(struct ata_channel *);
static void ata_sii_intr(void *);
static void ata_cmd_intr(void *);
static void ata_cmd_old_intr(void *);
@ -1622,6 +1624,7 @@ ata_sii_chipinit(device_t dev)
device_printf(dev, "unable to setup interrupt\n");
return ENXIO;
}
rid = 0x24;
if (!(ctlr->r_io2 = bus_alloc_resource(dev, SYS_RES_MEMORY, &rid,
0, ~0, 1, RF_ACTIVE)))
@ -1650,10 +1653,8 @@ ata_sii_chipinit(device_t dev)
pci_write_config(dev, 0xec, 0x40094009, 4);
ctlr->allocate = ata_sii_mio_allocate;
if (ctlr->chip->max_dma >= ATA_SA150) {
if (ctlr->chip->max_dma >= ATA_SA150)
ctlr->setmode = ata_sata_setmode;
ctlr->locking = ata_serialize;
}
else
ctlr->setmode = ata_sii_setmode;
}
@ -1699,16 +1700,30 @@ ata_sii_mio_allocate(device_t dev, struct ata_channel *ch)
ch->r_io[ATA_BMDTP_PORT].offset = 0x04 + (ch->unit << 3);
ch->r_io[ATA_BMDEVSPEC_0].res = ctlr->r_io2;
ch->r_io[ATA_BMDEVSPEC_0].offset = 0xa1 + (ch->unit << 6);
ch->r_io[ATA_BMDEVSPEC_1].res = ctlr->r_io2;
ch->r_io[ATA_BMDEVSPEC_1].offset = 0x100 + (ch->unit << 7);
ch->r_io[ATA_IDX_ADDR].res = ctlr->r_io2;
if (ctlr->chip->max_dma >= ATA_SA150)
ch->flags |= ATA_NO_SLAVE;
ctlr->dmainit(ch);
if (ctlr->chip->cfg2 & SIIBUG)
ch->dma->boundary = 8 * 1024;
ch->reset = ata_sii_reset;
return 0;
}
static void
ata_sii_reset(struct ata_channel *ch)
{
ATA_IDX_OUTL(ch, ATA_BMDEVSPEC_1, 0x00000001);
DELAY(25000);
ATA_IDX_OUTL(ch, ATA_BMDEVSPEC_1, 0x00000000);
}
static void
ata_sii_intr(void *data)
{
@ -1724,8 +1739,7 @@ ata_sii_intr(void *data)
if (ch->dma) {
int bmstat = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK;
if ((bmstat & (ATA_BMSTAT_ACTIVE | ATA_BMSTAT_INTERRUPT)) !=
ATA_BMSTAT_INTERRUPT)
if (!(bmstat & ATA_BMSTAT_INTERRUPT))
continue;
ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, bmstat & ~ATA_BMSTAT_ERROR);
DELAY(1);

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$");
#include <sys/disk.h>
#include <sys/cons.h>
#include <sys/sysctl.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <vm/vm.h>
#include <vm/pmap.h>
@ -55,6 +56,7 @@ __FBSDID("$FreeBSD$");
/* prototypes */
static void ad_detach(struct ata_device *);
static void ad_config(struct ata_device *);
static void ad_start(struct ata_device *);
static void ad_done(struct ata_request *);
static disk_open_t adopen;
@ -79,7 +81,9 @@ ad_attach(struct ata_device *atadev)
atadev->attach = NULL;
return;
}
atadev->softc = adp;
adp->device = atadev;
#ifdef ATA_STATIC_ID
adp->lun = (device_get_unit(atadev->channel->dev)<<1)+ATA_DEV(atadev->unit);
#else
@ -119,30 +123,16 @@ ad_attach(struct ata_device *atadev)
lbasize48 > 268435455)
adp->total_secs = lbasize48;
/* enable read caching */
ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0);
/* enable write caching if enabled */
if (ata_wc)
ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0);
else
ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0);
/* use multiple sectors/interrupt if device supports it */
adp->max_iosize = DEV_BSIZE;
if (ad_version(atadev->param->version_major)) {
int secsperint = max(1, min(atadev->param->sectors_intr, 16));
if (!ata_controlcmd(atadev, ATA_SET_MULTI, 0, 0, secsperint))
adp->max_iosize = secsperint * DEV_BSIZE;
}
/* setup the function ptrs */
atadev->detach = ad_detach;
atadev->config = ad_config;
atadev->start = ad_start;
atadev->softc = adp;
/* config device features */
ad_config(atadev);
/* lets create the disk device */
adp->max_iosize = DEV_BSIZE;
adp->disk.d_open = adopen;
adp->disk.d_strategy = adstrategy;
adp->disk.d_dump = addump;
@ -191,6 +181,30 @@ ad_detach(struct ata_device *atadev)
free(adp, M_AD);
}
static void
ad_config(struct ata_device *atadev)
{
struct ad_softc *adp = atadev->softc;
/* enable read caching */
ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0);
/* enable write caching if enabled */
if (ata_wc)
ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0);
else
ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0);
/* use multiple sectors/interrupt if device supports it */
adp->max_iosize = DEV_BSIZE;
if (ad_version(atadev->param->version_major)) {
int secsperint = max(1, min(atadev->param->sectors_intr, 16));
if (!ata_controlcmd(atadev, ATA_SET_MULTI, 0, 0, secsperint))
adp->max_iosize = secsperint * DEV_BSIZE;
}
}
static int
adopen(struct disk *dp)
{
@ -243,7 +257,7 @@ ad_start(struct ata_device *atadev)
request->device = atadev;
request->driver = bp;
request->callback = ad_done;
request->timeout = 10;
request->timeout = 5;
request->retries = 2;
request->data = bp->bio_data;
request->bytecount = bp->bio_bcount;

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -36,7 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/endian.h>
#include <sys/malloc.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <sys/bus.h>
#include <machine/bus.h>

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,6 +37,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/stdarg.h>
#include <machine/resource.h>

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -36,7 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/kernel.h>
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/mutex.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <sys/rman.h>
@ -69,9 +69,17 @@ ata_generic_hw(struct ata_channel *ch)
static int
ata_transaction(struct ata_request *request)
{
/* safety check, device might have been detached FIXME SOS */
if (!request->device->param) {
request->result = ENXIO;
return ATA_OP_FINISHED;
}
/* record the request as running */
request->device->channel->running = request;
ATA_DEBUG_RQ(request, "transaction");
/* disable ATAPI DMA writes if HW doesn't support it */
if ((request->device->channel->flags & ATA_ATAPI_DMA_RO) &&
((request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_WRITE)) ==
@ -83,7 +91,7 @@ ata_transaction(struct ata_request *request)
/* ATA PIO data transfer and control commands */
default:
{
/* record command direction here as our request might be done later */
/* record command direction here as our request might be gone later */
int write = (request->flags & ATA_R_WRITE);
/* issue command */
@ -106,6 +114,7 @@ ata_transaction(struct ata_request *request)
ata_pio_write(request, request->transfersize);
}
}
/* return and wait for interrupt */
return ATA_OP_CONTINUES;
@ -136,6 +145,7 @@ ata_transaction(struct ata_request *request)
request->result = EIO;
break;
}
/* return and wait for interrupt */
return ATA_OP_CONTINUES;
@ -294,16 +304,23 @@ ata_interrupt(void *data)
return;
}
ATA_DEBUG_RQ(request, "interrupt");
/* ignore interrupt if device is busy */
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
if (!(request->flags & ATA_R_TIMEOUT) &&
ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
DELAY(100);
if (!(ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_DRQ))
return;
}
ATA_DEBUG_RQ(request, "interrupt accepted");
/* clear interrupt and get status */
request->status = ATA_IDX_INB(ch, ATA_STATUS);
request->flags |= ATA_R_INTR_SEEN;
switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_CONTROL)) {
/* ATA PIO data transfer and control commands */
@ -492,6 +509,13 @@ ata_interrupt(void *data)
break;
}
/* if we timed out, we hold on to the channel, ata_reinit() will unlock */
if (request->flags & ATA_R_TIMEOUT) {
ata_finish(request);
return;
}
/* schedule completition for this request */
ata_finish(request);
/* unlock the ATA channel for new work */
@ -529,7 +553,7 @@ ata_reset(struct ata_channel *ch)
}
}
/* if nothing showed up no need to get any further */
/* if nothing showed up there is no need to get any further */
/* SOS is that too strong?, we just might loose devices here XXX */
ch->devices = 0;
if (!mask)
@ -539,7 +563,11 @@ ata_reset(struct ata_channel *ch)
ata_printf(ch, -1, "reset tp1 mask=%02x ostat0=%02x ostat1=%02x\n",
mask, ostat0, ostat1);
/* reset channel */
/* reset host end of channel (if supported) */
if (ch->reset)
ch->reset(ch);
/* reset (both) devices on this channel */
ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
DELAY(10);
ATA_IDX_OUTB(ch, ATA_ALTSTAT, ATA_A_IDS | ATA_A_RESET);
@ -617,6 +645,10 @@ ata_reset(struct ata_channel *ch)
DELAY(100000);
}
/* enable interrupt */
DELAY(10);
ATA_IDX_OUTB(ch, ATA_ALTSTAT, ATA_A_4BIT);
if (stat0 & ATA_S_BUSY)
mask &= ~0x01;
if (stat1 & ATA_S_BUSY)
@ -627,41 +659,6 @@ ata_reset(struct ata_channel *ch)
"reset tp2 mask=%02x stat0=%02x stat1=%02x devices=0x%b\n",
mask, stat0, stat1, ch->devices,
"\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
#if 0
if (!mask)
return;
if (mask & 0x01 && ostat0 != 0x00 &&
!(ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER))) {
ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_MASTER);
DELAY(10);
ATA_IDX_OUTB(ch, ATA_ERROR, 0x58);
ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0xa5);
err = ATA_IDX_INB(ch, ATA_ERROR);
lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
if (bootverbose)
ata_printf(ch, ATA_MASTER, "ATA err=0x%02x lsb=0x%02x\n", err, lsb);
if (err != 0x58 && lsb == 0xa5)
ch->devices |= ATA_ATA_MASTER;
}
if (mask & 0x02 && ostat1 != 0x00 &&
!(ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE))) {
ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_SLAVE);
DELAY(10);
ATA_IDX_OUTB(ch, ATA_ERROR, 0x58);
ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0xa5);
err = ATA_IDX_INB(ch, ATA_ERROR);
lsb = ATA_IDX_INB(ch, ATA_CYL_LSB);
if (bootverbose)
ata_printf(ch, ATA_SLAVE, "ATA err=0x%02x lsb=0x%02x\n", err, lsb);
if (err != 0x58 && lsb == 0xa5)
ch->devices |= ATA_ATA_SLAVE;
}
if (bootverbose)
ata_printf(ch, -1, "reset tp3 devices=0x%b\n", ch->devices,
"\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
#endif
}
static int
@ -736,9 +733,6 @@ ata_command(struct ata_device *atadev, u_int8_t command,
return -1;
}
/* enable interrupt */
ATA_IDX_OUTB(atadev->channel, ATA_ALTSTAT, ATA_A_4BIT);
/* only use 48bit addressing if needed (avoid bugs and overhead) */
if ((lba > 268435455 || count > 256) && atadev->param &&
atadev->param->support.command2 & ATA_SUPPORT_ADDRESS48) {

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -36,6 +36,7 @@ __FBSDID("$FreeBSD$");
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/malloc.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/stdarg.h>
#include <machine/resource.h>

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -37,8 +37,7 @@ __FBSDID("$FreeBSD$");
#include <sys/malloc.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <sys/rman.h>
@ -51,7 +50,6 @@ static char *ata_skey2str(u_int8_t);
/* local vars */
static MALLOC_DEFINE(M_ATA_REQ, "ATA request", "ATA request");
static int atadebug = 0;
/*
* ATA request related functions
@ -64,42 +62,65 @@ ata_alloc_request(void)
request = malloc(sizeof(struct ata_request), M_ATA_REQ, M_NOWAIT | M_ZERO);
if (!request)
printf("FAILURE - malloc ATA request failed\n");
sema_init(&request->done, 0, "ATA request done");
return request;
}
void
ata_free_request(struct ata_request *request)
{
sema_destroy(&request->done);
free(request, M_ATA_REQ);
}
void
ata_queue_request(struct ata_request *request)
{
/* mark request as virgin (it might be a reused one) */
/* mark request as virgin (it might be a retry) */
request->result = request->status = request->error = 0;
request->flags &= ~ATA_R_DONE;
/* put request on the locked queue at the specified location */
mtx_lock(&request->device->channel->queue_mtx);
if (request->flags & ATA_R_AT_HEAD)
TAILQ_INSERT_HEAD(&request->device->channel->ata_queue, request, chain);
else
TAILQ_INSERT_TAIL(&request->device->channel->ata_queue, request, chain);
mtx_unlock(&request->device->channel->queue_mtx);
if (request->device->channel->flags & ATA_IMMEDIATE_MODE) {
/* should we skip start ? */
if (!(request->flags & ATA_R_SKIPSTART))
ata_start(request->device->channel);
// request->flags |= ATA_R_DEBUG;
/* if this was a requeue op callback/sleep already setup */
if (request->flags & ATA_R_REQUEUE)
return;
/* arm timeout */
if (!request->timeout_handle.callout && !dumping) {
request->timeout_handle =
timeout((timeout_t*)ata_timeout, request, request->timeout*hz);
}
/* if this is not a callback and we havn't seen DONE yet -> sleep */
if (!request->callback) {
while (!(request->flags & ATA_R_DONE))
tsleep(request, PRIBIO, "atareq", hz/10);
/* kick HW into action */
if (request->device->channel->hw.transaction(request) ==
ATA_OP_CONTINUES) {
ATA_DEBUG_RQ(request, "wait for completition");
sema_wait(&request->done);
}
}
else {
/* put request on the locked queue at the specified location */
mtx_lock(&request->device->channel->queue_mtx);
if (request->flags & ATA_R_AT_HEAD)
TAILQ_INSERT_HEAD(&request->device->channel->ata_queue,
request, chain);
else
TAILQ_INSERT_TAIL(&request->device->channel->ata_queue,
request, chain);
mtx_unlock(&request->device->channel->queue_mtx);
ATA_DEBUG_RQ(request, "queued");
/* should we skip start ? */
if (!(request->flags & ATA_R_SKIPSTART))
ata_start(request->device->channel);
/* if this is a requeued request callback/sleep is already setup */
if (request->flags & ATA_R_REQUEUE)
return;
/* if this is not a callback wait until request is completed */
if (!request->callback) {
ATA_DEBUG_RQ(request, "wait for completition");
sema_wait(&request->done);
}
}
}
@ -156,14 +177,16 @@ ata_start(struct ata_channel *ch)
{
struct ata_request *request;
/* if in immediate mode, just skip start requests (stall queue) */
if (ch->flags & ATA_IMMEDIATE_MODE)
return;
/* lock the ATA HW for this request */
ch->locking(ch, ATA_LF_LOCK);
if (!ATA_LOCK_CH(ch, ATA_ACTIVE)) {
return;
}
if (atadebug && mtx_owned(&Giant)) printf("ata_start holds GIANT!!!\n");
/* if we dont have any work, ask the subdriver(s) */
mtx_lock(&ch->queue_mtx);
if (TAILQ_EMPTY(&ch->ata_queue)) {
@ -178,6 +201,8 @@ if (atadebug && mtx_owned(&Giant)) printf("ata_start holds GIANT!!!\n");
TAILQ_REMOVE(&ch->ata_queue, request, chain);
mtx_unlock(&ch->queue_mtx);
ATA_DEBUG_RQ(request, "starting");
/* arm timeout */
if (!request->timeout_handle.callout && !dumping) {
request->timeout_handle =
@ -188,8 +213,6 @@ if (atadebug && mtx_owned(&Giant)) printf("ata_start holds GIANT!!!\n");
if (ch->hw.transaction(request) == ATA_OP_CONTINUES)
return;
/* untimeout request */
untimeout((timeout_t *)ata_timeout, request, request->timeout_handle);
ata_finish(request);
}
else
@ -202,9 +225,16 @@ if (atadebug && mtx_owned(&Giant)) printf("ata_start holds GIANT!!!\n");
void
ata_finish(struct ata_request *request)
{
ATA_DEBUG_RQ(request, "taskqueue completition");
/* request is done schedule it for completition */
TASK_INIT(&request->task, 0, ata_completed, request);
taskqueue_enqueue(taskqueue_swi, &request->task);
if (request->device->channel->flags & ATA_IMMEDIATE_MODE) {
ata_completed(request, 0);
}
else {
TASK_INIT(&request->task, 0, ata_completed, request);
taskqueue_enqueue(taskqueue_swi, &request->task);
}
}
/* current command finished, clean up and return result */
@ -214,27 +244,60 @@ ata_completed(void *context, int pending)
struct ata_request *request = (struct ata_request *)context;
struct ata_channel *channel = request->device->channel;
/* untimeout request now we have control back */
untimeout((timeout_t *)ata_timeout, request, request->timeout_handle);
ATA_DEBUG_RQ(request, "completed called");
/* do the all the magic for completition evt retry etc etc */
if ((request->status & (ATA_S_CORR | ATA_S_ERROR)) == ATA_S_CORR)
ata_prtdev(request->device, "WARNING - %s soft error (ECC corrected)\n",
ata_cmd2str(request));
if (request->flags & ATA_R_TIMEOUT) {
ata_reinit(channel);
/* if this is a UDMA CRC error, retry request */
if (request->flags & ATA_R_DMA && request->error & ATA_E_ICRC) {
if (request->retries--) {
ata_prtdev(request->device,
"WARNING - %s UDMA ICRC error (retrying request)\n",
ata_cmd2str(request));
request->flags &= ~ATA_R_SKIPSTART;
/* if retries still permit, reinject this request */
if (request->retries-- > 0) {
request->flags &= ~(ATA_R_TIMEOUT | ATA_R_SKIPSTART);
request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE);
ata_queue_request(request);
return;
}
/* otherwise just finish with error */
else {
if (!(request->flags & ATA_R_QUIET))
ata_prtdev(request->device,
"FAILURE - %s timed out\n",
ata_cmd2str(request));
request->result = EIO;
}
}
else {
/* untimeout request now we have control back */
untimeout((timeout_t *)ata_timeout, request, request->timeout_handle);
/* do the all the magic for completition evt retry etc etc */
if ((request->status & (ATA_S_CORR | ATA_S_ERROR)) == ATA_S_CORR) {
ata_prtdev(request->device,
"WARNING - %s soft error (ECC corrected)",
ata_cmd2str(request));
if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
printf(" LBA=%llu", (unsigned long long)request->u.ata.lba);
printf("\n");
}
/* if this is a UDMA CRC error, retry request */
if (request->flags & ATA_R_DMA && request->error & ATA_E_ICRC) {
if (request->retries-- > 0) {
ata_prtdev(request->device,
"WARNING - %s UDMA ICRC error (retrying request)",
ata_cmd2str(request));
if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
printf(" LBA=%llu", (unsigned long long)request->u.ata.lba);
printf("\n");
request->flags &= ~ATA_R_SKIPSTART;
ata_queue_request(request);
return;
}
}
}
switch (request->flags & ATA_R_ATAPI) {
/* ATA errors */
default:
if (request->status & ATA_S_ERROR) {
@ -250,8 +313,7 @@ ata_completed(void *context, int pending)
if ((request->flags & ATA_R_DMA) &&
(request->dmastat & ATA_BMSTAT_ERROR))
printf(" dma=0x%02x", request->dmastat);
if (!(request->flags & ATA_R_ATAPI) &&
!(request->flags & ATA_R_CONTROL))
if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
printf(" LBA=%llu", (unsigned long long)request->u.ata.lba);
printf("\n");
}
@ -306,74 +368,67 @@ ata_completed(void *context, int pending)
break;
}
request->flags |= ATA_R_DONE;
ATA_DEBUG_RQ(request, "completed callback/wakeup");
if (request->callback)
(request->callback)(request);
else
wakeup(request);
sema_post(&request->done);
ata_start(channel);
}
static void
ata_timeout(struct ata_request *request)
{
struct ata_channel *ch = request->device->channel;
int quiet = request->flags & ATA_R_QUIET;
ATA_DEBUG_RQ(request, "timeout");
/* clear timeout etc */
request->timeout_handle.callout = NULL;
/* call hw.interrupt to try finish up the command */
ch->hw.interrupt(request->device->channel);
if (ch->running != request) {
if (!quiet)
ata_prtdev(request->device,
"WARNING - %s recovered from missing interrupt\n",
if (request->flags & ATA_R_INTR_SEEN) {
if (request->retries-- > 0) {
ata_prtdev(request->device,
"WARNING - %s interrupt was seen but timeout fired",
ata_cmd2str(request));
return;
}
if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
printf(" LBA=%llu", (unsigned long long)request->u.ata.lba);
printf("\n");
/* if this was a DMA request stop the engine to be on the safe side */
if (request->flags & ATA_R_DMA) {
request->dmastat =
request->device->channel->dma->stop(request->device->channel);
/* re-arm timeout */
if (!request->timeout_handle.callout && !dumping) {
request->timeout_handle =
timeout((timeout_t*)ata_timeout, request,
request->timeout * hz);
}
}
else {
ata_prtdev(request->device,
"WARNING - %s interrupt was seen but taskqueue stalled",
ata_cmd2str(request));
if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
printf(" LBA=%llu", (unsigned long long)request->u.ata.lba);
printf("\n");
ata_completed(request, 0);
}
return;
}
/* report that we timed out */
if (request->retries > 0 && !(request->flags & ATA_R_QUIET))
if (!(request->flags & ATA_R_QUIET)) {
ata_prtdev(request->device,
"TIMEOUT - %s retrying (%d retr%s left)\n",
"TIMEOUT - %s retrying (%d retr%s left)",
ata_cmd2str(request), request->retries,
request->retries == 1 ? "y" : "ies");
/* try to adjust HW's attitude towards work */
ata_reinit(request->device->channel);
/* if device disappeared nothing more to do here */
if (!request->device->softc) {
if (!(request->flags & ATA_R_QUIET))
ata_prtdev(request->device,
"FAILURE - %s device lockup/removed\n",
ata_cmd2str(request));
return;
if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL)))
printf(" LBA=%llu", (unsigned long long)request->u.ata.lba);
printf("\n");
}
/* if retries still permit, reinject this request */
if (request->retries-- > 0) {
request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE);
request->flags &= ~ATA_R_SKIPSTART;
ata_queue_request(request);
}
/* otherwise just schedule finish with error */
else {
if (!(request->flags & ATA_R_QUIET))
ata_prtdev(request->device,
"FAILURE - %s timed out\n",
ata_cmd2str(request));
request->status = ATA_S_ERROR;
TASK_INIT(&request->task, 0, ata_completed, request);
taskqueue_enqueue(taskqueue_swi, &request->task);
}
/* now simulate the missing interrupt */
request->flags |= ATA_R_TIMEOUT;
request->device->channel->hw.interrupt(request->device->channel);
return;
}
char *
@ -464,7 +519,16 @@ ata_cmd2str(struct ata_request *request)
case 0xe7: return ("FLUSHCACHE");
case 0xea: return ("FLUSHCACHE48");
case 0xec: return ("ATA_IDENTIFY");
case 0xef: return ("SETFEATURES");
case 0xef:
switch (request->u.ata.feature) {
case 0x03: return ("SETFEATURES SET TRANSFER MODE");
case 0x02: return ("SETFEATURES ENABLE WCACHE");
case 0x82: return ("SETFEATURES DISABLE WCACHE");
case 0xaa: return ("SETFEATURES ENABLE RCACHE");
case 0x55: return ("SETFEATURES DISABLE RCACHE");
}
sprintf(buffer, "SETFEATURES 0x%02x", request->u.ata.feature);
return buffer;
}
}
sprintf(buffer, "unknown CMD (0x%02x)", request->u.ata.command);

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 2000 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 2000 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
#include <sys/cons.h>
#include <sys/unistd.h>
#include <sys/kthread.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <sys/rman.h>
@ -711,7 +712,7 @@ arstrategy(struct bio *bp)
return;
}
buf1 = malloc(sizeof(struct ar_buf), M_AR, M_NOWAIT | M_ZERO);
buf1 = malloc(sizeof(struct ar_buf), M_AR, M_NOWAIT | M_ZERO); /* XXX */
buf1->bp.bio_pblkno = lba;
if ((buf1->drive = drv) > 0)
buf1->bp.bio_pblkno += rdp->offset;
@ -819,7 +820,7 @@ arstrategy(struct bio *bp)
((rdp->flags & AR_F_REBUILDING) &&
(rdp->disks[buf1->drive].flags & AR_DF_SPARE) &&
buf1->bp.bio_pblkno < rdp->lock_start)) {
buf2 = malloc(sizeof(struct ar_buf), M_AR, M_NOWAIT);
buf2 = malloc(sizeof(struct ar_buf), M_AR, M_NOWAIT); /* XXX */
bcopy(buf1, buf2, sizeof(struct ar_buf));
buf1->mirror = buf2;
buf2->mirror = buf1;
@ -1020,7 +1021,7 @@ ar_rebuild(void *arg)
rdp->lock_end = rdp->lock_start + AR_REBUILD_SIZE;
rdp->flags |= AR_F_REBUILDING;
splx(s);
buffer = malloc(AR_REBUILD_SIZE * DEV_BSIZE, M_AR, M_NOWAIT | M_ZERO);
buffer = malloc(AR_REBUILD_SIZE * DEV_BSIZE, M_AR, M_NOWAIT | M_ZERO); /* XXX */
/* now go copy entire disk(s) */
while (rdp->lock_end < (rdp->total_sectors / rdp->width)) {

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -46,7 +46,7 @@ __FBSDID("$FreeBSD$");
#include <sys/conf.h>
#include <sys/ctype.h>
#include <sys/taskqueue.h>
#include <sys/mutex.h>
#include <sys/sema.h>
#include <machine/bus.h>
#include <geom/geom.h>
#include <dev/ata/ata-all.h>

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/cdio.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <geom/geom_disk.h>

View File

@ -1,5 +1,5 @@
/*-
* Copyright (c) 1998 - 2003 Søren Schmidt <sos@FreeBSD.org>
* Copyright (c) 1998 - 2004 Søren Schmidt <sos@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -40,6 +40,7 @@ __FBSDID("$FreeBSD$");
#include <sys/bus.h>
#include <sys/mtio.h>
#include <sys/devicestat.h>
#include <sys/sema.h>
#include <sys/taskqueue.h>
#include <machine/bus.h>
#include <dev/ata/ata-all.h>