aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firewire
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2010-11-05 17:17:22 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2010-11-05 17:17:22 -0400
commitf69fa76482e654f7d94e4aa40ea0ebf04363396a (patch)
tree3cb1976afa6daf39a9e45551671ecfb5bb580e08 /drivers/firewire
parent2e5c36722d4c9c86281f25a1e963a6078c7fce6a (diff)
parent693fa7792e9db9f32da9436e633976fbacd04b55 (diff)
Merge branch 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6
* 'fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/ieee1394/linux1394-2.6: firewire: ohci: fix race when reading count in AR descriptor firewire: ohci: avoid reallocation of AR buffers firewire: ohci: fix race in AR split packet handling firewire: ohci: fix buffer overflow in AR split packet handling
Diffstat (limited to 'drivers/firewire')
-rw-r--r--drivers/firewire/ohci.c88
1 files changed, 65 insertions, 23 deletions
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
index 9dcb17d51aee..84eb607d6c03 100644
--- a/drivers/firewire/ohci.c
+++ b/drivers/firewire/ohci.c
@@ -577,17 +577,11 @@ static int ohci_update_phy_reg(struct fw_card *card, int addr,
577 return ret; 577 return ret;
578} 578}
579 579
580static int ar_context_add_page(struct ar_context *ctx) 580static void ar_context_link_page(struct ar_context *ctx,
581 struct ar_buffer *ab, dma_addr_t ab_bus)
581{ 582{
582 struct device *dev = ctx->ohci->card.device;
583 struct ar_buffer *ab;
584 dma_addr_t uninitialized_var(ab_bus);
585 size_t offset; 583 size_t offset;
586 584
587 ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
588 if (ab == NULL)
589 return -ENOMEM;
590
591 ab->next = NULL; 585 ab->next = NULL;
592 memset(&ab->descriptor, 0, sizeof(ab->descriptor)); 586 memset(&ab->descriptor, 0, sizeof(ab->descriptor));
593 ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | 587 ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE |
@@ -606,6 +600,19 @@ static int ar_context_add_page(struct ar_context *ctx)
606 600
607 reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); 601 reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
608 flush_writes(ctx->ohci); 602 flush_writes(ctx->ohci);
603}
604
605static int ar_context_add_page(struct ar_context *ctx)
606{
607 struct device *dev = ctx->ohci->card.device;
608 struct ar_buffer *ab;
609 dma_addr_t uninitialized_var(ab_bus);
610
611 ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
612 if (ab == NULL)
613 return -ENOMEM;
614
615 ar_context_link_page(ctx, ab, ab_bus);
609 616
610 return 0; 617 return 0;
611} 618}
@@ -730,16 +737,17 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
730static void ar_context_tasklet(unsigned long data) 737static void ar_context_tasklet(unsigned long data)
731{ 738{
732 struct ar_context *ctx = (struct ar_context *)data; 739 struct ar_context *ctx = (struct ar_context *)data;
733 struct fw_ohci *ohci = ctx->ohci;
734 struct ar_buffer *ab; 740 struct ar_buffer *ab;
735 struct descriptor *d; 741 struct descriptor *d;
736 void *buffer, *end; 742 void *buffer, *end;
743 __le16 res_count;
737 744
738 ab = ctx->current_buffer; 745 ab = ctx->current_buffer;
739 d = &ab->descriptor; 746 d = &ab->descriptor;
740 747
741 if (d->res_count == 0) { 748 res_count = ACCESS_ONCE(d->res_count);
742 size_t size, rest, offset; 749 if (res_count == 0) {
750 size_t size, size2, rest, pktsize, size3, offset;
743 dma_addr_t start_bus; 751 dma_addr_t start_bus;
744 void *start; 752 void *start;
745 753
@@ -750,29 +758,63 @@ static void ar_context_tasklet(unsigned long data)
750 */ 758 */
751 759
752 offset = offsetof(struct ar_buffer, data); 760 offset = offsetof(struct ar_buffer, data);
753 start = buffer = ab; 761 start = ab;
754 start_bus = le32_to_cpu(ab->descriptor.data_address) - offset; 762 start_bus = le32_to_cpu(ab->descriptor.data_address) - offset;
763 buffer = ab->data;
755 764
756 ab = ab->next; 765 ab = ab->next;
757 d = &ab->descriptor; 766 d = &ab->descriptor;
758 size = buffer + PAGE_SIZE - ctx->pointer; 767 size = start + PAGE_SIZE - ctx->pointer;
768 /* valid buffer data in the next page */
759 rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count); 769 rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count);
770 /* what actually fits in this page */
771 size2 = min(rest, (size_t)PAGE_SIZE - offset - size);
760 memmove(buffer, ctx->pointer, size); 772 memmove(buffer, ctx->pointer, size);
761 memcpy(buffer + size, ab->data, rest); 773 memcpy(buffer + size, ab->data, size2);
762 ctx->current_buffer = ab; 774
763 ctx->pointer = (void *) ab->data + rest; 775 while (size > 0) {
764 end = buffer + size + rest; 776 void *next = handle_ar_packet(ctx, buffer);
777 pktsize = next - buffer;
778 if (pktsize >= size) {
779 /*
780 * We have handled all the data that was
781 * originally in this page, so we can now
782 * continue in the next page.
783 */
784 buffer = next;
785 break;
786 }
787 /* move the next packet to the start of the buffer */
788 memmove(buffer, next, size + size2 - pktsize);
789 size -= pktsize;
790 /* fill up this page again */
791 size3 = min(rest - size2,
792 (size_t)PAGE_SIZE - offset - size - size2);
793 memcpy(buffer + size + size2,
794 (void *) ab->data + size2, size3);
795 size2 += size3;
796 }
765 797
766 while (buffer < end) 798 if (rest > 0) {
767 buffer = handle_ar_packet(ctx, buffer); 799 /* handle the packets that are fully in the next page */
800 buffer = (void *) ab->data +
801 (buffer - (start + offset + size));
802 end = (void *) ab->data + rest;
803
804 while (buffer < end)
805 buffer = handle_ar_packet(ctx, buffer);
768 806
769 dma_free_coherent(ohci->card.device, PAGE_SIZE, 807 ctx->current_buffer = ab;
770 start, start_bus); 808 ctx->pointer = end;
771 ar_context_add_page(ctx); 809
810 ar_context_link_page(ctx, start, start_bus);
811 } else {
812 ctx->pointer = start + PAGE_SIZE;
813 }
772 } else { 814 } else {
773 buffer = ctx->pointer; 815 buffer = ctx->pointer;
774 ctx->pointer = end = 816 ctx->pointer = end =
775 (void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count); 817 (void *) ab + PAGE_SIZE - le16_to_cpu(res_count);
776 818
777 while (buffer < end) 819 while (buffer < end)
778 buffer = handle_ar_packet(ctx, buffer); 820 buffer = handle_ar_packet(ctx, buffer);