Commit 18d62711 authored by Clemens Ladisch's avatar Clemens Ladisch Committed by Stefan Richter

firewire: prevent dropping of completed iso packet header data

The buffer for the header data of completed iso packets has a fixed
size, so it is possible to configure a stream with a big interval
between interrupt packets or with big headers so that this buffer would
overflow.  Previously, ohci.c would drop any data that would not fit,
but this could make unsuspecting applications believe that fewer than
the actual number of packets have completed.

Instead of dropping data, add calls to flush_iso_completion() so that
there are as many events as needed to report all of the data.
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
Signed-off-by: default avatarStefan Richter <stefanr@s5r6.in-berlin.de>
parent 910e76c6
...@@ -51,7 +51,7 @@ ...@@ -51,7 +51,7 @@
/* /*
* ABI version history is documented in linux/firewire-cdev.h. * ABI version history is documented in linux/firewire-cdev.h.
*/ */
#define FW_CDEV_KERNEL_VERSION 4 #define FW_CDEV_KERNEL_VERSION 5
#define FW_CDEV_VERSION_EVENT_REQUEST2 4 #define FW_CDEV_VERSION_EVENT_REQUEST2 4
#define FW_CDEV_VERSION_ALLOCATE_REGION_END 4 #define FW_CDEV_VERSION_ALLOCATE_REGION_END 4
......
...@@ -2689,7 +2689,7 @@ static void copy_iso_headers(struct iso_context *ctx, const u32 *dma_hdr) ...@@ -2689,7 +2689,7 @@ static void copy_iso_headers(struct iso_context *ctx, const u32 *dma_hdr)
u32 *ctx_hdr; u32 *ctx_hdr;
if (ctx->header_length + ctx->base.header_size > PAGE_SIZE) if (ctx->header_length + ctx->base.header_size > PAGE_SIZE)
return; flush_iso_completions(ctx);
ctx_hdr = ctx->header + ctx->header_length; ctx_hdr = ctx->header + ctx->header_length;
ctx->last_timestamp = (u16)le32_to_cpu((__force __le32)dma_hdr[0]); ctx->last_timestamp = (u16)le32_to_cpu((__force __le32)dma_hdr[0]);
...@@ -2826,16 +2826,16 @@ static int handle_it_packet(struct context *context, ...@@ -2826,16 +2826,16 @@ static int handle_it_packet(struct context *context,
sync_it_packet_for_cpu(context, d); sync_it_packet_for_cpu(context, d);
if (ctx->header_length + 4 < PAGE_SIZE) { if (ctx->header_length + 4 > PAGE_SIZE)
flush_iso_completions(ctx);
ctx_hdr = ctx->header + ctx->header_length; ctx_hdr = ctx->header + ctx->header_length;
ctx->last_timestamp = le16_to_cpu(last->res_count);
/* Present this value as big-endian to match the receive code */ /* Present this value as big-endian to match the receive code */
*ctx_hdr = cpu_to_be32( *ctx_hdr = cpu_to_be32((le16_to_cpu(pd->transfer_status) << 16) |
((u32)le16_to_cpu(pd->transfer_status) << 16) |
le16_to_cpu(pd->res_count)); le16_to_cpu(pd->res_count));
ctx->header_length += 4; ctx->header_length += 4;
}
ctx->last_timestamp = le16_to_cpu(last->res_count);
if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS)) if (last->control & cpu_to_le16(DESCRIPTOR_IRQ_ALWAYS))
flush_iso_completions(ctx); flush_iso_completions(ctx);
......
...@@ -207,12 +207,15 @@ struct fw_cdev_event_request2 { ...@@ -207,12 +207,15 @@ struct fw_cdev_event_request2 {
* @closure: See &fw_cdev_event_common; * @closure: See &fw_cdev_event_common;
* set by %FW_CDEV_CREATE_ISO_CONTEXT ioctl * set by %FW_CDEV_CREATE_ISO_CONTEXT ioctl
* @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_ISO_INTERRUPT * @type: See &fw_cdev_event_common; always %FW_CDEV_EVENT_ISO_INTERRUPT
* @cycle: Cycle counter of the interrupt packet * @cycle: Cycle counter of the last completed packet
* @header_length: Total length of following headers, in bytes * @header_length: Total length of following headers, in bytes
* @header: Stripped headers, if any * @header: Stripped headers, if any
* *
* This event is sent when the controller has completed an &fw_cdev_iso_packet * This event is sent when the controller has completed an &fw_cdev_iso_packet
* with the %FW_CDEV_ISO_INTERRUPT bit set. * with the %FW_CDEV_ISO_INTERRUPT bit set, or when there have been so many
* completed packets without the interrupt bit set that the kernel's internal
* buffer for @header is about to overflow. (In the latter case, kernels with
* ABI version < 5 drop header data up to the next interrupt packet.)
* *
* Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT): * Isochronous transmit events (context type %FW_CDEV_ISO_CONTEXT_TRANSMIT):
* *
...@@ -440,6 +443,8 @@ union fw_cdev_event { ...@@ -440,6 +443,8 @@ union fw_cdev_event {
* - added %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL, * - added %FW_CDEV_EVENT_ISO_INTERRUPT_MULTICHANNEL,
* %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL, and * %FW_CDEV_ISO_CONTEXT_RECEIVE_MULTICHANNEL, and
* %FW_CDEV_IOC_SET_ISO_CHANNELS * %FW_CDEV_IOC_SET_ISO_CHANNELS
* 5 (3.4) - send %FW_CDEV_EVENT_ISO_INTERRUPT events when needed to
* avoid dropping data
*/ */
/** /**
......
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