From 4330ef22d5cffd443a7af847124557a803d15327 Mon Sep 17 00:00:00 2001 From: Michael Lotz Date: Mon, 25 Sep 2006 18:52:50 +0000 Subject: [PATCH] * Added retries to the PhysicalMemoryAllocator. Memory is allocated and freed at a very high frequency, so low memory conditions shouldn't last very long. * Added a separate thread for cleaning up EHCI transfers. This makes it possible to actually always wait for the async advance interrupt without hindering execution of transfers. This pushes performance again and fixes the bug I introduced in the last change, that we could free yet cached descriptors when a previous async advance timed out. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@18929 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- .../usb/PhysicalMemoryAllocator.cpp | 83 ++++++++++--------- src/add-ons/kernel/busses/usb/ehci.cpp | 67 +++++++++++---- src/add-ons/kernel/busses/usb/ehci.h | 6 +- 3 files changed, 103 insertions(+), 53 deletions(-) diff --git a/src/add-ons/kernel/bus_managers/usb/PhysicalMemoryAllocator.cpp b/src/add-ons/kernel/bus_managers/usb/PhysicalMemoryAllocator.cpp index adf5117268..4245dbc10a 100644 --- a/src/add-ons/kernel/bus_managers/usb/PhysicalMemoryAllocator.cpp +++ b/src/add-ons/kernel/bus_managers/usb/PhysicalMemoryAllocator.cpp @@ -138,50 +138,59 @@ PhysicalMemoryAllocator::Allocate(size_t size, void **logicalAddress, } } - if (!_Lock()) - return B_ERROR; + int32 retries = 20; + while (retries-- > 0) { + if (!_Lock()) + return B_ERROR; - TRACE(("PMA: will use array %ld (blocksize: %ld) to allocate %ld bytes\n", arrayToUse, fBlockSize[arrayToUse], size)); - uint8 *targetArray = fArray[arrayToUse]; - uint32 arrayOffset = fArrayOffset[arrayToUse] % arrayLength; - for (size_t i = arrayOffset + 1; i != arrayOffset; i++) { - if (i >= arrayLength) - i -= arrayLength; + TRACE(("PMA: will use array %ld (blocksize: %ld) to allocate %ld bytes\n", arrayToUse, fBlockSize[arrayToUse], size)); + uint8 *targetArray = fArray[arrayToUse]; + uint32 arrayOffset = fArrayOffset[arrayToUse] % arrayLength; + for (size_t i = arrayOffset + 1; i != arrayOffset; i++) { + if (i >= arrayLength) + i -= arrayLength; - if (targetArray[i] == 0) { - // found a free slot - fArrayOffset[arrayToUse] = i; + if (targetArray[i] == 0) { + // found a free slot + fArrayOffset[arrayToUse] = i; - // fill upwards to the smallest block - uint32 fillSize = 1; - uint32 arrayIndex = i; - for (int32 j = arrayToUse; j >= 0; j--) { - memset(&fArray[j][arrayIndex], 1, fillSize); - fillSize <<= 1; - arrayIndex <<= 1; + // fill upwards to the smallest block + uint32 fillSize = 1; + uint32 arrayIndex = i; + for (int32 j = arrayToUse; j >= 0; j--) { + memset(&fArray[j][arrayIndex], 1, fillSize); + fillSize <<= 1; + arrayIndex <<= 1; + } + + // fill downwards to the biggest block + arrayIndex = i >> 1; + for (int32 j = arrayToUse + 1; j < fArrayCount; j++) { + fArray[j][arrayIndex]++; + if (fArray[j][arrayIndex] > 1) + break; + + arrayIndex >>= 1; + } + + _Unlock(); + size_t offset = fBlockSize[arrayToUse] * i; + *logicalAddress = (void *)((uint8 *)fLogicalBase + offset); + *physicalAddress = (void *)((uint8 *)fPhysicalBase + offset); + return B_OK; } - - // fill downwards to the biggest block - arrayIndex = i >> 1; - for (int32 j = arrayToUse + 1; j < fArrayCount; j++) { - fArray[j][arrayIndex]++; - if (fArray[j][arrayIndex] > 1) - break; - - arrayIndex >>= 1; - } - - _Unlock(); - size_t offset = fBlockSize[arrayToUse] * i; - *logicalAddress = (void *)((uint8 *)fLogicalBase + offset); - *physicalAddress = (void *)((uint8 *)fPhysicalBase + offset); - return B_OK; } + + // no slot found + _Unlock(); + + TRACE_ERROR(("PMA: found no free slot to store %ld bytes (%ld tries left)\n", size, retries)); + // we provide a scratch space here, memory will probably be freed + // as soon as some other transfer is completed and cleaned up. + // just wait a bit to give other threads a chance to free some slots. + snooze(100); } - // no slot found - _Unlock(); - TRACE_ERROR(("PMA: found no free slot to store %ld bytes\n", size)); return B_NO_MEMORY; } diff --git a/src/add-ons/kernel/busses/usb/ehci.cpp b/src/add-ons/kernel/busses/usb/ehci.cpp index 8cc407e4d8..e4d09c6cf2 100644 --- a/src/add-ons/kernel/busses/usb/ehci.cpp +++ b/src/add-ons/kernel/busses/usb/ehci.cpp @@ -105,7 +105,8 @@ EHCI::EHCI(pci_info *info, Stack *stack) fFirstTransfer(NULL), fLastTransfer(NULL), fFinishThread(-1), - fStopFinishThread(false), + fCleanupThread(-1), + fStopThreads(false), fFreeListHead(NULL), fRootHub(NULL), fRootHubAddress(0), @@ -212,7 +213,9 @@ EHCI::EHCI(pci_info *info, Stack *stack) // create semaphores the finisher thread will wait for fAsyncAdvanceSem = create_sem(0, "EHCI Async Advance"); fFinishTransfersSem = create_sem(0, "EHCI Finish Transfers"); - if (fFinishTransfersSem < B_OK || fAsyncAdvanceSem < B_OK) { + fCleanupSem = create_sem(0, "EHCI Cleanup"); + if (fFinishTransfersSem < B_OK || fAsyncAdvanceSem < B_OK + || fCleanupSem < B_OK) { TRACE_ERROR(("usb_ehci: failed to create semaphores\n")); return; } @@ -222,6 +225,11 @@ EHCI::EHCI(pci_info *info, Stack *stack) B_NORMAL_PRIORITY, (void *)this); resume_thread(fFinishThread); + // create cleanup service thread + fCleanupThread = spawn_kernel_thread(CleanupThread, "ehci cleanup thread", + B_NORMAL_PRIORITY, (void *)this); + resume_thread(fCleanupThread); + // install the interrupt handler and enable interrupts install_io_interrupt_handler(fPCIInfo->u.h0.interrupt_line, InterruptHandler, (void *)this, 0); @@ -275,10 +283,11 @@ EHCI::~EHCI() CancelAllPendingTransfers(); int32 result = 0; - fStopFinishThread = true; + fStopThreads = true; delete_sem(fAsyncAdvanceSem); delete_sem(fFinishTransfersSem); wait_for_thread(fFinishThread, &result); + wait_for_thread(fCleanupThread, &result); delete fRootHub; delete_area(fPeriodicFrameListArea); @@ -939,7 +948,7 @@ EHCI::FinishThread(void *data) void EHCI::FinishTransfers() { - while (!fStopFinishThread) { + while (!fStopThreads) { if (acquire_sem(fFinishTransfersSem) < B_OK) continue; @@ -1087,19 +1096,47 @@ EHCI::FinishTransfers() Unlock(); } } + + release_sem(fCleanupSem); + } + } +} + + +int32 +EHCI::CleanupThread(void *data) +{ + ((EHCI *)data)->Cleanup(); + return B_OK; +} + + +void +EHCI::Cleanup() +{ + ehci_qh *lastFreeListHead = NULL; + + while (!fStopThreads) { + if (acquire_sem(fCleanupSem) < B_OK) + continue; + + ehci_qh *freeListHead = fFreeListHead; + if (freeListHead == lastFreeListHead) + continue; + + // set the doorbell and wait for the host controller to notify us + WriteOpReg(EHCI_USBCMD, ReadOpReg(EHCI_USBCMD) | EHCI_USBCMD_INTONAAD); + if (acquire_sem(fAsyncAdvanceSem) < B_OK) + continue; + + ehci_qh *current = freeListHead; + while (current != lastFreeListHead) { + ehci_qh *next = (ehci_qh *)current->next_log; + FreeQueueHead(current); + current = next; } - if (fFreeListHead) { - // set the doorbell and wait for the host controller to notify us - WriteOpReg(EHCI_USBCMD, ReadOpReg(EHCI_USBCMD) | EHCI_USBCMD_INTONAAD); - if (acquire_sem_etc(fAsyncAdvanceSem, 1, B_RELATIVE_TIMEOUT, 1000) == B_OK) { - while (fFreeListHead) { - ehci_qh *next = (ehci_qh *)fFreeListHead->next_log; - FreeQueueHead(fFreeListHead); - fFreeListHead = next; - } - } - } + lastFreeListHead = freeListHead; } } diff --git a/src/add-ons/kernel/busses/usb/ehci.h b/src/add-ons/kernel/busses/usb/ehci.h index 4e475a4525..034ccfc094 100644 --- a/src/add-ons/kernel/busses/usb/ehci.h +++ b/src/add-ons/kernel/busses/usb/ehci.h @@ -68,6 +68,8 @@ static int32 InterruptHandler(void *data); static int32 FinishThread(void *data); void FinishTransfers(); +static int32 CleanupThread(void *data); + void Cleanup(); // Queue Head functions ehci_qh *CreateQueueHead(); @@ -141,7 +143,9 @@ static pci_module_info *sPCIModule; transfer_data *fLastTransfer; sem_id fFinishTransfersSem; thread_id fFinishThread; - bool fStopFinishThread; + sem_id fCleanupSem; + thread_id fCleanupThread; + bool fStopThreads; ehci_qh *fFreeListHead; // Root Hub