diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-11-05 17:17:22 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-11-05 17:17:22 -0400 |
| commit | f69fa76482e654f7d94e4aa40ea0ebf04363396a (patch) | |
| tree | 3cb1976afa6daf39a9e45551671ecfb5bb580e08 | |
| parent | 2e5c36722d4c9c86281f25a1e963a6078c7fce6a (diff) | |
| parent | 693fa7792e9db9f32da9436e633976fbacd04b55 (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
| -rw-r--r-- | drivers/firewire/ohci.c | 88 |
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 | ||
| 580 | static int ar_context_add_page(struct ar_context *ctx) | 580 | static 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 | |||
| 605 | static 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) | |||
| 730 | static void ar_context_tasklet(unsigned long data) | 737 | static 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); |
