EoE processing with kthread.
authorFlorian Pose <fp@igh-essen.com>
Tue, 30 Jun 2009 11:11:56 +0000
changeset 1489 f77a1182b6f4
parent 1488 3fb343e3fac0
child 1490 7da4e7486bbe
EoE processing with kthread.
NEWS
TODO
master/cdev.c
master/ethernet.c
master/ethernet.h
master/globals.h
master/master.c
master/master.h
--- a/NEWS	Mon Jun 29 14:27:06 2009 +0000
+++ b/NEWS	Tue Jun 30 11:11:56 2009 +0000
@@ -46,6 +46,7 @@
 * Module symbol versions file for ec_master.ko is installed to
   prefix/modules/ec_master.symvers.
 * Added 'ethercat eoe' command to display Ethernet over EtherCAT statistics.
+* Significantly improved EoE bandwidth by running EoE processing in a kthread.
 
 Changes in 1.4.0:
 
--- a/TODO	Mon Jun 29 14:27:06 2009 +0000
+++ b/TODO	Tue Jun 30 11:11:56 2009 +0000
@@ -21,6 +21,10 @@
     - Check if register 0x0980 is working, to avoid clearing it when
       configuring.
     - Create an interface to query the System Time Difference registers.
+* EoE:
+    - Only execute one EoE handler per cycle.
+    - Replace locking callbacks.
+    - Allow EoE with userspace application.
 * Implement 'ethercat foe_read --output-file ...'.
 * Fix unloading problem of ec_e100 driver.
 * Use ec_datagram_zero() where possible.
@@ -31,15 +35,11 @@
 * Limit bandwidth of state machine datagram.
 * Read alias from register 0x0012 instead of SII.
 * Finish library implementation.
-* Re-work EoE code.
-* Replace locking callbacks.
-* Implement EoE callbacks for library.
 * Rescan command.
 * Override sync manager size?
 * Remove ecrt_domain_state()?
 * Check force_config flag before error.
 * Remove allow_scanning flag.
-* Test File access over EtherCAT (FoE) reading.
 * Implement ecrt_master_slave() in kernel space.
 * Check for ioctl() interface version.
 * Improve application-triggered SDO transfers by moving the state machine into
@@ -81,7 +81,6 @@
 * Clear sync managers in INIT.
 * Read out CRC counters.
 * Configure slave ports to automatically open on link detection.
-* Only execute one EoE handler per EoE cycle.
 * Fix datagram errors on application loading/unloading.
 
 Less important issues:
--- a/master/cdev.c	Mon Jun 29 14:27:06 2009 +0000
+++ b/master/cdev.c	Tue Jun 30 11:11:56 2009 +0000
@@ -1645,9 +1645,9 @@
 	if (unlikely(!priv->requested))
 		return -EPERM;
 
-    spin_lock_bh(&master->internal_lock);
+    down(&master->io_sem);
     ecrt_master_send(master);
-    spin_unlock_bh(&master->internal_lock);
+    up(&master->io_sem);
     return 0;
 }
 
@@ -1664,9 +1664,9 @@
 	if (unlikely(!priv->requested))
 		return -EPERM;
 
-    spin_lock_bh(&master->internal_lock);
+    down(&master->io_sem);
     ecrt_master_receive(master);
-    spin_unlock_bh(&master->internal_lock);
+    up(&master->io_sem);
     return 0;
 }
 
@@ -1729,9 +1729,9 @@
 	if (unlikely(!priv->requested))
 		return -EPERM;
 
-    spin_lock_bh(&master->internal_lock);
+    down(&master->io_sem);
     ecrt_master_sync_reference_clock(master);
-    spin_unlock_bh(&master->internal_lock);
+    up(&master->io_sem);
     return 0;
 }
 
@@ -1748,9 +1748,9 @@
 	if (unlikely(!priv->requested))
 		return -EPERM;
 
-    spin_lock_bh(&master->internal_lock);
+    down(&master->io_sem);
     ecrt_master_sync_slave_clocks(master);
-    spin_unlock_bh(&master->internal_lock);
+    up(&master->io_sem);
     return 0;
 }
 
--- a/master/ethernet.c	Mon Jun 29 14:27:06 2009 +0000
+++ b/master/ethernet.c	Tue Jun 30 11:11:56 2009 +0000
@@ -52,7 +52,7 @@
  * 2 = Output actions.
  * 3 = Output actions and frame data.
  */
-#define EOE_DEBUG_LEVEL 0
+#define EOE_DEBUG_LEVEL 1
 
 /** Size of the EoE tx queue.
  */
@@ -60,7 +60,7 @@
 
 /** Number of tries.
  */
-#define EC_EOE_TRIES 10
+#define EC_EOE_TRIES 100
 
 /*****************************************************************************/
 
@@ -107,7 +107,7 @@
     eoe->tx_queue_active = 0;
     eoe->tx_queue_size = EC_EOE_TX_QUEUE_SIZE;
     eoe->tx_queued_frames = 0;
-    eoe->tx_queue_lock = SPIN_LOCK_UNLOCKED;
+    init_MUTEX(&eoe->tx_queue_sem);
     eoe->tx_frame_number = 0xFF;
     memset(&eoe->stats, 0, sizeof(struct net_device_stats));
 
@@ -116,6 +116,8 @@
     eoe->rx_rate = 0;
     eoe->tx_rate = 0;
     eoe->rate_jiffies = 0;
+    eoe->rx_idle = 1;
+    eoe->tx_idle = 1;
 
     /* device name eoe<MASTER>[as]<SLAVE>, because networking scripts don't
      * like hyphens etc. in interface names. */
@@ -206,7 +208,7 @@
 {
     ec_eoe_frame_t *frame, *next;
 
-    spin_lock_bh(&eoe->tx_queue_lock);
+    down(&eoe->tx_queue_sem);
 
     list_for_each_entry_safe(frame, next, &eoe->tx_queue, queue) {
         list_del(&frame->queue);
@@ -215,7 +217,7 @@
     }
     eoe->tx_queued_frames = 0;
 
-    spin_unlock_bh(&eoe->tx_queue_lock);
+    up(&eoe->tx_queue_sem);
 }
 
 /*****************************************************************************/
@@ -338,6 +340,18 @@
     return eoe->opened;
 }
 
+/*****************************************************************************/
+
+/** Returns the idle state.
+ *
+ * \retval 1 The device is idle.
+ * \retval 0 The device is busy.
+ */
+int ec_eoe_is_idle(const ec_eoe_t *eoe /**< EoE handler */)
+{
+    return eoe->rx_idle && eoe->tx_idle;
+}
+
 /******************************************************************************
  *  STATE PROCESSING FUNCTIONS
  *****************************************************************************/
@@ -349,8 +363,12 @@
  */
 void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */)
 {
-    if (eoe->slave->error_flag || !eoe->slave->master->main_device.link_state)
-        return;
+    if (eoe->slave->error_flag ||
+            !eoe->slave->master->main_device.link_state) {
+        eoe->rx_idle = 1;
+        eoe->tx_idle = 1;
+        return;
+    }
 
     ec_slave_mbox_prepare_check(eoe->slave, &eoe->datagram);
     eoe->queue_datagram = 1;
@@ -377,10 +395,12 @@
     }
 
     if (!ec_slave_mbox_check(&eoe->datagram)) {
+        eoe->rx_idle = 1;
         eoe->state = ec_eoe_state_tx_start;
         return;
     }
 
+    eoe->rx_idle = 0;
     ec_slave_mbox_prepare_fetch(eoe->slave, &eoe->datagram);
     eoe->queue_datagram = 1;
     eoe->state = ec_eoe_state_rx_fetch;
@@ -566,13 +586,18 @@
     unsigned int wakeup = 0;
 #endif
 
-    if (eoe->slave->error_flag || !eoe->slave->master->main_device.link_state)
-        return;
-
-    spin_lock_bh(&eoe->tx_queue_lock);
+    if (eoe->slave->error_flag ||
+            !eoe->slave->master->main_device.link_state) {
+        eoe->rx_idle = 1;
+        eoe->tx_idle = 1;
+        return;
+    }
+
+    down(&eoe->tx_queue_sem);
 
     if (!eoe->tx_queued_frames || list_empty(&eoe->tx_queue)) {
-        spin_unlock_bh(&eoe->tx_queue_lock);
+        up(&eoe->tx_queue_sem);
+        eoe->tx_idle = 1;
         // no data available.
         // start a new receive immediately.
         ec_eoe_state_rx_start(eoe);
@@ -592,7 +617,9 @@
     }
 
     eoe->tx_queued_frames--;
-    spin_unlock_bh(&eoe->tx_queue_lock);
+    up(&eoe->tx_queue_sem);
+
+    eoe->tx_idle = 0;
 
     eoe->tx_frame_number++;
     eoe->tx_frame_number %= 16;
@@ -633,15 +660,11 @@
         if (eoe->tries) {
             eoe->tries--; // try again
             eoe->queue_datagram = 1;
-#if EOE_DEBUG_LEVEL >= 1
-            EC_WARN("Failed to receive send datagram for %s. Retrying.\n",
-                    eoe->dev->name);
-#endif
         } else {
             eoe->stats.tx_errors++;
 #if EOE_DEBUG_LEVEL >= 1
-            EC_WARN("Failed to receive send datagram for %s. Giving up.\n",
-                    eoe->dev->name);
+            EC_WARN("Failed to receive send datagram for %s after %u tries.\n",
+                    eoe->dev->name, EC_EOE_TRIES);
 #endif
             eoe->state = ec_eoe_state_rx_start;
         }
@@ -652,13 +675,11 @@
         if (eoe->tries) {
             eoe->tries--; // try again
             eoe->queue_datagram = 1;
-#if EOE_DEBUG_LEVEL >= 1
-            EC_WARN("No sending response for %s. Retrying.\n", eoe->dev->name);
-#endif
         } else {
             eoe->stats.tx_errors++;
 #if EOE_DEBUG_LEVEL >= 1
-            EC_WARN("No sending response for %s. Giving up.\n", eoe->dev->name);
+            EC_WARN("No sending response for %s after %u tries.\n",
+                    eoe->dev->name, EC_EOE_TRIES);
 #endif
             eoe->state = ec_eoe_state_rx_start;
         }
@@ -700,6 +721,8 @@
     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
     ec_eoe_flush(eoe);
     eoe->opened = 1;
+    eoe->rx_idle = 0;
+    eoe->tx_idle = 0;
     netif_start_queue(dev);
     eoe->tx_queue_active = 1;
 #if EOE_DEBUG_LEVEL >= 2
@@ -717,6 +740,8 @@
 {
     ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
     netif_stop_queue(dev);
+    eoe->rx_idle = 1;
+    eoe->tx_idle = 1;
     eoe->tx_queue_active = 0;
     eoe->opened = 0;
     ec_eoe_flush(eoe);
@@ -756,14 +781,14 @@
 
     frame->skb = skb;
 
-    spin_lock_bh(&eoe->tx_queue_lock);
+    down(&eoe->tx_queue_sem);
     list_add_tail(&frame->queue, &eoe->tx_queue);
     eoe->tx_queued_frames++;
     if (eoe->tx_queued_frames == eoe->tx_queue_size) {
         netif_stop_queue(dev);
         eoe->tx_queue_active = 0;
     }
-    spin_unlock_bh(&eoe->tx_queue_lock);
+    up(&eoe->tx_queue_sem);
 
 #if EOE_DEBUG_LEVEL >= 2
     EC_DBG("EoE %s TX queued frame with %u octets (%u frames queued).\n",
--- a/master/ethernet.h	Mon Jun 29 14:27:06 2009 +0000
+++ b/master/ethernet.h	Tue Jun 30 11:11:56 2009 +0000
@@ -78,23 +78,28 @@
     struct net_device_stats stats; /**< device statistics */
     unsigned int opened; /**< net_device is opened */
     unsigned long rate_jiffies; /**< time of last rate output */
+
     struct sk_buff *rx_skb; /**< current rx socket buffer */
     off_t rx_skb_offset; /**< current write pointer in the socket buffer */
     size_t rx_skb_size; /**< size of the allocated socket buffer memory */
     uint8_t rx_expected_fragment; /**< next expected fragment number */
     uint32_t rx_counter; /**< octets received during last second */
     uint32_t rx_rate; /**< receive rate (bps) */
+    unsigned int rx_idle; /**< Idle flag. */
+
     struct list_head tx_queue; /**< queue for frames to send */
     unsigned int tx_queue_size; /**< Transmit queue size. */
     unsigned int tx_queue_active; /**< kernel netif queue started */
     unsigned int tx_queued_frames; /**< number of frames in the queue */
-    spinlock_t tx_queue_lock; /**< spinlock for the send queue */
+    struct semaphore tx_queue_sem; /**< Semaphore for the send queue. */
     ec_eoe_frame_t *tx_frame; /**< current TX frame */
     uint8_t tx_frame_number; /**< number of the transmitted frame */
     uint8_t tx_fragment_number; /**< number of the fragment */
     size_t tx_offset; /**< number of octets sent */
     uint32_t tx_counter; /**< octets transmitted during last second */
     uint32_t tx_rate; /**< transmit rate (bps) */
+    unsigned int tx_idle; /**< Idle flag. */
+
     unsigned int tries; /**< Tries. */
 };
 
@@ -105,6 +110,7 @@
 void ec_eoe_run(ec_eoe_t *);
 void ec_eoe_queue(ec_eoe_t *);
 int ec_eoe_is_open(const ec_eoe_t *);
+int ec_eoe_is_idle(const ec_eoe_t *);
 
 /*****************************************************************************/
 
--- a/master/globals.h	Mon Jun 29 14:27:06 2009 +0000
+++ b/master/globals.h	Tue Jun 30 11:11:56 2009 +0000
@@ -45,9 +45,6 @@
  * EtherCAT master
  *****************************************************************************/
 
-/** Clock frequency for the EoE state machines. */
-#define EC_EOE_FREQUENCY 1000
-
 /** Datagram timeout in microseconds. */
 #define EC_IO_TIMEOUT 500
 
--- a/master/master.c	Mon Jun 29 14:27:06 2009 +0000
+++ b/master/master.c	Tue Jun 30 11:11:56 2009 +0000
@@ -75,7 +75,7 @@
 static int ec_master_idle_thread(void *);
 static int ec_master_operation_thread(void *);
 #ifdef EC_EOE
-void ec_master_eoe_run(unsigned long);
+static int ec_master_eoe_thread(void *);
 #endif
 void ec_master_find_dc_ref_clock(ec_master_t *);
 
@@ -158,14 +158,11 @@
     master->thread = NULL;
 
 #ifdef EC_EOE
-    init_timer(&master->eoe_timer);
-    master->eoe_timer.function = ec_master_eoe_run;
-    master->eoe_timer.data = (unsigned long) master;
-    master->eoe_running = 0;
+    master->eoe_thread = NULL;
     INIT_LIST_HEAD(&master->eoe_handlers);
 #endif
 
-    master->internal_lock = SPIN_LOCK_UNLOCKED;
+    init_MUTEX(&master->io_sem);
     master->request_cb = NULL;
     master->release_cb = NULL;
     master->cb_data = NULL;
@@ -380,9 +377,10 @@
 
 /** Internal locking callback.
  */
-int ec_master_request_cb(void *master /**< callback data */)
-{
-    spin_lock(&((ec_master_t *) master)->internal_lock);
+int ec_master_request_cb(void *data /**< callback data */)
+{
+    ec_master_t *master = (ec_master_t *) data;
+    down(&master->io_sem);
     return 0;
 }
 
@@ -390,9 +388,10 @@
 
 /** Internal unlocking callback.
  */
-void ec_master_release_cb(void *master /**< callback data */)
-{
-    spin_unlock(&((ec_master_t *) master)->internal_lock);
+void ec_master_release_cb(void *data /**< callback data */)
+{
+    ec_master_t *master = (ec_master_t *) data;
+    up(&master->io_sem);
 }
 
 /*****************************************************************************/
@@ -953,9 +952,9 @@
         ec_datagram_output_stats(&master->fsm_datagram);
 
         // receive
-        spin_lock_bh(&master->internal_lock);
+        down(&master->io_sem);
         ecrt_master_receive(master);
-        spin_unlock_bh(&master->internal_lock);
+        up(&master->io_sem);
 
         if (master->fsm_datagram.state == EC_DATAGRAM_SENT)
             goto schedule;
@@ -967,10 +966,10 @@
         up(&master->master_sem);
 
         // queue and send
-        spin_lock_bh(&master->internal_lock);
+        down(&master->io_sem);
         ec_master_queue_datagram(master, &master->fsm_datagram);
         ecrt_master_send(master);
-        spin_unlock_bh(&master->internal_lock);
+        up(&master->io_sem);
         
 schedule:
         if (ec_fsm_master_idle(&master->fsm)) {
@@ -1039,7 +1038,9 @@
  */
 void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */)
 {
-    if (master->eoe_running) {
+    struct sched_param param = { .sched_priority = 0 };
+
+    if (master->eoe_thread) {
         EC_WARN("EoE already running!\n");
         return;
     }
@@ -1052,12 +1053,18 @@
         return;
     }
 
-    EC_INFO("Starting EoE processing.\n");
-    master->eoe_running = 1;
-
-    // start EoE processing
-    master->eoe_timer.expires = jiffies + 10;
-    add_timer(&master->eoe_timer);
+    EC_INFO("Starting EoE thread.\n");
+    master->eoe_thread = kthread_run(ec_master_eoe_thread, master,
+            "EtherCAT-EoE");
+    if (IS_ERR(master->eoe_thread)) {
+        int err = (int) PTR_ERR(master->eoe_thread);
+        EC_ERR("Failed to start EoE thread (error %i)!\n", err);
+        master->eoe_thread = NULL;
+        return;
+    }
+
+    sched_setscheduler(master->eoe_thread, SCHED_NORMAL, &param);
+    set_user_nice(master->eoe_thread, 0);
 }
 
 /*****************************************************************************/
@@ -1066,61 +1073,84 @@
  */
 void ec_master_eoe_stop(ec_master_t *master /**< EtherCAT master */)
 {
-    if (!master->eoe_running) return;
-
-    EC_INFO("Stopping EoE processing.\n");
-
-    del_timer_sync(&master->eoe_timer);
-    master->eoe_running = 0;
+    if (master->eoe_thread) {
+        EC_INFO("Stopping EoE thread.\n");
+
+        kthread_stop(master->eoe_thread);
+        master->eoe_thread = NULL;
+        EC_INFO("EoE thread exited.\n");
+    }
 }
 
 /*****************************************************************************/
 
 /** Does the Ethernet over EtherCAT processing.
  */
-void ec_master_eoe_run(unsigned long data /**< master pointer */)
-{
-    ec_master_t *master = (ec_master_t *) data;
+static int ec_master_eoe_thread(void *priv_data)
+{
+    ec_master_t *master = (ec_master_t *) priv_data;
     ec_eoe_t *eoe;
-    unsigned int none_open = 1;
-    unsigned long restart_jiffies;
-
-    list_for_each_entry(eoe, &master->eoe_handlers, list) {
-        if (ec_eoe_is_open(eoe)) {
-            none_open = 0;
-            break;
-        }
-    }
-    if (none_open)
-        goto queue_timer;
-
-    // receive datagrams
-    if (master->request_cb(master->cb_data))
-        goto queue_timer;
+    unsigned int none_open, sth_to_send, all_idle;
+
+    if (master->debug_level)
+        EC_DBG("EoE thread running.\n");
+
+    while (!kthread_should_stop()) {
+        none_open = 1;
+        all_idle = 1;
+
+        list_for_each_entry(eoe, &master->eoe_handlers, list) {
+            if (ec_eoe_is_open(eoe)) {
+                none_open = 0;
+                break;
+            }
+        }
+        if (none_open)
+            goto schedule;
+
+        // receive datagrams
+        if (master->request_cb(master->cb_data))
+            goto schedule;
+        
+        ecrt_master_receive(master);
+        master->release_cb(master->cb_data);
+
+        // actual EoE processing
+        sth_to_send = 0;
+        list_for_each_entry(eoe, &master->eoe_handlers, list) {
+            ec_eoe_run(eoe);
+            if (eoe->queue_datagram) {
+                sth_to_send = 1;
+            }
+            if (!ec_eoe_is_idle(eoe)) {
+                all_idle = 0;
+            }
+        }
+
+        if (sth_to_send) {
+            // send datagrams
+            if (master->request_cb(master->cb_data)) {
+                goto schedule;
+            }
+            list_for_each_entry(eoe, &master->eoe_handlers, list) {
+                ec_eoe_queue(eoe);
+            }
+            ecrt_master_send(master);
+            master->release_cb(master->cb_data);
+        }
+
+schedule:
+        if (all_idle) {
+            set_current_state(TASK_INTERRUPTIBLE);
+            schedule_timeout(1);
+        } else {
+            schedule();
+        }
+    }
     
-    ecrt_master_receive(master);
-    master->release_cb(master->cb_data);
-
-    // actual EoE processing
-    list_for_each_entry(eoe, &master->eoe_handlers, list) {
-        ec_eoe_run(eoe);
-    }
-
-    // send datagrams
-    if (master->request_cb(master->cb_data)) {
-        goto queue_timer;
-    }
-    list_for_each_entry(eoe, &master->eoe_handlers, list) {
-        ec_eoe_queue(eoe);
-    }
-    ecrt_master_send(master);
-    master->release_cb(master->cb_data);
-
- queue_timer:
-    restart_jiffies = HZ / EC_EOE_FREQUENCY;
-    if (!restart_jiffies) restart_jiffies = 1;
-    master->eoe_timer.expires = jiffies + restart_jiffies;
-    add_timer(&master->eoe_timer);
+    if (master->debug_level)
+        EC_DBG("EoE thread exiting...\n");
+    return 0;
 }
 #endif
 
--- a/master/master.h	Mon Jun 29 14:27:06 2009 +0000
+++ b/master/master.h	Tue Jun 30 11:11:56 2009 +0000
@@ -155,12 +155,11 @@
     struct task_struct *thread; /**< Master thread. */
 
 #ifdef EC_EOE
-    struct timer_list eoe_timer; /**< EoE timer object. */
-    unsigned int eoe_running; /**< \a True, if EoE processing is active. */
+    struct task_struct *eoe_thread; /**< EoE thread. */
     struct list_head eoe_handlers; /**< Ethernet over EtherCAT handlers. */
 #endif
 
-    spinlock_t internal_lock; /**< Spinlock used in \a IDLE phase. */
+    struct semaphore io_sem; /**< Semaphore used in \a IDLE phase. */
     int (*request_cb)(void *); /**< Lock request callback. */
     void (*release_cb)(void *); /**< Lock release callback. */
     void *cb_data; /**< Data parameter of locking callbacks. */