firewire: optimize iso queueing by setting wake only after the last packet
authorClemens Ladisch <clemens@ladisch.de>
Mon, 2 May 2011 07:33:56 +0000 (09:33 +0200)
committerStefan Richter <stefanr@s5r6.in-berlin.de>
Tue, 10 May 2011 20:53:45 +0000 (22:53 +0200)
When queueing iso packets, the run time is dominated by the two
MMIO accesses that set the DMA context's wake bit.  Because most
drivers submit packets in batches, we can save much time by
removing all but the last wakeup.

The internal kernel API is changed to require a call to
fw_iso_context_queue_flush() after a batch of queued packets.
The user space API does not change, so one call to
FW_CDEV_IOC_QUEUE_ISO must specify multiple packets to take
advantage of this optimization.

In my measurements, this patch reduces the time needed to queue
fifty skip packets from userspace to one sixth on a 2.5 GHz CPU,
or to one third at 800 MHz.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Stefan Richter <stefanr@s5r6.in-berlin.de>
drivers/firewire/core-card.c
drivers/firewire/core-cdev.c
drivers/firewire/core-iso.c
drivers/firewire/core.h
drivers/firewire/net.c
drivers/firewire/ohci.c
drivers/media/dvb/firewire/firedtv-fw.c
include/linux/firewire.h
sound/firewire/amdtp.c

index e119f1e6ba47900cdfe13191a3939b61f55bfa78..f05fc7bfceeb09eec1f19cec0ede696087d1fa00 100644 (file)
@@ -630,6 +630,10 @@ static int dummy_queue_iso(struct fw_iso_context *ctx, struct fw_iso_packet *p,
        return -ENODEV;
 }
 
+static void dummy_flush_queue_iso(struct fw_iso_context *ctx)
+{
+}
+
 static const struct fw_card_driver dummy_driver_template = {
        .read_phy_reg           = dummy_read_phy_reg,
        .update_phy_reg         = dummy_update_phy_reg,
@@ -641,6 +645,7 @@ static const struct fw_card_driver dummy_driver_template = {
        .start_iso              = dummy_start_iso,
        .set_iso_channels       = dummy_set_iso_channels,
        .queue_iso              = dummy_queue_iso,
+       .flush_queue_iso        = dummy_flush_queue_iso,
 };
 
 void fw_card_release(struct kref *kref)
index 2a3f1c4d6906cd9316a4a333570099e50e9f9171..64768c2194f1f8b2ad094934c5addb9025f24cb5 100644 (file)
@@ -1107,6 +1107,7 @@ static int ioctl_queue_iso(struct client *client, union ioctl_arg *arg)
                payload += u.packet.payload_length;
                count++;
        }
+       fw_iso_context_queue_flush(ctx);
 
        a->size    -= uptr_to_u64(p) - a->packets;
        a->packets  = uptr_to_u64(p);
index f872ede5af37768daecd79a9d95120aa5942fa8d..57c3973093ad13ab49355f6af6e9c38f05a5bcae 100644 (file)
@@ -185,6 +185,12 @@ int fw_iso_context_queue(struct fw_iso_context *ctx,
 }
 EXPORT_SYMBOL(fw_iso_context_queue);
 
+void fw_iso_context_queue_flush(struct fw_iso_context *ctx)
+{
+       ctx->card->driver->flush_queue_iso(ctx);
+}
+EXPORT_SYMBOL(fw_iso_context_queue_flush);
+
 int fw_iso_context_stop(struct fw_iso_context *ctx)
 {
        return ctx->card->driver->stop_iso(ctx);
index 25e729cde2f74262b6caae39a4f51e6138e64b66..0fe4e4e6eda72a696596c7dbcce1a7f0f6538569 100644 (file)
@@ -97,6 +97,8 @@ struct fw_card_driver {
                         struct fw_iso_buffer *buffer,
                         unsigned long payload);
 
+       void (*flush_queue_iso)(struct fw_iso_context *ctx);
+
        int (*stop_iso)(struct fw_iso_context *ctx);
 };
 
index 3f04dd3681cf7698bf5b8f377db07e84fb8c726c..b9762d07198d3a6fb4383ebc3fcc60c17c4a17c0 100644 (file)
@@ -881,7 +881,9 @@ static void fwnet_receive_broadcast(struct fw_iso_context *context,
 
        spin_unlock_irqrestore(&dev->lock, flags);
 
-       if (retval < 0)
+       if (retval >= 0)
+               fw_iso_context_queue_flush(dev->broadcast_rcv_context);
+       else
                fw_error("requeue failed\n");
 }
 
index f9f55703375edb4c7ba9545b58c83d009a68f265..438e6c83117087d8d10c13418ff3a68edf9314ff 100644 (file)
@@ -1192,9 +1192,6 @@ static void context_append(struct context *ctx,
        wmb(); /* finish init of new descriptors before branch_address update */
        ctx->prev->branch_address = cpu_to_le32(d_bus | z);
        ctx->prev = find_branch_descriptor(d, z);
-
-       reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
-       flush_writes(ctx->ohci);
 }
 
 static void context_stop(struct context *ctx)
@@ -1348,8 +1345,12 @@ static int at_context_queue_packet(struct context *ctx,
 
        context_append(ctx, d, z, 4 - z);
 
-       if (!ctx->running)
+       if (ctx->running) {
+               reg_write(ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
+               flush_writes(ohci);
+       } else {
                context_run(ctx, 0);
+       }
 
        return 0;
 }
@@ -3121,6 +3122,15 @@ static int ohci_queue_iso(struct fw_iso_context *base,
        return ret;
 }
 
+static void ohci_flush_queue_iso(struct fw_iso_context *base)
+{
+       struct context *ctx =
+                       &container_of(base, struct iso_context, base)->context;
+
+       reg_write(ctx->ohci, CONTROL_SET(ctx->regs), CONTEXT_WAKE);
+       flush_writes(ctx->ohci);
+}
+
 static const struct fw_card_driver ohci_driver = {
        .enable                 = ohci_enable,
        .read_phy_reg           = ohci_read_phy_reg,
@@ -3137,6 +3147,7 @@ static const struct fw_card_driver ohci_driver = {
        .free_iso_context       = ohci_free_iso_context,
        .set_iso_channels       = ohci_set_iso_channels,
        .queue_iso              = ohci_queue_iso,
+       .flush_queue_iso        = ohci_flush_queue_iso,
        .start_iso              = ohci_start_iso,
        .stop_iso               = ohci_stop_iso,
 };
index 8022b743af91a6ff0cba565b9b9438296ed5e1ef..864b6274c72901b9f02323138355a0a44869baea 100644 (file)
@@ -125,6 +125,7 @@ static void handle_iso(struct fw_iso_context *context, u32 cycle,
 
                i = (i + 1) & (N_PACKETS - 1);
        }
+       fw_iso_context_queue_flush(ctx->context);
        ctx->current_packet = i;
 }
 
index de90e1ff848854d61b556a71e28de9b5572989c8..c0fb405bb4359996bdec76d78ab57be12476c236 100644 (file)
@@ -440,6 +440,7 @@ int fw_iso_context_queue(struct fw_iso_context *ctx,
                         struct fw_iso_packet *packet,
                         struct fw_iso_buffer *buffer,
                         unsigned long payload);
+void fw_iso_context_queue_flush(struct fw_iso_context *ctx);
 int fw_iso_context_start(struct fw_iso_context *ctx,
                         int cycle, int sync, int tags);
 int fw_iso_context_stop(struct fw_iso_context *ctx);
index b18140ff2b93c48eb2a0dea6074e866a8340b56a..87657dd7714ccccb59f4922f12a880680b7838c8 100644 (file)
@@ -396,6 +396,7 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
 
        for (i = 0; i < packets; ++i)
                queue_out_packet(s, ++cycle);
+       fw_iso_context_queue_flush(s->context);
 }
 
 static int queue_initial_skip_packets(struct amdtp_out_stream *s)