+/*
+ * Log the round-trip time (RTT) of the received LCP echo-request.
+ *
+ * The header section at the beginning of lcp_rtt_file contains
+ * LCP_RTT_HEADER_LENGTH fields, each a u_int32_t in network byte order:
+ * [0] LCP_RTT_MAGIC
+ * [1] status (1: the file is open and is being written)
+ * [2] index of the most recently updated element
+ * [3] the value of the lcp-echo-interval parameter
+ *
+ * The header is followed by a ring buffer of LCP_RTT_ELEMENTS elements, each
+ * containing a pair of u_int32_t in network byte order with this content:
+ * [0] UNIX timestamp
+ * [1] bits 24-31: the number of lost LCP echo replies
+ * bits 0-23: the measured RTT in microseconds
+ *
+ * The timestamp is unsigned to support storing dates beyond 2038.
+ *
+ * Consumers of lcp_rtt_file are expected to:
+ * - read the complete file of arbitrary length
+ * - check the magic number
+ * - process the data elements starting at the index
+ * - ignore any elements with a timestamp of 0
+ */
+static void
+lcp_rtt_update_buffer (unsigned long rtt)
+{
+ volatile u_int32_t *const ring_header = lcp_rtt_buffer;
+ volatile u_int32_t *const ring_buffer = lcp_rtt_buffer
+ + LCP_RTT_HEADER_LENGTH;
+ unsigned int next_entry, lost;
+
+ /* choose the next entry where the data will be stored */
+ if (ntohl(ring_header[2]) >= (LCP_RTT_ELEMENTS - 1) * 2)
+ next_entry = 0; /* go back to the beginning */
+ else
+ next_entry = ntohl(ring_header[2]) + 2; /* use the next one */
+
+ /* update the data element */
+ /* storing the timestamp in an *unsigned* long allows dates up to 2106 */
+ ring_buffer[next_entry] = htonl((u_int32_t) time(NULL));
+ lost = lcp_echos_pending - 1;
+ if (lost > 0xFF)
+ lost = 0xFF; /* truncate the lost packets count to 256 */
+ if (rtt > 0xFFFFFF)
+ rtt = 0xFFFFFF; /* truncate the RTT to 16777216 */
+ /* use bits 24-31 for the lost packets count and bits 0-23 for the RTT */
+ ring_buffer[next_entry + 1] = htonl((u_int32_t) ((lost << 24) + rtt));
+
+ /* update the pointer to the (just updated) most current data element */
+ ring_header[2] = htonl(next_entry);
+
+ /* In theory, CPUs implementing a weakly-consistent memory model do not
+ * guarantee that these three memory store operations to the buffer will
+ * be seen in the same order by the reader process.
+ * This means that a process reading the file could see the index
+ * having been updated before the element that the index points to had
+ * been written.
+ * But in practice we expect that the read(2) system call used by
+ * consumers processes is atomic with respect to the following msync(2)
+ * call, so we ignore the issue.
+ */
+
+ if (msync(lcp_rtt_buffer, LCP_RTT_FILE_SIZE, MS_ASYNC) < 0)
+ error("msync() for %s failed: %m", lcp_rtt_file);
+}
+