astlpc: Add mctp_astlpc_tx_done() API
Add the mctp_astlpc_tx_done() API to help with packet transfer
performance when using the LPC binding with the Aspeed BMC. The goal
of the API is to tell the caller that the Transmit buffer has been
consumed by the remote side, i.e. the Rx_complete command has been
received locally. It can be helpful on the host side because of the
way the Aspeed BMC implements the KCS devices.
The Aspeed BMC's KCS device doesn't provide an interrupt when the ODR
register is read by the remote client/host. To workaround it, the
linux KCS driver for Aspeed arms a timer to periodically check (every
0.5 second in the current implementation) the state of the register
and generate an "Output Buffer Empty" (OBE) event to wake up any
client on the BMC, for example the mctp-demux-daemon.
Typically, the mctp-demux-daemon waits in the poll() system call and
wakes up when it receives a packet. When it's coming from the LPC bus
and KCS device, the remote writes a Tx_begin command in the IDR
register, which does generate an interrupt. To acknowledge the packet,
the mctp-demux-daemon writes a Tx_complete command in the ODR and can
then dispatch the request to the proper recipient (i.e. the PLDM
daemon). When it wants to send a message on the LPC bus, the
mctp-demux-daemon needs to wait till the ODR register has been read by
the remote. Because we don't have an interrupt to know when that
happens, the mctp-demux-daemon waits in poll() and will be awaken when
an OBE event is generated by a background thread processing the timer
interrupt. So when the mctp-demux-daemon enters poll() with something
to send on the LPC bus, if the ODR is not available immediately, it
will only be sent after the timer fires. Which could take up to 0.5s
with the current driver implementation.
So when the host sends a PLDM request, it is therefore crucial, for
good performance, that it reads the Rx_complete command out of the ODR
very quickly when it's sending a MCTP packet as to free it and make
sure the mctp-demux-daemon can send the reply immediately instead of
having to wait in poll(). That's where the new mctp_astlpc_tx_done()
helps: immediately after sending a message, the host can call
repeatedly mctp_astlpc_poll() to read the KCS device status and read
the ODR as fast as possible and with the mctp_astlpc_tx_done() API, it
knows when to stop. Pseudo code looks like this (ignoring that we
should timeout out of the loop after a while):
mctp_message_tx()
while (!mctp_astlpc_tx_done(astlpc)) {
mctp_astlpc_poll(astlpc);
}
Note that the API, while generic, is (so far) only useful when called
from a remote LPC endpoint.
Change-Id: I5e6d62aa142fe97449ccf9c9a2ade3cf45d02bf6
Signed-off-by: Frederic Barrat <fbarrat@linux.ibm.com>
Signed-off-by: Andrew Jeffery <andrew@aj.id.au>
diff --git a/astlpc.c b/astlpc.c
index 5d51d64..c8f7a68 100644
--- a/astlpc.c
+++ b/astlpc.c
@@ -1142,6 +1142,11 @@
return rc;
}
+bool mctp_astlpc_tx_done(struct mctp_binding_astlpc *astlpc)
+{
+ return astlpc->layout.tx.state == buffer_state_acquired;
+}
+
int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc)
{
uint8_t status, data;
diff --git a/libmctp-astlpc.h b/libmctp-astlpc.h
index 4d90894..110b998 100644
--- a/libmctp-astlpc.h
+++ b/libmctp-astlpc.h
@@ -41,6 +41,7 @@
struct mctp_binding *mctp_binding_astlpc_core(struct mctp_binding_astlpc *b);
+bool mctp_astlpc_tx_done(struct mctp_binding_astlpc *astlpc);
int mctp_astlpc_poll(struct mctp_binding_astlpc *astlpc);
/* fileio-based interface */