From 1393394ba6adde434020a48e5d9b7b22822150b0 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Wed, 23 Sep 2009 01:59:05 +0000 Subject: [PATCH] We need to guarantee that when cancel returns that the callback won't be called anymore. It could happen that the transfer was already in the process of being finished, so wasn't in the list of pending transfers anymore. Cancel would then return even though the callback wasn't called yet. This could lead to a callback being called after a driver was already unloaded (even after it cleaned up the pipes it used). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@33239 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/kernel/busses/usb/ehci.cpp | 11 +++++++++++ src/add-ons/kernel/busses/usb/ehci.h | 1 + src/add-ons/kernel/busses/usb/ohci.cpp | 11 +++++++++++ src/add-ons/kernel/busses/usb/ohci.h | 1 + src/add-ons/kernel/busses/usb/uhci.cpp | 11 +++++++++++ src/add-ons/kernel/busses/usb/uhci.h | 1 + 6 files changed, 36 insertions(+) diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index cb8f2ed1cf..08dbc66aa6 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -119,6 +119,7 @@ EHCI::EHCI(pci_info *info, Stack *stack) fCleanupThread(-1), fStopThreads(false), fFreeListHead(NULL), + fProcessingPipe(NULL), fRootHub(NULL), fRootHubAddress(0), fPortCount(0), @@ -941,6 +942,10 @@ EHCI::CancelQueuedTransfers(Pipe *pipe, bool force) list = next; } + // wait for any transfers that might have made it before canceling + while (fProcessingPipe == pipe) + snooze(1000); + // notify the finisher so it can clean up the canceled transfers release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE); return B_OK; @@ -1075,6 +1080,11 @@ EHCI::FinishTransfers() if (transfer == fLastTransfer) fLastTransfer = lastTransfer; + // store the currently processing pipe here so we can wait + // in cancel if we are processing something on the target pipe + if (!transfer->canceled) + fProcessingPipe = transfer->transfer->TransferPipe(); + transfer->link = NULL; Unlock(); } @@ -1135,6 +1145,7 @@ EHCI::FinishTransfers() } transfer->transfer->Finished(callbackStatus, actualLength); + fProcessingPipe = NULL; } // unlink hardware queue and delete the transfer diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index 1cf6e0c866..6c5417817b 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -157,6 +157,7 @@ static pci_module_info * sPCIModule; thread_id fCleanupThread; bool fStopThreads; ehci_qh * fFreeListHead; + Pipe * fProcessingPipe; // Root Hub EHCIRootHub * fRootHub; diff --git a/src/add-ons/kernel/busses/usb/ohci.cpp b/src/add-ons/kernel/busses/usb/ohci.cpp index 7a3179effd..ec67e3168d 100644 --- a/src/add-ons/kernel/busses/usb/ohci.cpp +++ b/src/add-ons/kernel/busses/usb/ohci.cpp @@ -71,6 +71,7 @@ OHCI::OHCI(pci_info *info, Stack *stack) fFinishTransfersSem(-1), fFinishThread(-1), fStopFinishThread(false), + fProcessingPipe(NULL), fRootHub(NULL), fRootHubAddress(0), fPortCount(0) @@ -472,6 +473,10 @@ OHCI::CancelQueuedTransfers(Pipe *pipe, bool force) list = next; } + // wait for any transfers that might have made it before canceling + while (fProcessingPipe == pipe) + snooze(1000); + // notify the finisher so it can clean up the canceled transfers release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE); return B_OK; @@ -1021,6 +1026,11 @@ OHCI::_FinishTransfers() if (transfer == fLastTransfer) fLastTransfer = lastTransfer; + // store the currently processing pipe here so we can wait + // in cancel if we are processing something on the target pipe + if (!transfer->canceled) + fProcessingPipe = transfer->transfer->TransferPipe(); + transfer->link = NULL; Unlock(); } @@ -1071,6 +1081,7 @@ OHCI::_FinishTransfers() } transfer->transfer->Finished(callbackStatus, actualLength); + fProcessingPipe = NULL; } if (callbackStatus != B_OK) { diff --git a/src/add-ons/kernel/busses/usb/ohci.h b/src/add-ons/kernel/busses/usb/ohci.h index b23ce6f46e..1117406116 100644 --- a/src/add-ons/kernel/busses/usb/ohci.h +++ b/src/add-ons/kernel/busses/usb/ohci.h @@ -163,6 +163,7 @@ static pci_module_info * sPCIModule; sem_id fFinishTransfersSem; thread_id fFinishThread; bool fStopFinishThread; + Pipe * fProcessingPipe; // Root Hub OHCIRootHub * fRootHub; diff --git a/src/add-ons/kernel/busses/usb/uhci.cpp b/src/add-ons/kernel/busses/usb/uhci.cpp index c9f7fb554f..30364b5af1 100644 --- a/src/add-ons/kernel/busses/usb/uhci.cpp +++ b/src/add-ons/kernel/busses/usb/uhci.cpp @@ -335,6 +335,7 @@ UHCI::UHCI(pci_info *info, Stack *stack) fFinishTransfersSem(-1), fFinishThread(-1), fStopThreads(false), + fProcessingPipe(NULL), fFreeList(NULL), fCleanupThread(-1), fCleanupSem(-1), @@ -795,6 +796,10 @@ UHCI::CancelQueuedTransfers(Pipe *pipe, bool force) list = next; } + // wait for any transfers that might have made it before canceling + while (fProcessingPipe == pipe) + snooze(1000); + // notify the finisher so it can clean up the canceled transfers release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE); return B_OK; @@ -1340,6 +1345,11 @@ UHCI::FinishTransfers() if (transfer == fLastTransfer) fLastTransfer = lastTransfer; + // store the currently processing pipe here so we can wait + // in cancel if we are processing something on the target pipe + if (!transfer->canceled) + fProcessingPipe = transfer->transfer->TransferPipe(); + transfer->link = NULL; Unlock(); } @@ -1399,6 +1409,7 @@ UHCI::FinishTransfers() } transfer->transfer->Finished(callbackStatus, actualLength); + fProcessingPipe = NULL; } // remove and free the hardware queue and its descriptors diff --git a/src/add-ons/kernel/busses/usb/uhci.h b/src/add-ons/kernel/busses/usb/uhci.h index e4623ea1b9..195fda68eb 100644 --- a/src/add-ons/kernel/busses/usb/uhci.h +++ b/src/add-ons/kernel/busses/usb/uhci.h @@ -233,6 +233,7 @@ static pci_module_info * sPCIModule; sem_id fFinishTransfersSem; thread_id fFinishThread; bool fStopThreads; + Pipe * fProcessingPipe; transfer_data * fFreeList; thread_id fCleanupThread;