X-Received: by 2002:a17:90b:4ac4:b0:1ba:3b4:d3c with SMTP id mh4-20020a17090b4ac400b001ba03b40d3cmr2379105pjb.201.1645507293261; Mon, 21 Feb 2022 21:21:33 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1645507293; cv=none; d=google.com; s=arc-20160816; b=0ApvY0ynUFwZG7fmt1SLBt2y94HkTSa0LYaFimEtO/gv8T6s4bs4LbeXoGEJXW83fR Pk49xfEjXOtf3M2SeKsNtlMbLIgyt65eCMVnRh2HyEvBoM7AY0XXJ3HkJnl4IJLyCr75 68mKY3HjEzeeF8XzJukdPeUGyfL/7vgHDO+/2NFwzJ7FwEoGEs0ZTaivgbnqtTpbHAIK N6U81sWfSpIoYjosfs+r0lQjH53Rs+L84eQgf7zTFW0faj5rW7z1Z9fRaBIS19dBfnYm fgBId/B+vsaWDCAWUgZgFpLs/XuBQ8kZa2MKMhzU0B6Fugt2SaJ4vosns8yQ+rfv3TDf O9Sw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:content-transfer-encoding:mime-version :references:in-reply-to:message-id:date:subject:cc:to:from :dkim-signature; bh=AH3hQsUn1T/lXJhfiUYvUkUtkgFsibZK+A7UVJghwao=; b=HijSFsB5st84ARL0VxoAd8XHmm5QXfFwljZUclv36En++dtxzSMrqCPbjEZKS+oUPN 0JNCjx2qCf1OptykgCdGpQGyklk8MssE+k7ZphYDabsyWIQYYaBA96OrTDBbbV1y7XZk cgVe0zRtmtQLHQmJJJIs163xKUGiHBh3po8P0IGhcZ3xb0lS+41geaLnavXrxGL3dD90 QexwS3vlcEujZhxjxYCS59wEpuZeIcjvcqzaUscnV/VaczCxEvGFlvUOh951sMrBBm8N 0n/rJnL67+zbEhYDBtk9vRlZBr66ed/SqRlApDCCPk68ZI4fvd2lfWuzLNOD3d4X3RMK BvFA== ARC-Authentication-Results: i=1; mx.google.com; dkim=temperror (no key for signature) header.i=@pqrs.dk header.s=google header.b=WSZtf6kJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from lindbergh.monkeyblade.net (lindbergh.monkeyblade.net. [2620:137:e000::1:18]) by mx.google.com with ESMTPS id q7si4642286plh.444.2022.02.21.21.21.32 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Feb 2022 21:21:33 -0800 (PST) Received-SPF: pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) client-ip=2620:137:e000::1:18; Authentication-Results: mx.google.com; dkim=temperror (no key for signature) header.i=@pqrs.dk header.s=google header.b=WSZtf6kJ; spf=pass (google.com: domain of linux-kernel-owner@vger.kernel.org designates 2620:137:e000::1:18 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by lindbergh.monkeyblade.net (Postfix) with ESMTP id ECBA8F65E1; Mon, 21 Feb 2022 20:52:40 -0800 (PST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S232360AbiBUSsF (ORCPT + 99 others); Mon, 21 Feb 2022 13:48:05 -0500 Received: from mxb-00190b01.gslb.pphosted.com ([23.128.96.19]:32890 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S232354AbiBUSr1 (ORCPT ); Mon, 21 Feb 2022 13:47:27 -0500 Received: from mail-wm1-x32f.google.com (mail-wm1-x32f.google.com [IPv6:2a00:1450:4864:20::32f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 65BD0D97 for ; Mon, 21 Feb 2022 10:46:40 -0800 (PST) Received: by mail-wm1-x32f.google.com with SMTP id k41so10056226wms.0 for ; Mon, 21 Feb 2022 10:46:40 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=pqrs.dk; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=AH3hQsUn1T/lXJhfiUYvUkUtkgFsibZK+A7UVJghwao=; b=WSZtf6kJssqO9GwX4qK8Qh9JEX+JYvAJ1+FTtvGEHvst/HhAgRyxq0kFXR72gUYGQD QEM9b4H7gq9fQfGB0rI6+nxkPcCmNj0lQjEWUZexPtM495o061WHgZOTztW1STWxr+e+ Oq5VC4yZyD1KUfgsSSPrqRg+DqmVcmmtlpeYs= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=AH3hQsUn1T/lXJhfiUYvUkUtkgFsibZK+A7UVJghwao=; b=lg9U9fvg+lqGGRkLCpPqnwXJPJvnTiam4p62EVUPgAExhElWFS9MZw+3GMyqAmLrnv u6s4mosCzVg//Gu78gFur1Ppz+kRzuQYSdFVrWURWEHM3yMqEv94zCWqjwgkfOfCOy1N NgW8zpL+Ihn7KSgq2rN/ThEBd/ACz3d3i9l3qBP47lZFSbHz9/TRnyydyP6nb3pnGJGX V5t8E341wPWR9jZUAvHnR8bLjLcChCeeUKzNAAjV+aFBZZZtiiAsTO4VRrye8eSlUrPE dnvmH3JClvzI11DwcTJ4au/TkDnWYMCQrOPVRaiJd6l+b8uMqmA6wPuU9v3VoRM1jwg/ djtg== X-Gm-Message-State: AOAM533Q9yTmwtPDsOVCVdihuCkMnmGO+6Xi1RgvWdC5dzSHkAULfBQx 0efOueeH/Zw5QLhDRrSDEQi3qw== X-Received: by 2002:a05:600c:3d98:b0:352:cdcf:ef7f with SMTP id bi24-20020a05600c3d9800b00352cdcfef7fmr301271wmb.28.1645469198812; Mon, 21 Feb 2022 10:46:38 -0800 (PST) Received: from capella.. (80.71.142.18.ipv4.parknet.dk. [80.71.142.18]) by smtp.gmail.com with ESMTPSA id 11-20020a05600c26cb00b0037ff53511f2sm140637wmv.31.2022.02.21.10.46.37 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 21 Feb 2022 10:46:38 -0800 (PST) From: =?UTF-8?q?Alvin=20=C5=A0ipraga?= To: Linus Walleij , Andrew Lunn , Vivien Didelot , Florian Fainelli , Vladimir Oltean , "David S. Miller" , Jakub Kicinski , Michael Rasmussen , =?UTF-8?q?Alvin=20=C5=A0ipraga?= Cc: Luiz Angelo Daros de Luca , =?UTF-8?q?Ar=C4=B1n=C3=A7=20=C3=9CNAL?= , netdev@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH v2 net-next 2/2] net: dsa: realtek: rtl8365mb: serialize indirect PHY register access Date: Mon, 21 Feb 2022 19:46:31 +0100 Message-Id: <20220221184631.252308-3-alvin@pqrs.dk> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220221184631.252308-1-alvin@pqrs.dk> References: <20220221184631.252308-1-alvin@pqrs.dk> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-1.7 required=5.0 tests=BAYES_00,DKIM_INVALID, DKIM_SIGNED,HEADER_FROM_DIFFERENT_DOMAINS,MAILING_LIST_MULTI,RDNS_NONE, SPF_HELO_NONE,T_SCC_BODY_TEXT_LINE autolearn=no autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on lindbergh.monkeyblade.net Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org From: Alvin Šipraga 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. Currently the rtl8365mb driver does not take any care to serialize the aforementioned access to the switch registers. In particular, it is permitted for other driver code to access other switch registers while the indirect PHY register access is ongoing. Locking is only done at the regmap level. This, however, is a bug: concurrent register access, even to unrelated switch registers, risks corrupting the PHY register value read back via the indirect access method described above. Arınç reported that the switch sometimes returns nonsense data when reading the PHY registers. In particular, a value of 0 causes the kernel's PHY subsystem to think that the link is down, but since most reads return correct data, the link then flip-flops between up and down over a period of time. The aforementioned bug can be readily observed by: 1. Enabling ftrace events for regmap and mdio 2. Polling BSMR PHY register for a connected port; it should always read the same (e.g. 0x79ed) 3. Wait for step 2 to give a different value Example command for step 2: while true; do phytool read swp2/2/0x01; done On my i.MX8MM, the above steps will yield a bogus value for the BSMR PHY register within a matter of seconds. The interleaved register access it then evident in the trace log: kworker/3:4-70 [003] ....... 1927.139849: regmap_reg_write: ethernet-switch reg=1004 val=bd phytool-16816 [002] ....... 1927.139979: regmap_reg_read: ethernet-switch reg=1f01 val=0 kworker/3:4-70 [003] ....... 1927.140381: regmap_reg_read: ethernet-switch reg=1005 val=0 phytool-16816 [002] ....... 1927.140468: regmap_reg_read: ethernet-switch reg=1d15 val=a69 kworker/3:4-70 [003] ....... 1927.140864: regmap_reg_read: ethernet-switch reg=1003 val=0 phytool-16816 [002] ....... 1927.140955: regmap_reg_write: ethernet-switch reg=1f02 val=2041 kworker/3:4-70 [003] ....... 1927.141390: regmap_reg_read: ethernet-switch reg=1002 val=0 phytool-16816 [002] ....... 1927.141479: regmap_reg_write: ethernet-switch reg=1f00 val=1 kworker/3:4-70 [003] ....... 1927.142311: regmap_reg_write: ethernet-switch reg=1004 val=be phytool-16816 [002] ....... 1927.142410: regmap_reg_read: ethernet-switch reg=1f01 val=0 kworker/3:4-70 [003] ....... 1927.142534: regmap_reg_read: ethernet-switch reg=1005 val=0 phytool-16816 [002] ....... 1927.142618: regmap_reg_read: ethernet-switch reg=1f04 val=0 phytool-16816 [002] ....... 1927.142641: mdio_access: SMI-0 read phy:0x02 reg:0x01 val:0x0000 <- ?! kworker/3:4-70 [003] ....... 1927.143037: regmap_reg_read: ethernet-switch reg=1001 val=0 kworker/3:4-70 [003] ....... 1927.143133: regmap_reg_read: ethernet-switch reg=1000 val=2d89 kworker/3:4-70 [003] ....... 1927.143213: regmap_reg_write: ethernet-switch reg=1004 val=be kworker/3:4-70 [003] ....... 1927.143291: regmap_reg_read: ethernet-switch reg=1005 val=0 kworker/3:4-70 [003] ....... 1927.143368: regmap_reg_read: ethernet-switch reg=1003 val=0 kworker/3:4-70 [003] ....... 1927.143443: regmap_reg_read: ethernet-switch reg=1002 val=6 The kworker here is polling MIB counters for stats, as evidenced by the register 0x1004 that we are writing to (RTL8365MB_MIB_ADDRESS_REG). This polling is performed every 3 seconds, but is just one example of such unsynchronized access. In Arınç's case, the driver was not using the switch IRQ, so the PHY subsystem was itself doing polling analogous to phytool in the above example. A test module was created [see second Link] to simulate such spurious switch register accesses while performing indirect PHY register reads and writes. Realtek was also consulted to confirm whether this is a known issue or not. The conclusion of these lines of inquiry is as follows: 1. Reading of PHY registers via indirect access will be aborted if, after executing the read operation (via a write to the INDIRECT_ACCESS_CTRL_REG), any register is accessed, other than INDIRECT_ACCESS_STATUS_REG. 2. The PHY register indirect read is only complete when INDIRECT_ACCESS_STATUS_REG reads zero. 3. The INDIRECT_ACCESS_DATA_REG, which is read to get the result of the PHY read, will contain the result of the last successful read operation. If there was spurious register access and the indirect read was aborted, then this register is not guaranteed to hold anything meaningful and the PHY read will silently fail. 4. PHY writes do not appear to be affected by this mechanism. 5. Other similar access routines, such as for MIB counters, although similar to the PHY indirect access method, are actually table access. Table access is not affected by spurious reads or writes of other registers. However, concurrent table access is not allowed. Currently this is protected via mib_lock, so there is nothing to fix. The above statements are corroborated both via the test module and through consultation with Realtek. In particular, Realtek states that this is simply a property of the hardware design and is not a hardware bug. 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 Reported-by: Luiz Angelo Daros de Luca Signed-off-by: Alvin Šipraga --- drivers/net/dsa/realtek/rtl8365mb.c | 54 ++++++++++++++++++----------- 1 file changed, 33 insertions(+), 21 deletions(-) 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; } -- 2.35.1