Switched to tx socket buffer ring to avoid race conditions when multiple
authorFlorian Pose <fp@igh-essen.com>
Thu, 09 Aug 2007 15:45:56 +0000
changeset 693 e341f1788608
parent 692 fe7cf37c33f1
child 694 54a169382e28
Switched to tx socket buffer ring to avoid race conditions when multiple
instances call ec_device_send() in short intervals.
master/device.c
master/device.h
--- a/master/device.c	Thu Aug 09 15:01:14 2007 +0000
+++ b/master/device.c	Thu Aug 09 15:45:56 2007 +0000
@@ -69,6 +69,8 @@
         ec_master_t *master /**< master owning the device */
         )
 {
+    unsigned int i;
+    struct ethhdr *eth;
 #ifdef EC_DEBUG_IF
     char ifname[10];
     char mb = 'x';
@@ -79,6 +81,7 @@
 #endif
 
     device->master = master;
+    device->tx_ring_index = 0;
 
 #ifdef EC_DEBUG_IF
     if (device == &master->main_device)
@@ -94,29 +97,33 @@
     }
 #endif
 
-    if (!(device->tx_skb = dev_alloc_skb(ETH_FRAME_LEN))) {
-        EC_ERR("Error allocating device socket buffer!\n");
-#ifdef EC_DEBUG_IF
-        goto out_debug;
-#else
-        goto out_return;
-#endif
-    }
-
-    // add Ethernet-II-header
-    skb_reserve(device->tx_skb, ETH_HLEN);
-    device->eth = (struct ethhdr *) skb_push(device->tx_skb, ETH_HLEN);
-    device->eth->h_proto = htons(0x88A4);
-    memset(device->eth->h_dest, 0xFF, ETH_ALEN);
+    for (i = 0; i < EC_TX_RING_SIZE; i++)
+        device->tx_skb[i] = NULL;
+
+    for (i = 0; i < EC_TX_RING_SIZE; i++) {
+        if (!(device->tx_skb[i] = dev_alloc_skb(ETH_FRAME_LEN))) {
+            EC_ERR("Error allocating device socket buffer!\n");
+            goto out_tx_ring;
+        }
+
+        // add Ethernet-II-header
+        skb_reserve(device->tx_skb[i], ETH_HLEN);
+        eth = (struct ethhdr *) skb_push(device->tx_skb[i], ETH_HLEN);
+        eth->h_proto = htons(0x88A4);
+        memset(eth->h_dest, 0xFF, ETH_ALEN);
+    }
 
     ec_device_detach(device); // resets remaining fields
     return 0;
 
-#ifdef EC_DEBUG_IF
- out_debug:
+out_tx_ring:
+    for (i = 0; i < EC_TX_RING_SIZE; i++)
+        if (device->tx_skb[i])
+            dev_kfree_skb(device->tx_skb[i]);
+#ifdef EC_DEBUG_IF
     ec_debug_clear(&device->dbg);
-#endif
- out_return:
+out_return:
+#endif
     return -1;
 }
 
@@ -128,8 +135,11 @@
 
 void ec_device_clear(ec_device_t *device /**< EtherCAT device */)
 {
+    unsigned int i;
+
     if (device->open) ec_device_close(device);
-    dev_kfree_skb(device->tx_skb);
+    for (i = 0; i < EC_TX_RING_SIZE; i++)
+        dev_kfree_skb(device->tx_skb[i]);
 #ifdef EC_DEBUG_IF
     ec_debug_clear(&device->dbg);
 #endif
@@ -147,14 +157,20 @@
         struct module *module /**< the device's module */
         )
 {
+    unsigned int i;
+    struct ethhdr *eth;
+
     ec_device_detach(device); // resets fields
 
     device->dev = net_dev;
     device->poll = poll;
     device->module = module;
 
-    device->tx_skb->dev = net_dev;
-    memcpy(device->eth->h_source, net_dev->dev_addr, ETH_ALEN);
+    for (i = 0; i < EC_TX_RING_SIZE; i++) {
+        device->tx_skb[i]->dev = net_dev;
+        eth = (struct ethhdr *) (device->tx_skb[i]->data + ETH_HLEN);
+        memcpy(eth->h_source, net_dev->dev_addr, ETH_ALEN);
+    }
 }
 
 /*****************************************************************************/
@@ -165,6 +181,8 @@
 
 void ec_device_detach(ec_device_t *device /**< EtherCAT device */)
 {
+    unsigned int i;
+
     device->dev = NULL;
     device->poll = NULL;
     device->module = NULL;
@@ -172,7 +190,8 @@
     device->link_state = 0; // down
     device->tx_count = 0;
     device->rx_count = 0;
-    device->tx_skb->dev = NULL;
+    for (i = 0; i < EC_TX_RING_SIZE; i++)
+        device->tx_skb[i]->dev = NULL;
 }
 
 /*****************************************************************************/
@@ -236,7 +255,12 @@
 
 uint8_t *ec_device_tx_data(ec_device_t *device /**< EtherCAT device */)
 {
-    return device->tx_skb->data + ETH_HLEN;
+    /* cycle through socket buffers, because otherwise there is a race
+     * condition, if multiple frames are sent and the DMA is not scheduled in
+     * between. */
+    device->tx_ring_index++;
+    device->tx_ring_index %= EC_TX_RING_SIZE;
+    return device->tx_skb[device->tx_ring_index]->data + ETH_HLEN;
 }
 
 /*****************************************************************************/
@@ -251,27 +275,29 @@
                     size_t size /**< number of bytes to send */
                     )
 {
+    struct sk_buff *skb = device->tx_skb[device->tx_ring_index];
+
     if (unlikely(!device->link_state)) // Link down
         return;
 
     // set the right length for the data
-    device->tx_skb->len = ETH_HLEN + size;
+    skb->len = ETH_HLEN + size;
 
     if (unlikely(device->master->debug_level > 1)) {
         EC_DBG("sending frame:\n");
-        ec_print_data(device->tx_skb->data + ETH_HLEN, size);
-    }
-
-#ifdef EC_DEBUG_IF
-    ec_debug_send(&device->dbg, device->tx_skb->data, ETH_HLEN + size);
+        ec_print_data(skb->data + ETH_HLEN, size);
+    }
+
+#ifdef EC_DEBUG_IF
+    ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size);
 #endif
 #ifdef EC_DEBUG_RING
     ec_device_debug_ring_append(
-            device, TX, device->tx_skb->data + ETH_HLEN, size);
+            device, TX, skb->data + ETH_HLEN, size);
 #endif
 
     // start sending
-    device->dev->hard_start_xmit(device->tx_skb, device->dev);
+    device->dev->hard_start_xmit(skb, device->dev);
     device->tx_count++;
 }
 
@@ -389,7 +415,7 @@
 
     if (unlikely(device->master->debug_level > 1)) {
         EC_DBG("Received frame:\n");
-        ec_print_data_diff(device->tx_skb->data + ETH_HLEN, ec_data, ec_size);
+        ec_print_data(ec_data, ec_size);
     }
 
 #ifdef EC_DEBUG_IF
--- a/master/device.h	Thu Aug 09 15:01:14 2007 +0000
+++ b/master/device.h	Thu Aug 09 15:45:56 2007 +0000
@@ -47,6 +47,8 @@
 #include "../devices/ecdev.h"
 #include "globals.h"
 
+#define EC_TX_RING_SIZE 2
+
 #ifdef EC_DEBUG_IF
 #include "debug.h"
 #endif
@@ -84,8 +86,8 @@
     struct module *module; /**< pointer to the device's owning module */
     uint8_t open; /**< true, if the net_device has been opened */
     uint8_t link_state; /**< device link state */
-    struct sk_buff *tx_skb; /**< transmit socket buffer */
-    struct ethhdr *eth; /**< pointer to ethernet header in socket buffer */
+    struct sk_buff *tx_skb[EC_TX_RING_SIZE]; /**< transmit skb ring */
+    unsigned int tx_ring_index;
     cycles_t cycles_poll; /**< cycles of last poll */
 #ifdef EC_DEBUG_RING
     struct timeval timeval_poll;