netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: "Russell King (Oracle)" <linux@armlinux.org.uk>
To: Christian Marangi <ansuelsmth@gmail.com>
Cc: "Andrew Lunn" <andrew+netdev@lunn.ch>,
	"David S. Miller" <davem@davemloft.net>,
	"Eric Dumazet" <edumazet@google.com>,
	"Jakub Kicinski" <kuba@kernel.org>,
	"Paolo Abeni" <pabeni@redhat.com>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Heiner Kallweit" <hkallweit1@gmail.com>,
	"Florian Fainelli" <florian.fainelli@broadcom.com>,
	"Broadcom internal kernel review list"
	<bcm-kernel-feedback-list@broadcom.com>,
	"Marek Behún" <kabel@kernel.org>,
	"Andrei Botila" <andrei.botila@oss.nxp.com>,
	"Sabrina Dubroca" <sd@queasysnail.net>,
	"Eric Woudstra" <ericwouds@gmail.com>,
	"Daniel Golle" <daniel@makrotopia.org>,
	netdev@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.or
Subject: Re: [net-next PATCH v7 5/6] net: phy: Add support for Aeonsemi AS21xxx PHYs
Date: Tue, 15 Apr 2025 10:37:49 +0100	[thread overview]
Message-ID: <Z_4o7SBGxHBdjWFZ@shell.armlinux.org.uk> (raw)
In-Reply-To: <20250410095443.30848-6-ansuelsmth@gmail.com>

On Thu, Apr 10, 2025 at 11:53:35AM +0200, Christian Marangi wrote:
> +#define VEND1_IPC_DATA0			0x5808
> +#define VEND1_IPC_DATA1			0x5809
> +#define VEND1_IPC_DATA2			0x580a
> +#define VEND1_IPC_DATA3			0x580b
> +#define VEND1_IPC_DATA4			0x580c
> +#define VEND1_IPC_DATA5			0x580d
> +#define VEND1_IPC_DATA6			0x580e
> +#define VEND1_IPC_DATA7			0x580f

Do you use any of these other than the first? If not, please remove
them, maybe adding a comment before the first stating that there are
8 registers.

> +static int aeon_ipcs_wait_cmd(struct phy_device *phydev, bool parity_status)
> +{
> +	u16 val;
> +
> +	/* Exit condition logic:
> +	 * - Wait for parity bit equal
> +	 * - Wait for status success, error OR ready
> +	 */
> +	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS, val,
> +					 FIELD_GET(AEON_IPC_STS_PARITY, val) == parity_status &&
> +					 (val & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_RCVD &&
> +					 (val & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_PROCESS &&
> +					 (val & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_BUSY,
> +					 AEON_IPC_DELAY, AEON_IPC_TIMEOUT, false);

Hmm. I'm wondering whether:

static bool aeon_ipc_ready(u16 val, bool parity_status)
{
	u16 status;

	if (FIELD_GET(AEON_IPC_STS_PARITY, val) != parity_status)
		return false;

	status = val & AEON_IPC_STS_STATUS;

	return status != AEON_IPC_STS_STATUS_RCVD &&
	       status != AEON_IPC_STS_STATUS_PROCESS &&
	       status != AEON_IPC_STS_STATUS_BUSY;
}

would be better, and then maybe you can fit the code into less than 80
columns. I'm not a fan of FIELD_PREP_CONST() when it causes differing
usage patterns like the above (FIELD_GET(AEON_IPC_STS_STATUS, val)
would match the coding style, and probably makes no difference to the
code emitted.)

> +}
> +
> +static int aeon_ipc_send_cmd(struct phy_device *phydev,
> +			     struct as21xxx_priv *priv,
> +			     u16 cmd, u16 *ret_sts)
> +{
> +	bool curr_parity;
> +	int ret;
> +
> +	/* The IPC sync by using a single parity bit.
> +	 * Each CMD have alternately this bit set or clear
> +	 * to understand correct flow and packet order.
> +	 */
> +	curr_parity = priv->parity_status;
> +	if (priv->parity_status)
> +		cmd |= AEON_IPC_CMD_PARITY;
> +
> +	/* Always update parity for next packet */
> +	priv->parity_status = !priv->parity_status;
> +
> +	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_CMD, cmd);
> +	if (ret)
> +		return ret;
> +
> +	/* Wait for packet to be processed */
> +	usleep_range(AEON_IPC_DELAY, AEON_IPC_DELAY + 5000);
> +
> +	/* With no ret_sts, ignore waiting for packet completion
> +	 * (ipc parity bit sync)
> +	 */
> +	if (!ret_sts)
> +		return 0;
> +
> +	ret = aeon_ipcs_wait_cmd(phydev, curr_parity);
> +	if (ret)
> +		return ret;
> +
> +	ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_STS);
> +	if (ret < 0)
> +		return ret;
> +
> +	*ret_sts = ret;
> +	if ((*ret_sts & AEON_IPC_STS_STATUS) != AEON_IPC_STS_STATUS_SUCCESS)
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static int aeon_ipc_send_msg(struct phy_device *phydev,
> +			     u16 opcode, u16 *data, unsigned int data_len,
> +			     u16 *ret_sts)
> +{
> +	struct as21xxx_priv *priv = phydev->priv;
> +	u16 cmd;
> +	int ret;
> +	int i;
> +
> +	/* IPC have a max of 8 register to transfer data,
> +	 * make sure we never exceed this.
> +	 */
> +	if (data_len > AEON_IPC_DATA_MAX)
> +		return -EINVAL;
> +
> +	mutex_lock(&priv->ipc_lock);
> +
> +	for (i = 0; i < data_len / sizeof(u16); i++)
> +		phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i),
> +			      data[i]);
> +
> +	cmd = FIELD_PREP(AEON_IPC_CMD_SIZE, data_len) |
> +	      FIELD_PREP(AEON_IPC_CMD_OPCODE, opcode);
> +	ret = aeon_ipc_send_cmd(phydev, priv, cmd, ret_sts);
> +	if (ret)
> +		phydev_err(phydev, "failed to send ipc msg for %x: %d\n",
> +			   opcode, ret);
> +
> +	mutex_unlock(&priv->ipc_lock);
> +
> +	return ret;
> +}
> +
> +static int aeon_ipc_rcv_msg(struct phy_device *phydev,
> +			    u16 ret_sts, u16 *data)
> +{
> +	struct as21xxx_priv *priv = phydev->priv;
> +	unsigned int size;
> +	int ret;
> +	int i;
> +
> +	if ((ret_sts & AEON_IPC_STS_STATUS) == AEON_IPC_STS_STATUS_ERROR)
> +		return -EINVAL;
> +
> +	/* Prevent IPC from stack smashing the kernel */
> +	size = FIELD_GET(AEON_IPC_STS_SIZE, ret_sts);
> +	if (size > AEON_IPC_DATA_MAX)
> +		return -EINVAL;
> +
> +	mutex_lock(&priv->ipc_lock);
> +
> +	for (i = 0; i < DIV_ROUND_UP(size, sizeof(u16)); i++) {
> +		ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_IPC_DATA(i));
> +		if (ret < 0) {
> +			size = ret;
> +			goto out;
> +		}
> +
> +		data[i] = ret;
> +	}
> +
> +out:
> +	mutex_unlock(&priv->ipc_lock);

I think the locking here is suspicious.

aeon_ipc_send_msg() takes the lock, writes the registers, and waits for
the success signal, returning the status, and then drops the lock.

Time passes.

aeon_ipc_rcv_msg() is then called, which extracts the number of bytes to
be read from the returned status, takes the lock, reads the registers,
and then drops the lock.

So, what can happen is:

	Thread 1			Thread 2
	aeon_ipc_send_msg()
					aeon_ipc_send_msg()
	aeon_ipc_rcv_msg()
					aeon_ipc_rcv_msg()

Which means thread 1 reads the reply from thread 2's message.

So, this lock does nothing to really protect against the IPC mechanism
against races.

I think you need to combine both into a single function that takes the
lock once and releases it once.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!

  parent reply	other threads:[~2025-04-15  9:38 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-04-10  9:53 [net-next PATCH v7 0/6] net: phy: Add support for new Aeonsemi PHYs Christian Marangi
2025-04-10  9:53 ` [net-next PATCH v7 1/6] net: phy: pass PHY driver to .match_phy_device OP Christian Marangi
2025-04-10  9:53 ` [net-next PATCH v7 2/6] net: phy: bcm87xx: simplify " Christian Marangi
2025-04-10  9:53 ` [net-next PATCH v7 3/6] net: phy: nxp-c45-tja11xx: " Christian Marangi
2025-04-15 10:56   ` Andrei Botila
2025-04-10  9:53 ` [net-next PATCH v7 4/6] net: phy: introduce genphy_match_phy_device() Christian Marangi
2025-04-10  9:53 ` [net-next PATCH v7 5/6] net: phy: Add support for Aeonsemi AS21xxx PHYs Christian Marangi
2025-04-14 23:36   ` Jakub Kicinski
2025-04-15  1:14   ` Andrew Lunn
2025-04-15 11:19     ` Christian Marangi
2025-04-15  9:37   ` Russell King (Oracle) [this message]
2025-04-15 11:24     ` Christian Marangi
2025-04-15 12:33       ` Andrew Lunn
2025-04-15 12:55       ` Russell King (Oracle)
2025-04-15 13:04         ` Christian Marangi
2025-04-10  9:53 ` [net-next PATCH v7 6/6] dt-bindings: net: Document support for Aeonsemi PHYs Christian Marangi
2025-04-15  9:56 ` [net-next PATCH v7 0/6] net: phy: Add support for new " Russell King (Oracle)

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=Z_4o7SBGxHBdjWFZ@shell.armlinux.org.uk \
    --to=linux@armlinux.org.uk \
    --cc=andrei.botila@oss.nxp.com \
    --cc=andrew+netdev@lunn.ch \
    --cc=ansuelsmth@gmail.com \
    --cc=bcm-kernel-feedback-list@broadcom.com \
    --cc=conor+dt@kernel.org \
    --cc=daniel@makrotopia.org \
    --cc=davem@davemloft.net \
    --cc=devicetree@vger.kernel.org \
    --cc=edumazet@google.com \
    --cc=ericwouds@gmail.com \
    --cc=florian.fainelli@broadcom.com \
    --cc=hkallweit1@gmail.com \
    --cc=kabel@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.or \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    --cc=robh@kernel.org \
    --cc=sd@queasysnail.net \
    /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).