* [PATCH net-next v3] selftests/net: Skip srv6_end_dt46_l3vpn_test if iproute2 too old
From: Alessio Faina @ 2026-07-15 12:28 UTC (permalink / raw)
To: netdev, linux-kselftest, Andrea Mayer
Cc: Po-Hsu Lin, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Simon Horman, Shuah Khan
In case iproute2 is older than version 5.14.0, released ~Sept 1, 2021,
the End.DT46 support is not available and the host_vpn_tests test contained
in the srv6_end_dt46_l3vpn_test.sh file is failing in some kernel backports.
This is the result of those tests:
################################################################################
TEST SECTION: SRv6 VPN connectivity test among hosts in the same tenant
################################################################################
TEST: IPv6 Hosts connectivity: hs-t100-1 -> hs-t100-2 (tenant 100) [ FAIL ]
TEST: IPv4 Hosts connectivity: hs-t100-1 -> hs-t100-2 (tenant 100) [ FAIL ]
TEST: IPv6 Hosts connectivity: hs-t100-2 -> hs-t100-1 (tenant 100) [ FAIL ]
TEST: IPv4 Hosts connectivity: hs-t100-2 -> hs-t100-1 (tenant 100) [ FAIL ]
TEST: IPv6 Hosts connectivity: hs-t200-3 -> hs-t200-4 (tenant 200) [ FAIL ]
TEST: IPv4 Hosts connectivity: hs-t200-3 -> hs-t200-4 (tenant 200) [ FAIL ]
TEST: IPv6 Hosts connectivity: hs-t200-4 -> hs-t200-3 (tenant 200) [ FAIL ]
TEST: IPv4 Hosts connectivity: hs-t200-4 -> hs-t200-3 (tenant 200) [ FAIL ]
To amend this, check the current running iproute2 supports the required
feature and, if not, just skip the entire test to avoid a failure.
Signed-off-by: Alessio Faina <alessio.faina@canonical.com>
---
v3:
- fix indentation in test_iproute2_supp_or_ksft_skip()
- fix subject to reflect full test suite skip
- https://lore.kernel.org/netdev/20260713095750.2671173-1-alessio.faina@canonical.com/
v2:
- skip entire test suite if iproute2 is too old
v1: https://lore.kernel.org/netdev/20260708152745.2430714-1-alessio.faina@canonical.com/
.../testing/selftests/net/srv6_end_dt46_l3vpn_test.sh | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
index a5e959a080bb..50e37d3217ea 100755
--- a/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
+++ b/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
@@ -536,6 +536,14 @@ host_vpn_isolation_tests()
done
}
+test_iproute2_supp_or_ksft_skip()
+{
+ if ! ip route add help 2>&1 | grep -qo "End.DT46"; then
+ echo "SKIP: Missing SRv6 End.DT46 support in iproute2"
+ exit "${ksft_skip}"
+ fi
+}
+
if [ "$(id -u)" -ne 0 ];then
echo "SKIP: Need root privileges"
exit $ksft_skip
@@ -546,6 +554,8 @@ if [ ! -x "$(command -v ip)" ]; then
exit $ksft_skip
fi
+test_iproute2_supp_or_ksft_skip
+
modprobe vrf &>/dev/null
if [ ! -e /proc/sys/net/vrf/strict_mode ]; then
echo "SKIP: vrf sysctl does not exist"
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v9 11/14] net: ipa: Switch to generic PAS TZ APIs
From: Alex Elder @ 2026-07-15 12:16 UTC (permalink / raw)
To: Bjorn Andersson, Sumit Garg
Cc: konradybcio, linux-arm-msm, devicetree, dri-devel, freedreno,
linux-media, netdev, linux-wireless, ath12k, linux-remoteproc,
robh, krzk+dt, conor+dt, robin.clark, sean, akhilpo, lumag,
abhinav.kumar, jesszhan0024, marijn.suijten, airlied, simona,
vikash.garodia, bod, mchehab, elder, andrew+netdev, davem,
edumazet, kuba, pabeni, jjohnson, mathieu.poirier,
trilokkumar.soni, mukesh.ojha, pavan.kondeti, jorge.ramirez,
tonyh, vignesh.viswanathan, srinivas.kandagatla, amirreza.zarrabi,
jenswi, op-tee, apurupa, skare, linux-kernel, Sumit Garg,
Alex Elder, Konrad Dybcio
In-Reply-To: <alJp8jMzcw9EPZoy@baldur>
On 7/11/26 11:06 AM, Bjorn Andersson wrote:
> On Thu, Jul 02, 2026 at 05:28:27PM +0530, Sumit Garg wrote:
>> From: Sumit Garg <sumit.garg@oss.qualcomm.com>
>>
>> Switch ipa client driver over to generic PAS TZ APIs. Generic PAS TZ
>> service allows to support multiple TZ implementation backends like QTEE
>> based SCM PAS service, OP-TEE based PAS service and any further future TZ
>> backend service.
>>
>
> Please find an immutable branch with the dependencies for this patch at:
> https://git.kernel.org/pub/scm/linux/kernel/git/qcom/linux.git 20260702115835.167602-2-sumit.garg@kernel.org
>
> Alternatively, if you think there will be no conflicting patches in the
> time leading up to next merge window provide an Ack and I can pick this
> through the qcom tree.
I don't see any reason there would be any conflicts (certainly
no difficult conflicts) with this commit, so I think you can
safely take it with the others through the Qualcomm tree.
So if my ack is what you're looking for:
Acked-by: Alex Elder <elder@riscstar.com>
Thank you Sumit and Bjorn.
>
> Thanks,
> Bjorn
>
>> Reviewed-by: Alex Elder <elder@riscstar.com>
>> Reviewed-by: Konrad Dybcio <konrad.dybcio@oss.qualcomm.com>
>> Signed-off-by: Sumit Garg <sumit.garg@oss.qualcomm.com>
>> ---
>> drivers/net/ipa/Kconfig | 2 +-
>> drivers/net/ipa/ipa_main.c | 13 ++++++++-----
>> 2 files changed, 9 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/net/ipa/Kconfig b/drivers/net/ipa/Kconfig
>> index 01d219d3760c..a9aff1b7977d 100644
>> --- a/drivers/net/ipa/Kconfig
>> +++ b/drivers/net/ipa/Kconfig
>> @@ -6,7 +6,7 @@ config QCOM_IPA
>> depends on QCOM_RPROC_COMMON || (QCOM_RPROC_COMMON=n && COMPILE_TEST)
>> depends on QCOM_AOSS_QMP || QCOM_AOSS_QMP=n
>> select QCOM_MDT_LOADER
>> - select QCOM_SCM
>> + select QCOM_PAS
>> select QCOM_QMI_HELPERS
>> help
>> Choose Y or M here to include support for the Qualcomm
>> diff --git a/drivers/net/ipa/ipa_main.c b/drivers/net/ipa/ipa_main.c
>> index 788dd99af2a4..3cd9e44680e9 100644
>> --- a/drivers/net/ipa/ipa_main.c
>> +++ b/drivers/net/ipa/ipa_main.c
>> @@ -14,7 +14,7 @@
>> #include <linux/pm_runtime.h>
>> #include <linux/types.h>
>>
>> -#include <linux/firmware/qcom/qcom_scm.h>
>> +#include <linux/firmware/qcom/qcom_pas.h>
>> #include <linux/soc/qcom/mdt_loader.h>
>>
>> #include "ipa.h"
>> @@ -624,10 +624,13 @@ static int ipa_firmware_load(struct device *dev)
>> }
>>
>> ret = qcom_mdt_load(dev, fw, path, IPA_PAS_ID, virt, phys, size, NULL);
>> - if (ret)
>> + if (ret) {
>> dev_err(dev, "error %d loading \"%s\"\n", ret, path);
>> - else if ((ret = qcom_scm_pas_auth_and_reset(IPA_PAS_ID)))
>> - dev_err(dev, "error %d authenticating \"%s\"\n", ret, path);
>> + } else {
>> + ret = qcom_pas_auth_and_reset(IPA_PAS_ID);
>> + if (ret)
>> + dev_err(dev, "error %d authenticating \"%s\"\n", ret, path);
>> + }
>>
>> memunmap(virt);
>> out_release_firmware:
>> @@ -758,7 +761,7 @@ static enum ipa_firmware_loader ipa_firmware_loader(struct device *dev)
>> return IPA_LOADER_INVALID;
>> out_self:
>> /* We need Trust Zone to load firmware; make sure it's available */
>> - if (qcom_scm_is_available())
>> + if (qcom_pas_is_available())
>> return IPA_LOADER_SELF;
>>
>> return IPA_LOADER_DEFER;
>> --
>> 2.53.0
>>
^ permalink raw reply
* [Patch net-next v2 2/2] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-15 12:10 UTC (permalink / raw)
To: Frank.Sae, andrew, hkallweit1, linux, davem, edumazet, kuba,
pabeni
Cc: netdev, linux-kernel, jianmin.wang, xiaolin.xu, ming.xu, jie.han
Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
2.5G/1000M/100M/10M speed.
Signed-off-by: Kyle Switch <kyle.switch@motor-comm.com>
---
v2:
1. Remove duplicate code and replace it with existing api.
drivers/net/phy/motorcomm.c | 1804 +++++++++++++++++++++++++++++++++--
1 file changed, 1716 insertions(+), 88 deletions(-)
diff --git a/drivers/net/phy/motorcomm.c b/drivers/net/phy/motorcomm.c
index 5071605a1a11..f60a79a401a6 100644
--- a/drivers/net/phy/motorcomm.c
+++ b/drivers/net/phy/motorcomm.c
@@ -1,9 +1,10 @@
// SPDX-License-Identifier: GPL-2.0+
/*
- * Motorcomm 8511/8521/8522/8531/8531S/8821 PHY driver.
+ * Motorcomm 8511/8521/8522/8531/8531S/8821/8824 PHY driver.
*
* Author: Peter Geis <pgwipeout@gmail.com>
* Author: Frank <Frank.Sae@motor-comm.com>
+ * Author: Kyle <kyle.switch@motor-comm.com>
*/
#include <linux/etherdevice.h>
@@ -12,12 +13,16 @@
#include <linux/phy.h>
#include <linux/property.h>
+/* used for phy8824 baseaddr */
+#define PHY_BASE_ADDR (1)
+
#define PHY_ID_YT8511 0x0000010a
#define PHY_ID_YT8521 0x0000011a
#define PHY_ID_YT8522 0x4f51e928
#define PHY_ID_YT8531 0x4f51e91b
#define PHY_ID_YT8531S 0x4f51e91a
#define PHY_ID_YT8821 0x4f51ea19
+#define PHY_ID_YT8824 0x4f51e8b8
/* YT8521/YT8531S/YT8821 Register Overview
* UTP Register space | FIBER Register space
* ------------------------------------------------------------
@@ -29,6 +34,18 @@
* ------------------------------------------------------------
*/
+/* YT8824 Register Overview
+ * UTP Register space | FIBER Register space
+ * ------------------------------------------------------------
+ * | UTP MII | FIBER MII |
+ * | UTP MMD | |
+ * | UTP Extended | FIBER Extended |
+ * | UTP Top Extended | FIBER Top Extended |
+ * ------------------------------------------------------------
+ * | Common Top Extended |
+ * ------------------------------------------------------------
+ */
+
/* 0x10 ~ 0x15 , 0x1E and 0x1F are common MII registers of yt phy */
/* Specific Function Control Register */
@@ -354,7 +371,7 @@
#define YT8821_UTP_EXT_MU_FINE_FR_CTRL_REG 0x4B5
#define YT8821_UTP_EXT_MU_FINE_FR_F_FFE GENMASK(14, 12)
-#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE GENMASK(10, 8)
+#define YT8821_UTP_EXT_MU_FINE_FR_F_FBE GENMASK(10, 8)
#define YT8821_UTP_EXT_VGA_LPF1_CAP_CTRL_REG 0x4D2
#define YT8821_UTP_EXT_VGA_LPF1_CAP_OTHER GENMASK(7, 4)
@@ -375,6 +392,10 @@
#define YT8821_CHIP_MODE_AUTO_BX2500_SGMII 0
#define YT8821_CHIP_MODE_FORCE_BX2500 1
+#define YT8824_RSSR_SPACE_MASK BIT(0)
+#define YT8824_RSSR_FIBER_SPACE (0x1)
+#define YT8824_RSSR_UTP_SPACE (0x0)
+
struct yt8521_priv {
/* combo_advertising is used for case of YT8521 in combo mode,
* this means that yt8521 may work in utp or fiber mode which depends
@@ -391,8 +412,29 @@ struct yt8521_priv {
* YT8521_RSSR_TO_BE_ARBITRATED
*/
u8 reg_page;
+ /* YT8824 reg space addr */
+ u8 phy_base_addr;
+ /* top extend reg addr */
+ u8 top_phy_addr;
};
+static inline int ytphy_top_write(struct phy_device *phydev, u32 regnum,
+ u16 val)
+{
+ struct yt8521_priv *priv = phydev->priv;
+ struct mii_bus *bus = phydev->mdio.bus;
+
+ return bus->write(bus, priv->top_phy_addr, regnum, val);
+}
+
+static inline int ytphy_top_read(struct phy_device *phydev, u32 regnum)
+{
+ struct yt8521_priv *priv = phydev->priv;
+ struct mii_bus *bus = phydev->mdio.bus;
+
+ return bus->read(bus, priv->top_phy_addr, regnum);
+}
+
/**
* ytphy_read_ext() - read a PHY's extended register
* @phydev: a pointer to a &struct phy_device
@@ -431,6 +473,70 @@ static int ytphy_read_ext_with_lock(struct phy_device *phydev, u16 regnum)
return ret;
}
+/**
+ * ytphy_read_top_ext() - read a PHY's top extended register for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @regnum: register number to read
+ *
+ * NOTE:The caller must have taken the MDIO bus lock.
+ *
+ * returns the value of regnum reg or negative error code
+ */
+static int ytphy_read_top_ext(struct phy_device *phydev, u16 regnum)
+{
+ int ret;
+
+ ret = ytphy_top_write(phydev, YTPHY_PAGE_SELECT, regnum);
+ if (ret < 0)
+ return ret;
+
+ return ytphy_top_read(phydev, YTPHY_PAGE_DATA);
+}
+
+static int ytphy_read_top_ext_with_lock(struct phy_device *phydev, u16 regnum)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+ ret = ytphy_read_top_ext(phydev, regnum);
+ phy_unlock_mdio_bus(phydev);
+
+ return ret;
+}
+
+/**
+ * ytphy_write_top_ext() - read a PHY's top extended register for YT8824
+ * @phydev: a pointer to a &struct phy_device
+ * @regnum: register number to read
+ *
+ * NOTE:The caller must have taken the MDIO bus lock.
+ *
+ * returns the value of regnum reg or negative error code
+ */
+static int ytphy_write_top_ext(struct phy_device *phydev, u16 regnum,
+ u16 val)
+{
+ int ret;
+
+ ret = ytphy_top_write(phydev, YTPHY_PAGE_SELECT, regnum);
+ if (ret < 0)
+ return ret;
+
+ return ytphy_top_write(phydev, YTPHY_PAGE_DATA, val);
+}
+
+static int ytphy_write_top_ext_with_lock(struct phy_device *phydev, u16 regnum,
+ u16 val)
+{
+ int ret;
+
+ phy_lock_mdio_bus(phydev);
+ ret = ytphy_write_top_ext(phydev, regnum, val);
+ phy_unlock_mdio_bus(phydev);
+
+ return ret;
+}
+
/**
* ytphy_write_ext() - write a PHY's extended register
* @phydev: a pointer to a &struct phy_device
@@ -3059,99 +3165,1620 @@ static int yt8821_resume(struct phy_device *phydev)
return yt8821_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
}
-static struct phy_driver motorcomm_phy_drvs[] = {
- {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
- .name = "YT8511 Gigabit Ethernet",
- .config_init = yt8511_config_init,
- .suspend = genphy_suspend,
- .resume = genphy_resume,
- .read_page = yt8511_read_page,
- .write_page = yt8511_write_page,
- },
- {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8521),
- .name = "YT8521 Gigabit Ethernet",
- .get_features = yt8521_get_features,
- .probe = yt8521_probe,
- .read_page = yt8521_read_page,
- .write_page = yt8521_write_page,
- .get_wol = ytphy_get_wol,
- .set_wol = ytphy_set_wol,
- .config_aneg = yt8521_config_aneg,
- .aneg_done = yt8521_aneg_done,
- .config_init = yt8521_config_init,
- .read_status = yt8521_read_status,
- .soft_reset = yt8521_soft_reset,
- .suspend = yt8521_suspend,
- .resume = yt8521_resume,
- .led_hw_is_supported = yt8521_led_hw_is_supported,
- .led_hw_control_set = yt8521_led_hw_control_set,
- .led_hw_control_get = yt8521_led_hw_control_get,
- },
- {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8522),
- .name = "YT8522 100 Megabit Ethernet",
- .config_aneg = genphy_config_aneg,
- .config_init = yt8522_config_init,
- .suspend = genphy_suspend,
- .resume = genphy_resume,
- },
- {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
- .name = "YT8531 Gigabit Ethernet",
- .probe = yt8531_probe,
- .config_init = yt8531_config_init,
- .suspend = genphy_suspend,
- .resume = genphy_resume,
- .get_wol = ytphy_get_wol,
- .set_wol = yt8531_set_wol,
- .link_change_notify = yt8531_link_change_notify,
- .led_hw_is_supported = yt8521_led_hw_is_supported,
- .led_hw_control_set = yt8521_led_hw_control_set,
- .led_hw_control_get = yt8521_led_hw_control_get,
- },
- {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
- .name = "YT8531S Gigabit Ethernet",
- .get_features = yt8521_get_features,
- .probe = yt8521_probe,
- .read_page = yt8521_read_page,
- .write_page = yt8521_write_page,
- .get_wol = ytphy_get_wol,
- .set_wol = ytphy_set_wol,
- .config_aneg = yt8521_config_aneg,
- .aneg_done = yt8521_aneg_done,
- .config_init = yt8521_config_init,
- .read_status = yt8521_read_status,
- .soft_reset = yt8521_soft_reset,
- .suspend = yt8521_suspend,
- .resume = yt8521_resume,
- },
- {
- PHY_ID_MATCH_EXACT(PHY_ID_YT8821),
- .name = "YT8821 2.5Gbps PHY",
- .get_features = yt8821_get_features,
- .read_page = yt8521_read_page,
- .write_page = yt8521_write_page,
- .get_wol = ytphy_get_wol,
- .set_wol = ytphy_set_wol,
- .config_aneg = genphy_config_aneg,
- .aneg_done = yt8821_aneg_done,
- .config_init = yt8821_config_init,
- .get_rate_matching = yt8821_get_rate_matching,
- .read_status = yt8821_read_status,
- .soft_reset = yt8821_soft_reset,
- .suspend = yt8821_suspend,
- .resume = yt8821_resume,
+/**
+ * yt8824_read_page() - read reg page
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns current reg space of yt8824 (YT8824_RSSR_FIBER_SPACE/
+ * YT8824_RSSR_UTP_SPACE) or negative errno code
+ */
+static int yt8824_read_page(struct phy_device *phydev)
+{
+ int old_page;
+
+ old_page = ytphy_read_top_ext_with_lock(phydev, YT8521_REG_SPACE_SELECT_REG);
+ if (old_page < 0)
+ return old_page;
+
+ if ((old_page & YT8824_RSSR_SPACE_MASK) == YT8824_RSSR_FIBER_SPACE)
+ return YT8824_RSSR_FIBER_SPACE;
+
+ return YT8824_RSSR_UTP_SPACE;
+};
+
+/**
+ * yt8824_write_page() - write reg page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE) to write.
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8824_write_page(struct phy_device *phydev, int page)
+{
+ int old_page;
+ u16 data;
+
+ old_page = ytphy_read_top_ext_with_lock(phydev, YT8521_REG_SPACE_SELECT_REG);
+ data = old_page & (~(0x1));
+ data |= page;
+
+ return ytphy_write_top_ext_with_lock(phydev, YT8521_REG_SPACE_SELECT_REG, data);
+};
+
+/**
+ * configuration YT8824 to one template test mode.
+ */
+static int yt8824_soft_reset_step1_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (old_page >= 0) {
+ if (reg_space == YT8824_RSSR_UTP_SPACE) {
+ ret = __phy_write_mmd(phydev, 0x1, 0x0084, 0x2000);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * enable YT8824 serdes isolate.
+ */
+static int yt8824_soft_reset_step2_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+ if (old_page >= 0) {
+ if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+ /* isolation */
+ ret |= BIT(10);
+ ret = __phy_write(phydev, MII_BMCR, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * do YT8824 UTP softreset.
+ */
+static int yt8824_soft_reset_step3_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ unsigned int retry = 12;
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+ if (old_page >= 0) {
+ if (reg_space == YT8824_RSSR_UTP_SPACE) {
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+ ret |= BMCR_RESET;
+ ret = __phy_write(phydev, MII_BMCR, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ do {
+ msleep(50);
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+ } while ((ret & BMCR_RESET) && --retry);
+ if (ret & BMCR_RESET)
+ goto err_restore_page;
+ }
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * disable YT8824 template test mode.
+ */
+static int yt8824_soft_reset_step4_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int port;
+ int ret;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (old_page >= 0) {
+ if (reg_space == YT8824_RSSR_UTP_SPACE) {
+ /* normal mode */
+ ret = __phy_write_mmd(phydev, 0x1, 0x0084, 0x0000);
+ if (ret < 0)
+ goto err_restore_page;
+ if (port == 0 || port == 2) {
+ /* read the calibration val of band after power on,
+ * * only for recording.
+ */
+ ret = ytphy_read_ext(phydev, 0x043e);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+ }
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * disable YT8824 serdes isolate.
+ */
+static int yt8824_soft_reset_step5_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ unsigned int retry = 12;
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+ if (old_page >= 0) {
+ if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+ /* disable isolation */
+ ret &= ~BIT(10);
+ /* soft reset */
+ ret |= BMCR_RESET;
+ ret = __phy_write(phydev, MII_BMCR, ret);
+ if (ret < 0)
+ return ret;
+ do {
+ msleep(50);
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+ } while ((ret & BMCR_RESET) && --retry);
+ if (ret & BMCR_RESET)
+ goto err_restore_page;
+ }
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_soft_reset() - called to issue a PHY software reset
+ * @phydev: a pointer to a &struct phy_device
+ * for internal YT8824
+ * 1) set utp template test mode
+ * 2) utp restart
+ * 3) disable utp template test mode
+ * for external YT8824
+ * 1) set utp template test mode
+ * 2) enable serdes isolate
+ * 3) utp restart
+ * 4) disable utp template test mode
+ * 5) disable serdes isolate
+ * returns 0 or negative errno code
+ */
+static int yt8824_soft_reset(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ /* invalid test mode */
+ ret = yt8824_soft_reset_step1_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ ret = yt8824_soft_reset_step3_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* normal mode */
+ ret = yt8824_soft_reset_step4_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* invalid test mode */
+ ret = yt8824_soft_reset_step1_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds isolation */
+ ret = yt8824_soft_reset_step2_paged(phydev,
+ YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* utp soft reset */
+ ret = yt8824_soft_reset_step3_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* normal mode */
+ ret = yt8824_soft_reset_step4_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_soft_reset_step5_paged(phydev,
+ YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+/**
+ * external YT8824 init
+ */
+static int yt8824_config_init_paged(struct phy_device *phydev, int reg_space)
+{
+ struct yt8521_priv *priv = phydev->priv;
+ int ret = 0, old_page;
+ u16 val_1, val_2, val_3, tmp;
+ int port;
+
+ port = phydev->mdio.addr - priv->phy_base_addr;
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (reg_space == YT8824_RSSR_FIBER_SPACE) {
+ /* read efuse */
+ val_1 = ytphy_read_top_ext_with_lock(phydev, 0xa13e);
+ if (val_1 < 0)
+ goto err_restore_page;
+
+ val_2 = ytphy_read_top_ext_with_lock(phydev, 0xa13f);
+ if (val_2 < 0)
+ goto err_restore_page;
+
+ val_3 = ytphy_read_top_ext_with_lock(phydev, 0xa140);
+ if (val_3 < 0)
+ goto err_restore_page;
+
+ if (port == 0) {
+ /* Serdes optimization */
+ ret = ytphy_write_ext(phydev, 0x04be, 0x000d);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x049f, 0x7ded);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04a9, 0x009f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* analog CDR */
+ ret = ytphy_write_ext(phydev, 0x0406, 0x0800);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized VCO */
+ ret = ytphy_write_ext(phydev, 0x0438, 0x9024);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0439, 0x00c0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized PLL lock */
+ ret = ytphy_read_ext(phydev, 0x0429);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(13) | BIT(12));
+ tmp = (val_1 & (BIT(7) | BIT(6)) >> 6);
+ ret |= (tmp << 12);
+ ret = ytphy_write_ext(phydev, 0x0429, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x0441);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(1) | BIT(0));
+ tmp = (val_1 & (BIT(5) | BIT(4)) >> 4);
+ ret |= tmp;
+ ret = ytphy_write_ext(phydev, 0x0441, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x042b);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(13) | BIT(12));
+ tmp = (val_3 & (BIT(1) | BIT(0)));
+ ret |= (tmp << 12);
+ ret = ytphy_write_ext(phydev, 0x042b, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x043a, 0x1006);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x042a, 0xf070);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x0491, 0x007f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7f7f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes training threshold */
+ ret = ytphy_write_ext(phydev, 0x0454, 0x0f14);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0497, 0x0a44);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* digital eye diagram of SerDes */
+ ret = ytphy_write_ext(phydev, 0x04cd, 0x0000);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes LDO */
+ ret = ytphy_read_ext(phydev, 0x04b5);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(6) | BIT(5) | BIT(4));
+ tmp = (val_2 & (BIT(4) | BIT(3) | BIT(2)) >> 2);
+ ret |= (tmp << 4);
+ ret = ytphy_write_ext(phydev, 0x04b5, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_read_ext(phydev, 0x04b4);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~(BIT(10) | BIT(9) | BIT(8));
+ tmp = (val_2 & (BIT(7) | BIT(6) | BIT(5)) >> 5);
+ ret |= (tmp << 8);
+ ret = ytphy_write_ext(phydev, 0x04b4, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized Serdes RX */
+ ret = ytphy_write_ext(phydev, 0x04af, 0x45e3);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x048a, 0x0fff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0408, 0x7c00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04d6, 0x007f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x044f, 0xff08);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized Serdes TX */
+ ret = ytphy_write_ext(phydev, 0x048e, 0x7d00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x000d, 0x0606);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* Serdes manual config */
+ ret = ytphy_write_ext(phydev, 0x04b0, 0x0804);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04b1, 0x7074);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04af, 0x45e7);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* restart calibration */
+ ret = ytphy_write_ext(phydev, 0x0003, 0x5603);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7fff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0492, 0x7f7f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x2000, 0x0040);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x2000, 0x0000);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+ /* TX preamble padded to 8; RX IPG always > 8 */
+ ret = __phy_write(phydev, 0x0017, 0x2007);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = __phy_write(phydev, 0x0000, 0x9000);
+ if (ret < 0)
+ goto err_restore_page;
+ } else if (reg_space == YT8824_RSSR_UTP_SPACE) {
+ /* power down */
+ ret = __phy_write(phydev, 0x0000, 0x1900);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* pll calibration */
+ ret = ytphy_write_ext(phydev, 0x0001, 0x0003);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa20e, 0x0cba);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa20a, 0xc3f1);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa20c, 0x1620);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2b6, 0x0a00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2b6, 0x0e00);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimization utp */
+ ret = ytphy_write_ext(phydev, 0x0001, 0x0003);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* enable nibble */
+ ret = ytphy_write_ext(phydev, 0xa003, 0x0003);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* idle err detect enable */
+ ret = ytphy_write_ext(phydev, 0x03d0, 0x5210);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 2.5G long cable performance */
+ ret = ytphy_write_ext(phydev, 0x0372, 0x5038);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x037c, 0x6068);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0388, 0x00a0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized fast retrain */
+ ret = ytphy_write_ext(phydev, 0x0359, 0x2140);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x000c, 0xc1a0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* 2.5G template tone */
+ ret = ytphy_write_ext(phydev, 0xa2fa, 0x0083);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04e2, 0x0149);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 2.5G template */
+ ret = ytphy_write_ext(phydev, 0x047e, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x047f, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0480, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0481, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 1000M cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x0336, 0xab0a);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0340, 0x301d);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* 100M template amplitude */
+ ret = ytphy_write_ext(phydev, 0x046e, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x046f, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0470, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0471, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 100M cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x030b, 0xaa1d);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x071f, 0x0036);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* 10M template amplitude */
+ ret = ytphy_write_ext(phydev, 0x046b, 0x1818);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x046c, 0x1818);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized 10M cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x0466, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0467, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0468, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0469, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimize utp 1000M performance */
+ ret = ytphy_write_ext(phydev, 0x034a, 0xff03);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x00f8, 0xb3ff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0059, 0x4040);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x032c, 0x5094);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x032d, 0xd094);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x032e, 0x5308);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x0322, 0x6440);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04d3, 0x5220);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x04d2, 0x5220);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized EMC CS */
+ ret = ytphy_write_ext(phydev, 0x00c8, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x00be, 0x6406);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0x037a, 0x40ff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* optimized EMC RE */
+ ret = ytphy_write_ext(phydev, 0x0482, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2d5, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2d6, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2d7, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa2d8, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa218, 0x006e);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa01d, 0xfff0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa01e, 0xfff0);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa01d, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_write_ext(phydev, 0xa01e, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * internal YT8824 init
+ */
+static int yt8824_internal_config_init_paged(struct phy_device *phydev,
+ int reg_space)
+{
+ struct yt8521_priv *priv = phydev->priv;
+ int old_page;
+ int port = 0;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ port = phydev->mdio.addr - priv->phy_base_addr;
+ ret = ytphy_write_ext(phydev, 0x1, 0x3);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = __phy_write(phydev, MII_BMCR, 0x1900);
+ if (ret < 0)
+ goto err_restore_page;
+ if (port == 0 || port == 2) {
+ ret = ytphy_write_ext(phydev, 0xa20e, 0xcba);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa20a, 0xc3f1);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa20c, 0x1620);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2b6, 0xa00);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2b6, 0xe00);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa003, 0x3);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+ ret = ytphy_write_ext(phydev, 0x3d0, 0x5210);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x372, 0x5038);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x37c, 0x6068);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x388, 0xa0);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x359, 0x2140);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_top_ext(phydev, 0xa2fa, 0x83);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x4e2, 0x149);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 2.5G tempate */
+ ret = ytphy_write_ext(phydev, 0x47e, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x47f, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x480, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x481, 0x3939);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 1000 cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x336, 0xab0a);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x340, 0x301d);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 1000 performance */
+ ret = ytphy_write_ext(phydev, 0x34a, 0xff03);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xf8, 0xb3ff);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x32c, 0x5094);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x32d, 0xd094);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x32e, 0x5308);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x322, 0x6440);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x4d3, 0x5220);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x4d2, 0x5220);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 100 tempate */
+ ret = ytphy_write_ext(phydev, 0x46e, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x46f, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x470, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x471, 0x4545);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 100 cable length threshold */
+ ret = ytphy_write_ext(phydev, 0x30b, 0xaa1d);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x71f, 0x36);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 10 tempate */
+ ret = ytphy_write_ext(phydev, 0x46b, 0x1818);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x46c, 0x1818);
+ if (ret < 0)
+ goto err_restore_page;
+ /* 10 tempate MAU*/
+ ret = ytphy_write_ext(phydev, 0x466, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x467, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x468, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x469, 0x6c6c);
+ if (ret < 0)
+ goto err_restore_page;
+ /* EMC CS */
+ ret = ytphy_write_ext(phydev, 0xc8, 0xfff);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xbe, 0x6406);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0x37a, 0x40ff);
+ if (ret < 0)
+ goto err_restore_page;
+ /* EMC RE*/
+ ret = ytphy_write_ext(phydev, 0x482, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+ if (port == 0 || port == 2) {
+ ret = ytphy_write_ext(phydev, 0x482, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2d5, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2d6, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2d7, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa2d8, 0x1f1f);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa218, 0x6e);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa01d, 0xfff0);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa01e, 0xfff0);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa01d, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = ytphy_write_ext(phydev, 0xa01e, 0xffff);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+ ret = ytphy_write_ext(phydev, 0xc, 0x41a1);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = __phy_write_mmd(phydev, 0x1, 0x84, 0x2000);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = __phy_write(phydev, MII_BMCR, 0x9140);
+ if (ret < 0)
+ goto err_restore_page;
+ ret = __phy_write_mmd(phydev, 0x1, 0x84, 0x0);
+ if (ret < 0)
+ goto err_restore_page;
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_config_init() - phy initializatioin
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * Returns: 0 or negative errno code
+ */
+static int yt8824_config_init(struct phy_device *phydev)
+{
+ struct yt8521_priv *priv = phydev->priv;
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ /* base addr and top addr update for internal YT8824*/
+ priv->phy_base_addr = 0x4;
+ priv->top_phy_addr = 0x9;
+ ret = yt8824_internal_config_init_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ } else {
+ ret = yt8824_config_init_paged(phydev, YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ ret = yt8824_config_init_paged(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ }
+ ret = yt8824_soft_reset(phydev);
+ if (ret < 0)
+ return ret;
+
+ netdev_info(phydev->attached_dev,
+ "%s done, phy addr: %d, phy base addr = %d\n",
+ __func__, phydev->mdio.addr, priv->phy_base_addr);
+
+ return 0;
+}
+
+static int yt8824_config_intr(struct phy_device *phydev)
+{
+ struct yt8521_priv *priv = phydev->priv;
+ int ret = 0, old_page;
+ int port;
+
+ port = phydev->mdio.addr - priv->phy_base_addr;
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
+ /* top ext reg 0xa000
+ * bit6 int_polarity 1'b0 low active, 1'b1 high active
+ */
+ ret = ytphy_top_read(phydev, YT8521_REG_SPACE_SELECT_REG);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_top_write(phydev, YT8521_REG_SPACE_SELECT_REG, ret & (~BIT(6)));
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa019
+ * bit5 intr_phy_pulse_en 1'b0 level, 1'b1 pulse
+ */
+ ret = ytphy_top_read(phydev, 0xa019);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_top_write(phydev, 0xa019, ret | BIT(5));
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa018 phy_pulse_lth bit14:8
+ * top ext reg 0xa019 timer_tick_sel bit8:7
+ * bit14:8 phy_pulse_lth bit8:7 timer_tick_sel pulse width
+ * 0x007a 0x0002 10ms pulse width
+ * 0x0064 0x0001 1ms pulse width
+ * 0x004e 0x0000 100ms pulse width
+ * 0x0009 0x0000 10ms pulse width(default)
+ */
+ ret = ytphy_top_read(phydev, 0xa018);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~0x7f00;
+ ret |= (0x0009 << 8);
+ ret = ytphy_top_write(phydev, 0xa018, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = ytphy_top_read(phydev, 0xa019);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~0x180;
+ ret |= (0x0000 << 7);
+ ret = ytphy_top_write(phydev, 0xa019, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa01c interrupt state(Read Clear)
+ * bit11 PHY3 interrupt 1: phy3 link up/down interrupt happened, 0: interrupt not happened
+ * bit10 PHY2 interrupt 1: phy2 link up/down interrupt happened, 0: interrupt not happened
+ * bit9 PHY1 interrupt 1: phy1 link up/down interrupt happened, 0: interrupt not happened
+ * bit8 PHY0 interrupt 1: phy0 link up/down interrupt happened, 0: interrupt not happened
+ *
+ * bit7 PHY3 link down 1: phy3 link down interrupt happened, 0: interrupt not happened
+ * bit6 PHY2 link down 1: phy2 link down interrupt happened, 0: interrupt not happened
+ * bit5 PHY1 link down 1: phy1 link down interrupt happened, 0: interrupt not happened
+ * bit4 PHY0 link down 1: phy0 link down interrupt happened, 0: interrupt not happened
+ *
+ * bit3 PHY3 link up 1: phy3 link up interrupt happened, 0: interrupt not happened
+ * bit2 PHY2 link up 1: phy2 link up interrupt happened, 0: interrupt not happened
+ * bit1 PHY1 link up 1: phy1 link up interrupt happened, 0: interrupt not happened
+ * bit0 PHY0 link up 1: phy0 link up interrupt happened, 0: interrupt not happened
+ */
+ ret = ytphy_top_read(phydev, 0xa01c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa01a
+ * bit5 intn_wol_mode 1'b1 enable intn_wol pin output
+ * bit4 intn_mode 1'b1 enable intn pin output
+ */
+ ret = ytphy_top_read(phydev, 0xa01a);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~BIT(5);
+ ret |= BIT(4);
+ ret = ytphy_top_write(phydev, 0xa01a, ret);
+ if (ret < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa01b interrupt mask
+ * bit11 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+ * bit7 1'b1 enable interrupt signal(link down) output to interrupt pin
+ * bit3 1'b1 enable interrupt signal(link up) output to interrupt pin
+ *
+ * bit10 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+ * bit6 1'b1 enable interrupt signal(link down) output to interrupt pin
+ * bit2 1'b1 enable interrupt signal(link up) output to interrupt pin
+ *
+ * bit9 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+ * bit5 1'b1 enable interrupt signal(link down) output to interrupt pin
+ * bit1 1'b1 enable interrupt signal(link up) output to interrupt pin
+ *
+ * bit8 1'b1 enable interrupt signal(link up/down) output to interrupt pin
+ * bit4 1'b1 enable interrupt signal(link down) output to interrupt pin
+ * bit0 1'b1 enable interrupt signal(link up) output to interrupt pin
+ */
+ ret = ytphy_top_read(phydev, 0xa01b);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret |= (BIT(port + 8) | BIT(port + 4) | BIT(port));
+ ret = ytphy_top_write(phydev, 0xa01b, ret);
+ if (ret < 0)
+ goto err_restore_page;
+ }
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+static irqreturn_t yt8824_handle_interrupt(struct phy_device *phydev)
+{
+ int ret = 0, old_page;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ /* top ext reg 0xa01c interrupt state(Read Clear)
+ * bit11 PHY3 interrupt 1: phy3 link up/down interrupt happened, 0: interrupt not happened
+ * bit10 PHY2 interrupt 1: phy2 link up/down interrupt happened, 0: interrupt not happened
+ * bit9 PHY1 interrupt 1: phy1 link up/down interrupt happened, 0: interrupt not happened
+ * bit8 PHY0 interrupt 1: phy0 link up/down interrupt happened, 0: interrupt not happened
+ * bit7 PHY3 link down 1: phy3 link down interrupt happened, 0: interrupt not happened
+ * bit6 PHY2 link down 1: phy2 link down interrupt happened, 0: interrupt not happened
+ * bit5 PHY1 link down 1: phy1 link down interrupt happened, 0: interrupt not happened
+ * bit4 PHY0 link down 1: phy0 link down interrupt happened, 0: interrupt not happened
+ * bit3 PHY3 link up 1: phy3 link up interrupt happened, 0: interrupt not happened
+ * bit2 PHY2 link up 1: phy2 link up interrupt happened, 0: interrupt not happened
+ * bit1 PHY1 link up 1: phy1 link up interrupt happened, 0: interrupt not happened
+ * bit0 PHY0 link up 1: phy0 link up interrupt happened, 0: interrupt not happened
+ */
+ ret = ytphy_read_top_ext(phydev, 0xa01c);
+ if (ret < 0)
+ goto err_restore_page;
+
+ phy_trigger_machine(phydev);
+
+err_restore_page:
+ phy_restore_page(phydev, old_page, ret);
+ if (ret > 0)
+ return IRQ_HANDLED;
+ else
+ return IRQ_NONE;
+}
+
+/**
+ * yt8824_config_aneg()
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8824_aneg_done(struct phy_device *phydev)
+{
+ int link = 0;
+ int old_page;
+ int ret = 0;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ link = !!(__phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG) &
+ YTPHY_SSR_LINK);
+
+ netdev_info(phydev->attached_dev, "%s, phy addr: %d, link_utp: %d\n",
+ __func__, phydev->mdio.addr, link);
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_read_status_paged() - determines the speed and duplex of one page
+ * @phydev: a pointer to a &struct phy_device
+ * @page: The reg page(YT8824_RSSR_FIBER_SPACE/YT8824_RSSR_UTP_SPACE) to
+ * operate.
+ *
+ * returns 1 (utp or fiber link),0 (no link) or negative errno code
+ */
+static int yt8824_read_status_paged(struct phy_device *phydev, int page,
+ int *status, int *lpa)
+{
+ int old_page;
+ int ret = 0;
+
+ page &= YT8824_RSSR_SPACE_MASK;
+ old_page = phy_select_page(phydev, page);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ ret = __phy_read(phydev, MII_LPA);
+ *lpa = ret;
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = __phy_read(phydev, YTPHY_SPECIFIC_STATUS_REG);
+ *status = ret;
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = !!(*status & YTPHY_SSR_LINK);
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_read_status() - determines the negotiated speed and duplex
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8824_read_status(struct phy_device *phydev)
+{
+ int link;
+ int lpa;
+ int val;
+
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+ phydev->link = 0;
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+
+ link = yt8824_read_status_paged(phydev,
+ YT8824_RSSR_UTP_SPACE, &val, &lpa);
+ if (link < 0)
+ return link;
+
+ if (link) {
+ phydev->link = 1;
+ phydev->pause = !!(lpa & BIT(10));
+ phydev->asym_pause = !!(lpa & BIT(11));
+
+ /* update speed & duplex */
+ yt8821_adjust_status(phydev, val);
+ } else {
+ phydev->link = 0;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+ }
+
+ return 0;
+}
+
+/**
+ * yt8824_utp_power_on()
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8824_utp_power_on(struct phy_device *phydev,
+ int reg_space)
+{
+ int old_page;
+ int ret;
+
+ old_page = phy_select_page(phydev, reg_space);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret &= ~BMCR_PDOWN;
+ ret &= ~BMCR_ISOLATE;
+
+ ret = __phy_write(phydev, MII_BMCR, ret);
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_utp_power_down()
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8824_utp_power_down(struct phy_device *phydev,
+ int reg_space)
+{
+ int ret = 0, old_page;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ ret = __phy_read(phydev, MII_BMCR);
+ if (ret < 0)
+ goto err_restore_page;
+
+ ret = __phy_write(phydev, MII_BMCR, ret | BMCR_PDOWN);
+ if (ret < 0)
+ goto err_restore_page;
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_power_on()
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ * need WA like softreset
+ */
+static int yt8824_power_on(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ /* invalid test mode */
+ ret = yt8824_soft_reset_step1_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* utp power on */
+ ret = yt8824_utp_power_on(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* normal mode */
+ ret = yt8824_soft_reset_step4_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* invalid test mode */
+ ret = yt8824_soft_reset_step1_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds isolation */
+ ret = yt8824_soft_reset_step2_paged(phydev,
+ YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* utp power on */
+ ret = yt8824_utp_power_on(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* normal mode */
+ ret = yt8824_soft_reset_step4_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_soft_reset_step5_paged(phydev,
+ YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int yt8824_resume(struct phy_device *phydev)
+{
+ return yt8824_power_on(phydev);
+}
+
+static int yt8824_power_down(struct phy_device *phydev)
+{
+ int ret;
+
+ if (phydev->interface == PHY_INTERFACE_MODE_INTERNAL) {
+ /* invalid test mode */
+ ret = yt8824_soft_reset_step1_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* utp power down */
+ ret = yt8824_utp_power_down(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ /* normal mode */
+ ret = yt8824_soft_reset_step4_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+ } else {
+ /* invalid test mode */
+ ret = yt8824_soft_reset_step1_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds isolation */
+ ret = yt8824_soft_reset_step2_paged(phydev,
+ YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* utp power down */
+ ret = yt8824_utp_power_down(phydev, YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* normal mode */
+ ret = yt8824_soft_reset_step4_paged(phydev,
+ YT8824_RSSR_UTP_SPACE);
+ if (ret < 0)
+ return ret;
+
+ /* sds soft reset and disable isolation */
+ ret = yt8824_soft_reset_step5_paged(phydev,
+ YT8824_RSSR_FIBER_SPACE);
+ if (ret < 0)
+ return ret;
+ }
+ return 0;
+}
+
+static int yt8824_suspend(struct phy_device *phydev)
+{
+ return yt8824_power_down(phydev);
+}
+
+/**
+ * yt8824_config_aneg()
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8824_config_aneg(struct phy_device *phydev)
+{
+ int phy_ctrl = 0;
+ int old_page;
+ int ret;
+
+ old_page = phy_select_page(phydev, YT8824_RSSR_UTP_SPACE);
+ if (old_page < 0)
+ goto err_restore_page;
+
+ if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+ phydev->advertising))
+ phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
+
+ ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
+ MDIO_AN_10GBT_CTRL,
+ MDIO_AN_10GBT_CTRL_ADV2_5G,
+ phy_ctrl);
+ if (ret)
+ goto err_restore_page;
+
+ ret = genphy_config_aneg(phydev);
+ if (ret)
+ goto err_restore_page;
+
+err_restore_page:
+ return phy_restore_page(phydev, old_page, ret);
+}
+
+/**
+ * yt8824_probe()
+ * @phydev: a pointer to a &struct phy_device
+ *
+ * returns 0 or negative errno code
+ */
+static int yt8824_probe(struct phy_device *phydev)
+{
+ struct device *dev = &phydev->mdio.dev;
+ struct yt8521_priv *priv;
+ u32 phy_base_addr;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ phydev->priv = priv;
+
+ /* get base addr and top addr for external YT8824 */
+ if (!device_property_read_u32(dev, "motorcomm,base-address",
+ &phy_base_addr))
+ priv->phy_base_addr = phy_base_addr;
+ else
+ priv->phy_base_addr = PHY_BASE_ADDR;
+
+ priv->top_phy_addr = priv->phy_base_addr + 4;
+
+ return 0;
+}
+
+static struct phy_driver motorcomm_phy_drvs[] = {
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
+ .name = "YT8511 Gigabit Ethernet",
+ .config_init = yt8511_config_init,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .read_page = yt8511_read_page,
+ .write_page = yt8511_write_page,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8521),
+ .name = "YT8521 Gigabit Ethernet",
+ .get_features = yt8521_get_features,
+ .probe = yt8521_probe,
+ .read_page = yt8521_read_page,
+ .write_page = yt8521_write_page,
+ .get_wol = ytphy_get_wol,
+ .set_wol = ytphy_set_wol,
+ .config_aneg = yt8521_config_aneg,
+ .aneg_done = yt8521_aneg_done,
+ .config_init = yt8521_config_init,
+ .read_status = yt8521_read_status,
+ .soft_reset = yt8521_soft_reset,
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
+ .led_hw_is_supported = yt8521_led_hw_is_supported,
+ .led_hw_control_set = yt8521_led_hw_control_set,
+ .led_hw_control_get = yt8521_led_hw_control_get,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8522),
+ .name = "YT8522 100 Megabit Ethernet",
+ .config_aneg = genphy_config_aneg,
+ .config_init = yt8522_config_init,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531),
+ .name = "YT8531 Gigabit Ethernet",
+ .probe = yt8531_probe,
+ .config_init = yt8531_config_init,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
+ .get_wol = ytphy_get_wol,
+ .set_wol = yt8531_set_wol,
+ .link_change_notify = yt8531_link_change_notify,
+ .led_hw_is_supported = yt8521_led_hw_is_supported,
+ .led_hw_control_set = yt8521_led_hw_control_set,
+ .led_hw_control_get = yt8521_led_hw_control_get,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S),
+ .name = "YT8531S Gigabit Ethernet",
+ .get_features = yt8521_get_features,
+ .probe = yt8521_probe,
+ .read_page = yt8521_read_page,
+ .write_page = yt8521_write_page,
+ .get_wol = ytphy_get_wol,
+ .set_wol = ytphy_set_wol,
+ .config_aneg = yt8521_config_aneg,
+ .aneg_done = yt8521_aneg_done,
+ .config_init = yt8521_config_init,
+ .read_status = yt8521_read_status,
+ .soft_reset = yt8521_soft_reset,
+ .suspend = yt8521_suspend,
+ .resume = yt8521_resume,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8821),
+ .name = "YT8821 2.5Gbps PHY",
+ .get_features = yt8821_get_features,
+ .read_page = yt8521_read_page,
+ .write_page = yt8521_write_page,
+ .get_wol = ytphy_get_wol,
+ .set_wol = ytphy_set_wol,
+ .config_aneg = genphy_config_aneg,
+ .aneg_done = yt8821_aneg_done,
+ .config_init = yt8821_config_init,
+ .get_rate_matching = yt8821_get_rate_matching,
+ .read_status = yt8821_read_status,
+ .soft_reset = yt8821_soft_reset,
+ .suspend = yt8821_suspend,
+ .resume = yt8821_resume,
+ },
+ {
+ PHY_ID_MATCH_EXACT(PHY_ID_YT8824),
+ .name = "YT8824 Quad Ports 2.5Gbps Ethernet",
+ .get_features = yt8821_get_features,
+ .read_page = yt8824_read_page,
+ .write_page = yt8824_write_page,
+ .config_intr = yt8824_config_intr,
+ .handle_interrupt = yt8824_handle_interrupt,
+ .probe = yt8824_probe,
+ .config_aneg = yt8824_config_aneg,
+ .aneg_done = yt8824_aneg_done,
+ .config_init = yt8824_config_init,
+ .read_status = yt8824_read_status,
+ .soft_reset = yt8824_soft_reset,
+ .suspend = yt8824_suspend,
+ .resume = yt8824_resume,
},
};
module_phy_driver(motorcomm_phy_drvs);
-MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821 PHY driver");
+MODULE_DESCRIPTION("Motorcomm 8511/8521/8531/8531S/8821/8824 PHY driver");
MODULE_AUTHOR("Peter Geis");
MODULE_AUTHOR("Frank");
+MODULE_AUTHOR("Kyle");
MODULE_LICENSE("GPL");
static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
@@ -3161,6 +4788,7 @@ static const struct mdio_device_id __maybe_unused motorcomm_tbl[] = {
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8531S) },
{ PHY_ID_MATCH_EXACT(PHY_ID_YT8821) },
+ { PHY_ID_MATCH_EXACT(PHY_ID_YT8824) },
{ /* sentinel */ }
};
--
2.25.1
^ permalink raw reply related
* Re: [PATCH v8 4/9] nvmem: layouts: Support fixed-layout as the nvmem device node itself
From: Bartosz Golaszewski @ 2026-07-15 12:07 UTC (permalink / raw)
To: Loic Poulain
Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
Russell King, Saravana Kannan, Christian Marangi
In-Reply-To: <20260703-block-as-nvmem-v8-4-98ae32bfc49a@oss.qualcomm.com>
On Fri, 3 Jul 2026 15:45:17 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> of_nvmem_layout_get_container() only looks for a child node named
> "nvmem-layout" to locate the cell definitions. This does not cover
> providers whose device tree node is itself the fixed-layout container,
> such as an eMMC boot partition block device whose fwnode points directly
> at a "fixed-layout" compatible partitions node.
>
> When no "nvmem-layout" child is present, fall back to returning the nvmem
> device node itself if it is compatible with "fixed-layout", so that its
> cells are parsed by nvmem_add_cells_from_fixed_layout().
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
> drivers/nvmem/layouts.c | 13 ++++++++++++-
> 1 file changed, 12 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/nvmem/layouts.c b/drivers/nvmem/layouts.c
> index b90584e1b99eab4217cbe7ec48373e18a7caf0dc..efa631ce7283bdd6c8ecda75915911b5e3a33c99 100644
> --- a/drivers/nvmem/layouts.c
> +++ b/drivers/nvmem/layouts.c
> @@ -167,7 +167,18 @@ static int nvmem_layout_bus_populate(struct nvmem_device *nvmem,
>
> struct device_node *of_nvmem_layout_get_container(struct nvmem_device *nvmem)
> {
> - return of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
> + struct device_node *np;
> +
> + /* Search for nvmem-layout child */
> + np = of_get_child_by_name(nvmem->dev.of_node, "nvmem-layout");
> + if (np)
> + return np;
> +
> + /* The nvmem of_node is itself a fixed-layout node */
> + if (of_device_is_compatible(nvmem->dev.of_node, "fixed-layout"))
> + return of_node_get(nvmem->dev.of_node);
> +
> + return NULL;
> }
> EXPORT_SYMBOL_GPL(of_nvmem_layout_get_container);
>
>
> --
> 2.34.1
>
>
I have it on my TODO list to convert nvmem layouts to be fwnode-agnostic. While
I'm not sure when I'll have the time to look into it, I think it makes sense
to not introduce any new OF-specific interfaces. Can you make it into:
struct fwnode_handle *nvmem_layout_get_container_node(struct
nvmem_device *nvmem);
by replacing of_get_child_by_name() with fwnode_get_name_child_node() and
of_device_is_compatible() with device_is_compatible()? That would mean less
churn in the future.
Other than that, it looks good.
Bart
^ permalink raw reply
* Re: [PATCH net-next v2] selftests/net: Skip srv6_end_dt46_l3vpn_test::host_vpn_tests if iproute2 too old
From: Alessio Faina @ 2026-07-15 12:04 UTC (permalink / raw)
To: Andrea Mayer
Cc: netdev, linux-kselftest, Po-Hsu Lin, David S. Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, Simon Horman,
Shuah Khan, stefano.salsano
In-Reply-To: <20260715111530.fc982bdd10eb499cef8e9196@uniroma2.it>
On Wed, Jul 15, 2026 at 11:15:30AM +0200, Andrea Mayer wrote:
> On Mon, 13 Jul 2026 11:57:50 +0200
> Alessio Faina <alessio.faina@canonical.com> wrote:
>
> > In case iproute2 is older than version 5.14.0, released ~Sept 1, 2021,
> > the End.DT46 support is not available and the host_vpn_tests test contained
> > in the srv6_end_dt46_l3vpn_test.sh file is failing in some kernel backports.
> > This is the result of those tests:
> >
> > ################################################################################
> > TEST SECTION: SRv6 VPN connectivity test among hosts in the same tenant
> > ################################################################################
> >
> > TEST: IPv6 Hosts connectivity: hs-t100-1 -> hs-t100-2 (tenant 100) [ FAIL ]
> >
> > TEST: IPv4 Hosts connectivity: hs-t100-1 -> hs-t100-2 (tenant 100) [ FAIL ]
> >
> > TEST: IPv6 Hosts connectivity: hs-t100-2 -> hs-t100-1 (tenant 100) [ FAIL ]
> >
> > TEST: IPv4 Hosts connectivity: hs-t100-2 -> hs-t100-1 (tenant 100) [ FAIL ]
> >
> > TEST: IPv6 Hosts connectivity: hs-t200-3 -> hs-t200-4 (tenant 200) [ FAIL ]
> >
> > TEST: IPv4 Hosts connectivity: hs-t200-3 -> hs-t200-4 (tenant 200) [ FAIL ]
> >
> > TEST: IPv6 Hosts connectivity: hs-t200-4 -> hs-t200-3 (tenant 200) [ FAIL ]
> >
> > TEST: IPv4 Hosts connectivity: hs-t200-4 -> hs-t200-3 (tenant 200) [ FAIL ]
> >
> > To amend this, check the current running iproute2 supports the required
> > feature and, if not, just skip the test to avoid a failure.
> >
> >
> > Signed-off-by: Alessio Faina <alessio.faina@canonical.com>
>
>
> Hi Alessio,
>
> Thanks for the v2, the code looks good to me.
>
> One question: since the check now exits the entire script before setup(),
> should the subject drop the "::host_vpn_tests" part?
>
> just a nit: test_iproute2_supp_or_ksft_skip() uses spaces, but most of this
> file uses tabs.
>
> Ciao
> Andrea
>
> > ---
> > v2:
> > - skip entire test suite if iproute2 is too old
> > v1: https://urldefense.com/v3/__https://lore.kernel.org/netdev/20260708152745.2430714-1-alessio.faina@canonical.com/__;!!O5Bi4QcV!CGb1HmTUSZ4JwCff0mVcoY_KnoHJmrGDAdGnPOEYWRVFi-J05H-ZtpLVZBNK12kwLaNmhzw7mrQw4M_eY5uuSft8PRuFHqU$
> >
> > .../testing/selftests/net/srv6_end_dt46_l3vpn_test.sh | 10 ++++++++++
> > 1 file changed, 10 insertions(+)
> >
> > diff --git a/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh b/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
> > index a5e959a080bb..cd7ac03e3907 100755
> > --- a/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
> > +++ b/tools/testing/selftests/net/srv6_end_dt46_l3vpn_test.sh
> > @@ -536,6 +536,14 @@ host_vpn_isolation_tests()
> > done
> > }
> >
> > +test_iproute2_supp_or_ksft_skip()
> > +{
> > + if ! ip route add help 2>&1 | grep -qo "End.DT46"; then
> > + echo "SKIP: Missing SRv6 End.DT46 support in iproute2"
> > + exit "${ksft_skip}"
> > + fi
> > +}
> > +
> > if [ "$(id -u)" -ne 0 ];then
> > echo "SKIP: Need root privileges"
> > exit $ksft_skip
> > @@ -546,6 +554,8 @@ if [ ! -x "$(command -v ip)" ]; then
> > exit $ksft_skip
> > fi
> >
> > +test_iproute2_supp_or_ksft_skip
> > +
> > modprobe vrf &>/dev/null
> > if [ ! -e /proc/sys/net/vrf/strict_mode ]; then
> > echo "SKIP: vrf sysctl does not exist"
> > --
> > 2.43.0
> >
Hi Andrea,
Yeah, I agree with you, the subject should be updated as well, I didn't
think about it. I will update the subject and the indentation in a V3
soon.
Kind regards,
-Alessio
^ permalink raw reply
* Re: [PATCH] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-15 11:59 UTC (permalink / raw)
To: Andrew Lunn
Cc: Frank.Sae, hkallweit1, linux, davem, edumazet, kuba, pabeni,
netdev, linux-kernel, jianmin.wang, ming.xu, xiaolin.xu, jie.han
In-Reply-To: <46c380a2-74ab-4690-a289-6050cb33c1a4@lunn.ch>
On 7/15/26 05:46, Andrew Lunn wrote:
>> +static inline int ytphy_top_write(struct phy_device *phydev, u32 regnum,
>> + u16 val)
>> +{
>> + struct yt8521_priv *priv = phydev->priv;
>> + struct mii_bus *bus = phydev->mdio.bus;
>> +
>> + return bus->write(bus, priv->top_phy_addr, regnum, val);
>> +}
>
> Please could you explain the architecture in detail. It seems like you
> are accessing registers of some other device on the bus.
>
> Maybe you need to use the code in phy_package.c?
Ans: YT8824 contains top extend reg space, and the addr is fixed, for
internal phy8824 the value is 9, and external phy8824 is (baseaddr + 4).
>
> Andrew
^ permalink raw reply
* Re: [PATCH] net: phy: Add driver for Motorcomm Quad 2.5GbE phy
From: Kyle Switch @ 2026-07-15 11:56 UTC (permalink / raw)
To: Andrew Lunn
Cc: Frank.Sae, hkallweit1, linux, davem, edumazet, kuba, pabeni,
netdev, linux-kernel, jianmin.wang, ming.xu, xiaolin.xu, jie.han
In-Reply-To: <a0e43216-d215-4c81-9954-c5e497b2d50f@lunn.ch>
On 7/15/26 05:43, Andrew Lunn wrote:
> On Tue, Jul 14, 2026 at 07:12:03PM +0800, Kyle Switch wrote:
>> Add a driver for motorcomm yt8824 quad 2.5G ethernet phy, supports
>> 2.5G/1000M/100M/10M speed.
>
> Please take a read of:
>
> https://www.kernel.org/doc/html/latest/process/maintainer-netdev.html
>
> and set the Subject line correctly.
>
>> +#define REG_MII_MMD_CTRL 0x0D
>> +#define REG_MII_MMD_DATA 0x0E
>
> include/uapi/linux/mii.h
>
> #define MII_MMD_CTRL 0x0d /* MMD Access Control Register */
> #define MII_MMD_DATA 0x0e /* MMD Access Data Register */
>
> Please don't repeat something which already exists. But i also wounder
> why you need these.
>
>> +static int ytphy_write_mmd(struct phy_device *phydev,
>> + u16 device, u16 reg,
>> + u16 value)
>> +{
>> + int ret;
>> +
>> + ret = __phy_write(phydev, REG_MII_MMD_CTRL, device);
>> + if (ret)
>> + return ret;
>> + ret = __phy_write(phydev, REG_MII_MMD_DATA, reg);
>> + if (ret)
>> + return ret;
>> + ret = __phy_write(phydev, REG_MII_MMD_CTRL, device | 0x4000);
>> + if (ret)
>> + return ret;
>> + ret = __phy_write(phydev, REG_MII_MMD_DATA, value);
>> +
>> + return ret;
>> +}
>
> Why are you reinventing these functions when they already exist?
Ans: Sorry, I didn't notice the existing interface before. next patch, i will remove
the duplicate functional interfaces include REG_MII_MMD_CTRL, REG_MII_MMD_DATA, and
ytphy_mmd_write api.
>> +
>> /**
>> * ytphy_get_wol() - report whether wake-on-lan is enabled
>> * @phydev: a pointer to a &struct phy_device
>> @@ -3059,99 +3187,1620 @@ static int yt8821_resume(struct phy_device *phydev)
>> return yt8821_modify_utp_fiber_bmcr(phydev, BMCR_PDOWN, 0);
>> }
>>
>> -static struct phy_driver motorcomm_phy_drvs[] = {
>> - {
>> - PHY_ID_MATCH_EXACT(PHY_ID_YT8511),
>> - .name = "YT8511 Gigabit Ethernet",
>> - .config_init = yt8511_config_init,
>> - .suspend = genphy_suspend,
>> - .resume = genphy_resume,
>> - .read_page = yt8511_read_page,
>> - .write_page = yt8511_write_page,
>> - },
>> - {
>> - PHY_ID_MATCH_EXACT(PHY_ID_YT8521),
>> - .name = "YT8521 Gigabit Ethernet",
>> - .get_features = yt8521_get_features,
>> - .probe = yt8521_probe,
>
> Why are these all stoppering? diff would not remove them unless
> something is changing.
>
Ans: after double check, no existing code logic has been modified. I guess the possible reason
is that git diff employs the Diff Algorithm, a "context minimization" strategy adopted to
represent changes in the most compact manner. It consolidates modifications into a single block.
> Andrew
>
> ---
> pw-bot: cr
^ permalink raw reply
* Re: [Intel-wired-lan] [PATCH net] i40e: xsk: fix multi-buffer XDP_PASS skb construction
From: Maciej Fijalkowski @ 2026-07-15 11:54 UTC (permalink / raw)
To: Loktionov, Aleksandr
Cc: Chenguang Zhao, Nguyen, Anthony L, Kitszel, Przemyslaw,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com,
intel-wired-lan@lists.osuosl.org, netdev@vger.kernel.org,
Chenguang Zhao
In-Reply-To: <IA3PR11MB8986AD34DC026A4FB3946407E5F82@IA3PR11MB8986.namprd11.prod.outlook.com>
On Wed, Jul 15, 2026 at 09:37:42AM +0000, Loktionov, Aleksandr wrote:
>
>
> > -----Original Message-----
> > From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> > Of Chenguang Zhao
> > Sent: Tuesday, July 14, 2026 4:51 AM
> > To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
> > Przemyslaw <przemyslaw.kitszel@intel.com>; andrew+netdev@lunn.ch;
> > davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> > pabeni@redhat.com
> > Cc: intel-wired-lan@lists.osuosl.org; netdev@vger.kernel.org;
> > chenguang.zhao@linux.dev; Chenguang Zhao <zhaochenguang@kylinos.cn>
> > Subject: [Intel-wired-lan] [PATCH net] i40e: xsk: fix multi-buffer
> > XDP_PASS skb construction
> >
> > From: Chenguang Zhao <zhaochenguang@kylinos.cn>
> >
> > When AF_XDP ZC receives a multi-buffer frame and the XDP program
> > returns XDP_PASS, i40e_construct_skb_zc() copies frags into a new skb.
> > The copy used skb_frag_page() as the memcpy source (page metadata
> > instead of packet data) and passed a virtual address to
> > __skb_fill_page_desc_noacc(), which expects a struct page *.
> >
> > Use skb_frag_address() for the copy, attach frags with
> > skb_add_rx_frag() so len/data_len/truesize are updated, and on
> > dev_alloc_page() failure free the skb via the shared out path so
> > xsk_buff_free() still runs and previously attached pages are released
> > by kfree_skb.
> >
> > Fixes: 1c9ba9c14658 ("i40e: xsk: add RX multi-buffer support")
> > Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> > ---
> > - Fix memcpy source: use skb_frag_address() instead of
> > skb_frag_page(),
> > which was copying page metadata rather than packet data.
> >
> > - Fix frag attachment: pass the allocated struct page * to the skb
> > frag
> > helper instead of the page virtual address.
> >
> > - Use skb_add_rx_frag() so skb->len, data_len and truesize are
> > updated
> > when attaching copied frags.
> >
> > - On mid-loop dev_alloc_page() failure, go through the shared out
> > path
> > so previously attached pages are released via kfree_skb and
> > xsk_buff_free() is still called.
> >
> > drivers/net/ethernet/intel/i40e/i40e_xsk.c | 13 +++++--------
> > 1 file changed, 5 insertions(+), 8 deletions(-)
> >
> > diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> > b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> > index 9f47388eaba5..a4247710c85b 100644
> > --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> > +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> > @@ -318,22 +318,19 @@ static struct sk_buff
> > *i40e_construct_skb_zc(struct i40e_ring *rx_ring,
> > goto out;
> >
> > for (int i = 0; i < nr_frags; i++) {
> > - struct skb_shared_info *skinfo = skb_shinfo(skb);
> > skb_frag_t *frag = &sinfo->frags[i];
> > + unsigned int frag_size = skb_frag_size(frag);
> > struct page *page;
> > - void *addr;
> >
> > page = dev_alloc_page();
> > if (!page) {
> > dev_kfree_skb(skb);
> > - return NULL;
> > + skb = NULL;
> > + goto out;
> > }
> > - addr = page_to_virt(page);
> >
> > - memcpy(addr, skb_frag_page(frag), skb_frag_size(frag));
> > -
> > - __skb_fill_page_desc_noacc(skinfo, skinfo->nr_frags++,
> > - addr, 0, skb_frag_size(frag));
> > + memcpy(page_to_virt(page), skb_frag_address(frag),
> > frag_size);
> > + skb_add_rx_frag(skb, i, page, 0, frag_size, PAGE_SIZE);
> > }
> >
> > out:
> > --
> > 2.25.1
>
> Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
Aleks, may I ask what is your intent here? Do you disagree with suggestion
I had regarding this change?
We could go with this as-is, right, but given we have a helper available
to do exactly what this patch touches, I think we could kill two birds
with one stone by removing home-grown code. However one might argue that
such change is a -next material.
Anyways, some explanation would be better than dropping plain tag.
>
^ permalink raw reply
* Re: [PATCH net v1] rxrpc: fix io_thread race in rxrpc_wake_up_io_thread()
From: Simon Horman @ 2026-07-15 11:43 UTC (permalink / raw)
To: xuanqiang.luo
Cc: David Howells, Marc Dionne, netdev, linux-afs, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, linux-kernel,
Xuanqiang Luo
In-Reply-To: <20260708093534.53486-1-xuanqiang.luo@linux.dev>
On Wed, Jul 08, 2026 at 05:35:34PM +0800, xuanqiang.luo@linux.dev wrote:
> From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
>
> rxrpc_wake_up_io_thread() checks local->io_thread before waking it, but
> then reloads the pointer for wake_up_process().
>
> local->io_thread is cleared with WRITE_ONCE() when the I/O thread exits, so
> the second load can see NULL even if the first load did not.
>
> Take a READ_ONCE() snapshot and use it for both the NULL check and the
> wake_up_process() call, as rxrpc_encap_rcv() already does.
>
> Fixes: 5800b1cf3fd8 ("rxrpc: Allow CHALLENGEs to the passed to the app for a RESPONSE")
> Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
Reviewed-by: Simon Horman <horms@kernel.org>
^ permalink raw reply
* [PATCH net v2] net/sched: serialize qdisc_rtab_list against concurrent get/put
From: Aldo Ariel Panzardo @ 2026-07-15 11:41 UTC (permalink / raw)
To: netdev
Cc: Jamal Hadi Salim, Jiri Pirko, Eric Dumazet, David S . Miller,
Jakub Kicinski, Paolo Abeni, Simon Horman, stable, linux-kernel,
Aldo Ariel Panzardo
In-Reply-To: <CANn89iLFBPHjjOZg5p7b=-vA++17oBLCJtGi8aJXnfJ3j67J1g@mail.gmail.com>
qdisc_get_rtab() and qdisc_put_rtab() mutate the process-global singly
linked list qdisc_rtab_list and a plain non-atomic 'int refcnt' with no
lock. This was only safe because every caller historically held the RTNL
mutex, which serialized all rate-table lookups, inserts and frees.
That invariant no longer holds. cls_flower sets
TCF_PROTO_OPS_DOIT_UNLOCKED, so tc_new_tfilter() keeps rtnl_held == false
for it and sets TCA_ACT_FLAGS_NO_RTNL. That flag propagates through
tcf_exts_validate_ex() -> tcf_action_init() -> tcf_action_init_1() ->
tcf_police_init(), which calls qdisc_get_rtab()/qdisc_put_rtab() with the
RTNL mutex NOT held. Two RTM_NEWTFILTER requests on different CPUs, each
adding a flower filter with a police action carrying the same rate, then
race on qdisc_rtab_list and on the non-atomic refcnt, leading to a
use-after-free / double-free of the kmalloc-2k struct qdisc_rate_table.
qdisc_rtab_list is a single global (not per-netns), so the corrupted
object is shared system-wide.
BUG: KASAN: slab-use-after-free in qdisc_put_rtab+0x12f/0x160
qdisc_put_rtab+0x12f/0x160
tcf_police_init+0xda9/0x1590
tcf_action_init_1+0x460/0x6b0
tcf_action_init+0x439/0xa40
tcf_exts_validate_ex+0x42d/0x550
fl_change+0xddd/0x7da0
tc_new_tfilter+0xaa7/0x2420
rtnetlink_rcv_msg+0x95e/0xe90
which belongs to the cache kmalloc-2k of size 2048
Protect qdisc_rtab_list and the refcount with a dedicated spinlock. The
(sleeping, GFP_KERNEL) allocation in qdisc_get_rtab() is performed before
taking the lock; if a concurrent inserter added an identical table in the
meantime the freshly allocated one is freed under the lock, so no
duplicate is leaked. qdisc_put_rtab() now decrements the refcount and
unlinks under the same lock.
Fixes: 470502de5bdb ("net: sched: unlock rules update API")
Suggested-by: Eric Dumazet <edumazet@google.com>
Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com>
Cc: stable@vger.kernel.org
---
v2:
- Rework qdisc_get_rtab() to allocate before taking the lock and free the
surplus table under the lock instead of dropping the lock to allocate
and re-scanning (Eric Dumazet).
- Add Fixes: tag and Cc: stable.
- Build-tested (CONFIG_NET_SCHED=y).
v1 was sent privately to security@kernel.org on 2026-07-11; moving to the
public list now that a fix is ready, as requested.
net/sched/sch_api.c | 24 +++++++++++++++++++-----
1 file changed, 19 insertions(+), 5 deletions(-)
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index 8a3236456db4..668bcd60d183 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -415,12 +415,13 @@ static __u8 __detect_linklayer(struct tc_ratespec *r, __u32 *rtab)
}
static struct qdisc_rate_table *qdisc_rtab_list;
+static DEFINE_SPINLOCK(qdisc_rtab_lock);
struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
struct nlattr *tab,
struct netlink_ext_ack *extack)
{
- struct qdisc_rate_table *rtab;
+ struct qdisc_rate_table *rtab, *new_rtab;
if (tab == NULL || r->rate == 0 ||
r->cell_log == 0 || r->cell_log >= 32 ||
@@ -429,15 +430,20 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
return NULL;
}
+ new_rtab = kmalloc_obj(*new_rtab);
+
+ spin_lock(&qdisc_rtab_lock);
for (rtab = qdisc_rtab_list; rtab; rtab = rtab->next) {
if (!memcmp(&rtab->rate, r, sizeof(struct tc_ratespec)) &&
!memcmp(&rtab->data, nla_data(tab), TC_RTAB_SIZE)) {
rtab->refcnt++;
+ spin_unlock(&qdisc_rtab_lock);
+ kfree(new_rtab);
return rtab;
}
}
- rtab = kmalloc_obj(*rtab);
+ rtab = new_rtab;
if (rtab) {
rtab->rate = *r;
rtab->refcnt = 1;
@@ -449,6 +455,7 @@ struct qdisc_rate_table *qdisc_get_rtab(struct tc_ratespec *r,
} else {
NL_SET_ERR_MSG(extack, "Failed to allocate new qdisc rate table");
}
+ spin_unlock(&qdisc_rtab_lock);
return rtab;
}
EXPORT_SYMBOL(qdisc_get_rtab);
@@ -457,18 +464,25 @@ void qdisc_put_rtab(struct qdisc_rate_table *tab)
{
struct qdisc_rate_table *rtab, **rtabp;
- if (!tab || --tab->refcnt)
+ if (!tab)
return;
+ spin_lock(&qdisc_rtab_lock);
+ if (--tab->refcnt) {
+ spin_unlock(&qdisc_rtab_lock);
+ return;
+ }
+
for (rtabp = &qdisc_rtab_list;
(rtab = *rtabp) != NULL;
rtabp = &rtab->next) {
if (rtab == tab) {
*rtabp = rtab->next;
- kfree(rtab);
- return;
+ break;
}
}
+ spin_unlock(&qdisc_rtab_lock);
+ kfree(tab);
}
EXPORT_SYMBOL(qdisc_put_rtab);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v4 1/1] ptp: ocp: add CPLD ISP support for ADVA TimeCard X1
From: Simon Horman @ 2026-07-15 11:15 UTC (permalink / raw)
To: maimon.sagi
Cc: Simon Horman, jonathan.lemon, vadim.fedorenko, richardcochran,
andrew+netdev, davem, edumazet, kuba, pabeni, linux-kernel,
netdev
In-Reply-To: <20260708065616.16814-1-maimon.sagi@gmail.com>
This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://netdev-ai.bots.linux.dev/sashiko/
---
ptp: ocp: add CPLD ISP support for ADVA TimeCard X1
This commit adds sysfs interfaces on the ADVA TimeCard X1 (PCI 0x0410) to
program a Lattice MachXO3 CPLD over a shared I2C bus. It exposes an
i2c_bus_ctrl attribute for arbitrating the bus against the on-board
MicroBlaze, and a cpld_i2c_xfer binary attribute that acts as an I2C
passthrough restricted to slave addresses 0x40 and 0x74.
> diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c
> index 35e911f1ad78..8df9feb030aa 100644
> --- a/drivers/ptp/ptp_ocp.c
> +++ b/drivers/ptp/ptp_ocp.c
[ ... ]
> @@ -416,6 +417,11 @@ struct ptp_ocp {
> dpll_tracker tracker;
> int signals_nr;
> int freq_in_nr;
> + /* cpld_i2c_xfer sysfs (adva_x1) */
> + struct mutex tap_i2c_lock;
> + int tap_i2c_adap_nr; /* adapter nr; -1 if absent */
> + u8 tap_i2c_rsp[21]; /* [status, read_data...] */
> + size_t tap_i2c_rsp_len;
> };
[ ... ]
> @@ -3188,6 +3194,12 @@ ptp_ocp_adva_board_init(struct ptp_ocp *bp, struct ocp_resource *r)
> ptp_ocp_nmea_out_init(bp);
> ptp_ocp_signal_init(bp);
>
> + /* tap_i2c is x1-only; the base adva variant has no CPLD I2C path. */
> + if (info->attr_groups == adva_timecard_x1_groups) {
> + mutex_init(&bp->tap_i2c_lock);
> + bp->tap_i2c_adap_nr = -1;
> + }
> +
[Critical]
Could this initialization order leave cpld_i2c_xfer non-functional in the
common configuration where xiic-i2c is already loaded when ptp_ocp probes?
The adva_x1 resource table places OCP_I2C_RESOURCE(i2c_ctrl) before the
ptp_ocp_adva_board_init entry, and ptp_ocp_register_resources iterates the
table sequentially. If xiic-i2c is built-in or preloaded,
platform_device_register_resndata() inside ptp_ocp_register_i2c() will run
the xiic-i2c probe synchronously:
ptp_ocp_register_i2c()
platform_device_register_resndata()
xiic-i2c probe -> i2c_add_adapter() -> device_add()
BUS_NOTIFY_ADD_DEVICE -> ptp_ocp_i2c_notifier_call()
WRITE_ONCE(bp->tap_i2c_adap_nr, ...->nr);
Control then returns to ptp_ocp_register_resources() which invokes
ptp_ocp_adva_board_init(), and the x1 branch above overwrites the just
cached number with -1.
No further BUS_NOTIFY_ADD_DEVICE fires for the same adapter, so
tap_i2c_adap_nr stays -1 for the device lifetime and every cpld_i2c_xfer
write() falls through i2c_get_adapter(-1) returning NULL and reports
-ENODEV.
Should tap_i2c_adap_nr be initialised earlier (before resource
registration) so the notifier's write is not clobbered?
> err = ptp_ocp_attr_group_add(bp, info->attr_groups);
> if (err)
> return err;
[ ... ]
> @@ -4224,6 +4236,210 @@ static const struct ocp_attr_group art_timecard_groups[] = {
[ ... ]
> +static ssize_t
> +ptp_ocp_cpld_i2c_write(struct file *file, struct kobject *kobj,
> + const struct bin_attribute *attr,
> + char *buf, loff_t off, size_t count)
> +{
[ ... ]
> + if (read_len > 0) {
> + u16 rd_flags = I2C_M_RD;
> +
> + if (flags & TAP_I2C_FLAG_NOSTART)
> + rd_flags |= I2C_M_NOSTART;
> + msgs[nmsgs].addr = addr;
> + msgs[nmsgs].flags = rd_flags;
> + msgs[nmsgs].len = read_len;
> + /* Allocate a DMA-safe bounce buffer if needed; see
> + * Documentation/i2c/dma-considerations.rst.
> + */
> + rdbuf = i2c_get_dma_safe_msg_buf(&msgs[nmsgs], 1);
> + if (!rdbuf) {
> + i2c_put_adapter(adap);
> + return -ENOMEM;
> + }
> + msgs[nmsgs].buf = rdbuf;
> + nmsgs++;
> + }
[High]
Can this leak the bounce buffer returned by i2c_get_dma_safe_msg_buf()?
Because I2C_M_DMA_SAFE is never set on this msg, i2c_get_dma_safe_msg_buf()
always returns a fresh kzalloc'd buffer. The code then aliases msg->buf
to that buffer:
rdbuf = i2c_get_dma_safe_msg_buf(&msgs[nmsgs], 1);
...
msgs[nmsgs].buf = rdbuf;
Later i2c_put_dma_safe_msg_buf(rdbuf, ..., ret > 0) is called, but that
helper begins with:
void i2c_put_dma_safe_msg_buf(u8 *buf, struct i2c_msg *msg, bool xferred)
{
if (!buf || buf == msg->buf)
return;
...
kfree(buf);
}
Since buf == msg->buf here, it returns without kfree().
In-tree callers (for example i2c-at91-master.c, mxs_i2c_xfer_msg,
mtk_i2c_do_transfer) keep msg->buf pointing at the caller's original
buffer and hold the bounce pointer in a separate driver variable so
buf != msg->buf at release time.
Would using a separate driver-owned pointer for the bounce buffer, or
setting msgs[nmsgs].flags |= I2C_M_DMA_SAFE and freeing with plain
kfree(), avoid the leak?
> +
> + /* Serialise transfer+publish so concurrent writers cannot overwrite
> + * each other's response in tap_i2c_rsp.
> + */
> + mutex_lock(&bp->tap_i2c_lock);
[ ... ]
> @@ -4902,6 +5125,8 @@ ptp_ocp_detach(struct ptp_ocp *bp)
> clk_hw_unregister_fixed_rate(bp->i2c_clk);
> if (bp->n_irqs)
> pci_free_irq_vectors(bp->pdev);
> + if (bp->tap_i2c_adap_nr != 0) /* initialised only on adva_x1 */
> + mutex_destroy(&bp->tap_i2c_lock);
> device_unregister(&bp->dev);
> }
[High]
Is this sentinel test reliable for deciding whether the mutex was
initialised?
mutex_init(&bp->tap_i2c_lock) only runs on adva_x1 in
ptp_ocp_adva_board_init(), but ptp_ocp_i2c_notifier_call() has no variant
filter — it walks up dev->parent looking for any driver named
KBUILD_MODNAME, matching fb, art and base adva just the same:
found:
bp = dev_get_drvdata(dev);
if (add) {
...
WRITE_ONCE(bp->tap_i2c_adap_nr, i2c_verify_adapter(child)->nr);
} else {
WRITE_ONCE(bp->tap_i2c_adap_nr, -1);
...
}
Every non-x1 board that has an OCP_I2C_RESOURCE will end up with
tap_i2c_adap_nr set to -1 (or any adapter number != 0) by the notifier,
so mutex_destroy(&bp->tap_i2c_lock) is then invoked on a never-initialised
mutex. With CONFIG_DEBUG_MUTEXES=y this runs DEBUG_LOCKS_WARN_ON() over
uninitialised memory, and under PREEMPT_RT mutex_destroy becomes a real
function that reads rt_mutex fields.
There is also the reverse case: adapter nr 0 is a legitimate I2C bus
number, so a genuine adva_x1 whose adapter happens to be 0 would skip
mutex_destroy even though the mutex was initialised.
Would a dedicated boolean flag set alongside mutex_init(), or gating on
info->attr_groups == adva_timecard_x1_groups explicitly, be a safer
condition?
> @@ -5217,11 +5442,16 @@ ptp_ocp_i2c_notifier_call(struct notifier_block *nb,
>
> found:
> bp = dev_get_drvdata(dev);
> - if (add)
> + if (add) {
> ptp_ocp_symlink(bp, child, "i2c");
> - else
> + /* Cache adapter number; cpld_i2c_xfer uses i2c_get_adapter()
> + * for a reference-counted, unbind-safe lookup.
> + */
> + WRITE_ONCE(bp->tap_i2c_adap_nr, i2c_verify_adapter(child)->nr);
> + } else {
> + WRITE_ONCE(bp->tap_i2c_adap_nr, -1); /* invalidate before free */
> sysfs_remove_link(&bp->dev.kobj, "i2c");
> -
> + }
> return 0;
> }
^ permalink raw reply
* Re: [RFC PATCH 01/10] net: stmmac: move XPCS lifetime management to platform drivers
From: Maxime Chevallier @ 2026-07-15 11:15 UTC (permalink / raw)
To: Christian Marangi, Coia Prant
Cc: kuba, davem, edumazet, pabeni, andrew+netdev, robh, krzk+dt,
heiko, netdev, linux-rockchip, devicetree, linux-arm-kernel,
linux-phy
In-Reply-To: <6a574873.3027643f.39eab0.b4c0@mx.google.com>
Hi Christian,
> I think the idea of Maxime is to test that series on most Scenario as
> possible to verify for fragility or regression on it.
I've tested your RFC on a few boards that use the .select_pcs() callback,
and didn't find any regressions :)
> (but just for Maxime the feature is getting actively used on OpenWrt by 3
> different SoC and no complain for now)
I have no doubt that the fwnode PCS registration works, however there's no
user of the .fill_available_pcs() path in this series. You may not have access
to HW to test this though, but this is my main concern that we discover issues
when using "internal" PCS with the new API.
I _may_ have time to play around with is on mvpp2, hard to say when though, but
feel free to send another iteration for review :)
Maxime
^ permalink raw reply
* Re: [PATCH net-next v3] net: skb: isolate skb data area allocations into a separate bucket
From: Pedro Falcato @ 2026-07-15 11:07 UTC (permalink / raw)
To: Harry Yoo
Cc: Paolo Abeni, David S. Miller, Eric Dumazet, Jakub Kicinski,
Simon Horman, Jason Xing, Kuniyuki Iwashima, netdev, linux-kernel,
linux-hardening, Kees Cook, linux-mm, Vlastimil Babka
In-Reply-To: <529e3997-d26f-4d8e-ad90-3329fe90e555@kernel.org>
On Wed, Jul 08, 2026 at 10:27:54PM +0900, Harry Yoo wrote:
>
>
> On 7/8/26 8:16 PM, Pedro Falcato wrote:
> > On Wed, Jul 08, 2026 at 10:30:50AM +0200, Paolo Abeni wrote:
> >> On 7/2/26 7:07 PM, Pedro Falcato wrote:> @@ -586,6 +586,8 @@ struct
> >> sk_buff *napi_build_skb(void *data, unsigned int frag_size)
> >>> }
> >>> EXPORT_SYMBOL(napi_build_skb);
> >>>
> >>> +static kmem_buckets *skb_data_buckets __ro_after_init;
> >>> +
> >>> static void *kmalloc_pfmemalloc(size_t obj_size, gfp_t flags, int node)
> >>> {
> >>> if (!gfp_pfmemalloc_allowed(flags))
> >>> @@ -593,7 +595,8 @@ static void *kmalloc_pfmemalloc(size_t obj_size, gfp_t flags, int node)
> >>> if (!obj_size)
> >>> return kmem_cache_alloc_node(net_hotdata.skb_small_head_cache,
> >>> flags, node);
> >>> - return kmalloc_node_track_caller(obj_size, flags, node);
> >>> + return kmem_buckets_alloc_node_track_caller(skb_data_buckets, obj_size,
> >>> + flags, node);
> >>
> >> Sashiko noted that some drivers may require GFP_DMA buckets, and the
> >> above may break them:
> >>
> >> https://sashiko.dev/#/patchset/20260702170728.168755-1-pfalcato%40suse.de
> >
> > Oh, this is really awkward. Adding linux-mm and slab maintainers for input here.
> >
> > Considering the current slab bucketing does not seem to duplicate DMA or
> > CGROUP caches, could it make sense to duplicate those as well?
>
> Could we specify what kmalloc types the user needs when creating
> kmem_buckets and duplicate caches for the requested kmalloc types only?
Perhaps. But do the users themselves know? alloc_skb() allows users to specify
random __GFP flags. We're bound to see some random caller do
alloc_skb(__GFP_ACCOUNT) ;)
In all honesty, I'm not quite sure what the best way forward here is. The most
transparent way is to bucket those other kmalloc types as well, but that might
very trivially result in a lot more caches (and possibly memory usage) for no
great reason. So perhaps specifying caches might do.
--
Pedro
^ permalink raw reply
* [PATCH net v3] psp: fix NULL genl_sock deref race with concurrent netns teardown
From: Kiran Kella @ 2026-07-15 11:07 UTC (permalink / raw)
To: daniel.zahka, kuba, willemdebruijn.kernel
Cc: davem, edumazet, pabeni, horms, weibunny, netdev, linux-kernel,
jayakrishnan.udayavarma, ajit.khaparde, akhilesh.samineni,
Kiran Kella, Vikas Gupta, Bhargava Marreddy
The race occurs between network namespace removal and PSP device
unregistration. When a netns is deleted while a PSP device associated
with that netns is concurrently being removed, psp_dev_unregister()
triggers psp_nl_notify_dev() to send a device change notification.
Concurrently, cleanup_net() running in the netns workqueue calls
genl_pernet_exit(), which sets net->genl_sock to NULL. If
genl_pernet_exit() wins the race, two sites in psp_nl_multicast_per_ns()
then dereference the NULL socket and crash:
CPU 0 (netns teardown) CPU 1 (PSP device unregister)
====================== =============================
cleanup_net [workqueue]
genl_pernet_exit() psp_dev_unregister()
net->genl_sock = NULL psp_nl_notify_dev()
psp_nl_multicast_per_ns()
build_ntf()
-> netlink_has_listeners(NULL)
/* crash */
genlmsg_multicast_netns()
-> nlmsg_multicast_filtered(NULL)
/* crash */
Both the main_net path (derived from psd->main_netdev) and each
assoc_net entry in psd->assoc_dev_list are affected.
Fix by replacing the bare dev_net() calls with maybe_get_net().
maybe_get_net() returns NULL if the namespace is already dying.
Holding the reference ensures genl_sock remains valid across both the
build_ntf() and genlmsg_multicast_netns() calls.
Fixes: 00c94ca2b99e ("psp: base PSP device support")
Fixes: 06c2dce2d0f6 ("psp: add new netlink cmd for dev-assoc and dev-disassoc")
Reviewed-by: Ajit Khaparde <ajit.khaparde@broadcom.com>
Reviewed-by: Vikas Gupta <vikas.gupta@broadcom.com>
Reviewed-by: Bhargava Marreddy <bhargava.marreddy@broadcom.com>
Reviewed-by: Akhilesh Samineni <akhilesh.samineni@broadcom.com>
Reviewed-by: Daniel Zahka <daniel.zahka@gmail.com>
Tested-by: Daniel Zahka <daniel.zahka@gmail.com>
Reviewed-by: Wei Wang <weibunny@fb.com>
Signed-off-by: Kiran Kella <kiran.kella@broadcom.com>
---
v3:
Fixed typo in the commit log
v2:
- get rid of the extra struct net *net, by doing
(!maybe_get_net(assoc_net)) directly (as suggested by Daniel Zahka)
v1: https://lore.kernel.org/all/20260703112431.2860506-1-kiran.kella@broadcom.com/
net/psp/psp_nl.c | 23 ++++++++++++++---------
1 file changed, 14 insertions(+), 9 deletions(-)
diff --git a/net/psp/psp_nl.c b/net/psp/psp_nl.c
index 9610d8c456ff..1438dbb07949 100644
--- a/net/psp/psp_nl.c
+++ b/net/psp/psp_nl.c
@@ -62,7 +62,10 @@ psp_nl_multicast_per_ns(struct psp_dev *psd, unsigned int group,
struct net *main_net;
struct sk_buff *ntf;
- main_net = dev_net(psd->main_netdev);
+ main_net = maybe_get_net(dev_net(psd->main_netdev));
+ if (!main_net)
+ return;
+
xa_init(&sent_nets);
list_for_each_entry(entry, &psd->assoc_dev_list, dev_list) {
@@ -77,21 +80,23 @@ psp_nl_multicast_per_ns(struct psp_dev *psd, unsigned int group,
if (ret == -EBUSY)
continue;
- ntf = build_ntf(psd, assoc_net, ctx);
- if (!ntf)
+ if (!maybe_get_net(assoc_net))
continue;
- genlmsg_multicast_netns(&psp_nl_family, assoc_net, ntf, 0,
- group, GFP_KERNEL);
+ ntf = build_ntf(psd, assoc_net, ctx);
+ if (ntf)
+ genlmsg_multicast_netns(&psp_nl_family, assoc_net, ntf,
+ 0, group, GFP_KERNEL);
+ put_net(assoc_net);
}
xa_destroy(&sent_nets);
/* Send to main device netns */
ntf = build_ntf(psd, main_net, ctx);
- if (!ntf)
- return;
- genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group,
- GFP_KERNEL);
+ if (ntf)
+ genlmsg_multicast_netns(&psp_nl_family, main_net, ntf, 0, group,
+ GFP_KERNEL);
+ put_net(main_net);
}
static struct sk_buff *psp_nl_clone_ntf(struct psp_dev *psd, struct net *net,
--
2.54.0
^ permalink raw reply related
* RE: [PATCH net] tipc: prevent node timer rearm after peer removal
From: Tung Quang Nguyen @ 2026-07-15 10:28 UTC (permalink / raw)
To: David Lee
Cc: David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Simon Horman, Richard Alpe, Jon Maloy,
Dominik 'Disconnect3d' Czarnota, netdev@vger.kernel.org,
tipc-discussion@lists.sourceforge.net,
linux-kernel@vger.kernel.org
In-Reply-To: <20260714070611.1700456-1-david.lee@trailofbits.com>
>Subject: [PATCH net] tipc: prevent node timer rearm after peer removal
>
>TIPC node deletion removes the node from lookup tables, calls
>timer_delete_sync(), and drops the timer reference. This stops an already
>running timer callback, but it does not prevent another racing path from
>rearming the same timer after deletion has started.
>
>Synthetic UDP discovery can race TIPC_NL_PEER_REMOVE and link property
>updates. When discovery recreates the first link, tipc_node_check_dest() calls
>mod_timer() and retakes the timer reference. If that happens after
>tipc_node_delete() has deleted the timer, the orphaned timer can later run
>after network namespace teardown has freed the per-net TIPC state and
>broadcast link.
>
>Use timer_shutdown_sync() when deleting a node. Shutdown has the same
>synchronization effect as timer_delete_sync(), and also prevents future
>rearming through mod_timer().
>
>Fixes: b34040227be7 ("tipc: add peer removal functionality")
>Signed-off-by: David Lee <david.lee@trailofbits.com>
>Assisted-by: Codex:gpt-5.5
>---
>Trail of Bits has a reproducer for this bug demonstrating Kernel Panic which can
>be shared further if needed.
>
> net/tipc/node.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
>diff --git a/net/tipc/node.c b/net/tipc/node.c index
>262b39ecf5f8..5b3ee44347be 100644
>--- a/net/tipc/node.c
>+++ b/net/tipc/node.c
>@@ -638,7 +638,7 @@ static void tipc_node_delete(struct tipc_node *node)
> trace_tipc_node_delete(node, true, " ");
> tipc_node_delete_from_list(node);
>
>- timer_delete_sync(&node->timer);
>+ timer_shutdown_sync(&node->timer);
sashiko reports:
Could this regression introduce a permanent memory leak of the tipc_node
structure if it races with tipc_node_check_dest()?
If thread A deletes a node and shuts down the timer, and thread B
concurrently processes a UDP discovery packet and calls
tipc_node_check_dest(), thread B might execute this sequence:
if (!mod_timer(&n->timer, intv))
tipc_node_get(n);
Since the timer is shut down, mod_timer() silently discards the operation,
does not enqueue the timer, and returns 0. Thread B then calls
tipc_node_get() expecting the timer callback to eventually execute and drop
the reference.
Because the timer is shut down and will never fire, tipc_node_timeout()
will never execute to drop this reference, leaking the unlinked node
permanently.
> tipc_node_put(node);
> }
>
>--
>2.43.0
>
^ permalink raw reply
* Re: [PATCH v2 1/5] ibmvnic: Move long delayed work on system_dfl_long_wq
From: Marco Crivellari @ 2026-07-15 10:28 UTC (permalink / raw)
To: Simon Horman
Cc: linux-kernel, netdev, tj, jiangshanlai, frederic, bigeasy, mhocko,
andrew+netdev, davem, edumazet, kuba, pabeni, enelsonmoore,
linux-usb
In-Reply-To: <20260711164434.1691221-3-horms@kernel.org>
Hi,
On Sat, Jul 11, 2026 at 6:46 PM Simon Horman <horms@kernel.org> wrote:
>
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
>
> Simon says: While I think that the pre-existing issue flagged below is
> orthogonal to this patch, I wonder if you could respond
> to the question regarding scope raised in the first
> issue flagged below.
>
> [...]
>
> [Severity: Medium]
> Will this leave the primary reset path bound to the per-cpu system_long_wq?
>
> While this patch partially migrates delayed reset works to system_dfl_long_wq,
> the initial reset work enqueued in ibmvnic_reset() is still bound to the
> per-CPU system_long_wq:
I will double check the code but converting that workqueue seems doable.
Thanks!
--
Marco Crivellari
SUSE Labs
^ permalink raw reply
* Re: [PATCH iwl] ice: acquire NVM lock around each flash read
From: Robert Malz @ 2026-07-15 10:22 UTC (permalink / raw)
To: Tony Nguyen
Cc: Przemek Kitszel, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Alexander Lobakin, Jacob Keller,
Jesse Brandeburg, intel-wired-lan, netdev, linux-kernel
In-Reply-To: <bc89cccb-3f22-42c9-9f96-382586d12fcf@intel.com>
Hey Tony,
Thanks for the review.
On Wed, Jul 15, 2026 at 1:47 AM Tony Nguyen <anthony.l.nguyen@intel.com> wrote:
>
>
>
> On 7/3/2026 3:32 AM, Robert Malz wrote:
>
> ...
>
> > @@ -92,12 +98,28 @@ ice_read_flat_nvm(struct ice_hw *hw, u32 offset, u32 *length, u8 *data,
> >
> > last_cmd = !(bytes_read + read_size < inlen);
> >
> > + status = ice_acquire_nvm(hw, ICE_RES_READ);
> > + if (status)
> > + break;
> > +
> > status = ice_aq_read_nvm(hw, ICE_AQC_NVM_START_POINT,
> > offset, read_size,
> > data + bytes_read, last_cmd,
> > read_shadow_ram, NULL);
>
> Sashiko says:
>
> If this chunk has last_cmd = false, doesn't releasing the NVM lock
> immediately
> after ice_aq_read_nvm() allow other entities to acquire the lock and issue
> their own NVM commands in the middle of our open read sequence?
[RM] I do think that is intended behavior. Reads should not be
stateful and there is no open read sequence to interrupt.
Each ice_aq_read_nvm call takes an absolute offset from the starting point.
> Also, if ice_acquire_nvm() fails on the next loop iteration, the loop breaks
> and we never send a command with last_cmd = true. Will this permanently leak
> the sequence state in the firmware?
[RM] I don't think there is a leak as there is no FW read sequence
state. As far as I know, lack of last_cmd does not leave anything
dangling in the FW.
>
> [TN] I'm seeing conflicting documentation on whether this bit matters
> for the read command. I'm working on getting clarification. If it does
> matter, we'll likely need to adjust this.
[RM] I'm happy to adjust the patch once you have more details on it.
>
> > - if (status)
> > + if (status) {
> > + /* ice_release_nvm() issues an AQ command that would
> > + * overwrite sq_last_status, which some callers
> > + * inspect after a failed read. Preserve the read's
> > + * AQ error across the release.
> > + */
> > + aq_err = hw->adminq.sq_last_status;
> > +
> > + ice_release_nvm(hw);
> > + hw->adminq.sq_last_status = aq_err;
>
> Does restoring hw->adminq.sq_last_status here without holding the Admin
> Queue
> lock risk overwriting the status of a concurrent AdminQ command?
> If another thread acquires the lock and sends a command immediately after
> ice_release_nvm(hw), this lockless write could corrupt its error status.
>
> [TN] I don't believe the AQ lock will work as desired we can't guarantee
> that we will have the lock directly following the release in order to
> restore the AQ error. Similar to the NVM lock issue, I think this is a
> small window but wanted to bring this here in case others had
> thoughts/comments on this.
[RM] Agree, this can cause issues. We can't drop sq_last_status as
ice_discover_flash_size depends on it.
Proposed fix: drop the save/restore sq_last_status and propagate the
read AQ error through a real return path instead of the shared global.
I could add optional enum libie_aq_err *read_aq_err out param to
ice_read_flat_nvm(), capture sq_last_status before the
ice_release_nvm, and have ice_discover_flash_size() test that instead.
The log-only callers (devlink/ethtool) don't care about it.
Let me know what you think about it.
>
> Thanks,
> Tony
>
> > break;
> > + }
> > +
> > + ice_release_nvm(hw);
> >
> > bytes_read += read_size;
> > offset += read_size;
>
>
Regards,
Robert
^ permalink raw reply
* [PATCH net-next] net: phy: at803x: Use a helper to check for phy reset existence
From: Maxime Chevallier @ 2026-07-15 10:13 UTC (permalink / raw)
To: Andrew Lunn, davem, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
Russell King, Heiner Kallweit
Cc: Maxime Chevallier, netdev, linux-kernel, thomas.petazzoni,
linux-arm-msm
The at803x family of devices are subjected to an errata that requires
hard-reseting the PHY upon link change.
That can only work if there's a physical reset line wired to the PHY,
which the driver checks by looking if there's a reset GPIO configured
for the MDIO device.
The reset may however be controlled through a reset controller, which
isn't accounted for in the errata handling.
Besides that, PHY drivers aren't expected to directly access the
mdiodev's resources directly, let's therefore wrap this with a phylib
helper, that uses a similar mdio helper to check for reset existence.
This was found in preparation for bus-level resource management for
better mdio scan support.
Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
drivers/net/phy/qcom/at803x.c | 2 +-
include/linux/mdio.h | 5 +++++
include/linux/phy.h | 5 +++++
3 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/net/phy/qcom/at803x.c b/drivers/net/phy/qcom/at803x.c
index ba4dc07752b6..6872dbf77856 100644
--- a/drivers/net/phy/qcom/at803x.c
+++ b/drivers/net/phy/qcom/at803x.c
@@ -537,7 +537,7 @@ static void at803x_link_change_notify(struct phy_device *phydev)
* in the FIFO. In such cases, the FIFO enters an error mode it
* cannot recover from by software.
*/
- if (phydev->state == PHY_NOLINK && phydev->mdio.reset_gpio) {
+ if (phydev->state == PHY_NOLINK && phy_device_has_reset(phydev)) {
struct at803x_context context;
at803x_context_save(phydev, &context);
diff --git a/include/linux/mdio.h b/include/linux/mdio.h
index 300805e66592..a7d9e3ae362a 100644
--- a/include/linux/mdio.h
+++ b/include/linux/mdio.h
@@ -86,6 +86,11 @@ static inline void *mdiodev_get_drvdata(struct mdio_device *mdio)
return dev_get_drvdata(&mdio->dev);
}
+static inline bool mdiodev_has_reset(struct mdio_device *mdio)
+{
+ return (mdio->reset_gpio || mdio->reset_ctrl);
+}
+
void mdio_device_free(struct mdio_device *mdiodev);
struct mdio_device *mdio_device_create(struct mii_bus *bus, int addr);
int mdio_device_register(struct mdio_device *mdiodev);
diff --git a/include/linux/phy.h b/include/linux/phy.h
index fc680901275b..beff1d6fcc7c 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -2231,6 +2231,11 @@ static inline void phy_device_reset(struct phy_device *phydev, int value)
mdio_device_reset(&phydev->mdio, value);
}
+static inline bool phy_device_has_reset(struct phy_device *phydev)
+{
+ return mdiodev_has_reset(&phydev->mdio);
+}
+
#define phydev_err(_phydev, format, args...) \
dev_err(&_phydev->mdio.dev, format, ##args)
--
2.55.0
^ permalink raw reply related
* Re: [PATCH net] esp: do not unref managed frag pages in esp_ssg_unref()
From: Steffen Klassert @ 2026-07-15 10:13 UTC (permalink / raw)
To: Maher Azzouzi
Cc: herbert, davem, netdev, edumazet, kuba, pabeni, w, linux-kernel
In-Reply-To: <20260712170530.9807-1-maherazz04@gmail.com>
On Sun, Jul 12, 2026 at 06:05:30PM +0100, Maher Azzouzi wrote:
> esp_ssg_unref() releases the page references held on the source
> scatterlist after the AEAD operation completes. It calls
> skb_page_unref() on every frag page for an out-of-place transform
> (req->src != req->dst), and in the error path of esp_output_tail()
> (already_unref == true) on the request's own scatterlist.
>
> This is wrong when the skb carries managed frags
> (SKBFL_MANAGED_FRAG_REFS). Managed frags are owned by a zerocopy ubuf
> and the skb does not hold a per-frag page reference; io_uring SEND_ZC
> with a registered buffer attaches the bvec pages this way via
> io_sg_from_iter(). The rest of the stack honours this invariant:
> skb_release_data() skips the per-frag unref when SKBFL_MANAGED_FRAG_REFS
> is set, and skb_zcopy_managed() is the guard used at the other unref
> sites.
>
> esp_ssg_unref() is missing that guard, so for a managed-frag skb it
> drops a page reference the skb never acquired. This can underflow the
> page reference count and free a page that is still in use.
>
> Guard the function with skb_zcopy_managed() so both unref paths are
> skipped for managed-frag skbs, matching skb_release_data().
>
> Fixes: cac2661c53f3 ("esp4: Avoid skb_cow_data whenever possible")
> Fixes: 03e2a30f6a27 ("esp6: Avoid skb_cow_data whenever possible")
> Signed-off-by: Maher Azzouzi <maherazz04@gmail.com>
Applied to the ipsec tree, thanks a lot!
^ permalink raw reply
* Re: [PATCH ipsec] xfrm6: fix out-of-bounds write in xfrm6_input_addr() when secpath is full
From: Steffen Klassert @ 2026-07-15 10:12 UTC (permalink / raw)
To: Xiang Mei
Cc: herbert, davem, nakam, edumazet, kuba, pabeni, horms, netdev,
bestswngs
In-Reply-To: <20260704210333.668216-1-xmei5@asu.edu>
On Sat, Jul 04, 2026 at 02:03:32PM -0700, Xiang Mei wrote:
> The depth check in xfrm6_input_addr() is off by one:
>
> if (1 + sp->len == XFRM_MAX_DEPTH)
> goto drop;
> ...
> sp->xvec[sp->len++] = x;
>
> xfrm_input() can leave sp->len == XFRM_MAX_DEPTH, and the transport-mode
> receive path re-enters IPv6 input via xfrm_trans_reinject() with that
> secpath preserved. If the inner packet carries a destination-options HAO
> option or a type-2 routing header, xfrm6_input_addr() is called with
> sp->len == XFRM_MAX_DEPTH; the check (1 + 6 == 6) is false, so
> sp->xvec[sp->len++] writes one slot past the 6-element xvec[]. The write
> stays within the sec_path allocation (invisible to KASAN); UBSAN_BOUNDS
> flags it and panics under panic_on_warn.
>
> Use "sp->len >= XFRM_MAX_DEPTH", matching xfrm_input(). This also
> restores one chain level the old check rejected at sp->len == 5.
>
> UBSAN: array-index-out-of-bounds in net/ipv6/xfrm6_input.c:309:10
> index 6 is out of range for type 'xfrm_state *[6]'
>
> Fixes: 9473e1f631de ("[XFRM] MIPv6: Fix to input RO state correctly.")
> Reported-by: Weiming Shi <bestswngs@gmail.com>
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Xiang Mei <xmei5@asu.edu>
Applied, thanks a lot!
^ permalink raw reply
* Re: [PATCH iwl-net v2 1/2] iavf: fix ASQ command buffer leak on init failure
From: luoxuanqiang @ 2026-07-15 9:40 UTC (permalink / raw)
To: Jagielski, Jedrzej, Nguyen, Anthony L, Kitszel, Przemyslaw,
intel-wired-lan@lists.osuosl.org
Cc: Andrew Lunn, Mitch Williams, Greg Rose, Sudheer Mogilappagari,
netdev@vger.kernel.org, Xuanqiang Luo, stable@vger.kernel.org
In-Reply-To: <MW4PR11MB5890DCEED74E887ACBC9161AF0F82@MW4PR11MB5890.namprd11.prod.outlook.com>
在 2026/7/15 17:28, Jagielski, Jedrzej 写道:
> From: xuanqiang.luo@linux.dev <xuanqiang.luo@linux.dev>
> Sent: Wednesday, July 15, 2026 10:26 AM
>
>> From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
>>
>> iavf_alloc_adminq_asq_ring() allocates cmd_buf before the remaining ASQ
>> resources. If iavf_alloc_asq_bufs() or iavf_config_asq_regs() fails, the
>> unwind path elides cmd_buf while freeing the other allocations.
>>
>> The ASQ count is not set until initialization succeeds, so the shutdown
>> path cannot reclaim the buffer. Free cmd_buf in the common unwind path.
>>
>> Fixes: d358aa9a7a2d ("i40evf: init code and hardware support")
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
>> ---
>> drivers/net/ethernet/intel/iavf/iavf_adminq.c | 1 +
>> 1 file changed, 1 insertion(+)
>>
>> diff --git a/drivers/net/ethernet/intel/iavf/iavf_adminq.c b/drivers/net/ethernet/intel/iavf/iavf_adminq.c
>> index 6937b7dd44cbb..40f76f9507f4b 100644
>> --- a/drivers/net/ethernet/intel/iavf/iavf_adminq.c
>> +++ b/drivers/net/ethernet/intel/iavf/iavf_adminq.c
>> @@ -60,6 +60,7 @@ static enum iavf_status iavf_alloc_adminq_arq_ring(struct iavf_hw *hw)
>> **/
>> static void iavf_free_adminq_asq(struct iavf_hw *hw)
>> {
>> + iavf_free_virt_mem(hw, &hw->aq.asq.cmd_buf);
>> iavf_free_dma_mem(hw, &hw->aq.asq.desc_buf);
>> }
>>
>> --
>> 2.43.0
> Looks fine, thanks!
>
> Reviewed-by: Jedrzej Jagielski <jedrzej.jagielski@intel.com>
>
> One note for the future - please be aware that there is minimal time period to be
> waited before resubmitting new patch revision, which is at least 24h for netdev/IWL
> mailing lists
Thanks for the reminder!
I also received a notification from netdev-bot, and I'll keep this in
mind for future revisions.
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH v2] idpf: disable PCIe PTM on probe failure and removal
From: Loktionov, Aleksandr @ 2026-07-15 9:38 UTC (permalink / raw)
To: Myeonghun Pak, Nguyen, Anthony L, Kitszel, Przemyslaw,
intel-wired-lan@lists.osuosl.org
Cc: Olech, Milena, Tantilov, Emil S, Andrew Lunn, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev@vger.kernel.org,
linux-kernel@vger.kernel.org, Ijae Kim
In-Reply-To: <20260715074342.49281-1-mhun512@gmail.com>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Myeonghun Pak
> Sent: Wednesday, July 15, 2026 9:44 AM
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
> Przemyslaw <przemyslaw.kitszel@intel.com>; intel-wired-
> lan@lists.osuosl.org
> Cc: Olech, Milena <milena.olech@intel.com>; Tantilov, Emil S
> <emil.s.tantilov@intel.com>; 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>; netdev@vger.kernel.org; linux-
> kernel@vger.kernel.org; Myeonghun Pak <mhun512@gmail.com>; Ijae Kim
> <ae878000@gmail.com>
> Subject: [Intel-wired-lan] [PATCH v2] idpf: disable PCIe PTM on probe
> failure and removal
>
> idpf_probe() enables PCIe Precision Time Measurement with
> pci_enable_ptm(pdev, NULL), which programs the PTM control bits and
> sets
> pdev->ptm_enabled when the bus/controller supports it.
>
> If a later probe step fails, the error path releases the allocated
> workqueues and adapter memory without disabling PTM. The remove path
> has the same imbalance when a successfully probed device is detached.
> In both cases, the PCI core's software PTM state and the device's PTM
> control bits remain set with no bound driver.
>
> Add pci_disable_ptm() to the common probe unwind after the PTM enable
> and to idpf_remove(). pci_disable_ptm() is a no-op when PTM was not
> enabled, so the non-fatal pci_enable_ptm() failure remains safe.
> pcim_enable_device() only arranges for pci_disable_device() and does
> not undo the PTM enable.
>
> Fixes: 8d5e12c5921c ("idpf: add initial PTP support")
> Co-developed-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
> ---
> Changes in v2:
> - Disable PTM in the probe error path, as requested by Emil Tantilov.
>
> drivers/net/ethernet/intel/idpf/idpf_main.c | 7 +++++--
> 1 file changed, 5 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/idpf/idpf_main.c
> b/drivers/net/ethernet/intel/idpf/idpf_main.c
> index 0dd741dcfc..f08ce13c20 100644
> --- a/drivers/net/ethernet/intel/idpf/idpf_main.c
> +++ b/drivers/net/ethernet/intel/idpf/idpf_main.c
> @@ -159,6 +159,7 @@ static void idpf_remove(struct pci_dev *pdev)
> mutex_destroy(&adapter->queue_lock);
> mutex_destroy(&adapter->vc_buf_lock);
>
> + pci_disable_ptm(pdev);
> pci_set_drvdata(pdev, NULL);
> kfree(adapter);
> }
> @@ -266,7 +267,7 @@ static int idpf_probe(struct pci_dev *pdev, const
> struct pci_device_id *ent)
> if (err) {
> pci_err(pdev, "DMA configuration failed: %pe\n",
> ERR_PTR(err));
>
> - goto err_free;
> + goto err_disable_ptm;
> }
>
> pci_set_master(pdev);
> @@ -279,7 +280,7 @@ static int idpf_probe(struct pci_dev *pdev, const
> struct pci_device_id *ent)
> if (!adapter->init_wq) {
> dev_err(dev, "Failed to allocate init workqueue\n");
> err = -ENOMEM;
> - goto err_free;
> + goto err_disable_ptm;
> }
>
> adapter->serv_wq = alloc_workqueue("%s-%s-service", @@ -366,6
> +367,8 @@ err_mbx_wq_alloc:
> destroy_workqueue(adapter->serv_wq);
> err_serv_wq_alloc:
> destroy_workqueue(adapter->init_wq);
> +err_disable_ptm:
> + pci_disable_ptm(pdev);
> err_free:
> kfree(adapter);
> return err;
> --
> 2.47.1
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
^ permalink raw reply
* RE: [Intel-wired-lan] [PATCH net] i40e: xsk: fix multi-buffer XDP_PASS skb construction
From: Loktionov, Aleksandr @ 2026-07-15 9:37 UTC (permalink / raw)
To: Chenguang Zhao, Nguyen, Anthony L, Kitszel, Przemyslaw,
andrew+netdev@lunn.ch, davem@davemloft.net, edumazet@google.com,
kuba@kernel.org, pabeni@redhat.com
Cc: intel-wired-lan@lists.osuosl.org, netdev@vger.kernel.org,
Chenguang Zhao
In-Reply-To: <20260714025112.284724-1-chenguang.zhao@linux.dev>
> -----Original Message-----
> From: Intel-wired-lan <intel-wired-lan-bounces@osuosl.org> On Behalf
> Of Chenguang Zhao
> Sent: Tuesday, July 14, 2026 4:51 AM
> To: Nguyen, Anthony L <anthony.l.nguyen@intel.com>; Kitszel,
> Przemyslaw <przemyslaw.kitszel@intel.com>; andrew+netdev@lunn.ch;
> davem@davemloft.net; edumazet@google.com; kuba@kernel.org;
> pabeni@redhat.com
> Cc: intel-wired-lan@lists.osuosl.org; netdev@vger.kernel.org;
> chenguang.zhao@linux.dev; Chenguang Zhao <zhaochenguang@kylinos.cn>
> Subject: [Intel-wired-lan] [PATCH net] i40e: xsk: fix multi-buffer
> XDP_PASS skb construction
>
> From: Chenguang Zhao <zhaochenguang@kylinos.cn>
>
> When AF_XDP ZC receives a multi-buffer frame and the XDP program
> returns XDP_PASS, i40e_construct_skb_zc() copies frags into a new skb.
> The copy used skb_frag_page() as the memcpy source (page metadata
> instead of packet data) and passed a virtual address to
> __skb_fill_page_desc_noacc(), which expects a struct page *.
>
> Use skb_frag_address() for the copy, attach frags with
> skb_add_rx_frag() so len/data_len/truesize are updated, and on
> dev_alloc_page() failure free the skb via the shared out path so
> xsk_buff_free() still runs and previously attached pages are released
> by kfree_skb.
>
> Fixes: 1c9ba9c14658 ("i40e: xsk: add RX multi-buffer support")
> Signed-off-by: Chenguang Zhao <zhaochenguang@kylinos.cn>
> ---
> - Fix memcpy source: use skb_frag_address() instead of
> skb_frag_page(),
> which was copying page metadata rather than packet data.
>
> - Fix frag attachment: pass the allocated struct page * to the skb
> frag
> helper instead of the page virtual address.
>
> - Use skb_add_rx_frag() so skb->len, data_len and truesize are
> updated
> when attaching copied frags.
>
> - On mid-loop dev_alloc_page() failure, go through the shared out
> path
> so previously attached pages are released via kfree_skb and
> xsk_buff_free() is still called.
>
> drivers/net/ethernet/intel/i40e/i40e_xsk.c | 13 +++++--------
> 1 file changed, 5 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> index 9f47388eaba5..a4247710c85b 100644
> --- a/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> +++ b/drivers/net/ethernet/intel/i40e/i40e_xsk.c
> @@ -318,22 +318,19 @@ static struct sk_buff
> *i40e_construct_skb_zc(struct i40e_ring *rx_ring,
> goto out;
>
> for (int i = 0; i < nr_frags; i++) {
> - struct skb_shared_info *skinfo = skb_shinfo(skb);
> skb_frag_t *frag = &sinfo->frags[i];
> + unsigned int frag_size = skb_frag_size(frag);
> struct page *page;
> - void *addr;
>
> page = dev_alloc_page();
> if (!page) {
> dev_kfree_skb(skb);
> - return NULL;
> + skb = NULL;
> + goto out;
> }
> - addr = page_to_virt(page);
>
> - memcpy(addr, skb_frag_page(frag), skb_frag_size(frag));
> -
> - __skb_fill_page_desc_noacc(skinfo, skinfo->nr_frags++,
> - addr, 0, skb_frag_size(frag));
> + memcpy(page_to_virt(page), skb_frag_address(frag),
> frag_size);
> + skb_add_rx_frag(skb, i, page, 0, frag_size, PAGE_SIZE);
> }
>
> out:
> --
> 2.25.1
Reviewed-by: Aleksandr Loktionov <aleksandr.loktionov@intel.com>
^ permalink raw reply
* RE: [PATCH iwl-net v2 1/2] iavf: fix ASQ command buffer leak on init failure
From: Jagielski, Jedrzej @ 2026-07-15 9:28 UTC (permalink / raw)
To: xuanqiang.luo@linux.dev, Nguyen, Anthony L, Kitszel, Przemyslaw,
intel-wired-lan@lists.osuosl.org
Cc: Andrew Lunn, Mitch Williams, Greg Rose, Sudheer Mogilappagari,
netdev@vger.kernel.org, Xuanqiang Luo, stable@vger.kernel.org
In-Reply-To: <20260715082548.56687-2-xuanqiang.luo@linux.dev>
From: xuanqiang.luo@linux.dev <xuanqiang.luo@linux.dev>
Sent: Wednesday, July 15, 2026 10:26 AM
>From: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
>
>iavf_alloc_adminq_asq_ring() allocates cmd_buf before the remaining ASQ
>resources. If iavf_alloc_asq_bufs() or iavf_config_asq_regs() fails, the
>unwind path elides cmd_buf while freeing the other allocations.
>
>The ASQ count is not set until initialization succeeds, so the shutdown
>path cannot reclaim the buffer. Free cmd_buf in the common unwind path.
>
>Fixes: d358aa9a7a2d ("i40evf: init code and hardware support")
>Cc: stable@vger.kernel.org
>Signed-off-by: Xuanqiang Luo <luoxuanqiang@kylinos.cn>
>---
> drivers/net/ethernet/intel/iavf/iavf_adminq.c | 1 +
> 1 file changed, 1 insertion(+)
>
>diff --git a/drivers/net/ethernet/intel/iavf/iavf_adminq.c b/drivers/net/ethernet/intel/iavf/iavf_adminq.c
>index 6937b7dd44cbb..40f76f9507f4b 100644
>--- a/drivers/net/ethernet/intel/iavf/iavf_adminq.c
>+++ b/drivers/net/ethernet/intel/iavf/iavf_adminq.c
>@@ -60,6 +60,7 @@ static enum iavf_status iavf_alloc_adminq_arq_ring(struct iavf_hw *hw)
> **/
> static void iavf_free_adminq_asq(struct iavf_hw *hw)
> {
>+ iavf_free_virt_mem(hw, &hw->aq.asq.cmd_buf);
> iavf_free_dma_mem(hw, &hw->aq.asq.desc_buf);
> }
>
>--
>2.43.0
Looks fine, thanks!
Reviewed-by: Jedrzej Jagielski <jedrzej.jagielski@intel.com>
One note for the future - please be aware that there is minimal time period to be
waited before resubmitting new patch revision, which is at least 24h for netdev/IWL
mailing lists
^ permalink raw reply
* Re: [PATCH bpf v3 0/2] bpf, sockmap: Fix FIONREAD for sockets without a verdict program
From: patchwork-bot+netdevbpf @ 2026-07-15 9:20 UTC (permalink / raw)
To: Mattia Meleleo
Cc: bpf, netdev, john.fastabend, jakub, jiayuan.chen, memxor, emil
In-Reply-To: <20260708-fionread-no-verdict-v3-0-b4ee31b3af53@coralogix.com>
Hello:
This series was applied to bpf/bpf.git (master)
by Kumar Kartikeya Dwivedi <memxor@gmail.com>:
On Wed, 08 Jul 2026 18:54:59 +0200 you wrote:
> Sockets added to a sockmap/sockhash with no stream/skb verdict program
> attached answer FIONREAD with 0 even when unread data is pending in
> sk_receive_queue. Fix tcp_bpf_ioctl() to account for the receive queue
> in that case, and add a selftest.
>
> Changes in v3:
> - Remove unused sk_psock_msg_inq()
> - Link to v2: https://patch.msgid.link/20260708-fionread-no-verdict-v2-0-29dd293621c7@coralogix.com
>
> [...]
Here is the summary with links:
- [bpf,v3,1/2] bpf, sockmap: Account for receive queue in FIONREAD without a verdict program
https://git.kernel.org/bpf/bpf/c/04af4efde58a
- [bpf,v3,2/2] selftests/bpf: Test FIONREAD on a sockmap socket without a verdict program
https://git.kernel.org/bpf/bpf/c/a42f05cc4c1d
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox