core: Reuse buffers for tx, allow message pools

Use new m_msg_alloc/m_msg_free operations for whole-message
MCTP buffers. m_realloc is no longer used, instead the maximum
sized buffer is allocated for each reassembly.
This allows applications to keep a pool of MCTP message buffers.

Don't create a queue of packets to transmit, instead reuse a single
binding-provided tx_storage buffer for each transmitted packet, which
can be static for bindings that have a known maximum packet size.

Asynchronous users/bindings can no longer rely on the core for queueing
TX packets, instead they should test mctp_is_tx_ready() prior to calling
mctp_message_tx(). The stack will return -EBUSY from mctp_message_tx()
if there is already a message pending to send.

Bindings must be updated to add the tx_storage member, and the core will
no longer free packets passed to mctp_bus_rx().

Change-Id: I2598bb91026ccef01b268c52b06c0f8e20bebb1e
Signed-off-by: Matt Johnston <matt@codeconstruct.com.au>
diff --git a/libmctp.h b/libmctp.h
index d3c5ed1..a3e1331 100644
--- a/libmctp.h
+++ b/libmctp.h
@@ -52,25 +52,35 @@
 struct mctp_pktbuf {
 	size_t start, end, size;
 	size_t mctp_hdr_off;
-	struct mctp_pktbuf *next;
+	bool alloc;
 	unsigned char data[];
 };
 
+#define MCTP_PKTBUF_SIZE(payload)                                              \
+	(MCTP_PACKET_SIZE(payload) + sizeof(struct mctp_pktbuf))
+
+struct mctp;
+struct mctp_bus;
 struct mctp_binding;
 
-struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *hw, size_t len);
+/* Initialise a mctp_pktbuf in static storage. Should not be freed.
+ * Storage must be sized to fit the binding,
+ * MCTP_PKTBUF_SIZE(binding->pkt_size + binding->pkt_header + binding->pkt_trailer) */
+struct mctp_pktbuf *mctp_pktbuf_init(struct mctp_binding *binding,
+				     void *storage);
+/* Allocate and initialise a mctp_pktbuf. Should be freed with
+ * mctp_pktbuf_free */
+struct mctp_pktbuf *mctp_pktbuf_alloc(struct mctp_binding *binding, size_t len);
 void mctp_pktbuf_free(struct mctp_pktbuf *pkt);
 struct mctp_hdr *mctp_pktbuf_hdr(struct mctp_pktbuf *pkt);
 void *mctp_pktbuf_data(struct mctp_pktbuf *pkt);
-size_t mctp_pktbuf_size(struct mctp_pktbuf *pkt);
+size_t mctp_pktbuf_size(const struct mctp_pktbuf *pkt);
 void *mctp_pktbuf_alloc_start(struct mctp_pktbuf *pkt, size_t size);
 void *mctp_pktbuf_alloc_end(struct mctp_pktbuf *pkt, size_t size);
 int mctp_pktbuf_push(struct mctp_pktbuf *pkt, const void *data, size_t len);
 void *mctp_pktbuf_pop(struct mctp_pktbuf *pkt, size_t len);
 
 /* MCTP core */
-struct mctp;
-struct mctp_bus;
 
 struct mctp *mctp_init(void);
 void mctp_set_max_message_size(struct mctp *mctp, size_t message_size);
@@ -106,13 +116,40 @@
 
 int mctp_set_rx_all(struct mctp *mctp, mctp_rx_fn fn, void *data);
 
+/* Transmit a message.
+ * @msg: The message buffer to send. Must be suitable for
+ * free(), or the custom mctp_set_alloc_ops() m_msg_free.
+ * The mctp stack will take ownership of the buffer
+ * and release it when message transmission is complete or fails.
+ *
+ * If an asynchronous binding is being used, it will return -EBUSY if
+ * a message is already pending for transmission (msg will be freed as usual).
+ * Asynchronous users can test mctp_is_tx_ready() prior to sending.
+ */
+int mctp_message_tx_alloced(struct mctp *mctp, mctp_eid_t eid, bool tag_owner,
+			    uint8_t msg_tag, void *msg, size_t msg_len);
+
+/* Transmit a message.
+ * @msg: The message buffer to send. Ownership of this buffer
+ * remains with the caller (a copy is made internally with __mctp_msg_alloc).
+ *
+ * If an asynchronous binding is being used, it will return -EBUSY if
+ * a message is already pending for transmission.
+ * Asynchronous users can test mctp_is_tx_ready() prior to sending.
+ *
+ * This is equivalent to duplicating `msg` then calling mctp_message_tx_alloc().
+ */
 int mctp_message_tx(struct mctp *mctp, mctp_eid_t eid, bool tag_owner,
-		    uint8_t msg_tag, void *msg, size_t msg_len);
+		    uint8_t msg_tag, const void *msg, size_t msg_len);
+
+bool mctp_is_tx_ready(struct mctp *mctp, mctp_eid_t eid);
 
 /* hardware bindings */
 
 /**
  * @tx: Binding function to transmit one packet on the interface
+ * @tx_storage: A buffer for transmitting packets. Must be sized
+ * as MCTP_PKTBUF_SIZE(mtu).
  *      Return:
  *      * 0 - Success, pktbuf can be released
  *	* -EMSGSIZE - Packet exceeds binding MTU, pktbuf must be dropped
@@ -126,6 +163,7 @@
 	size_t pkt_size;
 	size_t pkt_header;
 	size_t pkt_trailer;
+	void *tx_storage;
 	int (*start)(struct mctp_binding *binding);
 	int (*tx)(struct mctp_binding *binding, struct mctp_pktbuf *pkt);
 	mctp_rx_fn control_rx;
@@ -141,8 +179,12 @@
 void mctp_bus_rx(struct mctp_binding *binding, struct mctp_pktbuf *pkt);
 
 /* environment-specific allocation */
-void mctp_set_alloc_ops(void *(*alloc)(size_t), void (*free)(void *),
-			void *(realloc)(void *, size_t));
+void mctp_set_alloc_ops(void *(*m_alloc)(size_t), void (*m_free)(void *),
+			void *(*m_msg_alloc)(size_t, void *),
+			void (*m_msg_free)(void *, void *));
+/* Gets/sets context that will be passed to custom m_msg_ ops */
+void *mctp_get_alloc_ctx(struct mctp *mctp);
+void mctp_set_alloc_ctx(struct mctp *mctp, void *ctx);
 
 /* environment-specific logging */