From: Jeremy Kerr <jk@codeconstruct.com.au>
To: Klaus Jensen <its@irrelevant.dk>, qemu-devel@nongnu.org
Cc: "Andrew Jeffery" <andrew@aj.id.au>,
"Keith Busch" <kbusch@kernel.org>,
"Corey Minyard" <cminyard@mvista.com>,
"Peter Delevoryas" <peter@pjd.dev>,
qemu-arm@nongnu.org, "Peter Maydell" <peter.maydell@linaro.org>,
qemu-block@nongnu.org, "Joel Stanley" <joel@jms.id.au>,
"Cédric Le Goater" <clg@kaod.org>,
"Klaus Jensen" <k.jensen@samsung.com>,
"Matt Johnston" <matt@codeconstruct.com.au>
Subject: Re: [PATCH RFC 2/3] hw/i2c: add mctp core
Date: Fri, 18 Nov 2022 13:56:50 +0800 [thread overview]
Message-ID: <d8a8549c6fc29650131046ee00b7968ebedf886b.camel@codeconstruct.com.au> (raw)
In-Reply-To: <20221116084312.35808-3-its@irrelevant.dk>
Hi Klaus,
> Add an abstract MCTP over I2C endpoint model. This implements MCTP
> control message handling as well as handling the actual I2C transport
> (packetization).
>
> Devices are intended to derive from this and implement the class
> methods.
Looks good, nice to see how it's used by the nmi device later too.
A couple of issues with the state machine though, comments inline, and
a bit of a patch below.
> +static void i2c_mctp_tx(void *opaque)
> +{
> + DeviceState *dev = DEVICE(opaque);
> + I2CBus *i2c = I2C_BUS(qdev_get_parent_bus(dev));
> + I2CSlave *slave = I2C_SLAVE(dev);
> + MCTPI2CEndpoint *mctp = MCTP_I2C_ENDPOINT(dev);
> + MCTPI2CEndpointClass *mc = MCTP_I2C_ENDPOINT_GET_CLASS(mctp);
> + MCTPI2CPacket *pkt = (MCTPI2CPacket *)mctp->buffer;
> + uint8_t flags = 0;
> +
> + switch (mctp->tx.state) {
> + case I2C_MCTP_STATE_TX_SEND_BYTE:
> + if (mctp->pos < mctp->len) {
> + uint8_t byte = mctp->buffer[mctp->pos];
> +
> + trace_i2c_mctp_tx_send_byte(mctp->pos, byte);
> +
> + /* send next byte */
> + i2c_send_async(i2c, byte);
> +
> + mctp->pos++;
> +
> + break;
> + }
> +
> + /* packet sent */
> + i2c_end_transfer(i2c);
If we're sending a control message, mctp->len will be set to the control
msg len here, then:
> +
> + /* fall through */
> +
> + case I2C_MCTP_STATE_TX_START_SEND:
> + if (mctp->tx.is_control) {
> + /* packet payload is already in buffer */
> + flags |= MCTP_H_FLAGS_SOM | MCTP_H_FLAGS_EOM;
> + } else {
> + /* get message bytes from derived device */
> + mctp->len = mc->get_message_bytes(mctp, pkt->mctp.payload,
> + I2C_MCTP_MAXMTU, &flags);
> + }
... it doesn't get cleared above, so:
> +
> + if (!mctp->len) {
... we don't hit this conditional, and hence keep sending unlimited
bytes. This presents as continuous interrupts to the aspeed i2c driver
when replying to any control message.
I think we need a mctp->len = 0 with the i2c_end_transfer(). With that,
I can get control protocol communication working with mctpd.
> +static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event event)
> +{
> + MCTPI2CEndpoint *mctp = MCTP_I2C_ENDPOINT(i2c);
> + MCTPI2CEndpointClass *mc = MCTP_I2C_ENDPOINT_GET_CLASS(mctp);
> + MCTPI2CPacket *pkt = (MCTPI2CPacket *)mctp->buffer;
> + size_t payload_len;
> + uint8_t pec;
> +
> + switch (event) {
> + case I2C_START_SEND:
> + if (mctp->state != I2C_MCTP_STATE_IDLE) {
> + return -1;
mctp->state may (validly) be I2C_MCTP_STATE_RX here, if we're receiving
the start event for the second packet of a multi-packet message.
> + }
> +
> + /* the i2c core eats the slave address, so put it back in */
> + pkt->i2c.dest = i2c->address << 1;
> + mctp->len = 1;
> +
> + mctp->state = I2C_MCTP_STATE_RX_STARTED;
> +
> + return 0;
> +
> + case I2C_FINISH:
> + payload_len = mctp->len - (1 + offsetof(MCTPI2CPacket, mctp.payload));
> +
> + if (pkt->i2c.byte_count + 3 != mctp->len - 1) {
> + trace_i2c_mctp_drop_invalid_length(pkt->i2c.byte_count +
> 3,
> + mctp->len - 1);
> + goto drop;
> + }
> +
> + pec = i2c_smbus_pec(0, mctp->buffer, mctp->len - 1);
> + if (mctp->buffer[mctp->len - 1] != pec) {
> + trace_i2c_mctp_drop_invalid_pec(mctp->buffer[mctp->len - 1], pec);
> + goto drop;
> + }
> +
> + if (pkt->mctp.hdr.eid.dest != mctp->my_eid) {
> + trace_i2c_mctp_drop_invalid_eid(pkt->mctp.hdr.eid.dest,
> + mctp->my_eid);
> + goto drop;
> + }
> +
> + if (pkt->mctp.hdr.flags & MCTP_H_FLAGS_SOM) {
> + mctp->tx.is_control = false;
> +
> + if (mctp->state == I2C_MCTP_STATE_RX) {
> + mc->reset_message(mctp);
> + }
> +
> + mctp->state = I2C_MCTP_STATE_RX;
> +
> + mctp->tx.addr = pkt->i2c.source;
> + mctp->tx.eid = pkt->mctp.hdr.eid.source;
> + mctp->tx.flags = pkt->mctp.hdr.flags & 0x7;
> + mctp->tx.pktseq = (pkt->mctp.hdr.flags >> 4) & 0x3;
> +
> + if ((pkt->mctp.payload[0] & 0x7f) == MCTP_MESSAGE_TYPE_CONTROL) {
> + mctp->tx.is_control = true;
> +
> + i2c_mctp_handle_control(mctp);
> +
> + return 0;
> + }
> + } else if (mctp->state == I2C_MCTP_STATE_RX_STARTED) {
> + trace_i2c_mctp_drop("expected SOM");
> + goto drop;
> + } else if (((pkt->mctp.hdr.flags >> 4) & 0x3) != (mctp->tx.pktseq++ & 0x3)) {
The pktseq is the sequence number of the last packet seen, so you want a
pre-increment there: ++mctp->tx.pktseq & 0x3
} else if (((pkt->mctp.hdr.flags >> 4) & 0x3) != (++mctp->tx.pktseq & 0x3)) {
With those changes, I can get control protocol going, and multi-packet
messages work. There's a couple of failures from unsupported commands,
but otherwise looks good:
# mctp addr add 8 dev mctpi2c6
# mctp link set mctpi2c6 up
# mctp link set mctpi2c6 mtu 254
# systemctl restart mctpd
# busctl --no-pager call xyz.openbmc_project.MCTP \
/xyz/openbmc_project/mctp au.com.CodeConstruct.MCTP \
SetupEndpoint say mctpi2c6 1 0x1d
yisb 9 1 "/xyz/openbmc_project/mctp/1/9" true
# mctp route del 9 via mctpi2c6
# mctp route add 9 via mctpi2c6 mtu 68
# mi-mctp 1 9 info
nmi message type 0x2 not handled
Identify Controller failed, no quirks applied
NVMe MI subsys info:
num ports: 1
major ver: 1
minor ver: 1
NVMe MI port info:
port 0
type SMBus[2]
MCTP MTU: 64
MEB size: 0
SMBus address: 0x00
VPD access freq: 0x00
MCTP address: 0x1d
MCTP access freq: 0x01
NVMe basic management: disabled
nmi command 0x1 not handled
mi-mctp: can't perform Health Status Poll operation: Success
# mi-mctp 1 9 get-config 0
nmi message type 0x2 not handled
Identify Controller failed, no quirks applied
SMBus access frequency (port 0): 100k [0x1]
MCTP MTU (port 0): 64
I've included a patch below, with some fixes for the above, in case that
helps.
Cheers,
Jeremy
diff --git a/hw/i2c/mctp.c b/hw/i2c/mctp.c
index 46376de95a..1775deb46f 100644
--- a/hw/i2c/mctp.c
+++ b/hw/i2c/mctp.c
@@ -78,6 +78,9 @@ static void i2c_mctp_tx(void *opaque)
/* packet sent */
i2c_end_transfer(i2c);
+ /* end of any control data */
+ mctp->len = 0;
+
/* fall through */
case I2C_MCTP_STATE_TX_START_SEND:
@@ -228,7 +231,9 @@ static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event event)
switch (event) {
case I2C_START_SEND:
- if (mctp->state != I2C_MCTP_STATE_IDLE) {
+ if (mctp->state == I2C_MCTP_STATE_IDLE) {
+ mctp->state = I2C_MCTP_STATE_RX_STARTED;
+ } else if (mctp->state != I2C_MCTP_STATE_RX) {
return -1;
}
@@ -236,8 +241,6 @@ static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event event)
pkt->i2c.dest = i2c->address << 1;
mctp->len = 1;
- mctp->state = I2C_MCTP_STATE_RX_STARTED;
-
return 0;
case I2C_FINISH:
@@ -285,7 +288,7 @@ static int i2c_mctp_event_cb(I2CSlave *i2c, enum i2c_event event)
} else if (mctp->state == I2C_MCTP_STATE_RX_STARTED) {
trace_i2c_mctp_drop("expected SOM");
goto drop;
- } else if (((pkt->mctp.hdr.flags >> 4) & 0x3) != (mctp->tx.pktseq++ & 0x3)) {
+ } else if (((pkt->mctp.hdr.flags >> 4) & 0x3) != (++mctp->tx.pktseq & 0x3)) {
trace_i2c_mctp_drop_invalid_pktseq((pkt->mctp.hdr.flags >> 4) & 0x3,
mctp->tx.pktseq & 0x3);
goto drop;
next prev parent reply other threads:[~2022-11-18 5:57 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-11-16 8:43 [PATCH 0/3] hw/{i2c, nvme}: mctp endpoint, nvme management interface model Klaus Jensen
2022-11-16 8:43 ` [PATCH maybe-7.2 1/3] hw/i2c: only schedule pending master when bus is idle Klaus Jensen
2022-11-16 13:16 ` Peter Maydell
2022-11-16 13:43 ` Corey Minyard
2022-11-16 15:58 ` Cédric Le Goater
2022-11-17 6:40 ` Klaus Jensen
2022-11-17 6:56 ` Cédric Le Goater
2022-11-17 7:37 ` Klaus Jensen
2022-11-17 8:01 ` Cédric Le Goater
2022-11-17 11:58 ` Klaus Jensen
2022-11-17 13:40 ` Cédric Le Goater
2022-11-18 6:59 ` Klaus Jensen
2022-11-22 8:45 ` Klaus Jensen
2022-11-16 8:43 ` [PATCH RFC 2/3] hw/i2c: add mctp core Klaus Jensen
2022-11-16 14:27 ` Corey Minyard
2022-11-17 6:51 ` Klaus Jensen
2022-11-18 5:56 ` Jeremy Kerr [this message]
2022-11-18 6:15 ` Jeremy Kerr
2022-11-18 7:03 ` Klaus Jensen
2022-11-18 7:09 ` Jeremy Kerr
2022-11-18 7:01 ` Klaus Jensen
2022-11-21 8:04 ` Matt Johnston
2022-11-16 8:43 ` [PATCH RFC 3/3] hw/nvme: add nvme management interface model Klaus Jensen
2022-11-18 7:56 ` Philippe Mathieu-Daudé
2022-11-18 7:58 ` Klaus Jensen
2022-11-16 9:18 ` [PATCH 0/3] hw/{i2c,nvme}: mctp endpoint, " Jeremy Kerr
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=d8a8549c6fc29650131046ee00b7968ebedf886b.camel@codeconstruct.com.au \
--to=jk@codeconstruct.com.au \
--cc=andrew@aj.id.au \
--cc=clg@kaod.org \
--cc=cminyard@mvista.com \
--cc=its@irrelevant.dk \
--cc=joel@jms.id.au \
--cc=k.jensen@samsung.com \
--cc=kbusch@kernel.org \
--cc=matt@codeconstruct.com.au \
--cc=peter.maydell@linaro.org \
--cc=peter@pjd.dev \
--cc=qemu-arm@nongnu.org \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).