# HG changeset patch # User Florian Pose # Date 1161009494 0 # Node ID ff37601361a8d3dc29919b608d0e6f4333a6cbfd # Parent c5d2fb08e43f015dc54c2d897d48eae91206b6f2 Fixed serveral races while starting up under high CPU load. diff -r c5d2fb08e43f -r ff37601361a8 master/datagram.c --- a/master/datagram.c Mon Oct 16 09:07:49 2006 +0000 +++ b/master/datagram.c Mon Oct 16 14:38:14 2006 +0000 @@ -77,8 +77,11 @@ datagram->index = 0x00; datagram->working_counter = 0x00; datagram->state = EC_DATAGRAM_INIT; + datagram->cycles_queued = 0; datagram->cycles_sent = 0; - datagram->check_once_more = 0; + datagram->jiffies_sent = 0; + datagram->cycles_received = 0; + datagram->jiffies_received = 0; } /*****************************************************************************/ diff -r c5d2fb08e43f -r ff37601361a8 master/datagram.h --- a/master/datagram.h Mon Oct 16 09:07:49 2006 +0000 +++ b/master/datagram.h Mon Oct 16 14:38:14 2006 +0000 @@ -110,6 +110,7 @@ { struct list_head list; /**< needed by domain datagram lists */ struct list_head queue; /**< master datagram queue item */ + struct list_head sent; /**< master list item for sent datagrams */ ec_datagram_type_t type; /**< datagram type (APRD, BWR, etc) */ ec_address_t address; /**< receipient address */ uint8_t *data; /**< datagram data */ @@ -118,8 +119,11 @@ uint8_t index; /**< datagram index (set by master) */ uint16_t working_counter; /**< working counter */ ec_datagram_state_t state; /**< datagram state */ + cycles_t cycles_queued; /**< time, the datagram was queued */ cycles_t cycles_sent; /**< time, the datagram was sent */ - uint8_t check_once_more; /**< one more try in case of timeout */ + unsigned long jiffies_sent; /**< jiffies when datagram was sent */ + cycles_t cycles_received; /**< time, the datagram was received */ + unsigned long jiffies_received; /**< jiffies when datagram was received */ } ec_datagram_t; diff -r c5d2fb08e43f -r ff37601361a8 master/device.c --- a/master/device.c Mon Oct 16 09:07:49 2006 +0000 +++ b/master/device.c Mon Oct 16 14:38:14 2006 +0000 @@ -230,6 +230,8 @@ void ec_device_call_isr(ec_device_t *device /**< EtherCAT device */) { + device->cycles_isr = get_cycles(); + device->jiffies_isr = jiffies; if (likely(device->isr)) device->isr(0, device->dev, NULL); } diff -r c5d2fb08e43f -r ff37601361a8 master/device.h --- a/master/device.h Mon Oct 16 09:07:49 2006 +0000 +++ b/master/device.h Mon Oct 16 14:38:14 2006 +0000 @@ -66,6 +66,8 @@ uint8_t open; /**< true, if the net_device has been opened */ struct sk_buff *tx_skb; /**< transmit socket buffer */ ec_isr_t isr; /**< pointer to the device's interrupt service routine */ + cycles_t cycles_isr; /**< cycles of last ISR call */ + unsigned long jiffies_isr; /**< jiffies of last ISR call */ struct module *module; /**< pointer to the device's owning module */ uint8_t link_state; /**< device link state */ #ifdef EC_DBG_IF diff -r c5d2fb08e43f -r ff37601361a8 master/fsm.c --- a/master/fsm.c Mon Oct 16 09:07:49 2006 +0000 +++ b/master/fsm.c Mon Oct 16 14:38:14 2006 +0000 @@ -1613,8 +1613,8 @@ return; } + fsm->sii_start = datagram->cycles_sent; fsm->sii_check_once_more = 1; - fsm->sii_start = get_cycles(); // issue check/fetch datagram if (fsm->sii_mode) { @@ -1649,7 +1649,8 @@ // check "busy bit" if (EC_READ_U8(datagram->data + 1) & 0x81) { // still busy... timeout? - if (get_cycles() - fsm->sii_start >= (cycles_t) 10 * cpu_khz) { + if (datagram->cycles_received + - fsm->sii_start >= (cycles_t) 10 * cpu_khz) { if (!fsm->sii_check_once_more) { EC_ERR("SII: Read timeout.\n"); fsm->sii_state = ec_fsm_error; @@ -1727,7 +1728,8 @@ return; } - fsm->sii_start = get_cycles(); + fsm->sii_start = datagram->cycles_sent; + fsm->sii_check_once_more = 1; // issue check/fetch datagram ec_datagram_nprd(datagram, fsm->slave->station_address, 0x502, 2); @@ -1754,10 +1756,14 @@ if (EC_READ_U8(datagram->data + 1) & 0x82) { // still busy... timeout? - if (get_cycles() - fsm->sii_start >= (cycles_t) 10 * cpu_khz) { - EC_ERR("SII: Write timeout.\n"); - fsm->sii_state = ec_fsm_error; - return; + if (datagram->cycles_received + - fsm->sii_start >= (cycles_t) 10 * cpu_khz) { + if (!fsm->sii_check_once_more) { + EC_ERR("SII: Write timeout.\n"); + fsm->sii_state = ec_fsm_error; + return; + } + fsm->sii_check_once_more = 0; } // issue check/fetch datagram again @@ -1788,7 +1794,7 @@ ec_datagram_t *datagram = &fsm->datagram; ec_slave_t *slave = fsm->slave; - fsm->change_jiffies = jiffies; + fsm->change_take_time = 1; // write new state to slave ec_datagram_npwr(datagram, slave->station_address, 0x0120, 2); @@ -1815,8 +1821,13 @@ return; } + if (fsm->change_take_time) { + fsm->change_take_time = 0; + fsm->change_jiffies = datagram->jiffies_sent; + } + if (datagram->working_counter != 1) { - if (jiffies - fsm->change_jiffies >= 3 * HZ) { + if (datagram->jiffies_received - fsm->change_jiffies >= 3 * HZ) { fsm->change_state = ec_fsm_error; EC_ERR("Failed to set state 0x%02X on slave %i: Slave did not" " respond.\n", fsm->change_new, fsm->slave->ring_position); @@ -1830,7 +1841,7 @@ return; } - fsm->change_jiffies = jiffies; + fsm->change_take_time = 1; // read AL status from slave ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2); @@ -1857,6 +1868,11 @@ return; } + if (fsm->change_take_time) { + fsm->change_take_time = 0; + fsm->change_jiffies = datagram->jiffies_sent; + } + slave->current_state = EC_READ_U8(datagram->data); if (slave->current_state == fsm->change_new) { @@ -1878,7 +1894,8 @@ return; } - if (jiffies - fsm->change_jiffies >= 100 * HZ / 1000) { // 100ms + if (datagram->jiffies_received + - fsm->change_jiffies >= 100 * HZ / 1000) { // 100ms // timeout while checking fsm->change_state = ec_fsm_error; EC_ERR("Timeout while setting state 0x%02X on slave %i.\n", @@ -1987,7 +2004,7 @@ return; } - fsm->change_jiffies = jiffies; + fsm->change_take_time = 1; // read new AL status ec_datagram_nprd(datagram, slave->station_address, 0x0130, 2); @@ -2014,6 +2031,11 @@ return; } + if (fsm->change_take_time) { + fsm->change_take_time = 0; + fsm->change_jiffies = datagram->jiffies_sent; + } + ack_state = EC_READ_U8(datagram->data); if (ack_state == slave->current_state) { @@ -2023,7 +2045,8 @@ return; } - if (jiffies - fsm->change_jiffies >= 100 * HZ / 1000) { // 100ms + if (datagram->jiffies_received + - fsm->change_jiffies >= 100 * HZ / 1000) { // 100ms // timeout while checking slave->current_state = EC_SLAVE_STATE_UNKNOWN; fsm->change_state = ec_fsm_error; @@ -2097,7 +2120,7 @@ return; } - fsm->coe_start = get_cycles(); + fsm->coe_start = datagram->cycles_sent; ec_slave_mbox_prepare_check(slave, datagram); // can not fail. ec_master_queue_datagram(fsm->master, datagram); @@ -2123,7 +2146,8 @@ } if (!ec_slave_mbox_check(datagram)) { - if (get_cycles() - fsm->coe_start >= (cycles_t) 100 * cpu_khz) { + if (datagram->cycles_received + - fsm->coe_start >= (cycles_t) 100 * cpu_khz) { fsm->coe_state = ec_fsm_error; EC_ERR("Timeout while checking SDO configuration on slave %i.\n", slave->ring_position); diff -r c5d2fb08e43f -r ff37601361a8 master/fsm.h --- a/master/fsm.h Mon Oct 16 09:07:49 2006 +0000 +++ b/master/fsm.h Mon Oct 16 14:38:14 2006 +0000 @@ -72,11 +72,12 @@ unsigned int sii_mode; /**< SII reading done by APRD (0) or NPRD (1) */ uint8_t sii_value[4]; /**< raw SII value (32bit) */ cycles_t sii_start; /**< sii start */ - uint8_t sii_check_once_more; /**< do one more check in case of timeout */ + uint8_t sii_check_once_more; /**< one more try after timeout */ void (*change_state)(ec_fsm_t *); /**< slave state change state function */ ec_slave_state_t change_new; /**< input: new state */ unsigned long change_jiffies; /**< change timer */ + uint8_t change_take_time; /**< take sending timestamp */ void (*coe_state)(ec_fsm_t *); /**< CoE state function */ ec_sdo_data_t *sdodata; /**< input/output: SDO data object */ diff -r c5d2fb08e43f -r ff37601361a8 master/master.c --- a/master/master.c Mon Oct 16 09:07:49 2006 +0000 +++ b/master/master.c Mon Oct 16 14:38:14 2006 +0000 @@ -311,6 +311,7 @@ list_add_tail(&datagram->queue, &master->datagram_queue); datagram->state = EC_DATAGRAM_QUEUED; + datagram->cycles_queued = get_cycles(); } /*****************************************************************************/ @@ -322,15 +323,18 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */) { - ec_datagram_t *datagram; + ec_datagram_t *datagram, *next; size_t datagram_size; uint8_t *frame_data, *cur_data; void *follows_word; - cycles_t cycles_start, cycles_end; + cycles_t cycles_start, cycles_sent, cycles_end; + unsigned long jiffies_sent; unsigned int frame_count, more_datagrams_waiting; - + struct list_head sent_datagrams; + + cycles_start = get_cycles(); frame_count = 0; - cycles_start = get_cycles(); + INIT_LIST_HEAD(&sent_datagrams); if (unlikely(master->debug_level > 1)) EC_DBG("ec_master_send_datagrams\n"); @@ -354,9 +358,7 @@ break; } - datagram->state = EC_DATAGRAM_SENT; - datagram->cycles_sent = cycles_start; - datagram->check_once_more = 1; + list_add_tail(&datagram->sent, &sent_datagrams); datagram->index = master->datagram_index++; if (unlikely(master->debug_level > 1)) @@ -384,7 +386,7 @@ cur_data += EC_DATAGRAM_FOOTER_SIZE; } - if (cur_data - frame_data == EC_FRAME_HEADER_SIZE) { + if (list_empty(&sent_datagrams)) { if (unlikely(master->debug_level > 1)) EC_DBG("nothing to send.\n"); break; @@ -403,6 +405,17 @@ // send frame ec_device_send(master->device, cur_data - frame_data); + cycles_sent = get_cycles(); + jiffies_sent = jiffies; + + // set datagram states and sending timestamps + list_for_each_entry_safe(datagram, next, &sent_datagrams, sent) { + datagram->state = EC_DATAGRAM_SENT; + datagram->cycles_sent = cycles_sent; + datagram->jiffies_sent = jiffies_sent; + list_del_init(&datagram->sent); // empty list of sent datagrams + } + frame_count++; } while (more_datagrams_waiting); @@ -498,6 +511,8 @@ // dequeue the received datagram datagram->state = EC_DATAGRAM_RECEIVED; + datagram->cycles_received = master->device->cycles_isr; + datagram->jiffies_received = master->device->jiffies_isr; list_del_init(&datagram->queue); } } @@ -1063,8 +1078,7 @@ int ec_master_measure_bus_time(ec_master_t *master) { ec_datagram_t datagram; - cycles_t cycles_start, cycles_end, cycles_timeout; - uint32_t times[100], sum, min, max, i; + uint32_t cur, sum, min, max, i; ec_datagram_init(&datagram); @@ -1074,7 +1088,7 @@ return -1; } - cycles_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000); + ecrt_master_receive(master); sum = 0; min = 0xFFFFFFFF; @@ -1083,11 +1097,9 @@ for (i = 0; i < 100; i++) { ec_master_queue_datagram(master, &datagram); ecrt_master_send(master); - cycles_start = get_cycles(); - - while (1) { // active waiting - ec_device_call_isr(master->device); - cycles_end = get_cycles(); // take current time + + while (1) { + ecrt_master_receive(master); if (datagram.state == EC_DATAGRAM_RECEIVED) { break; @@ -1096,25 +1108,25 @@ EC_WARN("Failed to measure bus time.\n"); goto error; } - else if (cycles_end - cycles_start >= cycles_timeout) { + else if (datagram.state == EC_DATAGRAM_TIMED_OUT) { EC_WARN("Timeout while measuring bus time.\n"); goto error; } } - times[i] = (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz; - sum += times[i]; - if (times[i] > max) max = times[i]; - if (times[i] < min) min = times[i]; - } - - EC_INFO("Bus time is (min/avg/max) %u/%u.%u/%u us.\n", + cur = (unsigned int) (datagram.cycles_received + - datagram.cycles_sent) * 1000 / cpu_khz; + sum += cur; + if (cur > max) max = cur; + if (cur < min) min = cur; + } + + EC_INFO("Bus time is (min/avg/max) %u / %u.%u / %u us.\n", min, sum / 100, sum % 100, max); + ec_datagram_clear(&datagram); return 0; error: - // Dequeue and free datagram - list_del(&datagram.queue); ec_datagram_clear(&datagram); return -1; } @@ -1309,24 +1321,28 @@ void ecrt_master_receive(ec_master_t *master /**< EtherCAT master */) { ec_datagram_t *datagram, *next; - cycles_t cycles_received, cycles_timeout; - + cycles_t cycles_timeout; + + // receive datagrams ec_device_call_isr(master->device); - cycles_received = get_cycles(); - cycles_timeout = EC_IO_TIMEOUT /* us */ * cpu_khz / 1000; + cycles_timeout = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000); // dequeue all datagrams that timed out list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) { switch (datagram->state) { + case EC_DATAGRAM_QUEUED: + if (master->device->cycles_isr + - datagram->cycles_queued > cycles_timeout) { + list_del_init(&datagram->queue); + datagram->state = EC_DATAGRAM_TIMED_OUT; + master->stats.timeouts++; + ec_master_output_stats(master); + } + break; case EC_DATAGRAM_SENT: - case EC_DATAGRAM_QUEUED: - if (cycles_received - datagram->cycles_sent > cycles_timeout) { - if (datagram->state == EC_DATAGRAM_SENT - && datagram->check_once_more) { - datagram->check_once_more = 0; - break; - } + if (master->device->cycles_isr + - datagram->cycles_sent > cycles_timeout) { list_del_init(&datagram->queue); datagram->state = EC_DATAGRAM_TIMED_OUT; master->stats.timeouts++; @@ -1359,11 +1375,12 @@ ecrt_master_send(master); - cycles_start = get_cycles(); // take sending time - cycles_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000); + cycles_start = get_cycles(); + cycles_timeout = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000); // active waiting while (1) { + udelay(100); cycles_end = get_cycles(); if (cycles_end - cycles_start >= cycles_timeout) break; }