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:
parent
53ad1814cb
commit
4ca876701d
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user