core: Add allocated tag expiry, mctp_set_now_op()

Allocated tags expire after 6 seconds, requiring a time source.
The default will use clock_gettime(), though implementations can set
MCTP_DEFAULT_CLOCK_GETTIME=0 and use mctp_set_now_op() for custom
behavior.

Change-Id: Iaacf500bcd458c987f4b79c698bac9ae5ad5a73e
Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
diff --git a/core-internal.h b/core-internal.h
index 7608a41..c8e7b25 100644
--- a/core-internal.h
+++ b/core-internal.h
@@ -24,6 +24,13 @@
 #define MCTP_REQ_TAGS MCTP_REASSEMBLY_CTXS
 #endif
 
+#ifndef MCTP_DEFAULT_CLOCK_GETTIME
+#define MCTP_DEFAULT_CLOCK_GETTIME 1
+#endif
+
+/* Tag expiry timeout, in milliseconds */
+static const uint64_t MCTP_TAG_TIMEOUT = 6000;
+
 /* Internal data structures */
 
 enum mctp_bus_state {
@@ -73,6 +80,8 @@
 	mctp_eid_t local;
 	mctp_eid_t remote;
 	uint8_t tag;
+	/* time of tag expiry */
+	uint64_t expiry;
 };
 
 struct mctp {
@@ -102,4 +111,7 @@
 	size_t max_message_size;
 
 	void *alloc_ctx;
+
+	uint64_t (*platform_now)(void *);
+	void *platform_now_ctx;
 };
diff --git a/core.c b/core.c
index 3a4ed89..e28f6bc 100644
--- a/core.c
+++ b/core.c
@@ -20,6 +20,10 @@
 #include "compiler.h"
 #include "core-internal.h"
 
+#if MCTP_DEFAULT_CLOCK_GETTIME
+#include <time.h>
+#endif
+
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
 #endif
@@ -238,6 +242,19 @@
 	return mctp;
 }
 
+#if MCTP_DEFAULT_CLOCK_GETTIME
+static uint64_t mctp_default_now(void *ctx __attribute__((unused)))
+{
+	struct timespec tp;
+	int rc = clock_gettime(CLOCK_MONOTONIC, &tp);
+	if (rc) {
+		/* Should not be possible */
+		return 0;
+	}
+	return (uint64_t)tp.tv_sec * 1000 + tp.tv_nsec / 1000000;
+}
+#endif
+
 int mctp_setup(struct mctp *mctp, size_t struct_mctp_size)
 {
 	if (struct_mctp_size < sizeof(struct mctp)) {
@@ -246,6 +263,9 @@
 	}
 	memset(mctp, 0, sizeof(*mctp));
 	mctp->max_message_size = MCTP_MAX_MESSAGE_SIZE;
+#if MCTP_DEFAULT_CLOCK_GETTIME
+	mctp->platform_now = mctp_default_now;
+#endif
 	return 0;
 }
 
@@ -893,11 +913,24 @@
 				       msg_len);
 }
 
+void mctp_set_now_op(struct mctp *mctp, uint64_t (*now)(void *), void *ctx)
+{
+	assert(now);
+	mctp->platform_now = now;
+	mctp->platform_now_ctx = ctx;
+}
+
+uint64_t mctp_now(struct mctp *mctp)
+{
+	assert(mctp->platform_now);
+	return mctp->platform_now(mctp->platform_now_ctx);
+}
+
 static void mctp_dealloc_tag(struct mctp_bus *bus, mctp_eid_t local,
 			     mctp_eid_t remote, uint8_t tag)
 {
 	struct mctp *mctp = bus->binding->mctp;
-	if (local == 0 || remote == 0) {
+	if (local == 0) {
 		return;
 	}
 
@@ -907,6 +940,7 @@
 			r->local = 0;
 			r->remote = 0;
 			r->tag = 0;
+			r->expiry = 0;
 			return;
 		}
 	}
@@ -916,17 +950,16 @@
 			  mctp_eid_t remote, uint8_t *ret_tag)
 {
 	assert(local != 0);
-	assert(remote != 0);
+	uint64_t now = mctp_now(mctp);
 
 	uint8_t used = 0;
 	struct mctp_req_tag *spare = NULL;
 	/* Find which tags and slots are used/spare */
 	for (size_t i = 0; i < ARRAY_SIZE(mctp->req_tags); i++) {
 		struct mctp_req_tag *r = &mctp->req_tags[i];
-		if (r->local == 0) {
+		if (r->local == 0 || r->expiry < now) {
 			spare = r;
 		} else {
-			// TODO: check timeouts
 			if (r->local == local && r->remote == remote) {
 				used |= 1 << r->tag;
 			}
@@ -944,6 +977,7 @@
 			spare->local = local;
 			spare->remote = remote;
 			spare->tag = tag;
+			spare->expiry = now + MCTP_TAG_TIMEOUT;
 			*ret_tag = tag;
 			mctp->tag_round_robin = (tag + 1) % 8;
 			return 0;
diff --git a/libmctp.h b/libmctp.h
index 1075f7a..f78132b 100644
--- a/libmctp.h
+++ b/libmctp.h
@@ -224,6 +224,15 @@
 #define MCTP_LOG_INFO	 6
 #define MCTP_LOG_DEBUG	 7
 
+/* Environment-specific time functionality */
+/* The `now` callback returns a timestamp in milliseconds.
+ * Timestamps should be monotonically increasing, and can have an arbitrary
+ * origin. (As long as returned timestamps aren't too close to UINT64_MAX, not
+ * a problem forany reasonable implementation). */
+void mctp_set_now_op(struct mctp *mctp, uint64_t (*now)(void *), void *ctx);
+/* Returns a timestamp in milliseconds */
+uint64_t mctp_now(struct mctp *mctp);
+
 #ifdef __cplusplus
 }
 #endif