When canceling transfers, do not call the callbacks with the lock held. This

prevents deadlocks in cases where a new transfer is scheduled from within the
cancel callback. This is an edge case, as generally you don't want to schedule
anything when explicitly canceling transfers, but there are a valid use cases
when you cancel because of a timeout and then have to reset the device for
example. Note that this hides bug #2353, where the cancel case is probably
not handled correctly.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25861 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Michael Lotz 2008-06-08 13:19:33 +00:00
parent 53ad1814cb
commit 4ca876701d
3 changed files with 69 additions and 6 deletions

View File

@ -888,9 +888,15 @@ EHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
if (!Lock())
return B_ERROR;
struct transfer_entry {
Transfer * transfer;
transfer_entry * next;
};
transfer_entry *list = NULL;
transfer_data *current = fFirstTransfer;
while (current) {
if (current->transfer->TransferPipe() == pipe) {
if (current->transfer && current->transfer->TransferPipe() == pipe) {
// clear the active bit so the descriptors are canceled
ehci_qtd *descriptor = (ehci_qtd *)current->queue_head->element_log;
while (descriptor) {
@ -902,7 +908,14 @@ EHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
// if the transfer is canceled by force, the one causing the
// cancel is probably not the one who initiated the transfer
// and the callback is likely not safe anymore
current->transfer->Finished(B_CANCELED, 0);
transfer_entry *entry
= (transfer_entry *)malloc(sizeof(transfer_entry));
if (entry != NULL) {
entry->transfer = current->transfer;
current->transfer = NULL;
entry->next = list;
list = entry;
}
}
current->canceled = true;
@ -913,6 +926,14 @@ EHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
Unlock();
while (list != NULL) {
transfer_entry *next = list->next;
list->transfer->Finished(B_CANCELED, 0);
delete list->transfer;
free(list);
list = next;
}
// notify the finisher so it can clean up the canceled transfers
release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE);
return B_OK;

View File

@ -426,9 +426,15 @@ OHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
if (!Lock())
return B_ERROR;
struct transfer_entry {
Transfer * transfer;
transfer_entry * next;
};
transfer_entry *list = NULL;
transfer_data *current = fFirstTransfer;
while (current) {
if (current->transfer->TransferPipe() == pipe) {
if (current->transfer && current->transfer->TransferPipe() == pipe) {
// Check if the skip bit is already set
if (!(current->endpoint->flags & OHCI_ENDPOINT_SKIP)) {
current->endpoint->flags |= OHCI_ENDPOINT_SKIP;
@ -445,7 +451,14 @@ OHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
// If the transfer is canceled by force, the one causing the
// cancel is probably not the one who initiated the transfer
// and the callback is likely not safe anymore
current->transfer->Finished(B_CANCELED, 0);
transfer_entry *entry
= (transfer_entry *)malloc(sizeof(transfer_entry));
if (entry != NULL) {
entry->transfer = current->transfer;
current->transfer = NULL;
entry->next = list;
list = entry;
}
}
current->canceled = true;
}
@ -454,6 +467,14 @@ OHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
Unlock();
while (list != NULL) {
transfer_entry *next = list->next;
list->transfer->Finished(B_CANCELED, 0);
delete list->transfer;
free(list);
list = next;
}
// notify the finisher so it can clean up the canceled transfers
release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE);
return B_OK;

View File

@ -614,9 +614,15 @@ UHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
if (!Lock())
return B_ERROR;
struct transfer_entry {
Transfer * transfer;
transfer_entry * next;
};
transfer_entry *list = NULL;
transfer_data *current = fFirstTransfer;
while (current) {
if (current->transfer->TransferPipe() == pipe) {
if (current->transfer && current->transfer->TransferPipe() == pipe) {
// clear the active bit so the descriptors are canceled
uhci_td *descriptor = current->first_descriptor;
while (descriptor) {
@ -628,7 +634,14 @@ UHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
// if the transfer is canceled by force, the one causing the
// cancel is probably not the one who initiated the transfer
// and the callback is likely not safe anymore
current->transfer->Finished(B_CANCELED, 0);
transfer_entry *entry
= (transfer_entry *)malloc(sizeof(transfer_entry));
if (entry != NULL) {
entry->transfer = current->transfer;
current->transfer = NULL;
entry->next = list;
list = entry;
}
}
current->canceled = true;
@ -638,6 +651,14 @@ UHCI::CancelQueuedTransfers(Pipe *pipe, bool force)
Unlock();
while (list != NULL) {
transfer_entry *next = list->next;
list->transfer->Finished(B_CANCELED, 0);
delete list->transfer;
free(list);
list = next;
}
// notify the finisher so it can clean up the canceled transfers
release_sem_etc(fFinishTransfersSem, 1, B_DO_NOT_RESCHEDULE);
return B_OK;