1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-24 11:29:10 +00:00

Add support for using DMA on dump, greatly speeds up the dump process.

Add dump support in ataraid.
This commit is contained in:
Søren Schmidt 2006-01-18 09:14:55 +00:00
parent 9238d0aff6
commit f5f55db308
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=154507
9 changed files with 1263 additions and 1331 deletions

View File

@ -63,7 +63,6 @@ static struct cdevsw ata_cdevsw = {
};
/* prototypes */
static void ata_interrupt(void *);
static void ata_boot_attach(void);
static device_t ata_add_child(device_t, struct ata_device *, int);
static int ata_getparam(struct ata_device *, int);
@ -139,7 +138,7 @@ ata_attach(device_t dev)
return ENXIO;
}
if ((error = bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS,
ata_interrupt, ch, &ch->ih))) {
(driver_intr_t *)ata_interrupt, ch, &ch->ih))) {
device_printf(dev, "unable to setup interrupt\n");
return error;
}
@ -317,7 +316,7 @@ ata_resume(device_t dev)
return error;
}
static void
int
ata_interrupt(void *data)
{
struct ata_channel *ch = (struct ata_channel *)data;
@ -325,20 +324,17 @@ ata_interrupt(void *data)
mtx_lock(&ch->state_mtx);
do {
/* ignore interrupt if its not for us */
if (ch->hw.status && !ch->hw.status(ch->dev))
break;
/* do we have a running request */
if (!(request = ch->running))
break;
ATA_DEBUG_RQ(request, "interrupt");
/* ignore interrupt if device is busy */
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
DELAY(100);
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
break;
}
/* check for the right state */
/* safetycheck for the right state */
if (ch->state != ATA_ACTIVE && ch->state != ATA_STALL_QUEUE) {
device_printf(request->dev, "interrupt on idle channel ignored\n");
break;
@ -355,10 +351,11 @@ ata_interrupt(void *data)
mtx_unlock(&ch->state_mtx);
ATA_LOCKING(ch->dev, ATA_LF_UNLOCK);
ata_finish(request);
return;
return 1;
}
} while (0);
mtx_unlock(&ch->state_mtx);
return 0;
}
/*

View File

@ -470,6 +470,7 @@ struct ata_dma {
/* structure holding lowlevel functions */
struct ata_lowlevel {
int (*status)(device_t dev);
int (*begin_transaction)(struct ata_request *request);
int (*end_transaction)(struct ata_request *request);
int (*command)(struct ata_request *request);
@ -495,6 +496,7 @@ struct ata_channel {
#define ATA_USE_16BIT 0x02
#define ATA_ATAPI_DMA_RO 0x04
#define ATA_NO_48BIT_DMA 0x08
#define ATA_ALWAYS_DMASTAT 0x10
int devices; /* what is present */
#define ATA_ATA_MASTER 0x01
@ -534,6 +536,7 @@ int ata_detach(device_t dev);
int ata_reinit(device_t dev);
int ata_suspend(device_t dev);
int ata_resume(device_t dev);
int ata_interrupt(void *data);
int ata_device_ioctl(device_t dev, u_long cmd, caddr_t data);
int ata_identify(device_t dev);
void ata_default_registers(device_t dev);
@ -558,6 +561,8 @@ char *ata_cmd2str(struct ata_request *request);
/* ata-lowlevel.c: */
void ata_generic_hw(device_t dev);
int ata_begin_transaction(struct ata_request *);
int ata_end_transaction(struct ata_request *);
void ata_generic_reset(device_t dev);
int ata_generic_command(struct ata_request *request);

File diff suppressed because it is too large Load Diff

View File

@ -289,45 +289,23 @@ ad_ioctl(struct disk *disk, u_long cmd, void *data, int flag, struct thread *td)
static int
ad_dump(void *arg, void *virtual, vm_offset_t physical,
off_t offset, size_t length)
off_t offset, size_t length)
{
struct disk *dp = arg;
device_t dev = dp->d_drv1;
struct ata_device *atadev = device_get_softc(dev);
struct ad_softc *adp = device_get_ivars(dev);
struct ata_channel *ch = device_get_softc(device_get_parent(dev));
struct ata_request request;
struct bio bp;
if (!adp)
return ENXIO;
/* length zero is special and really means flush buffers to media */
if (!length)
return ata_controlcmd(dp->d_drv1, ATA_FLUSHCACHE, 0, 0, 0);
bzero(&request, sizeof(struct ata_request));
request.dev = dev;
if (length) {
request.data = virtual;
request.bytecount = length;
request.transfersize = min(length, atadev->max_iosize);
request.flags = ATA_R_WRITE;
if (atadev->max_iosize > DEV_BSIZE)
request.u.ata.command = ATA_WRITE_MUL;
else
request.u.ata.command = ATA_WRITE;
request.u.ata.lba = offset / DEV_BSIZE;
request.u.ata.count = request.bytecount / DEV_BSIZE;
}
else {
request.u.ata.command = ATA_FLUSHCACHE;
request.flags = ATA_R_CONTROL;
}
if (ch->hw.begin_transaction(&request) == ATA_OP_CONTINUES) {
do {
DELAY(20);
} while (ch->hw.end_transaction(&request) == ATA_OP_CONTINUES);
}
if (request.status & ATA_S_ERROR)
return EIO;
return 0;
bzero(&bp, sizeof(struct bio));
bp.bio_disk = dp;
bp.bio_pblkno = offset / DEV_BSIZE;
bp.bio_bcount = length;
bp.bio_data = virtual;
bp.bio_cmd = BIO_WRITE;
ad_strategy(&bp);
return bp.bio_error;
}
static void

View File

@ -46,8 +46,7 @@ __FBSDID("$FreeBSD$");
#include <ata_if.h>
/* prototypes */
static int ata_begin_transaction(struct ata_request *);
static int ata_end_transaction(struct ata_request *);
static int ata_generic_status(device_t dev);
static int ata_wait(struct ata_channel *ch, struct ata_device *, u_int8_t);
static void ata_pio_read(struct ata_request *, int);
static void ata_pio_write(struct ata_request *, int);
@ -62,11 +61,12 @@ ata_generic_hw(device_t dev)
ch->hw.begin_transaction = ata_begin_transaction;
ch->hw.end_transaction = ata_end_transaction;
ch->hw.status = ata_generic_status;
ch->hw.command = ata_generic_command;
}
/* must be called with ATA channel locked and state_mtx held */
static int
int
ata_begin_transaction(struct ata_request *request)
{
struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
@ -220,7 +220,7 @@ ata_begin_transaction(struct ata_request *request)
}
/* must be called with ATA channel locked and state_mtx held */
static int
int
ata_end_transaction(struct ata_request *request)
{
struct ata_channel *ch = device_get_softc(device_get_parent(request->dev));
@ -470,7 +470,7 @@ ata_end_transaction(struct ata_request *request)
return ATA_OP_CONTINUES;
}
/* must be called with ATA channel locked */
/* must be called with ATA channel locked and state_mtx held */
void
ata_generic_reset(device_t dev)
{
@ -605,6 +605,20 @@ ata_generic_reset(device_t dev)
"\20\4ATAPI_SLAVE\3ATAPI_MASTER\2ATA_SLAVE\1ATA_MASTER");
}
/* must be called with ATA channel locked and state_mtx held */
int
ata_generic_status(device_t dev)
{
struct ata_channel *ch = device_get_softc(dev);
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
DELAY(100);
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
return 0;
}
return 1;
}
static int
ata_wait(struct ata_channel *ch, struct ata_device *atadev, u_int8_t mask)
{

View File

@ -203,7 +203,8 @@ ata_pci_attach(device_t dev)
RF_ACTIVE);
}
ctlr->chipinit(dev);
if (ctlr->chipinit(dev))
return ENXIO;
/* attach all channels on this controller */
for (unit = 0; unit < ctlr->channels; unit++) {
@ -420,10 +421,42 @@ ata_pci_allocate(device_t dev)
}
}
ata_generic_hw(dev);
ata_pci_hw(dev);
return 0;
}
void
ata_pci_hw(device_t dev)
{
struct ata_channel *ch = device_get_softc(dev);
ata_generic_hw(dev);
ch->hw.status = ata_pci_status;
}
int
ata_pci_status(device_t dev)
{
struct ata_channel *ch = device_get_softc(dev);
if (ch->dma && ((ch->flags & ATA_ALWAYS_DMASTAT) ||
(ch->dma->flags & ATA_DMA_ACTIVE))) {
int bmstat = ATA_IDX_INB(ch, ATA_BMSTAT_PORT) & ATA_BMSTAT_MASK;
if ((bmstat & (ATA_BMSTAT_ACTIVE | ATA_BMSTAT_INTERRUPT)) !=
ATA_BMSTAT_INTERRUPT)
return 0;
ATA_IDX_OUTB(ch, ATA_BMSTAT_PORT, bmstat & ~ATA_BMSTAT_ERROR);
DELAY(1);
}
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) {
DELAY(100);
if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY)
return 0;
}
return 1;
}
static int
ata_pci_dmastart(device_t dev)
{

View File

@ -392,6 +392,9 @@ int ata_pci_release_resource(device_t dev, device_t child, int type, int rid, st
int ata_pci_setup_intr(device_t dev, device_t child, struct resource *irq, int flags, driver_intr_t *function, void *argument, void **cookiep);
int ata_pci_teardown_intr(device_t dev, device_t child, struct resource *irq, void *cookie);
int ata_pci_allocate(device_t dev);
void ata_pci_hw(device_t dev);
int ata_pci_status(device_t dev);
/* global prototypes ata-chipset.c */
int ata_generic_ident(device_t);
@ -413,7 +416,6 @@ int ata_sii_ident(device_t);
int ata_sis_ident(device_t);
int ata_via_ident(device_t);
int ata_legacy(device_t);
struct ata_chip_id *ata_match_chip(device_t, struct ata_chip_id *);
/* global prototypes ata-dma.c */
void ata_dmainit(device_t);

View File

@ -94,7 +94,8 @@ ata_queue_request(struct ata_request *request)
/* if this is not a callback wait until request is completed */
if (!request->callback) {
ATA_DEBUG_RQ(request, "wait for completition");
while (sema_timedwait(&request->done, request->timeout * hz * 4)) {
while (!dumping &&
sema_timedwait(&request->done, request->timeout * hz * 4)) {
device_printf(request->dev,
"req=%p %s semaphore timeout !! DANGER Will Robinson !!\n",
request, ata_cmd2str(request));
@ -200,6 +201,13 @@ ata_start(device_t dev)
ata_finish(request);
return;
}
if (dumping) {
mtx_unlock(&ch->state_mtx);
mtx_unlock(&ch->queue_mtx);
while (!ata_interrupt(ch))
DELAY(10);
return;
}
}
mtx_unlock(&ch->state_mtx);
}
@ -216,7 +224,8 @@ ata_finish(struct ata_request *request)
* if in ATA_STALL_QUEUE state or request has ATA_R_DIRECT flags set
* we need to call ata_complete() directly here (no taskqueue involvement)
*/
if ((ch->state & ATA_STALL_QUEUE) || (request->flags & ATA_R_DIRECT)) {
if (dumping ||
(ch->state & ATA_STALL_QUEUE) || (request->flags & ATA_R_DIRECT)) {
ATA_DEBUG_RQ(request, "finish directly");
ata_completed(request, 0);
}

View File

@ -111,7 +111,7 @@ static int testing = 0;
/* device structures */
static disk_strategy_t ata_raid_strategy;
//static dumper_t ata_raid_dump;
static dumper_t ata_raid_dump;
static void
ata_raid_attach(struct ar_softc *rdp, int writeback)
@ -134,7 +134,7 @@ ata_raid_attach(struct ar_softc *rdp, int writeback)
buffer[0] = '\0';
rdp->disk = disk_alloc();
rdp->disk->d_strategy = ata_raid_strategy;
//rdp->disk->d_dump = ata_raid_dump;
rdp->disk->d_dump = ata_raid_dump;
rdp->disk->d_name = "ar";
rdp->disk->d_sectorsize = DEV_BSIZE;
rdp->disk->d_mediasize = (off_t)rdp->total_sectors * DEV_BSIZE;
@ -751,6 +751,35 @@ ata_raid_done(struct ata_request *request)
ata_free_request(request);
}
static int
ata_raid_dump(void *arg, void *virtual, vm_offset_t physical,
off_t offset, size_t length)
{
struct disk *dp = arg;
struct ar_softc *rdp = dp->d_drv1;
struct bio bp;
/* length zero is special and really means flush buffers to media */
if (!length) {
int disk, error;
for (disk = 0, error = 0; disk < rdp->total_disks; disk++)
if (rdp->disks[disk].dev)
error |= ata_controlcmd(rdp->disks[disk].dev,
ATA_FLUSHCACHE, 0, 0, 0);
return (error ? EIO : 0);
}
bzero(&bp, sizeof(struct bio));
bp.bio_disk = dp;
bp.bio_pblkno = offset / DEV_BSIZE;
bp.bio_bcount = length;
bp.bio_data = virtual;
bp.bio_cmd = BIO_WRITE;
ata_raid_strategy(&bp);
return bp.bio_error;
}
static void
ata_raid_config_changed(struct ar_softc *rdp, int writeback)
{