aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire/ohci.c
diff options
context:
space:
mode:
authorClemens Ladisch <clemens@ladisch.de>2010-10-25 05:42:20 -0400
committerStefan Richter <stefanr@s5r6.in-berlin.de>2010-10-30 17:37:19 -0400
commita1f805e5e73a8fe166b71c6592d3837df0cd5e2e (patch)
treef8bc4a2d8d0bcaf4a1b7d4c547d7a2525459f852 /drivers/firewire/ohci.c
parent85f7ffd5d2b320f73912b15fe8cef34bae297daf (diff)
firewire: ohci: fix race in AR split packet handling
When handling an AR buffer that has been completely filled, we assumed that its descriptor will not be read by the controller and can be overwritten. However, when the last received packet happens to end at the end of the buffer, the controller might not yet have moved on to the next buffer and might read the branch address later. If we overwrite and free the page before that, the DMA context will either go dead because of an invalid Z value, or go off into some random memory. To fix this, ensure that the descriptor does not get overwritten by using only the actual buffer instead of the entire page for reassembling the split packet. Furthermore, to avoid freeing the page too early, move on to the next buffer only when some data in it guarantees that the controller has moved on. This should eliminate the remaining firewire-net problems. Signed-off-by: Clemens Ladisch <clemens@ladisch.de> Cc: 2.6.22-2.6.36 <stable@kernel.org> Tested-by: Maxim Levitsky <maximlevitsky@gmail.com> Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Diffstat (limited to 'drivers/firewire/ohci.c')
-rw-r--r--drivers/firewire/ohci.c35
1 files changed, 21 insertions, 14 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 5826ae333b19..7570b71a2453 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -750,20 +750,19 @@ static void ar_context_tasklet(unsigned long data)
750 */ 750 */
751 751
752 offset = offsetof(struct ar_buffer, data); 752 offset = offsetof(struct ar_buffer, data);
753 start = buffer = ab; 753 start = ab;
754 start_bus = le32_to_cpu(ab->descriptor.data_address) - offset; 754 start_bus = le32_to_cpu(ab->descriptor.data_address) - offset;
755 buffer = ab->data;
755 756
756 ab = ab->next; 757 ab = ab->next;
757 d = &ab->descriptor; 758 d = &ab->descriptor;
758 size = buffer + PAGE_SIZE - ctx->pointer; 759 size = start + PAGE_SIZE - ctx->pointer;
759 /* valid buffer data in the next page */ 760 /* valid buffer data in the next page */
760 rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count); 761 rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count);
761 /* what actually fits in this page */ 762 /* what actually fits in this page */
762 size2 = min(rest, (size_t)PAGE_SIZE - size); 763 size2 = min(rest, (size_t)PAGE_SIZE - offset - size);
763 memmove(buffer, ctx->pointer, size); 764 memmove(buffer, ctx->pointer, size);
764 memcpy(buffer + size, ab->data, size2); 765 memcpy(buffer + size, ab->data, size2);
765 ctx->current_buffer = ab;
766 ctx->pointer = (void *) ab->data + rest;
767 766
768 while (size > 0) { 767 while (size > 0) {
769 void *next = handle_ar_packet(ctx, buffer); 768 void *next = handle_ar_packet(ctx, buffer);
@@ -782,22 +781,30 @@ static void ar_context_tasklet(unsigned long data)
782 size -= pktsize; 781 size -= pktsize;
783 /* fill up this page again */ 782 /* fill up this page again */
784 size3 = min(rest - size2, 783 size3 = min(rest - size2,
785 (size_t)PAGE_SIZE - size - size2); 784 (size_t)PAGE_SIZE - offset - size - size2);
786 memcpy(buffer + size + size2, 785 memcpy(buffer + size + size2,
787 (void *) ab->data + size2, size3); 786 (void *) ab->data + size2, size3);
788 size2 += size3; 787 size2 += size3;
789 } 788 }
790 789
791 /* handle the packets that are fully in the next page */ 790 if (rest > 0) {
792 buffer = (void *) ab->data + (buffer - (start + size)); 791 /* handle the packets that are fully in the next page */
793 end = (void *) ab->data + rest; 792 buffer = (void *) ab->data +
793 (buffer - (start + offset + size));
794 end = (void *) ab->data + rest;
794 795
795 while (buffer < end) 796 while (buffer < end)
796 buffer = handle_ar_packet(ctx, buffer); 797 buffer = handle_ar_packet(ctx, buffer);
798
799 ctx->current_buffer = ab;
800 ctx->pointer = end;
797 801
798 dma_free_coherent(ohci->card.device, PAGE_SIZE, 802 dma_free_coherent(ohci->card.device, PAGE_SIZE,
799 start, start_bus); 803 start, start_bus);
800 ar_context_add_page(ctx); 804 ar_context_add_page(ctx);
805 } else {
806 ctx->pointer = start + PAGE_SIZE;
807 }
801 } else { 808 } else {
802 buffer = ctx->pointer; 809 buffer = ctx->pointer;
803 ctx->pointer = end = 810 ctx->pointer = end =