Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f69fa764 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
* '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
Loading
Loading
Loading
Loading
+65 −23
Original line number Original line Diff line number Diff line
@@ -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)


	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)
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)
		 */
		 */


		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;
		}

		if (rest > 0) {
			/* 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)
			while (buffer < end)
				buffer = handle_ar_packet(ctx, buffer);
				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);