Commit f69fa764 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 2e5c3672 693fa779
...@@ -577,17 +577,11 @@ static int ohci_update_phy_reg(struct fw_card *card, int addr, ...@@ -577,17 +577,11 @@ static int ohci_update_phy_reg(struct fw_card *card, int addr,
return ret; return ret;
} }
static int ar_context_add_page(struct ar_context *ctx) static void ar_context_link_page(struct ar_context *ctx,
struct ar_buffer *ab, dma_addr_t ab_bus)
{ {
struct device *dev = ctx->ohci->card.device;
struct ar_buffer *ab;
dma_addr_t uninitialized_var(ab_bus);
size_t offset; size_t offset;
ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
if (ab == NULL)
return -ENOMEM;
ab->next = NULL; ab->next = NULL;
memset(&ab->descriptor, 0, sizeof(ab->descriptor)); memset(&ab->descriptor, 0, sizeof(ab->descriptor));
ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE | ab->descriptor.control = cpu_to_le16(DESCRIPTOR_INPUT_MORE |
...@@ -606,6 +600,19 @@ static int ar_context_add_page(struct ar_context *ctx) ...@@ -606,6 +600,19 @@ static int ar_context_add_page(struct ar_context *ctx)
reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE); reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
flush_writes(ctx->ohci); flush_writes(ctx->ohci);
}
static int ar_context_add_page(struct ar_context *ctx)
{
struct device *dev = ctx->ohci->card.device;
struct ar_buffer *ab;
dma_addr_t uninitialized_var(ab_bus);
ab = dma_alloc_coherent(dev, PAGE_SIZE, &ab_bus, GFP_ATOMIC);
if (ab == NULL)
return -ENOMEM;
ar_context_link_page(ctx, ab, ab_bus);
return 0; return 0;
} }
...@@ -730,16 +737,17 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer) ...@@ -730,16 +737,17 @@ static __le32 *handle_ar_packet(struct ar_context *ctx, __le32 *buffer)
static void ar_context_tasklet(unsigned long data) static void ar_context_tasklet(unsigned long data)
{ {
struct ar_context *ctx = (struct ar_context *)data; struct ar_context *ctx = (struct ar_context *)data;
struct fw_ohci *ohci = ctx->ohci;
struct ar_buffer *ab; struct ar_buffer *ab;
struct descriptor *d; struct descriptor *d;
void *buffer, *end; void *buffer, *end;
__le16 res_count;
ab = ctx->current_buffer; ab = ctx->current_buffer;
d = &ab->descriptor; d = &ab->descriptor;
if (d->res_count == 0) { res_count = ACCESS_ONCE(d->res_count);
size_t size, rest, offset; if (res_count == 0) {
size_t size, size2, rest, pktsize, size3, offset;
dma_addr_t start_bus; dma_addr_t start_bus;
void *start; void *start;
...@@ -750,29 +758,63 @@ static void ar_context_tasklet(unsigned long data) ...@@ -750,29 +758,63 @@ static void ar_context_tasklet(unsigned long data)
*/ */
offset = offsetof(struct ar_buffer, data); offset = offsetof(struct ar_buffer, data);
start = buffer = ab; start = ab;
start_bus = le32_to_cpu(ab->descriptor.data_address) - offset; start_bus = le32_to_cpu(ab->descriptor.data_address) - offset;
buffer = ab->data;
ab = ab->next; ab = ab->next;
d = &ab->descriptor; d = &ab->descriptor;
size = buffer + PAGE_SIZE - ctx->pointer; size = start + PAGE_SIZE - ctx->pointer;
/* valid buffer data in the next page */
rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count); rest = le16_to_cpu(d->req_count) - le16_to_cpu(d->res_count);
/* what actually fits in this page */
size2 = min(rest, (size_t)PAGE_SIZE - offset - size);
memmove(buffer, ctx->pointer, size); memmove(buffer, ctx->pointer, size);
memcpy(buffer + size, ab->data, rest); memcpy(buffer + size, ab->data, size2);
ctx->current_buffer = ab;
ctx->pointer = (void *) ab->data + rest; while (size > 0) {
end = buffer + size + rest; void *next = handle_ar_packet(ctx, buffer);
pktsize = next - buffer;
if (pktsize >= size) {
/*
* We have handled all the data that was
* originally in this page, so we can now
* continue in the next page.
*/
buffer = next;
break;
}
/* move the next packet to the start of the buffer */
memmove(buffer, next, size + size2 - pktsize);
size -= pktsize;
/* fill up this page again */
size3 = min(rest - size2,
(size_t)PAGE_SIZE - offset - size - size2);
memcpy(buffer + size + size2,
(void *) ab->data + size2, size3);
size2 += size3;
}
while (buffer < end) if (rest > 0) {
buffer = handle_ar_packet(ctx, buffer); /* handle the packets that are fully in the next page */
buffer = (void *) ab->data +
(buffer - (start + offset + size));
end = (void *) ab->data + rest;
while (buffer < end)
buffer = handle_ar_packet(ctx, buffer);
dma_free_coherent(ohci->card.device, PAGE_SIZE, ctx->current_buffer = ab;
start, start_bus); ctx->pointer = end;
ar_context_add_page(ctx);
ar_context_link_page(ctx, start, start_bus);
} else {
ctx->pointer = start + PAGE_SIZE;
}
} else { } else {
buffer = ctx->pointer; buffer = ctx->pointer;
ctx->pointer = end = ctx->pointer = end =
(void *) ab + PAGE_SIZE - le16_to_cpu(d->res_count); (void *) ab + PAGE_SIZE - le16_to_cpu(res_count);
while (buffer < end) while (buffer < end)
buffer = handle_ar_packet(ctx, buffer); buffer = handle_ar_packet(ctx, buffer);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment