EoE processing with kthread.

This commit is contained in:
Florian Pose 2009-06-30 11:11:56 +00:00
parent e9b881e6db
commit f7e14d2915
8 changed files with 161 additions and 104 deletions

1
NEWS
View File

@ -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:

9
TODO
View File

@ -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:

View File

@ -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;
}

View File

@ -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<MASTER>[as]<SLAVE>, 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",

View File

@ -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 *);
/*****************************************************************************/

View File

@ -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

View File

@ -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, &param);
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

View File

@ -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. */