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