diff options
author | Kristian Høgsberg <krh@redhat.com> | 2007-02-06 14:49:32 -0500 |
---|---|---|
committer | Stefan Richter <stefanr@s5r6.in-berlin.de> | 2007-03-09 16:02:51 -0500 |
commit | 730c32f58ba81b3a4fe6d19c7d9e9829dd96d363 (patch) | |
tree | 79149d002b095ca27582d4d7ef00c8eefec67170 /drivers/firewire | |
parent | 72e318e07e1fa9840bfdd5788421fc6dc51a93de (diff) |
firewire: Implement proper transaction cancelation.
Drivers such as fw-sbp2 had no way to properly cancel in-progress
transactions, which could leave a pending transaction or an unset
packet in the low-level queues after kfree'ing the containing
structure. fw_cancel_transaction() lets drivers cancel a submitted
transaction.
Signed-off-by: Kristian Høgsberg <krh@redhat.com>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire')
-rw-r--r-- | drivers/firewire/fw-card.c | 7 | ||||
-rw-r--r-- | drivers/firewire/fw-ohci.c | 33 | ||||
-rw-r--r-- | drivers/firewire/fw-sbp2.c | 3 | ||||
-rw-r--r-- | drivers/firewire/fw-transaction.c | 54 | ||||
-rw-r--r-- | drivers/firewire/fw-transaction.h | 5 |
5 files changed, 95 insertions, 7 deletions
diff --git a/drivers/firewire/fw-card.c b/drivers/firewire/fw-card.c index 7f5dc43ec131..f785b1005284 100644 --- a/drivers/firewire/fw-card.c +++ b/drivers/firewire/fw-card.c | |||
@@ -475,6 +475,12 @@ dummy_send_response(struct fw_card *card, struct fw_packet *packet) | |||
475 | } | 475 | } |
476 | 476 | ||
477 | static int | 477 | static int |
478 | dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet) | ||
479 | { | ||
480 | return -ENOENT; | ||
481 | } | ||
482 | |||
483 | static int | ||
478 | dummy_enable_phys_dma(struct fw_card *card, | 484 | dummy_enable_phys_dma(struct fw_card *card, |
479 | int node_id, int generation) | 485 | int node_id, int generation) |
480 | { | 486 | { |
@@ -487,6 +493,7 @@ static struct fw_card_driver dummy_driver = { | |||
487 | .update_phy_reg = dummy_update_phy_reg, | 493 | .update_phy_reg = dummy_update_phy_reg, |
488 | .set_config_rom = dummy_set_config_rom, | 494 | .set_config_rom = dummy_set_config_rom, |
489 | .send_request = dummy_send_request, | 495 | .send_request = dummy_send_request, |
496 | .cancel_packet = dummy_cancel_packet, | ||
490 | .send_response = dummy_send_response, | 497 | .send_response = dummy_send_response, |
491 | .enable_phys_dma = dummy_enable_phys_dma, | 498 | .enable_phys_dma = dummy_enable_phys_dma, |
492 | }; | 499 | }; |
diff --git a/drivers/firewire/fw-ohci.c b/drivers/firewire/fw-ohci.c index 02b2b69c8741..e6fa3496183e 100644 --- a/drivers/firewire/fw-ohci.c +++ b/drivers/firewire/fw-ohci.c | |||
@@ -79,6 +79,7 @@ struct at_context { | |||
79 | struct fw_ohci *ohci; | 79 | struct fw_ohci *ohci; |
80 | dma_addr_t descriptor_bus; | 80 | dma_addr_t descriptor_bus; |
81 | dma_addr_t buffer_bus; | 81 | dma_addr_t buffer_bus; |
82 | struct fw_packet *current_packet; | ||
82 | 83 | ||
83 | struct list_head list; | 84 | struct list_head list; |
84 | 85 | ||
@@ -489,6 +490,7 @@ at_context_setup_packet(struct at_context *ctx, struct list_head *list) | |||
489 | ctx->descriptor_bus | z); | 490 | ctx->descriptor_bus | z); |
490 | reg_write(ctx->ohci, control_set(ctx->regs), | 491 | reg_write(ctx->ohci, control_set(ctx->regs), |
491 | CONTEXT_RUN | CONTEXT_WAKE); | 492 | CONTEXT_RUN | CONTEXT_WAKE); |
493 | ctx->current_packet = packet; | ||
492 | } else { | 494 | } else { |
493 | /* We dont return error codes from this function; all | 495 | /* We dont return error codes from this function; all |
494 | * transmission errors are reported through the | 496 | * transmission errors are reported through the |
@@ -524,6 +526,12 @@ static void at_context_tasklet(unsigned long data) | |||
524 | 526 | ||
525 | at_context_stop(ctx); | 527 | at_context_stop(ctx); |
526 | 528 | ||
529 | /* If the head of the list isn't the packet that just got | ||
530 | * transmitted, the packet got cancelled before we finished | ||
531 | * transmitting it. */ | ||
532 | if (ctx->current_packet != packet) | ||
533 | goto skip_to_next; | ||
534 | |||
527 | if (packet->payload_length > 0) { | 535 | if (packet->payload_length > 0) { |
528 | dma_unmap_single(ohci->card.device, packet->payload_bus, | 536 | dma_unmap_single(ohci->card.device, packet->payload_bus, |
529 | packet->payload_length, DMA_TO_DEVICE); | 537 | packet->payload_length, DMA_TO_DEVICE); |
@@ -564,6 +572,7 @@ static void at_context_tasklet(unsigned long data) | |||
564 | } else | 572 | } else |
565 | complete_transmission(packet, evt - 16, &list); | 573 | complete_transmission(packet, evt - 16, &list); |
566 | 574 | ||
575 | skip_to_next: | ||
567 | /* If more packets are queued, set up the next one. */ | 576 | /* If more packets are queued, set up the next one. */ |
568 | if (!list_empty(&ctx->list)) | 577 | if (!list_empty(&ctx->list)) |
569 | at_context_setup_packet(ctx, &list); | 578 | at_context_setup_packet(ctx, &list); |
@@ -1012,6 +1021,29 @@ static void ohci_send_response(struct fw_card *card, struct fw_packet *packet) | |||
1012 | at_context_transmit(&ohci->at_response_ctx, packet); | 1021 | at_context_transmit(&ohci->at_response_ctx, packet); |
1013 | } | 1022 | } |
1014 | 1023 | ||
1024 | static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet) | ||
1025 | { | ||
1026 | struct fw_ohci *ohci = fw_ohci(card); | ||
1027 | LIST_HEAD(list); | ||
1028 | unsigned long flags; | ||
1029 | |||
1030 | spin_lock_irqsave(&ohci->lock, flags); | ||
1031 | |||
1032 | if (packet->ack == 0) { | ||
1033 | fw_notify("cancelling packet %p (header[0]=%08x)\n", | ||
1034 | packet, packet->header[0]); | ||
1035 | |||
1036 | complete_transmission(packet, RCODE_CANCELLED, &list); | ||
1037 | } | ||
1038 | |||
1039 | spin_unlock_irqrestore(&ohci->lock, flags); | ||
1040 | |||
1041 | do_packet_callbacks(ohci, &list); | ||
1042 | |||
1043 | /* Return success if we actually cancelled something. */ | ||
1044 | return list_empty(&list) ? -ENOENT : 0; | ||
1045 | } | ||
1046 | |||
1015 | static int | 1047 | static int |
1016 | ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation) | 1048 | ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation) |
1017 | { | 1049 | { |
@@ -1339,6 +1371,7 @@ static const struct fw_card_driver ohci_driver = { | |||
1339 | .set_config_rom = ohci_set_config_rom, | 1371 | .set_config_rom = ohci_set_config_rom, |
1340 | .send_request = ohci_send_request, | 1372 | .send_request = ohci_send_request, |
1341 | .send_response = ohci_send_response, | 1373 | .send_response = ohci_send_response, |
1374 | .cancel_packet = ohci_cancel_packet, | ||
1342 | .enable_phys_dma = ohci_enable_phys_dma, | 1375 | .enable_phys_dma = ohci_enable_phys_dma, |
1343 | 1376 | ||
1344 | .allocate_iso_context = ohci_allocate_iso_context, | 1377 | .allocate_iso_context = ohci_allocate_iso_context, |
diff --git a/drivers/firewire/fw-sbp2.c b/drivers/firewire/fw-sbp2.c index 54cad3a5dfb8..bb133398feec 100644 --- a/drivers/firewire/fw-sbp2.c +++ b/drivers/firewire/fw-sbp2.c | |||
@@ -348,6 +348,9 @@ static void sbp2_cancel_orbs(struct fw_unit *unit) | |||
348 | spin_unlock_irqrestore(&device->card->lock, flags); | 348 | spin_unlock_irqrestore(&device->card->lock, flags); |
349 | 349 | ||
350 | list_for_each_entry_safe(orb, next, &list, link) { | 350 | list_for_each_entry_safe(orb, next, &list, link) { |
351 | if (fw_cancel_transaction(device->card, &orb->t) == 0) | ||
352 | continue; | ||
353 | |||
351 | orb->rcode = RCODE_CANCELLED; | 354 | orb->rcode = RCODE_CANCELLED; |
352 | orb->callback(orb, NULL); | 355 | orb->callback(orb, NULL); |
353 | } | 356 | } |
diff --git a/drivers/firewire/fw-transaction.c b/drivers/firewire/fw-transaction.c index fb3b77e1bb2d..5394569a1c89 100644 --- a/drivers/firewire/fw-transaction.c +++ b/drivers/firewire/fw-transaction.c | |||
@@ -59,20 +59,52 @@ | |||
59 | #define phy_config_root_id(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23)) | 59 | #define phy_config_root_id(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23)) |
60 | #define phy_identifier(id) ((id) << 30) | 60 | #define phy_identifier(id) ((id) << 30) |
61 | 61 | ||
62 | static void | 62 | static int |
63 | close_transaction(struct fw_transaction *t, struct fw_card *card, int rcode, | 63 | close_transaction(struct fw_transaction *transaction, |
64 | u32 * payload, size_t length) | 64 | struct fw_card *card, int rcode, |
65 | u32 *payload, size_t length) | ||
65 | { | 66 | { |
67 | struct fw_transaction *t; | ||
66 | unsigned long flags; | 68 | unsigned long flags; |
67 | 69 | ||
68 | spin_lock_irqsave(&card->lock, flags); | 70 | spin_lock_irqsave(&card->lock, flags); |
69 | card->tlabel_mask &= ~(1 << t->tlabel); | 71 | list_for_each_entry(t, &card->transaction_list, link) { |
70 | list_del(&t->link); | 72 | if (t == transaction) { |
73 | list_del(&t->link); | ||
74 | card->tlabel_mask &= ~(1 << t->tlabel); | ||
75 | break; | ||
76 | } | ||
77 | } | ||
71 | spin_unlock_irqrestore(&card->lock, flags); | 78 | spin_unlock_irqrestore(&card->lock, flags); |
72 | 79 | ||
73 | t->callback(card, rcode, payload, length, t->callback_data); | 80 | if (&t->link != &card->transaction_list) { |
81 | t->callback(card, rcode, payload, length, t->callback_data); | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | return -ENOENT; | ||
74 | } | 86 | } |
75 | 87 | ||
88 | /* Only valid for transactions that are potentially pending (ie have | ||
89 | * been sent). */ | ||
90 | int | ||
91 | fw_cancel_transaction(struct fw_card *card, | ||
92 | struct fw_transaction *transaction) | ||
93 | { | ||
94 | /* Cancel the packet transmission if it's still queued. That | ||
95 | * will call the packet transmission callback which cancels | ||
96 | * the transaction. */ | ||
97 | |||
98 | if (card->driver->cancel_packet(card, &transaction->packet) == 0) | ||
99 | return 0; | ||
100 | |||
101 | /* If the request packet has already been sent, we need to see | ||
102 | * if the transaction is still pending and remove it in that case. */ | ||
103 | |||
104 | return close_transaction(transaction, card, RCODE_CANCELLED, NULL, 0); | ||
105 | } | ||
106 | EXPORT_SYMBOL(fw_cancel_transaction); | ||
107 | |||
76 | static void | 108 | static void |
77 | transmit_complete_callback(struct fw_packet *packet, | 109 | transmit_complete_callback(struct fw_packet *packet, |
78 | struct fw_card *card, int status) | 110 | struct fw_card *card, int status) |
@@ -162,6 +194,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel, | |||
162 | 194 | ||
163 | packet->speed = speed; | 195 | packet->speed = speed; |
164 | packet->generation = generation; | 196 | packet->generation = generation; |
197 | packet->ack = 0; | ||
165 | } | 198 | } |
166 | 199 | ||
167 | /** | 200 | /** |
@@ -298,8 +331,14 @@ void fw_flush_transactions(struct fw_card *card) | |||
298 | card->tlabel_mask = 0; | 331 | card->tlabel_mask = 0; |
299 | spin_unlock_irqrestore(&card->lock, flags); | 332 | spin_unlock_irqrestore(&card->lock, flags); |
300 | 333 | ||
301 | list_for_each_entry_safe(t, next, &list, link) | 334 | list_for_each_entry_safe(t, next, &list, link) { |
335 | card->driver->cancel_packet(card, &t->packet); | ||
336 | |||
337 | /* At this point cancel_packet will never call the | ||
338 | * transaction callback, since we just took all the | ||
339 | * transactions out of the list. So do it here.*/ | ||
302 | t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data); | 340 | t->callback(card, RCODE_CANCELLED, NULL, 0, t->callback_data); |
341 | } | ||
303 | } | 342 | } |
304 | 343 | ||
305 | static struct fw_address_handler * | 344 | static struct fw_address_handler * |
@@ -531,6 +570,7 @@ allocate_request(struct fw_packet *p) | |||
531 | request->response.speed = p->speed; | 570 | request->response.speed = p->speed; |
532 | request->response.timestamp = t; | 571 | request->response.timestamp = t; |
533 | request->response.generation = p->generation; | 572 | request->response.generation = p->generation; |
573 | request->response.ack = 0; | ||
534 | request->response.callback = free_response_callback; | 574 | request->response.callback = free_response_callback; |
535 | request->ack = p->ack; | 575 | request->ack = p->ack; |
536 | request->length = length; | 576 | request->length = length; |
diff --git a/drivers/firewire/fw-transaction.h b/drivers/firewire/fw-transaction.h index 50ec2c683a31..8f0283cf1a7a 100644 --- a/drivers/firewire/fw-transaction.h +++ b/drivers/firewire/fw-transaction.h | |||
@@ -391,6 +391,8 @@ struct fw_card_driver { | |||
391 | 391 | ||
392 | void (*send_request) (struct fw_card *card, struct fw_packet *packet); | 392 | void (*send_request) (struct fw_card *card, struct fw_packet *packet); |
393 | void (*send_response) (struct fw_card *card, struct fw_packet *packet); | 393 | void (*send_response) (struct fw_card *card, struct fw_packet *packet); |
394 | /* Calling cancel is valid once a packet has been submitted. */ | ||
395 | int (*cancel_packet) (struct fw_card *card, struct fw_packet *packet); | ||
394 | 396 | ||
395 | /* Allow the specified node ID to do direct DMA out and in of | 397 | /* Allow the specified node ID to do direct DMA out and in of |
396 | * host memory. The card will disable this for all node when | 398 | * host memory. The card will disable this for all node when |
@@ -421,6 +423,9 @@ fw_send_request(struct fw_card *card, struct fw_transaction *t, | |||
421 | void *data, size_t length, | 423 | void *data, size_t length, |
422 | fw_transaction_callback_t callback, void *callback_data); | 424 | fw_transaction_callback_t callback, void *callback_data); |
423 | 425 | ||
426 | int fw_cancel_transaction(struct fw_card *card, | ||
427 | struct fw_transaction *transaction); | ||
428 | |||
424 | void fw_flush_transactions(struct fw_card *card); | 429 | void fw_flush_transactions(struct fw_card *card); |
425 | 430 | ||
426 | void fw_send_phy_config(struct fw_card *card, | 431 | void fw_send_phy_config(struct fw_card *card, |