firewire: fw-ohci: fix IOMMU resource exhaustion
authorStefan Richter <stefanr@s5r6.in-berlin.de>
Tue, 16 Dec 2008 19:34:23 +0000 (20:34 +0100)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 18 Dec 2008 17:13:38 +0000 (09:13 -0800)
commit 1d1dc5e83f3299c108a4e44d58cc4bfef48c876a upstream.

There is a DMA map/ unmap imbalance whenever a block write request
packet is sent and then dequeued with ohci_cancel_packet.  The latter
may happen frequently if the AR resp tasklet is executed before the AT
req tasklet for the same transaction.

Add the missing dma_unmap_single.  This fixes
https://bugzilla.redhat.com/show_bug.cgi?id=475156

Reported-by: Emmanuel Kowalski
Tested-by: Emmanuel Kowalski
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/firewire/fw-ohci.c
drivers/firewire/fw-transaction.c
drivers/firewire/fw-transaction.h

index 251416f2148f36c72cdd8afa0ac74a792c7db5fa..595759c97f524cd1ff881e8828bb19fbbc358421 100644 (file)
@@ -958,6 +958,7 @@ at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
                        packet->ack = RCODE_SEND_ERROR;
                        return -1;
                }
+               packet->payload_bus = payload_bus;
 
                d[2].req_count    = cpu_to_le16(packet->payload_length);
                d[2].data_address = cpu_to_le32(payload_bus);
@@ -1009,7 +1010,6 @@ static int handle_at_packet(struct context *context,
        struct driver_data *driver_data;
        struct fw_packet *packet;
        struct fw_ohci *ohci = context->ohci;
-       dma_addr_t payload_bus;
        int evt;
 
        if (last->transfer_status == 0)
@@ -1022,9 +1022,8 @@ static int handle_at_packet(struct context *context,
                /* This packet was cancelled, just continue. */
                return 1;
 
-       payload_bus = le32_to_cpu(last->data_address);
-       if (payload_bus != 0)
-               dma_unmap_single(ohci->card.device, payload_bus,
+       if (packet->payload_bus)
+               dma_unmap_single(ohci->card.device, packet->payload_bus,
                                 packet->payload_length, DMA_TO_DEVICE);
 
        evt = le16_to_cpu(last->transfer_status) & 0x1f;
@@ -1681,6 +1680,10 @@ static int ohci_cancel_packet(struct fw_card *card, struct fw_packet *packet)
        if (packet->ack != 0)
                goto out;
 
+       if (packet->payload_bus)
+               dma_unmap_single(ohci->card.device, packet->payload_bus,
+                                packet->payload_length, DMA_TO_DEVICE);
+
        log_ar_at_event('T', packet->speed, packet->header, 0x20);
        driver_data->packet = NULL;
        packet->ack = RCODE_CANCELLED;
index e5d1a0b64fcf1b714a186d31a8b7aabf90e7aa97..27f7c7e7b68da8a3b5a44413ed1603738d044127 100644 (file)
@@ -207,6 +207,7 @@ fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
        packet->speed = speed;
        packet->generation = generation;
        packet->ack = 0;
+       packet->payload_bus = 0;
 }
 
 /**
@@ -541,6 +542,8 @@ fw_fill_response(struct fw_packet *response, u32 *request_header,
                BUG();
                return;
        }
+
+       response->payload_bus = 0;
 }
 EXPORT_SYMBOL(fw_fill_response);
 
index 81b15ba91d3198bc1ef0241f2d02a45494e5b73a..04cc9b00119e04285a078a7655c924b0b59a7cef 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/list.h>
 #include <linux/spinlock_types.h>
 #include <linux/timer.h>
+#include <linux/types.h>
 #include <linux/workqueue.h>
 
 #define TCODE_IS_READ_REQUEST(tcode)   (((tcode) & ~1) == 4)
@@ -153,6 +154,7 @@ struct fw_packet {
        size_t header_length;
        void *payload;
        size_t payload_length;
+       dma_addr_t payload_bus;
        u32 timestamp;
 
        /*