Fixed serveral races while starting up under high CPU load.
authorFlorian Pose <fp@igh-essen.com>
Mon, 16 Oct 2006 14:38:14 +0000
changeset 398 ff37601361a8
parent 397 c5d2fb08e43f
child 399 40375a50be4e
Fixed serveral races while starting up under high CPU load.
master/datagram.c
master/datagram.h
master/device.c
master/device.h
master/fsm.c
master/fsm.h
master/master.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;
 }
 
 /*****************************************************************************/
--- 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;
 
--- 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);
 }
 
--- 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
--- 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);
--- 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 */
--- 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;
     }