EoE processing with kthread.
--- 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, ¶m);
+ 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. */