From 2f5c87c7c6d765a86952ba664f961d9b86cd90a0 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Wed, 29 Mar 2023 02:31:13 -0700 Subject: [PATCH] dev: Add an "abortPending" method to the DMA port class. This will abort any pending transactions that have been given to the port. Change-Id: Ie5f2c702530656a0c4590461369d430abead14cd Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/69437 Maintainer: Gabe Black Tested-by: kokoro Reviewed-by: Gabe Black --- src/dev/dma_device.cc | 84 +++++++++++++++++++++++++++++++++++-------- src/dev/dma_device.hh | 19 ++++++++-- 2 files changed, 86 insertions(+), 17 deletions(-) diff --git a/src/dev/dma_device.cc b/src/dev/dma_device.cc index ebda635442..24e931ef8d 100644 --- a/src/dev/dma_device.cc +++ b/src/dev/dma_device.cc @@ -81,6 +81,8 @@ DmaPort::handleRespPacket(PacketPtr pkt, Tick delay) void DmaPort::handleResp(DmaReqState *state, Addr addr, Addr size, Tick delay) { + assert(pendingCount != 0); + pendingCount--; DPRINTF(DMA, "Received response %s for addr: %#x size: %d nb: %d," \ " tot: %d sched %d\n", MemCmd(state->cmd).toString(), addr, size, @@ -93,11 +95,22 @@ DmaPort::handleResp(DmaReqState *state, Addr addr, Addr size, Tick delay) state->numBytes += size; assert(state->totBytes >= state->numBytes); - // If we have reached the total number of bytes for this DMA request, - // then signal the completion and delete the sate. - if (state->totBytes == state->numBytes) { - assert(pendingCount != 0); - pendingCount--; + bool all_bytes = (state->totBytes == state->numBytes); + if (state->aborted) { + // If this request was aborted, check to see if its in flight accesses + // have finished. There may be packets for more than one request in + // flight at a time, so check for finished requests, or no more + // packets. + if (all_bytes || pendingCount == 0) { + // If yes, signal its abort event (if any) and delete the state. + if (state->abortEvent) { + device->schedule(state->abortEvent, curTick()); + } + delete state; + } + } else if (all_bytes) { + // If we have reached the end of this DMA request, then signal the + // completion and delete the sate. if (state->completionEvent) { delay += state->delay; device->schedule(state->completionEvent, curTick() + delay); @@ -166,8 +179,9 @@ DmaPort::drain() void DmaPort::recvReqRetry() { - assert(transmitList.size()); - trySendTimingReq(); + retryPending = false; + if (transmitList.size()) + trySendTimingReq(); } void @@ -184,7 +198,6 @@ DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, transmitList.push_back( new DmaReqState(cmd, addr, cacheLineSize, size, data, flag, requestorId, sid, ssid, event, delay)); - pendingCount++; // In zero time, also initiate the sending of the packets for the request // we have just created. For atomic this involves actually completing all @@ -200,6 +213,42 @@ DmaPort::dmaAction(Packet::Command cmd, Addr addr, int size, Event *event, defaultSid, defaultSSid, delay, flag); } +void +DmaPort::abortPending() +{ + if (inRetry) { + delete inRetry; + inRetry = nullptr; + } + + if (pendingCount && !transmitList.empty()) { + auto *state = transmitList.front(); + if (state->numBytes != state->gen.complete()) { + // In flight packets refer to the transmission at the front of the + // list, and not a transmission whose packets have all been sent + // but not completed. Preserve the state so the packets don't have + // dangling pointers. + transmitList.pop_front(); + state->aborted = true; + } + } + + // Get rid of requests that haven't started yet. + while (!transmitList.empty()) { + auto *state = transmitList.front(); + if (state->abortEvent) + device->schedule(state->abortEvent, curTick()); + delete state; + transmitList.pop_front(); + } + + if (sendEvent.scheduled()) + device->deschedule(sendEvent); + + if (pendingCount == 0) + signalDrainDone(); +} + void DmaPort::trySendTimingReq() { @@ -216,14 +265,17 @@ DmaPort::trySendTimingReq() // Check if this was the last packet now, since hypothetically the packet // response may come immediately, and state may be deleted. bool last = state->gen.last(); - if (!sendTimingReq(pkt)) + if (sendTimingReq(pkt)) { + pendingCount++; + } else { + retryPending = true; inRetry = pkt; - if (!inRetry) { + } + if (!retryPending) { + state->gen.next(); // If that was the last packet from this request, pop it from the list. if (last) transmitList.pop_front(); - else - state->gen.next(); DPRINTF(DMA, "-- Done\n"); // If there is more to do, then do so. if (!transmitList.empty()) { @@ -236,8 +288,8 @@ DmaPort::trySendTimingReq() DPRINTF(DMA, "-- Failed, waiting for retry\n"); } - DPRINTF(DMA, "TransmitList: %d, inRetry: %d\n", - transmitList.size(), inRetry ? 1 : 0); + DPRINTF(DMA, "TransmitList: %d, retryPending: %d\n", + transmitList.size(), retryPending ? 1 : 0); } bool @@ -246,6 +298,7 @@ DmaPort::sendAtomicReq(DmaReqState *state) PacketPtr pkt = state->createPacket(); DPRINTF(DMA, "Sending DMA for addr: %#x size: %d\n", state->gen.addr(), state->gen.size()); + pendingCount++; Tick lat = sendAtomic(pkt); // Check if we're done, since handleResp may delete state. @@ -258,6 +311,7 @@ bool DmaPort::sendAtomicBdReq(DmaReqState *state) { bool done = false; + pendingCount++; auto bd_it = memBackdoors.contains(state->gen.addr()); if (bd_it == memBackdoors.end()) { @@ -336,7 +390,7 @@ DmaPort::sendDma() if (sys->isTimingMode()) { // If we are either waiting for a retry or are still waiting after // sending the last packet, then do not proceed. - if (inRetry || sendEvent.scheduled()) { + if (retryPending || sendEvent.scheduled()) { DPRINTF(DMA, "Can't send immediately, waiting to send\n"); return; } diff --git a/src/dev/dma_device.hh b/src/dev/dma_device.hh index 2a3468c988..92b44bf5f6 100644 --- a/src/dev/dma_device.hh +++ b/src/dev/dma_device.hh @@ -85,6 +85,12 @@ class DmaPort : public RequestPort, public Drainable * complete. */ Event *completionEvent; + /** Event to call on the device when this transaction is aborted. */ + Event *abortEvent; + + /** Whether this request was aborted. */ + bool aborted = false; + /** Total number of bytes that this transaction involves. */ const Addr totBytes; @@ -115,8 +121,9 @@ class DmaPort : public RequestPort, public Drainable DmaReqState(Packet::Command _cmd, Addr addr, Addr chunk_sz, Addr tb, uint8_t *_data, Request::Flags _flags, RequestorID _id, - uint32_t _sid, uint32_t _ssid, Event *ce, Tick _delay) - : completionEvent(ce), totBytes(tb), delay(_delay), + uint32_t _sid, uint32_t _ssid, Event *ce, Tick _delay, + Event *ae=nullptr) + : completionEvent(ce), abortEvent(ae), totBytes(tb), delay(_delay), gen(addr, tb, chunk_sz), data(_data), flags(_flags), id(_id), sid(_sid), ssid(_ssid), cmd(_cmd) {} @@ -168,6 +175,11 @@ class DmaPort : public RequestPort, public Drainable /** The packet (if any) waiting for a retry to send. */ PacketPtr inRetry = nullptr; + /** + * Whether the other side expects us to wait for a retry. We may have + * decided not to actually send the packet by the time we get the retry. + */ + bool retryPending = false; /** Default streamId */ const uint32_t defaultSid; @@ -195,6 +207,9 @@ class DmaPort : public RequestPort, public Drainable uint8_t *data, uint32_t sid, uint32_t ssid, Tick delay, Request::Flags flag=0); + // Abort and remove any pending DMA transmissions. + void abortPending(); + bool dmaPending() const { return pendingCount > 0; } DrainState drain() override;