Message ID | 20220221184631.252308-3-alvin@pqrs.dk (mailing list archive) |
---|---|
State | Accepted |
Commit | 2796728460b822d549841e0341752b263dc265c4 |
Delegated to: | Netdev Maintainers |
Headers | show |
Series | net: dsa: realtek: fix PHY register read corruption | expand |
On Mon, Feb 21, 2022 at 07:46:31PM +0100, Alvin Šipraga wrote: > To fix this problem, one must guard against regmap access while the > PHY indirect register read is executing. Fix this by using the newly > introduced "nolock" regmap in all PHY-related functions, and by aquiring > the regmap mutex at the top level of the PHY register access callbacks. > Although no issue has been observed with PHY register _writes_, this > change also serializes the indirect access method there. This is done > purely as a matter of convenience and for reasons of symmetry. Reviewed-by: Vladimir Oltean <olteanv@gmail.com>
On Mon, Feb 21, 2022 at 7:46 PM Alvin Šipraga <alvin@pqrs.dk> wrote: > Realtek switches in the rtl8365mb family can access the PHY registers of > the internal PHYs via the switch registers. This method is called > indirect access. At a high level, the indirect PHY register access > method involves reading and writing some special switch registers in a > particular sequence. This works for both SMI and MDIO connected > switches. What I worry about is whether we need to do a similar patch to rtl8366rb_phy_read() and rtl8366rb_phy_write() in rtl8366rb.c? And what about the upcoming rtl8367 driver? > To fix this problem, one must guard against regmap access while the > PHY indirect register read is executing. Fix this by using the newly > introduced "nolock" regmap in all PHY-related functions, and by aquiring > the regmap mutex at the top level of the PHY register access callbacks. > Although no issue has been observed with PHY register _writes_, this > change also serializes the indirect access method there. This is done > purely as a matter of convenience and for reasons of symmetry. > > Fixes: 4af2950c50c8 ("net: dsa: realtek-smi: add rtl8365mb subdriver for RTL8365MB-VC") > Link: https://lore.kernel.org/netdev/CAJq09z5FCgG-+jVT7uxh1a-0CiiFsoKoHYsAWJtiKwv7LXKofQ@mail.gmail.com/ > Link: https://lore.kernel.org/netdev/871qzwjmtv.fsf@bang-olufsen.dk/ > Reported-by: Arınç ÜNAL <arinc.unal@arinc9.com> > Reported-by: Luiz Angelo Daros de Luca <luizluca@gmail.com> > Signed-off-by: Alvin Šipraga <alsi@bang-olufsen.dk> This is a beautiful patch. Excellent job. Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Yours, Linus Walleij
Hi Linus, Linus Walleij <linus.walleij@linaro.org> writes: > On Mon, Feb 21, 2022 at 7:46 PM Alvin Šipraga <alvin@pqrs.dk> wrote: > >> Realtek switches in the rtl8365mb family can access the PHY registers of >> the internal PHYs via the switch registers. This method is called >> indirect access. At a high level, the indirect PHY register access >> method involves reading and writing some special switch registers in a >> particular sequence. This works for both SMI and MDIO connected >> switches. > > What I worry about is whether we need to do a similar patch to > rtl8366rb_phy_read() and rtl8366rb_phy_write() in > rtl8366rb.c? Unfortunately I do not have the hardware to test rtl8366rb.c, so I can only speculate. But I gave some hints in the commit message which might help in checking whether or not it is an issue on that hardware as well. The code for rtl8366rb_phy_read() looks similar, but since this is a quirk of the hardware design, it could be that it is not necessary. The only way is to test. If you or somebody else can confirm that it is an issue for RTL8366RB as well, I will happily send a patch to the list for testing. You can for example try spamming PHY register reads with phytool while also reading off switch registers via regmap debugfs. See also the discussion in [1]. [1] https://lore.kernel.org/netdev/878rukib4f.fsf@bang-olufsen.dk/ > > And what about the upcoming rtl8367 driver? Is this a hypothetical driver or have I missed something on the list? If you mean the changes Luiz has sent for net-next, then it is covered by this series. Those switches (RTL8367S, RTL8367RB?) are in the same family as the RTL8365MB-VC and are supported by rtl8365mb.c. > >> To fix this problem, one must guard against regmap access while the >> PHY indirect register read is executing. Fix this by using the newly >> introduced "nolock" regmap in all PHY-related functions, and by aquiring >> the regmap mutex at the top level of the PHY register access callbacks. >> Although no issue has been observed with PHY register _writes_, this >> change also serializes the indirect access method there. This is done >> purely as a matter of convenience and for reasons of symmetry. >> >> Fixes: 4af2950c50c8 ("net: dsa: realtek-smi: add rtl8365mb subdriver for RTL8365MB-VC") >> Link: https://lore.kernel.org/netdev/CAJq09z5FCgG-+jVT7uxh1a-0CiiFsoKoHYsAWJtiKwv7LXKofQ@mail.gmail.com/ >> Link: https://lore.kernel.org/netdev/871qzwjmtv.fsf@bang-olufsen.dk/ >> Reported-by: Arınç ÜNAL <arinc.unal@arinc9.com> >> Reported-by: Luiz Angelo Daros de Luca <luizluca@gmail.com> >> Signed-off-by: Alvin Šipraga <alsi@bang-olufsen.dk> > > This is a beautiful patch. Excellent job. > Reviewed-by: Linus Walleij <linus.walleij@linaro.org> Thank you Linus for your kind words. Kind regards, Alvin
On Tue, Feb 22, 2022 at 1:19 AM Alvin Šipraga <ALSI@bang-olufsen.dk> wrote: > > On Mon, Feb 21, 2022 at 7:46 PM Alvin Šipraga <alvin@pqrs.dk> wrote: > > What I worry about is whether we need to do a similar patch to > > rtl8366rb_phy_read() and rtl8366rb_phy_write() in > > rtl8366rb.c? > > Unfortunately I do not have the hardware to test rtl8366rb.c, so I can > only speculate. But I gave some hints in the commit message which might > help in checking whether or not it is an issue on that hardware as > well. The code for rtl8366rb_phy_read() looks similar, but since this is > a quirk of the hardware design, it could be that it is not > necessary. The only way is to test. I can test it. > If you or somebody else can confirm that it is an issue for RTL8366RB as > well, I will happily send a patch to the list for testing. You can for > example try spamming PHY register reads with phytool while also reading > off switch registers via regmap debugfs. See also the discussion in [1]. > > [1] https://lore.kernel.org/netdev/878rukib4f.fsf@bang-olufsen.dk/ If you have time to write a patch I'd love testing it! Yours, Linus Walleij
Linus Walleij <linus.walleij@linaro.org> writes: > On Tue, Feb 22, 2022 at 1:19 AM Alvin Šipraga <ALSI@bang-olufsen.dk> wrote: >> > On Mon, Feb 21, 2022 at 7:46 PM Alvin Šipraga <alvin@pqrs.dk> wrote: > >> > What I worry about is whether we need to do a similar patch to >> > rtl8366rb_phy_read() and rtl8366rb_phy_write() in >> > rtl8366rb.c? >> >> Unfortunately I do not have the hardware to test rtl8366rb.c, so I can >> only speculate. But I gave some hints in the commit message which might >> help in checking whether or not it is an issue on that hardware as >> well. The code for rtl8366rb_phy_read() looks similar, but since this is >> a quirk of the hardware design, it could be that it is not >> necessary. The only way is to test. > > I can test it. Great! I have attached a test patch to insert these stray register accesses inside the PHY read/write functions: 0001-TEST-net-dsa-realtek-rtl8366rb-provoke-stray-reg-acc.patch Set the module parameter stray_access to 1 in order to enable stray register access. I did this in case you don't build it as a module but still want to do A/B testing - just remove it if I goofed. > >> If you or somebody else can confirm that it is an issue for RTL8366RB as >> well, I will happily send a patch to the list for testing. You can for >> example try spamming PHY register reads with phytool while also reading >> off switch registers via regmap debugfs. See also the discussion in [1]. >> >> [1] https://lore.kernel.org/netdev/878rukib4f.fsf@bang-olufsen.dk/ > > If you have time to write a patch I'd love testing it! Attached also a patch with a proposed fix - if indeed it is an issue - along the same lines as the second patch in this series. You'll also need the first patch from this series (attached for convenience): 0001-net-dsa-realtek-allow-subdrivers-to-externally-lock-.patch 0002-TEST-net-dsa-realtek-rtl8366rb-serialize-indirect-PH.patch All patches are against net-next and build-tested only, so you might want to double check if the results are unexpected. Kind regards, Alvin > > Yours, > Linus Walleij
diff --git a/drivers/net/dsa/realtek/rtl8365mb.c b/drivers/net/dsa/realtek/rtl8365mb.c index 2ed592147c20..c39d6b744597 100644 --- a/drivers/net/dsa/realtek/rtl8365mb.c +++ b/drivers/net/dsa/realtek/rtl8365mb.c @@ -590,7 +590,7 @@ static int rtl8365mb_phy_poll_busy(struct realtek_priv *priv) { u32 val; - return regmap_read_poll_timeout(priv->map, + return regmap_read_poll_timeout(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_STATUS_REG, val, !val, 10, 100); } @@ -604,7 +604,7 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, /* Set OCP prefix */ val = FIELD_GET(RTL8365MB_PHY_OCP_ADDR_PREFIX_MASK, ocp_addr); ret = regmap_update_bits( - priv->map, RTL8365MB_GPHY_OCP_MSB_0_REG, + priv->map_nolock, RTL8365MB_GPHY_OCP_MSB_0_REG, RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, FIELD_PREP(RTL8365MB_GPHY_OCP_MSB_0_CFG_CPU_OCPADR_MASK, val)); if (ret) @@ -617,8 +617,8 @@ static int rtl8365mb_phy_ocp_prepare(struct realtek_priv *priv, int phy, ocp_addr >> 1); val |= FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_ADDRESS_OCPADR_9_6_MASK, ocp_addr >> 6); - ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, - val); + ret = regmap_write(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_ADDRESS_REG, val); if (ret) return ret; @@ -631,36 +631,42 @@ static int rtl8365mb_phy_ocp_read(struct realtek_priv *priv, int phy, u32 val; int ret; + mutex_lock(&priv->map_lock); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) - return ret; + goto out; ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); if (ret) - return ret; + goto out; /* Execute read operation */ val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_RW_READ); - ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); + ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, + val); if (ret) - return ret; + goto out; ret = rtl8365mb_phy_poll_busy(priv); if (ret) - return ret; + goto out; /* Get PHY register data */ - ret = regmap_read(priv->map, RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, - &val); + ret = regmap_read(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_READ_DATA_REG, &val); if (ret) - return ret; + goto out; *data = val & 0xFFFF; - return 0; +out: + mutex_unlock(&priv->map_lock); + + return ret; } static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, @@ -669,32 +675,38 @@ static int rtl8365mb_phy_ocp_write(struct realtek_priv *priv, int phy, u32 val; int ret; + mutex_lock(&priv->map_lock); + ret = rtl8365mb_phy_poll_busy(priv); if (ret) - return ret; + goto out; ret = rtl8365mb_phy_ocp_prepare(priv, phy, ocp_addr); if (ret) - return ret; + goto out; /* Set PHY register data */ - ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, - data); + ret = regmap_write(priv->map_nolock, + RTL8365MB_INDIRECT_ACCESS_WRITE_DATA_REG, data); if (ret) - return ret; + goto out; /* Execute write operation */ val = FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_CMD_VALUE) | FIELD_PREP(RTL8365MB_INDIRECT_ACCESS_CTRL_RW_MASK, RTL8365MB_INDIRECT_ACCESS_CTRL_RW_WRITE); - ret = regmap_write(priv->map, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, val); + ret = regmap_write(priv->map_nolock, RTL8365MB_INDIRECT_ACCESS_CTRL_REG, + val); if (ret) - return ret; + goto out; ret = rtl8365mb_phy_poll_busy(priv); if (ret) - return ret; + goto out; + +out: + mutex_unlock(&priv->map_lock); return 0; }