aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKristian Høgsberg <krh@redhat.com>2007-02-06 14:49:32 -0500
committerStefan Richter <stefanr@s5r6.in-berlin.de>2007-03-09 16:02:51 -0500
commit730c32f58ba81b3a4fe6d19c7d9e9829dd96d363 (patch)
tree79149d002b095ca27582d4d7ef00c8eefec67170
parent72e318e07e1fa9840bfdd5788421fc6dc51a93de (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>
-rw-r--r--drivers/firewire/fw-card.c7
-rw-r--r--drivers/firewire/fw-ohci.c33
-rw-r--r--drivers/firewire/fw-sbp2.c3
-rw-r--r--drivers/firewire/fw-transaction.c54
-rw-r--r--drivers/firewire/fw-transaction.h5
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
477static int 477static int
478dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
479{
480 return -ENOENT;
481}
482
483static int
478dummy_enable_phys_dma(struct fw_card *card, 484dummy_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
1024static 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
1015static int 1047static int
1016ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation) 1048ohci_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
62static void 62static int
63close_transaction(struct fw_transaction *t, struct fw_card *card, int rcode, 63close_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). */
90int
91fw_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}
106EXPORT_SYMBOL(fw_cancel_transaction);
107
76static void 108static void
77transmit_complete_callback(struct fw_packet *packet, 109transmit_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
305static struct fw_address_handler * 344static 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
426int fw_cancel_transaction(struct fw_card *card,
427 struct fw_transaction *transaction);
428
424void fw_flush_transactions(struct fw_card *card); 429void fw_flush_transactions(struct fw_card *card);
425 430
426void fw_send_phy_config(struct fw_card *card, 431void fw_send_phy_config(struct fw_card *card,