*
* TODO:
* Implement next:
- * - track RTT, distance, loss, etc. => requires extra data structures!
- * => fragment UUIDs should be 'sequential' 32-bit numbers, for cummulative
- * acks with Bitmask
- * => fragments carry their own offsets, so receiver will NOT require
- * seeing all the possible values
- * => can generate 'fresh' UUIDs for each retransmission to track it
- * => but need to keep 'map' of 32-bit UUID to queue!
- * => message UUIDs are currently overkill with 256 bits
- * => cummulative acks for messages include full UUID list
- * => message UUID size should be chosen to result in 8 byte alignment
- * => could encode 32-bit counter + 32-bit queue UUID in 64-bit message
- * UUID?
- * => Use multihashmap32 on counter for lookup in hashmap?
- * => FIXME: 2x32 bit introduced, but still set to random value
- * instead of using the generators!
- * (and generators not initialized to random starting values either)
- * - consider replacing random `struct GNUNET_ShortHashCode` message UUIDs
- * with (incrementing) 64-bit numbers (compacting both
- * `struct TransportReliabilityBox` and `struct
- * TransportReliabilityAckMessage`), and using *different* UUIDs for each
- * transmission (even of the same message!)
+ * - FIXME: looping over neighbours when calling forward_dv_learn()!
+ * - FIXME: transmit_on_queue: track dvh we may be using and pass it to
+ * fragment_message() and reliability_box_message() if applicable
* - proper use/initialization of timestamps in messages exchanged
* during DV learning
* - persistence of monotonic time from DVInit to prevent
*
* Later:
* - review retransmission logic, right now there is no smartness there!
- * => congestion control, flow control, etc (requires RTT, loss, etc.)
+ * => congestion control, flow control, etc
*
* Optimizations:
- * - use shorthashmap on msg_uuid's when matching reliability/fragment ACKs
- * against our pending message queue (requires additional per neighbour
- * hash map to be maintained, avoids possible linear scan on pending msgs)
+ * - AcknowledgementUUIDPs are overkill with 256 bits (128 would do)
+ * => Need 128 bit hash map though!
* - queue_send_msg and route_message both by API design have to make copies
* of the payload, and route_message on top of that requires a malloc/free.
* Change design to approximate "zero" copy better...
*/
#define IN_PACKET_SIZE_WITHOUT_MTU 128
+/**
+ * Number of slots we keep of historic data for computation of
+ * goodput / message loss ratio.
+ */
+#define GOODPUT_AGING_SLOTS 4
+
/**
* Minimum number of hops we should forward DV learn messages
* even if they are NOT useful for us in hope of looping
};
+/**
+ * Information we keep per #GOODPUT_AGING_SLOTS about historic
+ * (or current) transmission performance.
+ */
+struct TransmissionHistoryEntry
+{
+ /**
+ * Number of bytes actually sent in the interval.
+ */
+ uint64_t bytes_sent;
+
+ /**
+ * Number of bytes received and acknowledged by the other peer in
+ * the interval.
+ */
+ uint64_t bytes_received;
+};
+
+
+/**
+ * Performance data for a transmission possibility.
+ */
+struct PerformanceData
+{
+ /**
+ * Weighted average for the RTT.
+ */
+ struct GNUNET_TIME_Relative aged_rtt;
+
+ /**
+ * Historic performance data, using a ring buffer of#GOODPUT_AGING_SLOTS
+ * entries.
+ */
+ struct TransmissionHistoryEntry the[GOODPUT_AGING_SLOTS];
+
+ /**
+ * What was the last age when we wrote to @e the? Used to clear
+ * old entries when the age advances.
+ */
+ unsigned int last_age;
+};
+
+
/**
* Client connected to the transport service.
*/
*/
struct GNUNET_TIME_Absolute path_valid_until;
+ /**
+ * Performance data for this transmission possibility.
+ */
+ struct PerformanceData pd;
+
/**
* Number of hops in total to the `target` (excluding @e next_hop and `target`
* itself). Thus 0 still means a distance of 2 hops (to @e next_hop and then
*/
struct GNUNET_SCHEDULER_Task *visibility_task;
- /**
- * Our current RTT estimate for this queue.
- */
- struct GNUNET_TIME_Relative rtt;
-
/**
* How long do *we* consider this @e address to be valid? In the past or
* zero if we have not yet validated it. Can be updated based on
*/
struct GNUNET_TIME_Absolute validated_until;
+ /**
+ * Performance data for this queue.
+ */
+ struct PerformanceData pd;
+
/**
* Message ID generator for transmissions on this queue to the
* communicator.
static unsigned int pa_count;
+/**
+ * Get an offset into the transmission history buffer for `struct
+ * PerformanceData`. Note that the caller must perform the required
+ * modulo #GOODPUT_AGING_SLOTS operation before indexing into the
+ * array!
+ *
+ * An 'age' lasts 15 minute slots.
+ *
+ * @return current age of the world
+ */
+static unsigned int
+get_age ()
+{
+ struct GNUNET_TIME_Absolute now;
+
+ now = GNUNET_TIME_absolute_get ();
+ return now.abs_value_us / GNUNET_TIME_UNIT_MINUTES.rel_value_us / 15;
+}
+
+
/**
* Release @a pa data structure.
*
/**
- * FIXME: comment!
+ * Given the key material in @a km and the initialization vector
+ * @a iv, setup the key material for the backchannel in @a key.
+ *
+ * @param km raw master secret
+ * @param iv initialization vector
+ * @param key[out] symmetric cipher and HMAC state to generate
*/
static void
bc_setup_key_state_from_km (const struct GNUNET_HashCode *km,
}
+/**
+ * Check if we have advanced to another age since the last time. If
+ * so, purge ancient statistics (more than GOODPUT_AGING_SLOTS before
+ * the current age)
+ *
+ * @param pd[in,out] data to update
+ * @param age current age
+ */
+static void
+update_pd_age (struct PerformanceData *pd, unsigned int age)
+{
+ unsigned int sage;
+
+ if (age == pd->last_age)
+ return; /* nothing to do */
+ sage = GNUNET_MAX (pd->last_age, age - 2 * GOODPUT_AGING_SLOTS);
+ for (unsigned int i = sage; i <= age - GOODPUT_AGING_SLOTS; i++)
+ {
+ struct TransmissionHistoryEntry *the = &pd->the[i % GOODPUT_AGING_SLOTS];
+
+ the->bytes_sent = 0;
+ the->bytes_received = 0;
+ }
+ pd->last_age = age;
+}
+
+
+/**
+ * Update @a pd based on the latest @a rtt and the number of bytes
+ * that were confirmed to be successfully transmitted.
+ *
+ * @param pd[in,out] data to update
+ * @param rtt latest round-trip time
+ * @param bytes_transmitted_ok number of bytes receiver confirmed as received
+ */
+static void
+update_performance_data (struct PerformanceData *pd,
+ struct GNUNET_TIME_Relative rtt,
+ uint16_t bytes_transmitted_ok)
+{
+ uint64_t nval = rtt.rel_value_us;
+ uint64_t oval = pd->aged_rtt.rel_value_us;
+ unsigned int age = get_age ();
+ struct TransmissionHistoryEntry *the = &pd->the[age % GOODPUT_AGING_SLOTS];
+
+ if (oval == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
+ pd->aged_rtt = rtt;
+ else
+ pd->aged_rtt.rel_value_us = (nval + 7 * oval) / 8;
+ update_pd_age (pd, age);
+ the->bytes_received += bytes_transmitted_ok;
+}
+
+
/**
* We have successfully transmitted data via @a q, update metrics.
*
struct GNUNET_TIME_Relative rtt,
uint16_t bytes_transmitted_ok)
{
- // FIXME: implement!
+ update_performance_data (&q->pd, rtt, bytes_transmitted_ok);
}
struct GNUNET_TIME_Relative rtt,
uint16_t bytes_transmitted_ok)
{
- // FIXME: implement!
+ update_performance_data (&dvh->pd, rtt, bytes_transmitted_ok);
}
return;
}
q->validated_until = vs->validated_until;
- q->rtt = vs->validation_rtt;
+ q->pd.aged_rtt = vs->validation_rtt;
n = q->neighbour;
if (GNUNET_NO != n->core_visible)
return; /* nothing changed, we are done here */
message urgency and size when delaying ACKs, etc.) */
update_pm_next_attempt (s,
GNUNET_TIME_relative_to_absolute (
- GNUNET_TIME_relative_multiply (queue->rtt, 4)));
+ GNUNET_TIME_relative_multiply (queue->pd.aged_rtt,
+ 4)));
}
/* finally, re-schedule queue transmission task itself */
for (struct Queue *q = neighbour->queue_head; NULL != q;
q = q->next_neighbour)
{
- struct MonitorEvent me = {.rtt = q->rtt,
+ struct MonitorEvent me = {.rtt = q->pd.aged_rtt,
.cs = q->cs,
.num_msg_pending = q->num_msg_pending,
.num_bytes_pending = q->num_bytes_pending};
ctx->q = q;
/* OPTIMIZE-FIXME: in the future, add reliability / goodput
statistics and consider those as well here? */
- if (q->rtt.rel_value_us < DV_QUALITY_RTT_THRESHOLD.rel_value_us)
+ if (q->pd.aged_rtt.rel_value_us < DV_QUALITY_RTT_THRESHOLD.rel_value_us)
do_inc = GNUNET_YES;
}
if (GNUNET_YES == do_inc)
queue = GNUNET_malloc (sizeof (struct Queue) + addr_len);
queue->tc = tc;
queue->address = (const char *) &queue[1];
- queue->rtt = GNUNET_TIME_UNIT_FOREVER_REL;
+ queue->pd.aged_rtt = GNUNET_TIME_UNIT_FOREVER_REL;
queue->qid = aqm->qid;
queue->mtu = ntohl (aqm->mtu);
queue->nt = (enum GNUNET_NetworkType) ntohl (aqm->nt);
memcpy (&queue[1], addr, addr_len);
/* notify monitors about new queue */
{
- struct MonitorEvent me = {.rtt = queue->rtt, .cs = queue->cs};
+ struct MonitorEvent me = {.rtt = queue->pd.aged_rtt, .cs = queue->cs};
notify_monitors (&neighbour->pid, queue->address, queue->nt, &me);
}