core: Add mctp_message_tx_request() function
This allocates a tag for messages sent with TO bit set.
Change-Id: Ia8403a06aa449e0218a30edf5ad69781c70d7c52
Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
diff --git a/core.c b/core.c
index 274cd91..58826bc 100644
--- a/core.c
+++ b/core.c
@@ -30,6 +30,16 @@
#define MCTP_MAX_BUSSES 2
#endif
+/* Concurrent reassembly contexts. */
+#ifndef MCTP_REASSEMBLY_CTXS
+#define MCTP_REASSEMBLY_CTXS 16
+#endif
+
+/* Outbound request tags */
+#ifndef MCTP_REQ_TAGS
+#define MCTP_REQ_TAGS MCTP_REASSEMBLY_CTXS
+#endif
+
/* Internal data structures */
enum mctp_bus_state {
@@ -74,6 +84,13 @@
size_t fragment_size;
};
+struct mctp_req_tag {
+ /* 0 is an unused entry */
+ mctp_eid_t local;
+ mctp_eid_t remote;
+ uint8_t tag;
+};
+
struct mctp {
int n_busses;
struct mctp_bus busses[MCTP_MAX_BUSSES];
@@ -86,10 +103,13 @@
mctp_capture_fn capture;
void *capture_data;
- /* Message reassembly.
- * @todo: flexible context count
- */
- struct mctp_msg_ctx msg_ctxs[16];
+ /* Message reassembly. */
+ struct mctp_msg_ctx msg_ctxs[MCTP_REASSEMBLY_CTXS];
+
+ /* Allocated outbound TO tags */
+ struct mctp_req_tag req_tags[MCTP_REQ_TAGS];
+ /* used to avoid always allocating tag 0 */
+ uint8_t tag_round_robin;
enum {
ROUTE_ENDPOINT,
@@ -107,6 +127,8 @@
static int mctp_message_tx_on_bus(struct mctp_bus *bus, mctp_eid_t src,
mctp_eid_t dest, bool tag_owner,
uint8_t msg_tag, void *msg, size_t msg_len);
+static void mctp_dealloc_tag(struct mctp_bus *bus, mctp_eid_t local,
+ mctp_eid_t remote, uint8_t tag);
struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *binding, size_t len)
{
@@ -531,6 +553,11 @@
if (mctp->route_policy == ROUTE_ENDPOINT &&
mctp_rx_dest_is_local(bus, dest)) {
+ /* Note responses to allocated tags */
+ if (!tag_owner) {
+ mctp_dealloc_tag(bus, dest, src, msg_tag);
+ }
+
/* Handle MCTP Control Messages: */
if (len >= sizeof(struct mctp_ctrl_msg_hdr)) {
struct mctp_ctrl_msg_hdr *msg_hdr = buf;
@@ -961,6 +988,95 @@
msg_len);
}
+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) {
+ return;
+ }
+
+ for (size_t i = 0; i < ARRAY_SIZE(mctp->req_tags); i++) {
+ struct mctp_req_tag *r = &mctp->req_tags[i];
+ if (r->local == local && r->remote == remote && r->tag == tag) {
+ r->local = 0;
+ r->remote = 0;
+ r->tag = 0;
+ return;
+ }
+ }
+}
+
+static int mctp_alloc_tag(struct mctp *mctp, mctp_eid_t local,
+ mctp_eid_t remote, uint8_t *ret_tag)
+{
+ assert(local != 0);
+ assert(remote != 0);
+
+ 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) {
+ spare = r;
+ } else {
+ // TODO: check timeouts
+ if (r->local == local && r->remote == remote) {
+ used |= 1 << r->tag;
+ }
+ }
+ }
+
+ if (spare == NULL) {
+ // All req_tag slots are in-use
+ return -EBUSY;
+ }
+
+ for (uint8_t t = 0; t < 8; t++) {
+ uint8_t tag = (t + mctp->tag_round_robin) % 8;
+ if ((used & 1 << tag) == 0) {
+ spare->local = local;
+ spare->remote = remote;
+ spare->tag = tag;
+ *ret_tag = tag;
+ mctp->tag_round_robin = (tag + 1) % 8;
+ return 0;
+ }
+ }
+
+ // All 8 tags are used for this src/dest pair
+ return -EBUSY;
+}
+
+int mctp_message_tx_request(struct mctp *mctp, mctp_eid_t eid, void *msg,
+ size_t msg_len, uint8_t *ret_alloc_msg_tag)
+{
+ int rc;
+ struct mctp_bus *bus;
+
+ bus = find_bus_for_eid(mctp, eid);
+ if (!bus) {
+ __mctp_msg_free(msg, mctp);
+ return 0;
+ }
+
+ uint8_t alloc_tag;
+ rc = mctp_alloc_tag(mctp, bus->eid, eid, &alloc_tag);
+ if (rc) {
+ mctp_prdebug("Failed allocating tag");
+ __mctp_msg_free(msg, mctp);
+ return rc;
+ }
+
+ if (ret_alloc_msg_tag) {
+ *ret_alloc_msg_tag = alloc_tag;
+ }
+
+ return mctp_message_tx_alloced(mctp, eid, true, alloc_tag, msg,
+ msg_len);
+}
+
bool mctp_is_tx_ready(struct mctp *mctp, mctp_eid_t eid)
{
struct mctp_bus *bus;