From 7997389772efc7249ab5ae3d010514301652cdb8 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Thu, 9 Apr 2009 15:24:40 +0000 Subject: [PATCH] Implement DMA transfers in the new ATA bus_manager. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@30069 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../kernel/bus_managers/ata/ATAChannel.cpp | 86 +++++++++++++++---- .../kernel/bus_managers/ata/ATADevice.cpp | 61 +++++++------ .../kernel/bus_managers/ata/ATAModule.cpp | 8 +- .../kernel/bus_managers/ata/ATAPrivate.h | 25 ++++-- 4 files changed, 128 insertions(+), 52 deletions(-) diff --git a/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp b/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp index 72edd74def..71e9145ec6 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATAChannel.cpp @@ -14,6 +14,7 @@ ATAChannel::ATAChannel(device_node *node) fChannelID(0), fController(NULL), fCookie(NULL), + fExpectsDMATransfer(false), fStatus(B_NO_INIT), fSCSIBus(NULL), fDeviceCount(0), @@ -21,7 +22,9 @@ ATAChannel::ATAChannel(device_node *node) fUseDMA(true), fRequest(NULL) { - mutex_init(&fLock, "ata channel"); + mutex_init(&fExecutionLock, "ata io execution"); + B_INITIALIZE_SPINLOCK(&fDMATransferLock); + fDMATransferCondition.Init(this, "ata dma transfer"); gDeviceManager->get_attr_uint32(node, ATA_CHANNEL_ID_ITEM, &fChannelID, true); @@ -80,12 +83,14 @@ ATAChannel::ATAChannel(device_node *node) fStatus = gDeviceManager->get_driver(parent, (driver_module_info **)&fController, &fCookie); gDeviceManager->put_node(parent); + + fController->set_channel(fCookie, this); } ATAChannel::~ATAChannel() { - mutex_lock(&fLock); + mutex_lock(&fExecutionLock); if (fDevices) { for (uint8 i = 0; i < fDeviceCount; i++) @@ -94,7 +99,7 @@ ATAChannel::~ATAChannel() } delete fRequest; - mutex_destroy(&fLock); + mutex_destroy(&fExecutionLock); } @@ -217,10 +222,10 @@ status_t ATAChannel::ExecuteIO(scsi_ccb *ccb) { TRACE_FUNCTION("%p\n", ccb); - if (mutex_trylock(&fLock) != B_OK) + if (mutex_trylock(&fExecutionLock) != B_OK) return B_BUSY; - MutexLocker _(&fLock, true); + MutexLocker _(&fExecutionLock, true); fRequest->SetCCB(ccb); if (ccb->cdb[0] == SCSI_OP_REQUEST_SENSE) { @@ -445,6 +450,10 @@ ATAChannel::WaitForIdle() status_t ATAChannel::SendRequest(ATARequest *request, uint32 flags) { + // disable interrupts for PIO transfers, enable them for DMA + _WriteControl((flags & ATA_DMA_TRANSFER) != 0 ? 0 + : ATA_DEVICE_CONTROL_DISABLE_INTS); + ATADevice *device = request->Device(); if (device->Select() != B_OK || WaitForIdle() != B_OK) { // resetting the device here will discard current configuration, @@ -479,7 +488,7 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask) // wait for the device to finish current command (device no longer busy) status_t result = Wait(0, ATA_STATUS_BUSY, 0, request->Timeout()); if (result != B_OK) { - TRACE_ERROR("timeout\n"); + TRACE_ERROR("timeout waiting for request finish\n"); request->SetStatus(SCSI_CMD_TIMEOUT); return result; } @@ -564,12 +573,45 @@ ATAChannel::FinishRequest(ATARequest *request, uint32 flags, uint8 errorMask) } +status_t +ATAChannel::PrepareDMA(ATARequest *request) +{ + scsi_ccb *ccb = request->CCB(); + return fController->prepare_dma(fCookie, ccb->sg_list, ccb->sg_count, + request->IsWrite()); +} + + +status_t +ATAChannel::FinishDMA() +{ + return fController->finish_dma(fCookie); +} + + status_t ATAChannel::ExecuteDMATransfer(ATARequest *request) { - TRACE_ERROR("dma transfers unimplemented\n"); - request->SetStatus(SCSI_SEQUENCE_FAIL); - return B_ERROR; + InterruptsSpinLocker locker(fDMATransferLock); + fExpectsDMATransfer = true; + ConditionVariableEntry entry; + fDMATransferCondition.Add(&entry); + locker.Unlock(); + + fController->start_dma(fCookie); + bigtime_t timeout = system_time() + request->Timeout(); + status_t waitResult = entry.Wait(B_ABSOLUTE_TIMEOUT, timeout); + + locker.Lock(); + fExpectsDMATransfer = false; + locker.Unlock(); + + if (waitResult != B_OK) { + TRACE_ERROR("timeout waiting for DMA transfer\n"); + return B_TIMED_OUT; + } + + return B_OK; } @@ -612,14 +654,6 @@ ATAChannel::ExecutePIOTransfer(ATARequest *request) result = B_ERROR; } - if (result == B_OK) { - result = FinishRequest(request, ATA_WAIT_FINISH - | ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED); - } - - if (result != B_OK) - request->SetStatus(SCSI_SEQUENCE_FAIL); - return result; } @@ -632,6 +666,24 @@ ATAChannel::ReadPIO(uint8 *buffer, size_t length) } +void +ATAChannel::Interrupt(uint8 status) +{ + SpinLocker locker(fDMATransferLock); + if (!fExpectsDMATransfer) { + TRACE_ERROR("interrupt when not expecting transfer\n"); + return; + } + + if ((status & ATA_STATUS_BUSY) != 0) { + TRACE_ERROR(("interrupt while device is busy\n")); + return; + } + + fDMATransferCondition.NotifyAll(); +} + + status_t ATAChannel::_ReadRegs(ata_task_file *taskFile, ata_reg_mask mask) { diff --git a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp index 3c802c4c68..ff780c7f06 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATADevice.cpp @@ -17,6 +17,7 @@ ATADevice::ATADevice(ATAChannel *channel, uint8 index) fUse48Bits(false), fUseDMA(channel->UseDMA()), fDMAMode(0), + fDMAFailures(0), fTotalSectors(0), fRegisterMask(0) { @@ -513,18 +514,15 @@ status_t ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address, uint32 sectorCount) { - request->SetUseDMA(fUseDMA && _PrepareDMA(request) == B_OK); - if (!request->UseDMA()) { - status_t result = _PreparePIO(request); - if (result != B_OK) { - TRACE_ERROR("failed to prepare pio transfer\n"); - return result; - } - } + request->SetUseDMA(fUseDMA && fChannel->PrepareDMA(request) == B_OK); + if (!request->UseDMA()) + request->PrepareSGInfo(); request->SetBlocksLeft(sectorCount); if (_FillTaskFile(request, address) != B_OK) { TRACE_ERROR("failed to setup transfer request\n"); + if (request->UseDMA()) + fChannel->FinishDMA(); return B_ERROR; } @@ -537,13 +535,39 @@ ATADevice::ExecuteReadWrite(ATARequest *request, uint64 address, status_t result = fChannel->SendRequest(request, flags); if (result != B_OK) { TRACE_ERROR("failed to send transfer request\n"); + if (request->UseDMA()) + fChannel->FinishDMA(); return result; } - if (request->UseDMA()) - return fChannel->ExecuteDMATransfer(request); + if (request->UseDMA()) { + result = fChannel->ExecuteDMATransfer(request); + status_t dmaResult = fChannel->FinishDMA(); + if (result == B_OK && dmaResult == B_OK) { + fDMAFailures = 0; + request->CCB()->data_resid = 0; + } else { + if (dmaResult != B_OK) { + request->SetSense(SCSIS_KEY_HARDWARE_ERROR, + SCSIS_ASC_LUN_COM_FAILURE); + fDMAFailures++; + if (fDMAFailures >= ATA_MAX_DMA_FAILURES) { + TRACE_ALWAYS("disabling DMA after %u failures\n", + fDMAFailures); + fUseDMA = false; + } + } else { + // timeout + request->SetStatus(SCSI_CMD_TIMEOUT); + } + } + } else { + if (fChannel->ExecutePIOTransfer(request) != B_OK) + request->SetStatus(SCSI_SEQUENCE_FAIL); + } - return fChannel->ExecutePIOTransfer(request); + return fChannel->FinishRequest(request, ATA_WAIT_FINISH + | ATA_DEVICE_READY_REQUIRED, ATA_ERROR_ABORTED); } @@ -621,18 +645,3 @@ ATADevice::_FillTaskFile(ATARequest *request, uint64 address) return B_OK; } - - -status_t -ATADevice::_PrepareDMA(ATARequest *request) -{ - return B_ERROR; -} - - -status_t -ATADevice::_PreparePIO(ATARequest *request) -{ - request->PrepareSGInfo(); - return B_OK; -} diff --git a/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp b/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp index 1d8b5079b5..0bcf1c5ac2 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp +++ b/src/add-ons/kernel/bus_managers/ata/ATAModule.cpp @@ -205,7 +205,13 @@ ata_channel_added(device_node *parent) status_t ata_interrupt_handler(void *cookie, uint8 status) { - return B_UNHANDLED_INTERRUPT; + ATAChannel *channel = (ATAChannel *)cookie; + channel->Interrupt(status); + + // the controller driver already checks if its interrupt status indicates + // that the interrupt was for this device, so we are only here if that's + // the case. + return B_HANDLED_INTERRUPT; } diff --git a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h index 4a99efe629..26d2ffc35d 100644 --- a/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h +++ b/src/add-ons/kernel/bus_managers/ata/ATAPrivate.h @@ -25,6 +25,7 @@ #include "ATATracing.h" #include "ata_device_infoblock.h" +#define ATA_MAX_DMA_FAILURES 3 #define ATA_STANDARD_TIMEOUT 10 * 1000 * 1000 #define ATA_RELEASE_TIMEOUT 10 * 1000 * 1000 #define ATA_SIGNATURE_ATAPI 0xeb140101 @@ -35,10 +36,10 @@ enum { ATA_DEVICE_READY_REQUIRED = 0x01, ATA_IS_WRITE = 0x02, - ATA_DMA_TRANSFER = 0x03, - ATA_CHECK_ERROR_BIT = 0x04, - ATA_WAIT_FINISH = 0x08, - ATA_WAIT_ANY_BIT = 0x10 + ATA_DMA_TRANSFER = 0x04, + ATA_CHECK_ERROR_BIT = 0x08, + ATA_WAIT_FINISH = 0x10, + ATA_WAIT_ANY_BIT = 0x20 }; @@ -91,11 +92,16 @@ public: uint32 flags, uint8 errorMask); // data transfers + status_t PrepareDMA(ATARequest *request); + status_t FinishDMA(); + status_t ExecuteDMATransfer(ATARequest *request); status_t ExecutePIOTransfer(ATARequest *request); status_t ReadPIO(uint8 *buffer, size_t length); + void Interrupt(uint8 status); + private: status_t _ReadRegs(ata_task_file *taskFile, ata_reg_mask mask); @@ -125,7 +131,12 @@ private: uint32 fChannelID; ata_controller_interface * fController; void * fCookie; - mutex fLock; + + mutex fExecutionLock; + spinlock fDMATransferLock; + ConditionVariable fDMATransferCondition; + bool fExpectsDMATransfer; + status_t fStatus; scsi_bus fSCSIBus; uint8 fDeviceCount; @@ -178,9 +189,6 @@ private: status_t _FillTaskFile(ATARequest *request, uint64 address); - status_t _PrepareDMA(ATARequest *request); - status_t _PreparePIO(ATARequest *request); - const char * _DebugContext() { return fDebugContext; }; ATAChannel * fChannel; @@ -189,6 +197,7 @@ private: bool fUse48Bits; bool fUseDMA; uint8 fDMAMode; + uint8 fDMAFailures; uint64 fTotalSectors; ata_device_infoblock fInfoBlock; ata_task_file fTaskFile;