From f7e14d29155a90712e6f756b6300b5e2971ddb17 Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Tue, 30 Jun 2009 11:11:56 +0000 Subject: [PATCH] EoE processing with kthread. --- NEWS | 1 + TODO | 9 ++- master/cdev.c | 16 ++--- master/ethernet.c | 69 ++++++++++++++------- master/ethernet.h | 8 ++- master/globals.h | 3 - master/master.c | 154 +++++++++++++++++++++++++++------------------- master/master.h | 5 +- 8 files changed, 161 insertions(+), 104 deletions(-) diff --git a/NEWS b/NEWS index cc37216f..c8f63118 100644 --- a/NEWS +++ b/NEWS @@ -46,6 +46,7 @@ Changes since 1.4.0: * 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: diff --git a/TODO b/TODO index 05d9df51..111ba705 100644 --- a/TODO +++ b/TODO @@ -21,6 +21,10 @@ Version 1.5.0: - 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 @@ Version 1.5.0: * 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 @@ Smaller issues: * 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: diff --git a/master/cdev.c b/master/cdev.c index 0449ea10..ead75236 100644 --- a/master/cdev.c +++ b/master/cdev.c @@ -1645,9 +1645,9 @@ int ec_cdev_ioctl_send( 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 @@ int ec_cdev_ioctl_receive( 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 @@ int ec_cdev_ioctl_sync_ref( 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 @@ int ec_cdev_ioctl_sync_slaves( 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; } diff --git a/master/ethernet.c b/master/ethernet.c index 6f9c4bd1..513dde93 100644 --- a/master/ethernet.c +++ b/master/ethernet.c @@ -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 @@ int ec_eoe_init( 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 @@ int ec_eoe_init( eoe->rx_rate = 0; eoe->tx_rate = 0; eoe->rate_jiffies = 0; + eoe->rx_idle = 1; + eoe->tx_idle = 1; /* device name eoe[as], because networking scripts don't * like hyphens etc. in interface names. */ @@ -206,7 +208,7 @@ void ec_eoe_flush(ec_eoe_t *eoe /**< EoE handler */) { 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 @@ void ec_eoe_flush(ec_eoe_t *eoe /**< EoE handler */) } eoe->tx_queued_frames = 0; - spin_unlock_bh(&eoe->tx_queue_lock); + up(&eoe->tx_queue_sem); } /*****************************************************************************/ @@ -338,6 +340,18 @@ int ec_eoe_is_open(const ec_eoe_t *eoe /**< EoE handler */) 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 @@ int ec_eoe_is_open(const ec_eoe_t *eoe /**< EoE handler */) */ void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */) { - if (eoe->slave->error_flag || !eoe->slave->master->main_device.link_state) + 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 @@ void ec_eoe_state_rx_check(ec_eoe_t *eoe /**< EoE handler */) } 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 @@ void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */) unsigned int wakeup = 0; #endif - if (eoe->slave->error_flag || !eoe->slave->master->main_device.link_state) + if (eoe->slave->error_flag || + !eoe->slave->master->main_device.link_state) { + eoe->rx_idle = 1; + eoe->tx_idle = 1; return; + } - spin_lock_bh(&eoe->tx_queue_lock); + 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 @@ void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */) } 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 @@ void ec_eoe_state_tx_sent(ec_eoe_t *eoe /**< EoE handler */) 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 @@ void ec_eoe_state_tx_sent(ec_eoe_t *eoe /**< EoE handler */) 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 @@ int ec_eoedev_open(struct net_device *dev /**< EoE net_device */) 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 @@ int ec_eoedev_stop(struct net_device *dev /**< EoE net_device */) { 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 @@ int ec_eoedev_tx(struct sk_buff *skb, /**< transmit socket buffer */ 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", diff --git a/master/ethernet.h b/master/ethernet.h index c6d35852..f14a8e87 100644 --- a/master/ethernet.h +++ b/master/ethernet.h @@ -78,23 +78,28 @@ struct ec_eoe 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_clear(ec_eoe_t *); 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 *); /*****************************************************************************/ diff --git a/master/globals.h b/master/globals.h index 414e6fa5..0a00a4cb 100644 --- a/master/globals.h +++ b/master/globals.h @@ -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 diff --git a/master/master.c b/master/master.c index 58ba82cd..8311f4a1 100644 --- a/master/master.c +++ b/master/master.c @@ -75,7 +75,7 @@ void ec_master_clear_domains(ec_master_t *); 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 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */ 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 @@ void ec_master_clear_domains(ec_master_t *master) /** Internal locking callback. */ -int ec_master_request_cb(void *master /**< callback data */) +int ec_master_request_cb(void *data /**< callback data */) { - spin_lock(&((ec_master_t *) master)->internal_lock); + ec_master_t *master = (ec_master_t *) data; + down(&master->io_sem); return 0; } @@ -390,9 +388,10 @@ int ec_master_request_cb(void *master /**< callback data */) /** Internal unlocking callback. */ -void ec_master_release_cb(void *master /**< callback data */) +void ec_master_release_cb(void *data /**< callback data */) { - spin_unlock(&((ec_master_t *) master)->internal_lock); + ec_master_t *master = (ec_master_t *) data; + up(&master->io_sem); } /*****************************************************************************/ @@ -953,9 +952,9 @@ static int ec_master_idle_thread(void *priv_data) 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 @@ static int ec_master_idle_thread(void *priv_data) 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 @@ schedule: */ 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 @@ void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */) return; } - EC_INFO("Starting EoE processing.\n"); - master->eoe_running = 1; + 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; + } - // start EoE processing - master->eoe_timer.expires = jiffies + 10; - add_timer(&master->eoe_timer); + sched_setscheduler(master->eoe_thread, SCHED_NORMAL, ¶m); + set_user_nice(master->eoe_thread, 0); } /*****************************************************************************/ @@ -1066,61 +1073,84 @@ void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */) */ void ec_master_eoe_stop(ec_master_t *master /**< EtherCAT master */) { - if (!master->eoe_running) return; + if (master->eoe_thread) { + EC_INFO("Stopping EoE thread.\n"); - EC_INFO("Stopping EoE processing.\n"); - - del_timer_sync(&master->eoe_timer); - master->eoe_running = 0; + 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 */) +static int ec_master_eoe_thread(void *priv_data) { - ec_master_t *master = (ec_master_t *) data; + ec_master_t *master = (ec_master_t *) priv_data; ec_eoe_t *eoe; - unsigned int none_open = 1; - unsigned long restart_jiffies; + unsigned int none_open, sth_to_send, all_idle; - list_for_each_entry(eoe, &master->eoe_handlers, list) { - if (ec_eoe_is_open(eoe)) { - none_open = 0; - break; + 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(); } } - if (none_open) - goto queue_timer; - - // receive datagrams - if (master->request_cb(master->cb_data)) - goto queue_timer; - 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 diff --git a/master/master.h b/master/master.h index 76eb5b98..5982e0fe 100644 --- a/master/master.h +++ b/master/master.h @@ -155,12 +155,11 @@ struct ec_master { 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. */