Merge Official Source
Signed-off-by: Tianling Shen <cnsztl@immortalwrt.org>
This commit is contained in:
@@ -117,6 +117,7 @@ openwrt,one)
|
||||
ubootenv_add_ubi_default
|
||||
;;
|
||||
smartrg,sdg-8733|\
|
||||
smartrg,sdg-8733a|\
|
||||
smartrg,sdg-8734)
|
||||
local envdev=$(find_mmc_part "u-boot-env" "mmcblk0")
|
||||
ubootenv_add_uci_config "$envdev" "0x0" "0x8000" "0x8000"
|
||||
|
||||
@@ -77,7 +77,6 @@ h3c,tx1801-plus|\
|
||||
h3c,tx1806|\
|
||||
iptime,ax2004m|\
|
||||
jcg,q20|\
|
||||
jcg,q20-pb-boot|\
|
||||
linksys,e7350|\
|
||||
netgear,eax12|\
|
||||
netgear,wax202|\
|
||||
|
||||
@@ -13,7 +13,7 @@ include $(INCLUDE_DIR)/kernel.mk
|
||||
|
||||
|
||||
PKG_NAME:=linux
|
||||
PKG_PATCHVER:=5.15
|
||||
PKG_PATCHVER:=6.6
|
||||
# Manually include kernel version and hash from kernel details file
|
||||
include $(INCLUDE_DIR)/kernel-$(PKG_PATCHVER)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
--- a/arch/mips/Kconfig
|
||||
+++ b/arch/mips/Kconfig
|
||||
@@ -2988,6 +2988,9 @@ choice
|
||||
@@ -2820,6 +2820,9 @@ choice
|
||||
config HZ_256
|
||||
bool "256 HZ" if SYS_SUPPORTS_256HZ || SYS_SUPPORTS_ARBIT_HZ
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
config HZ_1000
|
||||
bool "1000 HZ" if SYS_SUPPORTS_1000HZ || SYS_SUPPORTS_ARBIT_HZ
|
||||
|
||||
@@ -3039,6 +3042,7 @@ config HZ
|
||||
@@ -2871,6 +2874,7 @@ config HZ
|
||||
default 128 if HZ_128
|
||||
default 250 if HZ_250
|
||||
default 256 if HZ_256
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
From 7ed95633bff19950069c348b94c9c13164a57a2a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Wed, 18 Jan 2023 20:20:39 +0100
|
||||
Subject: [PATCH] linux/netlink: drop NL_SET_ERR_MSG for kernel modules
|
||||
|
||||
We don't need NL_SET_ERR_MSG_MOD for bpf modules and we can drop it to
|
||||
solve missing KBUILD_MODNAME define.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
---
|
||||
include/linux/netlink.h | 3 ---
|
||||
1 file changed, 3 deletions(-)
|
||||
|
||||
diff --git a/include/linux/netlink.h b/include/linux/netlink.h
|
||||
index 61b1c7f..93561fb 100644
|
||||
--- a/include/linux/netlink.h
|
||||
+++ b/include/linux/netlink.h
|
||||
@@ -98,9 +98,6 @@ struct netlink_ext_ack {
|
||||
__extack->_msg = __msg; \
|
||||
} while (0)
|
||||
|
||||
-#define NL_SET_ERR_MSG_MOD(extack, msg) \
|
||||
- NL_SET_ERR_MSG((extack), KBUILD_MODNAME ": " msg)
|
||||
-
|
||||
#define NL_SET_BAD_ATTR_POLICY(extack, attr, pol) do { \
|
||||
if ((extack)) { \
|
||||
(extack)->bad_attr = (attr); \
|
||||
--
|
||||
2.38.1
|
||||
|
||||
@@ -12,11 +12,9 @@ Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
include/net/flow_offload.h | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/include/net/flow_offload.h b/include/net/flow_offload.h
|
||||
index 7a2b022..f17c485 100644
|
||||
--- a/include/net/flow_offload.h
|
||||
+++ b/include/net/flow_offload.h
|
||||
@@ -321,7 +321,7 @@ flow_action_mixed_hw_stats_check(const struct flow_action *action,
|
||||
@@ -376,7 +376,7 @@ flow_action_mixed_hw_stats_check(const s
|
||||
|
||||
flow_action_for_each(i, action_entry, action) {
|
||||
if (i && action_entry->hw_stats != last_hw_stats) {
|
||||
@@ -25,7 +23,7 @@ index 7a2b022..f17c485 100644
|
||||
return false;
|
||||
}
|
||||
last_hw_stats = action_entry->hw_stats;
|
||||
@@ -356,11 +356,11 @@ __flow_action_hw_stats_check(const struct flow_action *action,
|
||||
@@ -411,11 +411,11 @@ __flow_action_hw_stats_check(const struc
|
||||
|
||||
if (!check_allow_bit &&
|
||||
~action_entry->hw_stats & FLOW_ACTION_HW_STATS_ANY) {
|
||||
@@ -39,6 +37,3 @@ index 7a2b022..f17c485 100644
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
--
|
||||
2.38.1
|
||||
|
||||
|
||||
@@ -11,9 +11,9 @@ include $(INCLUDE_DIR)/kernel.mk
|
||||
PKG_NAME:=qosify
|
||||
PKG_SOURCE_URL=$(PROJECT_GIT)/project/qosify.git
|
||||
PKG_SOURCE_PROTO:=git
|
||||
PKG_SOURCE_DATE:=2023-03-07
|
||||
PKG_SOURCE_VERSION:=9a47ea4b683dd845ec94534fcd82d3117c9ab313
|
||||
PKG_MIRROR_HASH:=3d97456dcd13f481beff9a87d939e4bd671577865ed7cfc11b00d6e40f3e5621
|
||||
PKG_SOURCE_DATE:=2024-09-20
|
||||
PKG_SOURCE_VERSION:=1501e0935175d713ad229d88a8401dbfddc0a6b4
|
||||
PKG_MIRROR_HASH:=6d6e07285a6c46f040ba88555accc64c291837f9726944b9e41ec4efbaaf6c20
|
||||
PKG_RELEASE:=1
|
||||
|
||||
PKG_LICENSE:=GPL-2.0
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
From b77c0ecdc7915e5c5c515da1aa6cfaf6f4eb8351 Mon Sep 17 00:00:00 2001
|
||||
From: Mathew McBride <matt@traverse.com.au>
|
||||
Date: Wed, 28 Sep 2022 16:39:31 +1000
|
||||
Subject: [PATCH] arm: disable code size reduction measures
|
||||
(gc-sections,-f*-sections)
|
||||
|
||||
This interferes with the EFI boot stub on armv7l.
|
||||
|
||||
Signed-off-by: Mathew McBride <matt@traverse.com.au>
|
||||
---
|
||||
arch/arm/Kconfig | 1 -
|
||||
1 file changed, 1 deletion(-)
|
||||
|
||||
--- a/arch/arm/Kconfig
|
||||
+++ b/arch/arm/Kconfig
|
||||
@@ -124,7 +124,6 @@ config ARM
|
||||
select HAVE_VIRT_CPU_ACCOUNTING_GEN
|
||||
select IRQ_FORCED_THREADING
|
||||
select LOCK_MM_AND_FIND_VMA
|
||||
- select HAVE_LD_DEAD_CODE_DATA_ELIMINATION
|
||||
select MODULES_USE_ELF_REL
|
||||
select NEED_DMA_MAP_STATE
|
||||
select OF_EARLY_FLATTREE if OF
|
||||
@@ -1,44 +0,0 @@
|
||||
From f3763a0c1b07273218cbf5886bdf8df9df501111 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:10 +0200
|
||||
Subject: [PATCH 03/14] net: dpaa2-eth: don't use -ENOTSUPP error code
|
||||
|
||||
dpaa2_eth_setup_dpni() is called from the probe path and
|
||||
dpaa2_eth_set_link_ksettings() is propagated to user space.
|
||||
|
||||
include/linux/errno.h says that ENOTSUPP is "Defined for the NFSv3
|
||||
protocol". Conventional wisdom has it to not use it in networking
|
||||
drivers. Replace it with -EOPNOTSUPP.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 2 +-
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 2 +-
|
||||
2 files changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
@@ -3618,7 +3618,7 @@ static int dpaa2_eth_setup_dpni(struct f
|
||||
dev_err(dev, "DPNI version %u.%u not supported, need >= %u.%u\n",
|
||||
priv->dpni_ver_major, priv->dpni_ver_minor,
|
||||
DPNI_VER_MAJOR, DPNI_VER_MINOR);
|
||||
- err = -ENOTSUPP;
|
||||
+ err = -EOPNOTSUPP;
|
||||
goto close;
|
||||
}
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
||||
@@ -118,7 +118,7 @@ dpaa2_eth_set_link_ksettings(struct net_
|
||||
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
||||
|
||||
if (!dpaa2_eth_is_type_phy(priv))
|
||||
- return -ENOTSUPP;
|
||||
+ return -EOPNOTSUPP;
|
||||
|
||||
return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings);
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
From 022a11062261dc4703da846d3bf4d194ef6bebf5 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:11 +0200
|
||||
Subject: [PATCH 04/14] net: dpaa2: replace dpaa2_mac_is_type_fixed() with
|
||||
dpaa2_mac_is_type_phy()
|
||||
|
||||
dpaa2_mac_is_type_fixed() is a header with no implementation and no
|
||||
callers, which is referenced from the documentation though. It can be
|
||||
deleted.
|
||||
|
||||
On the other hand, it would be useful to reuse the code between
|
||||
dpaa2_eth_is_type_phy() and dpaa2_switch_port_is_type_phy(). That common
|
||||
code should be called dpaa2_mac_is_type_phy(), so let's create that.
|
||||
|
||||
The removal and the addition are merged into the same patch because,
|
||||
in fact, is_type_phy() is the logical opposite of is_type_fixed().
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
.../ethernet/freescale/dpaa2/mac-phy-support.rst | 9 ++++++---
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h | 7 +------
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h | 10 ++++++++--
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h | 7 +------
|
||||
4 files changed, 16 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst
|
||||
+++ b/Documentation/networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support.rst
|
||||
@@ -181,10 +181,13 @@ when necessary using the below listed AP
|
||||
- int dpaa2_mac_connect(struct dpaa2_mac *mac);
|
||||
- void dpaa2_mac_disconnect(struct dpaa2_mac *mac);
|
||||
|
||||
-A phylink integration is necessary only when the partner DPMAC is not of TYPE_FIXED.
|
||||
-One can check for this condition using the below API::
|
||||
+A phylink integration is necessary only when the partner DPMAC is not of
|
||||
+``TYPE_FIXED``. This means it is either of ``TYPE_PHY``, or of
|
||||
+``TYPE_BACKPLANE`` (the difference being the two that in the ``TYPE_BACKPLANE``
|
||||
+mode, the MC firmware does not access the PCS registers). One can check for
|
||||
+this condition using the following helper::
|
||||
|
||||
- - bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,struct fsl_mc_io *mc_io);
|
||||
+ - static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac);
|
||||
|
||||
Before connection to a MAC, the caller must allocate and populate the
|
||||
dpaa2_mac structure with the associated net_device, a pointer to the MC portal
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
|
||||
@@ -733,12 +733,7 @@ static inline unsigned int dpaa2_eth_rx_
|
||||
|
||||
static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv)
|
||||
{
|
||||
- if (priv->mac &&
|
||||
- (priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
|
||||
- priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE))
|
||||
- return true;
|
||||
-
|
||||
- return false;
|
||||
+ return dpaa2_mac_is_type_phy(priv->mac);
|
||||
}
|
||||
|
||||
static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv)
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.h
|
||||
@@ -30,8 +30,14 @@ struct dpaa2_mac {
|
||||
struct phy *serdes_phy;
|
||||
};
|
||||
|
||||
-bool dpaa2_mac_is_type_fixed(struct fsl_mc_device *dpmac_dev,
|
||||
- struct fsl_mc_io *mc_io);
|
||||
+static inline bool dpaa2_mac_is_type_phy(struct dpaa2_mac *mac)
|
||||
+{
|
||||
+ if (!mac)
|
||||
+ return false;
|
||||
+
|
||||
+ return mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
|
||||
+ mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE;
|
||||
+}
|
||||
|
||||
int dpaa2_mac_open(struct dpaa2_mac *mac);
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
|
||||
@@ -230,12 +230,7 @@ static inline bool dpaa2_switch_supports
|
||||
static inline bool
|
||||
dpaa2_switch_port_is_type_phy(struct ethsw_port_priv *port_priv)
|
||||
{
|
||||
- if (port_priv->mac &&
|
||||
- (port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
|
||||
- port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE))
|
||||
- return true;
|
||||
-
|
||||
- return false;
|
||||
+ return dpaa2_mac_is_type_phy(port_priv->mac);
|
||||
}
|
||||
|
||||
static inline bool dpaa2_switch_port_has_mac(struct ethsw_port_priv *port_priv)
|
||||
@@ -1,88 +0,0 @@
|
||||
From 97c07369ab8bf9895e05d4b468f18e6567263154 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:12 +0200
|
||||
Subject: [PATCH 05/14] net: dpaa2-mac: absorb phylink_start() call into
|
||||
dpaa2_mac_start()
|
||||
|
||||
The phylink handling is intended to be hidden inside the dpaa2_mac
|
||||
object. Move the phylink_start() call into dpaa2_mac_start(), and
|
||||
phylink_stop() into dpaa2_mac_stop().
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 5 +----
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 8 ++++++++
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 5 +----
|
||||
3 files changed, 10 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
@@ -2083,10 +2083,8 @@ static int dpaa2_eth_open(struct net_dev
|
||||
goto enable_err;
|
||||
}
|
||||
|
||||
- if (dpaa2_eth_is_type_phy(priv)) {
|
||||
+ if (dpaa2_eth_is_type_phy(priv))
|
||||
dpaa2_mac_start(priv->mac);
|
||||
- phylink_start(priv->mac->phylink);
|
||||
- }
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -2160,7 +2158,6 @@ static int dpaa2_eth_stop(struct net_dev
|
||||
int retries = 10;
|
||||
|
||||
if (dpaa2_eth_is_type_phy(priv)) {
|
||||
- phylink_stop(priv->mac->phylink);
|
||||
dpaa2_mac_stop(priv->mac);
|
||||
} else {
|
||||
netif_tx_stop_all_queues(net_dev);
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
@@ -336,12 +336,20 @@ static void dpaa2_mac_set_supported_inte
|
||||
|
||||
void dpaa2_mac_start(struct dpaa2_mac *mac)
|
||||
{
|
||||
+ ASSERT_RTNL();
|
||||
+
|
||||
if (mac->serdes_phy)
|
||||
phy_power_on(mac->serdes_phy);
|
||||
+
|
||||
+ phylink_start(mac->phylink);
|
||||
}
|
||||
|
||||
void dpaa2_mac_stop(struct dpaa2_mac *mac)
|
||||
{
|
||||
+ ASSERT_RTNL();
|
||||
+
|
||||
+ phylink_stop(mac->phylink);
|
||||
+
|
||||
if (mac->serdes_phy)
|
||||
phy_power_off(mac->serdes_phy);
|
||||
}
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
|
||||
@@ -703,10 +703,8 @@ static int dpaa2_switch_port_open(struct
|
||||
|
||||
dpaa2_switch_enable_ctrl_if_napi(ethsw);
|
||||
|
||||
- if (dpaa2_switch_port_is_type_phy(port_priv)) {
|
||||
+ if (dpaa2_switch_port_is_type_phy(port_priv))
|
||||
dpaa2_mac_start(port_priv->mac);
|
||||
- phylink_start(port_priv->mac->phylink);
|
||||
- }
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -718,7 +716,6 @@ static int dpaa2_switch_port_stop(struct
|
||||
int err;
|
||||
|
||||
if (dpaa2_switch_port_is_type_phy(port_priv)) {
|
||||
- phylink_stop(port_priv->mac->phylink);
|
||||
dpaa2_mac_stop(port_priv->mac);
|
||||
} else {
|
||||
netif_tx_stop_all_queues(netdev);
|
||||
@@ -1,50 +0,0 @@
|
||||
From 095ef388f714d622aa503fcccf20dc4095b72762 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:13 +0200
|
||||
Subject: [PATCH 06/14] net: dpaa2-mac: remove defensive check in
|
||||
dpaa2_mac_disconnect()
|
||||
|
||||
dpaa2_mac_disconnect() will only be called with a NULL mac->phylink if
|
||||
dpaa2_mac_connect() failed, or was never called.
|
||||
|
||||
The callers are these:
|
||||
|
||||
dpaa2_eth_disconnect_mac():
|
||||
|
||||
if (dpaa2_eth_is_type_phy(priv))
|
||||
dpaa2_mac_disconnect(priv->mac);
|
||||
|
||||
dpaa2_switch_port_disconnect_mac():
|
||||
|
||||
if (dpaa2_switch_port_is_type_phy(port_priv))
|
||||
dpaa2_mac_disconnect(port_priv->mac);
|
||||
|
||||
priv->mac can be NULL, but in that case, dpaa2_eth_is_type_phy() returns
|
||||
false, and dpaa2_mac_disconnect() is never called. Similar for
|
||||
dpaa2-switch.
|
||||
|
||||
When priv->mac is non-NULL, it means that dpaa2_mac_connect() returned
|
||||
zero (success), and therefore, priv->mac->phylink is also a valid
|
||||
pointer.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 3 ---
|
||||
1 file changed, 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
@@ -446,9 +446,6 @@ err_pcs_destroy:
|
||||
|
||||
void dpaa2_mac_disconnect(struct dpaa2_mac *mac)
|
||||
{
|
||||
- if (!mac->phylink)
|
||||
- return;
|
||||
-
|
||||
phylink_disconnect_phy(mac->phylink);
|
||||
phylink_destroy(mac->phylink);
|
||||
dpaa2_pcs_destroy(mac);
|
||||
@@ -1,101 +0,0 @@
|
||||
From 06efc9b8a1360cad83cae6e71558e5458cc1fbf3 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:14 +0200
|
||||
Subject: [PATCH 07/14] net: dpaa2-eth: assign priv->mac after
|
||||
dpaa2_mac_connect() call
|
||||
|
||||
There are 2 requirements for correct code:
|
||||
|
||||
- Any time the driver accesses the priv->mac pointer at runtime, it
|
||||
either holds NULL to indicate a DPNI-DPNI connection (or unconnected
|
||||
DPNI), or a struct dpaa2_mac whose phylink instance was fully
|
||||
initialized (created and connected to the PHY). No changes are made to
|
||||
priv->mac while it is being used. Currently, rtnl_lock() watches over
|
||||
the call to dpaa2_eth_connect_mac(), so it serves the purpose of
|
||||
serializing this with all readers of priv->mac.
|
||||
|
||||
- dpaa2_mac_connect() should run unlocked, because inside it are 2
|
||||
phylink calls with incompatible locking requirements: phylink_create()
|
||||
requires that the rtnl_mutex isn't held, and phylink_fwnode_phy_connect()
|
||||
requires that the rtnl_mutex is held. The only way to solve those
|
||||
contradictory requirements is to let dpaa2_mac_connect() take
|
||||
rtnl_lock() when it needs to.
|
||||
|
||||
To solve both requirements, we need to identify the writer side of the
|
||||
priv->mac pointer, which can be wrapped in a mutex private to the driver
|
||||
in a future patch. The dpaa2_mac_connect() cannot be part of the writer
|
||||
side critical section, because of an AB/BA deadlock with rtnl_lock().
|
||||
|
||||
So the strategy needs to be that where we prepare the DPMAC by calling
|
||||
dpaa2_mac_connect(), and only make priv->mac point to it once it's fully
|
||||
prepared. This ensures that the writer side critical section has the
|
||||
absolute minimum surface it can.
|
||||
|
||||
The reverse strategy is adopted in the dpaa2_eth_disconnect_mac() code
|
||||
path. This makes sure that priv->mac is NULL when we start tearing down
|
||||
the DPMAC that we disconnected from, and concurrent code will simply not
|
||||
see it.
|
||||
|
||||
No locking changes in this patch (concurrent code is still blocked by
|
||||
the rtnl_mutex).
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
.../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 21 +++++++++++--------
|
||||
1 file changed, 12 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
@@ -4448,9 +4448,8 @@ static int dpaa2_eth_connect_mac(struct
|
||||
err = dpaa2_mac_open(mac);
|
||||
if (err)
|
||||
goto err_free_mac;
|
||||
- priv->mac = mac;
|
||||
|
||||
- if (dpaa2_eth_is_type_phy(priv)) {
|
||||
+ if (dpaa2_mac_is_type_phy(mac)) {
|
||||
err = dpaa2_mac_connect(mac);
|
||||
if (err && err != -EPROBE_DEFER)
|
||||
netdev_err(priv->net_dev, "Error connecting to the MAC endpoint: %pe",
|
||||
@@ -4459,11 +4458,12 @@ static int dpaa2_eth_connect_mac(struct
|
||||
goto err_close_mac;
|
||||
}
|
||||
|
||||
+ priv->mac = mac;
|
||||
+
|
||||
return 0;
|
||||
|
||||
err_close_mac:
|
||||
dpaa2_mac_close(mac);
|
||||
- priv->mac = NULL;
|
||||
err_free_mac:
|
||||
kfree(mac);
|
||||
return err;
|
||||
@@ -4471,15 +4471,18 @@ err_free_mac:
|
||||
|
||||
static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
|
||||
{
|
||||
- if (dpaa2_eth_is_type_phy(priv))
|
||||
- dpaa2_mac_disconnect(priv->mac);
|
||||
+ struct dpaa2_mac *mac = priv->mac;
|
||||
+
|
||||
+ priv->mac = NULL;
|
||||
|
||||
- if (!dpaa2_eth_has_mac(priv))
|
||||
+ if (!mac)
|
||||
return;
|
||||
|
||||
- dpaa2_mac_close(priv->mac);
|
||||
- kfree(priv->mac);
|
||||
- priv->mac = NULL;
|
||||
+ if (dpaa2_mac_is_type_phy(mac))
|
||||
+ dpaa2_mac_disconnect(mac);
|
||||
+
|
||||
+ dpaa2_mac_close(mac);
|
||||
+ kfree(mac);
|
||||
}
|
||||
|
||||
static irqreturn_t dpni_irq0_handler_thread(int irq_num, void *arg)
|
||||
@@ -1,73 +0,0 @@
|
||||
From a5e7f7e277bd4403c45c1c7922d56d0eb08dbc7c Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:15 +0200
|
||||
Subject: [PATCH 08/14] net: dpaa2-switch: assign port_priv->mac after
|
||||
dpaa2_mac_connect() call
|
||||
|
||||
The dpaa2-switch has the exact same locking requirements when connected
|
||||
to a DPMAC, so it needs port_priv->mac to always point either to NULL,
|
||||
or to a DPMAC with a fully initialized phylink instance.
|
||||
|
||||
Make the same preparatory change in the dpaa2-switch driver as in the
|
||||
dpaa2-eth one.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
.../ethernet/freescale/dpaa2/dpaa2-switch.c | 21 +++++++++++--------
|
||||
1 file changed, 12 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
|
||||
@@ -1450,9 +1450,8 @@ static int dpaa2_switch_port_connect_mac
|
||||
err = dpaa2_mac_open(mac);
|
||||
if (err)
|
||||
goto err_free_mac;
|
||||
- port_priv->mac = mac;
|
||||
|
||||
- if (dpaa2_switch_port_is_type_phy(port_priv)) {
|
||||
+ if (dpaa2_mac_is_type_phy(mac)) {
|
||||
err = dpaa2_mac_connect(mac);
|
||||
if (err) {
|
||||
netdev_err(port_priv->netdev,
|
||||
@@ -1462,11 +1461,12 @@ static int dpaa2_switch_port_connect_mac
|
||||
}
|
||||
}
|
||||
|
||||
+ port_priv->mac = mac;
|
||||
+
|
||||
return 0;
|
||||
|
||||
err_close_mac:
|
||||
dpaa2_mac_close(mac);
|
||||
- port_priv->mac = NULL;
|
||||
err_free_mac:
|
||||
kfree(mac);
|
||||
return err;
|
||||
@@ -1474,15 +1474,18 @@ err_free_mac:
|
||||
|
||||
static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv)
|
||||
{
|
||||
- if (dpaa2_switch_port_is_type_phy(port_priv))
|
||||
- dpaa2_mac_disconnect(port_priv->mac);
|
||||
+ struct dpaa2_mac *mac = port_priv->mac;
|
||||
+
|
||||
+ port_priv->mac = NULL;
|
||||
|
||||
- if (!dpaa2_switch_port_has_mac(port_priv))
|
||||
+ if (!mac)
|
||||
return;
|
||||
|
||||
- dpaa2_mac_close(port_priv->mac);
|
||||
- kfree(port_priv->mac);
|
||||
- port_priv->mac = NULL;
|
||||
+ if (dpaa2_mac_is_type_phy(mac))
|
||||
+ dpaa2_mac_disconnect(mac);
|
||||
+
|
||||
+ dpaa2_mac_close(mac);
|
||||
+ kfree(mac);
|
||||
}
|
||||
|
||||
static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg)
|
||||
@@ -1,111 +0,0 @@
|
||||
From ce44b6ed9ee65efa9b3025552c513842eabcab88 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:16 +0200
|
||||
Subject: [PATCH 09/14] net: dpaa2: publish MAC stringset to ethtool -S even if
|
||||
MAC is missing
|
||||
|
||||
DPNIs and DPSW objects can connect and disconnect at runtime from DPMAC
|
||||
objects on the same fsl-mc bus. The DPMAC object also holds "ethtool -S"
|
||||
unstructured counters. Those counters are only shown for the entity
|
||||
owning the netdev (DPNI, DPSW) if it's connected to a DPMAC.
|
||||
|
||||
The ethtool stringset code path is split into multiple callbacks, but
|
||||
currently, connecting and disconnecting the DPMAC takes the rtnl_lock().
|
||||
This blocks the entire ethtool code path from running, see
|
||||
ethnl_default_doit() -> rtnl_lock() -> ops->prepare_data() ->
|
||||
strset_prepare_data().
|
||||
|
||||
This is going to be a problem if we are going to no longer require
|
||||
rtnl_lock() when connecting/disconnecting the DPMAC, because the DPMAC
|
||||
could appear between ops->get_sset_count() and ops->get_strings().
|
||||
If it appears out of the blue, we will provide a stringset into an array
|
||||
that was dimensioned thinking the DPMAC wouldn't be there => array
|
||||
accessed out of bounds.
|
||||
|
||||
There isn't really a good way to work around that, and I don't want to
|
||||
put too much pressure on the ethtool framework by playing locking games.
|
||||
Just make the DPMAC counters be always available. They'll be zeroes if
|
||||
the DPNI or DPSW isn't connected to a DPMAC.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c | 12 +++---------
|
||||
.../ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c | 11 ++---------
|
||||
2 files changed, 5 insertions(+), 18 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
||||
@@ -186,7 +186,6 @@ static int dpaa2_eth_set_pauseparam(stru
|
||||
static void dpaa2_eth_get_strings(struct net_device *netdev, u32 stringset,
|
||||
u8 *data)
|
||||
{
|
||||
- struct dpaa2_eth_priv *priv = netdev_priv(netdev);
|
||||
u8 *p = data;
|
||||
int i;
|
||||
|
||||
@@ -200,22 +199,17 @@ static void dpaa2_eth_get_strings(struct
|
||||
strscpy(p, dpaa2_ethtool_extras[i], ETH_GSTRING_LEN);
|
||||
p += ETH_GSTRING_LEN;
|
||||
}
|
||||
- if (dpaa2_eth_has_mac(priv))
|
||||
- dpaa2_mac_get_strings(p);
|
||||
+ dpaa2_mac_get_strings(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int dpaa2_eth_get_sset_count(struct net_device *net_dev, int sset)
|
||||
{
|
||||
- int num_ss_stats = DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS;
|
||||
- struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
||||
-
|
||||
switch (sset) {
|
||||
case ETH_SS_STATS: /* ethtool_get_stats(), ethtool_get_drvinfo() */
|
||||
- if (dpaa2_eth_has_mac(priv))
|
||||
- num_ss_stats += dpaa2_mac_get_sset_count();
|
||||
- return num_ss_stats;
|
||||
+ return DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS +
|
||||
+ dpaa2_mac_get_sset_count();
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
|
||||
@@ -145,14 +145,9 @@ dpaa2_switch_set_link_ksettings(struct n
|
||||
static int
|
||||
dpaa2_switch_ethtool_get_sset_count(struct net_device *netdev, int sset)
|
||||
{
|
||||
- struct ethsw_port_priv *port_priv = netdev_priv(netdev);
|
||||
- int num_ss_stats = DPAA2_SWITCH_NUM_COUNTERS;
|
||||
-
|
||||
switch (sset) {
|
||||
case ETH_SS_STATS:
|
||||
- if (port_priv->mac)
|
||||
- num_ss_stats += dpaa2_mac_get_sset_count();
|
||||
- return num_ss_stats;
|
||||
+ return DPAA2_SWITCH_NUM_COUNTERS + dpaa2_mac_get_sset_count();
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@@ -161,7 +156,6 @@ dpaa2_switch_ethtool_get_sset_count(stru
|
||||
static void dpaa2_switch_ethtool_get_strings(struct net_device *netdev,
|
||||
u32 stringset, u8 *data)
|
||||
{
|
||||
- struct ethsw_port_priv *port_priv = netdev_priv(netdev);
|
||||
u8 *p = data;
|
||||
int i;
|
||||
|
||||
@@ -172,8 +166,7 @@ static void dpaa2_switch_ethtool_get_str
|
||||
ETH_GSTRING_LEN);
|
||||
p += ETH_GSTRING_LEN;
|
||||
}
|
||||
- if (port_priv->mac)
|
||||
- dpaa2_mac_get_strings(p);
|
||||
+ dpaa2_mac_get_strings(p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
From c838d9fd7e6ba9ddd6006bf0a296396266e9f121 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:17 +0200
|
||||
Subject: [PATCH 10/14] net: dpaa2-switch replace direct MAC access with
|
||||
dpaa2_switch_port_has_mac()
|
||||
|
||||
The helper function will gain a lockdep annotation in a future patch.
|
||||
Make sure to benefit from it.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
|
||||
@@ -189,7 +189,7 @@ static void dpaa2_switch_ethtool_get_sta
|
||||
dpaa2_switch_ethtool_counters[i].name, err);
|
||||
}
|
||||
|
||||
- if (port_priv->mac)
|
||||
+ if (dpaa2_switch_port_has_mac(port_priv))
|
||||
dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i);
|
||||
}
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
From e0ea63162cb5f1ca7f844d6ef5fc4079448ee2d5 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:18 +0200
|
||||
Subject: [PATCH 11/14] net: dpaa2-eth: connect to MAC before requesting the
|
||||
"endpoint changed" IRQ
|
||||
|
||||
dpaa2_eth_connect_mac() is called both from dpaa2_eth_probe() and from
|
||||
dpni_irq0_handler_thread().
|
||||
|
||||
It could happen that the DPNI gets connected to a DPMAC on the fsl-mc
|
||||
bus exactly during probe, as soon as the "endpoint change" interrupt is
|
||||
requested in dpaa2_eth_setup_irqs(). This will cause the
|
||||
dpni_irq0_handler_thread() to register a phylink instance for that DPMAC.
|
||||
|
||||
Then, the probing function will also try to register a phylink instance
|
||||
for the same DPMAC, operation which should fail (and this will fail the
|
||||
probing of the driver).
|
||||
|
||||
Reorder dpaa2_eth_setup_irqs() and dpaa2_eth_connect_mac(), such that
|
||||
dpni_irq0_handler_thread() never races with the DPMAC-related portion of
|
||||
the probing path.
|
||||
|
||||
Also reorder dpaa2_eth_disconnect_mac() to be in the mirror position of
|
||||
dpaa2_eth_connect_mac() in the teardown path.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
.../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 18 +++++++++---------
|
||||
1 file changed, 9 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
@@ -4715,6 +4715,10 @@ static int dpaa2_eth_probe(struct fsl_mc
|
||||
}
|
||||
#endif
|
||||
|
||||
+ err = dpaa2_eth_connect_mac(priv);
|
||||
+ if (err)
|
||||
+ goto err_connect_mac;
|
||||
+
|
||||
err = dpaa2_eth_setup_irqs(dpni_dev);
|
||||
if (err) {
|
||||
netdev_warn(net_dev, "Failed to set link interrupt, fall back to polling\n");
|
||||
@@ -4727,10 +4731,6 @@ static int dpaa2_eth_probe(struct fsl_mc
|
||||
priv->do_link_poll = true;
|
||||
}
|
||||
|
||||
- err = dpaa2_eth_connect_mac(priv);
|
||||
- if (err)
|
||||
- goto err_connect_mac;
|
||||
-
|
||||
err = dpaa2_eth_dl_alloc(priv);
|
||||
if (err)
|
||||
goto err_dl_register;
|
||||
@@ -4766,13 +4766,13 @@ err_dl_port_add:
|
||||
err_dl_trap_register:
|
||||
dpaa2_eth_dl_free(priv);
|
||||
err_dl_register:
|
||||
- dpaa2_eth_disconnect_mac(priv);
|
||||
-err_connect_mac:
|
||||
if (priv->do_link_poll)
|
||||
kthread_stop(priv->poll_thread);
|
||||
else
|
||||
fsl_mc_free_irqs(dpni_dev);
|
||||
err_poll_thread:
|
||||
+ dpaa2_eth_disconnect_mac(priv);
|
||||
+err_connect_mac:
|
||||
dpaa2_eth_free_rings(priv);
|
||||
err_alloc_rings:
|
||||
err_csum:
|
||||
@@ -4820,9 +4820,6 @@ static int dpaa2_eth_remove(struct fsl_m
|
||||
#endif
|
||||
|
||||
unregister_netdev(net_dev);
|
||||
- rtnl_lock();
|
||||
- dpaa2_eth_disconnect_mac(priv);
|
||||
- rtnl_unlock();
|
||||
|
||||
dpaa2_eth_dl_port_del(priv);
|
||||
dpaa2_eth_dl_traps_unregister(priv);
|
||||
@@ -4833,6 +4830,9 @@ static int dpaa2_eth_remove(struct fsl_m
|
||||
else
|
||||
fsl_mc_free_irqs(ls_dev);
|
||||
|
||||
+ rtnl_lock();
|
||||
+ dpaa2_eth_disconnect_mac(priv);
|
||||
+ rtnl_unlock();
|
||||
dpaa2_eth_free_rings(priv);
|
||||
free_percpu(priv->fd);
|
||||
free_percpu(priv->sgt_cache);
|
||||
@@ -1,320 +0,0 @@
|
||||
From 5e448a17dfa2e95166534df7f677a3694ef6187d Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:19 +0200
|
||||
Subject: [PATCH 12/14] net: dpaa2-eth: serialize changes to priv->mac with a
|
||||
mutex
|
||||
|
||||
The dpaa2 architecture permits dynamic connections between objects on
|
||||
the fsl-mc bus, specifically between a DPNI object (represented by a
|
||||
struct net_device) and a DPMAC object (represented by a struct phylink).
|
||||
|
||||
The DPNI driver is notified when those connections are created/broken
|
||||
through the dpni_irq0_handler_thread() method. To ensure that ethtool
|
||||
operations, as well as netdev up/down operations serialize with the
|
||||
connection/disconnection of the DPNI with a DPMAC,
|
||||
dpni_irq0_handler_thread() takes the rtnl_lock() to block those other
|
||||
operations from taking place.
|
||||
|
||||
There is code called by dpaa2_mac_connect() which wants to acquire the
|
||||
rtnl_mutex once again, see phylink_create() -> phylink_register_sfp() ->
|
||||
sfp_bus_add_upstream() -> rtnl_lock(). So the strategy doesn't quite
|
||||
work out, even though it's fairly simple.
|
||||
|
||||
Create a different strategy, where all code paths in the dpaa2-eth
|
||||
driver access priv->mac only while they are holding priv->mac_lock.
|
||||
The phylink instance is not created or connected to the PHY under the
|
||||
priv->mac_lock, but only assigned to priv->mac then. This will eliminate
|
||||
the reliance on the rtnl_mutex.
|
||||
|
||||
Add lockdep annotations and put comments where holding the lock is not
|
||||
necessary, and priv->mac can be dereferenced freely.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
.../net/ethernet/freescale/dpaa2/dpaa2-eth.c | 43 ++++++++++++--
|
||||
.../net/ethernet/freescale/dpaa2/dpaa2-eth.h | 6 ++
|
||||
.../ethernet/freescale/dpaa2/dpaa2-ethtool.c | 58 +++++++++++++++----
|
||||
3 files changed, 91 insertions(+), 16 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
@@ -2021,8 +2021,11 @@ static int dpaa2_eth_link_state_update(s
|
||||
|
||||
/* When we manage the MAC/PHY using phylink there is no need
|
||||
* to manually update the netif_carrier.
|
||||
+ * We can avoid locking because we are called from the "link changed"
|
||||
+ * IRQ handler, which is the same as the "endpoint changed" IRQ handler
|
||||
+ * (the writer to priv->mac), so we cannot race with it.
|
||||
*/
|
||||
- if (dpaa2_eth_is_type_phy(priv))
|
||||
+ if (dpaa2_mac_is_type_phy(priv->mac))
|
||||
goto out;
|
||||
|
||||
/* Chech link state; speed / duplex changes are not treated yet */
|
||||
@@ -2061,6 +2064,8 @@ static int dpaa2_eth_open(struct net_dev
|
||||
priv->dpbp_dev->obj_desc.id, priv->bpid);
|
||||
}
|
||||
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+
|
||||
if (!dpaa2_eth_is_type_phy(priv)) {
|
||||
/* We'll only start the txqs when the link is actually ready;
|
||||
* make sure we don't race against the link up notification,
|
||||
@@ -2079,6 +2084,7 @@ static int dpaa2_eth_open(struct net_dev
|
||||
|
||||
err = dpni_enable(priv->mc_io, 0, priv->mc_token);
|
||||
if (err < 0) {
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
netdev_err(net_dev, "dpni_enable() failed\n");
|
||||
goto enable_err;
|
||||
}
|
||||
@@ -2086,6 +2092,8 @@ static int dpaa2_eth_open(struct net_dev
|
||||
if (dpaa2_eth_is_type_phy(priv))
|
||||
dpaa2_mac_start(priv->mac);
|
||||
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
+
|
||||
return 0;
|
||||
|
||||
enable_err:
|
||||
@@ -2157,6 +2165,8 @@ static int dpaa2_eth_stop(struct net_dev
|
||||
int dpni_enabled = 0;
|
||||
int retries = 10;
|
||||
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+
|
||||
if (dpaa2_eth_is_type_phy(priv)) {
|
||||
dpaa2_mac_stop(priv->mac);
|
||||
} else {
|
||||
@@ -2164,6 +2174,8 @@ static int dpaa2_eth_stop(struct net_dev
|
||||
netif_carrier_off(net_dev);
|
||||
}
|
||||
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
+
|
||||
/* On dpni_disable(), the MC firmware will:
|
||||
* - stop MAC Rx and wait for all Rx frames to be enqueued to software
|
||||
* - cut off WRIOP dequeues from egress FQs and wait until transmission
|
||||
@@ -2489,12 +2501,20 @@ static int dpaa2_eth_ts_ioctl(struct net
|
||||
static int dpaa2_eth_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
|
||||
{
|
||||
struct dpaa2_eth_priv *priv = netdev_priv(dev);
|
||||
+ int err;
|
||||
|
||||
if (cmd == SIOCSHWTSTAMP)
|
||||
return dpaa2_eth_ts_ioctl(dev, rq, cmd);
|
||||
|
||||
- if (dpaa2_eth_is_type_phy(priv))
|
||||
- return phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+
|
||||
+ if (dpaa2_eth_is_type_phy(priv)) {
|
||||
+ err = phylink_mii_ioctl(priv->mac->phylink, rq, cmd);
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
@@ -4458,7 +4478,9 @@ static int dpaa2_eth_connect_mac(struct
|
||||
goto err_close_mac;
|
||||
}
|
||||
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
priv->mac = mac;
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -4471,9 +4493,12 @@ err_free_mac:
|
||||
|
||||
static void dpaa2_eth_disconnect_mac(struct dpaa2_eth_priv *priv)
|
||||
{
|
||||
- struct dpaa2_mac *mac = priv->mac;
|
||||
+ struct dpaa2_mac *mac;
|
||||
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+ mac = priv->mac;
|
||||
priv->mac = NULL;
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
|
||||
if (!mac)
|
||||
return;
|
||||
@@ -4492,6 +4517,7 @@ static irqreturn_t dpni_irq0_handler_thr
|
||||
struct fsl_mc_device *dpni_dev = to_fsl_mc_device(dev);
|
||||
struct net_device *net_dev = dev_get_drvdata(dev);
|
||||
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
||||
+ bool had_mac;
|
||||
int err;
|
||||
|
||||
err = dpni_get_irq_status(dpni_dev->mc_io, 0, dpni_dev->mc_handle,
|
||||
@@ -4509,7 +4535,12 @@ static irqreturn_t dpni_irq0_handler_thr
|
||||
dpaa2_eth_update_tx_fqids(priv);
|
||||
|
||||
rtnl_lock();
|
||||
- if (dpaa2_eth_has_mac(priv))
|
||||
+ /* We can avoid locking because the "endpoint changed" IRQ
|
||||
+ * handler is the only one who changes priv->mac at runtime,
|
||||
+ * so we are not racing with anyone.
|
||||
+ */
|
||||
+ had_mac = !!priv->mac;
|
||||
+ if (had_mac)
|
||||
dpaa2_eth_disconnect_mac(priv);
|
||||
else
|
||||
dpaa2_eth_connect_mac(priv);
|
||||
@@ -4610,6 +4641,8 @@ static int dpaa2_eth_probe(struct fsl_mc
|
||||
priv = netdev_priv(net_dev);
|
||||
priv->net_dev = net_dev;
|
||||
|
||||
+ mutex_init(&priv->mac_lock);
|
||||
+
|
||||
priv->iommu_domain = iommu_get_domain_for_dev(dev);
|
||||
|
||||
priv->tx_tstamp_type = HWTSTAMP_TX_OFF;
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.h
|
||||
@@ -580,6 +580,8 @@ struct dpaa2_eth_priv {
|
||||
#endif
|
||||
|
||||
struct dpaa2_mac *mac;
|
||||
+ /* Serializes changes to priv->mac */
|
||||
+ struct mutex mac_lock;
|
||||
struct workqueue_struct *dpaa2_ptp_wq;
|
||||
struct work_struct tx_onestep_tstamp;
|
||||
struct sk_buff_head tx_skbs;
|
||||
@@ -733,11 +735,15 @@ static inline unsigned int dpaa2_eth_rx_
|
||||
|
||||
static inline bool dpaa2_eth_is_type_phy(struct dpaa2_eth_priv *priv)
|
||||
{
|
||||
+ lockdep_assert_held(&priv->mac_lock);
|
||||
+
|
||||
return dpaa2_mac_is_type_phy(priv->mac);
|
||||
}
|
||||
|
||||
static inline bool dpaa2_eth_has_mac(struct dpaa2_eth_priv *priv)
|
||||
{
|
||||
+ lockdep_assert_held(&priv->mac_lock);
|
||||
+
|
||||
return priv->mac ? true : false;
|
||||
}
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
||||
@@ -86,11 +86,16 @@ static void dpaa2_eth_get_drvinfo(struct
|
||||
static int dpaa2_eth_nway_reset(struct net_device *net_dev)
|
||||
{
|
||||
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
||||
+ int err = -EOPNOTSUPP;
|
||||
+
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
|
||||
if (dpaa2_eth_is_type_phy(priv))
|
||||
- return phylink_ethtool_nway_reset(priv->mac->phylink);
|
||||
+ err = phylink_ethtool_nway_reset(priv->mac->phylink);
|
||||
+
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
|
||||
- return -EOPNOTSUPP;
|
||||
+ return err;
|
||||
}
|
||||
|
||||
static int
|
||||
@@ -98,10 +103,18 @@ dpaa2_eth_get_link_ksettings(struct net_
|
||||
struct ethtool_link_ksettings *link_settings)
|
||||
{
|
||||
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
||||
+ int err;
|
||||
|
||||
- if (dpaa2_eth_is_type_phy(priv))
|
||||
- return phylink_ethtool_ksettings_get(priv->mac->phylink,
|
||||
- link_settings);
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+
|
||||
+ if (dpaa2_eth_is_type_phy(priv)) {
|
||||
+ err = phylink_ethtool_ksettings_get(priv->mac->phylink,
|
||||
+ link_settings);
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
|
||||
link_settings->base.autoneg = AUTONEG_DISABLE;
|
||||
if (!(priv->link_state.options & DPNI_LINK_OPT_HALF_DUPLEX))
|
||||
@@ -116,11 +129,17 @@ dpaa2_eth_set_link_ksettings(struct net_
|
||||
const struct ethtool_link_ksettings *link_settings)
|
||||
{
|
||||
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
||||
+ int err = -EOPNOTSUPP;
|
||||
|
||||
- if (!dpaa2_eth_is_type_phy(priv))
|
||||
- return -EOPNOTSUPP;
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+
|
||||
+ if (dpaa2_eth_is_type_phy(priv))
|
||||
+ err = phylink_ethtool_ksettings_set(priv->mac->phylink,
|
||||
+ link_settings);
|
||||
|
||||
- return phylink_ethtool_ksettings_set(priv->mac->phylink, link_settings);
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
+
|
||||
+ return err;
|
||||
}
|
||||
|
||||
static void dpaa2_eth_get_pauseparam(struct net_device *net_dev,
|
||||
@@ -129,11 +148,16 @@ static void dpaa2_eth_get_pauseparam(str
|
||||
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
||||
u64 link_options = priv->link_state.options;
|
||||
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+
|
||||
if (dpaa2_eth_is_type_phy(priv)) {
|
||||
phylink_ethtool_get_pauseparam(priv->mac->phylink, pause);
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
return;
|
||||
}
|
||||
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
+
|
||||
pause->rx_pause = dpaa2_eth_rx_pause_enabled(link_options);
|
||||
pause->tx_pause = dpaa2_eth_tx_pause_enabled(link_options);
|
||||
pause->autoneg = AUTONEG_DISABLE;
|
||||
@@ -152,9 +176,17 @@ static int dpaa2_eth_set_pauseparam(stru
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
- if (dpaa2_eth_is_type_phy(priv))
|
||||
- return phylink_ethtool_set_pauseparam(priv->mac->phylink,
|
||||
- pause);
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+
|
||||
+ if (dpaa2_eth_is_type_phy(priv)) {
|
||||
+ err = phylink_ethtool_set_pauseparam(priv->mac->phylink,
|
||||
+ pause);
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
+
|
||||
if (pause->autoneg)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
@@ -307,8 +339,12 @@ static void dpaa2_eth_get_ethtool_stats(
|
||||
}
|
||||
*(data + i++) = buf_cnt;
|
||||
|
||||
+ mutex_lock(&priv->mac_lock);
|
||||
+
|
||||
if (dpaa2_eth_has_mac(priv))
|
||||
dpaa2_mac_get_ethtool_stats(priv->mac, data + i);
|
||||
+
|
||||
+ mutex_unlock(&priv->mac_lock);
|
||||
}
|
||||
|
||||
static int dpaa2_eth_prep_eth_rule(struct ethhdr *eth_value, struct ethhdr *eth_mask,
|
||||
@@ -1,203 +0,0 @@
|
||||
From 80d12452a5f160c39d63efc1be07df36f9d07133 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:20 +0200
|
||||
Subject: [PATCH 13/14] net: dpaa2-switch: serialize changes to priv->mac with
|
||||
a mutex
|
||||
|
||||
The dpaa2-switch driver uses a DPMAC in the same way as the dpaa2-eth
|
||||
driver, so we need to duplicate the locking solution established by the
|
||||
previous change to the switch driver as well.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
.../freescale/dpaa2/dpaa2-switch-ethtool.c | 32 +++++++++++++++----
|
||||
.../ethernet/freescale/dpaa2/dpaa2-switch.c | 31 ++++++++++++++++--
|
||||
.../ethernet/freescale/dpaa2/dpaa2-switch.h | 2 ++
|
||||
3 files changed, 55 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch-ethtool.c
|
||||
@@ -60,11 +60,18 @@ dpaa2_switch_get_link_ksettings(struct n
|
||||
{
|
||||
struct ethsw_port_priv *port_priv = netdev_priv(netdev);
|
||||
struct dpsw_link_state state = {0};
|
||||
- int err = 0;
|
||||
+ int err;
|
||||
|
||||
- if (dpaa2_switch_port_is_type_phy(port_priv))
|
||||
- return phylink_ethtool_ksettings_get(port_priv->mac->phylink,
|
||||
- link_ksettings);
|
||||
+ mutex_lock(&port_priv->mac_lock);
|
||||
+
|
||||
+ if (dpaa2_switch_port_is_type_phy(port_priv)) {
|
||||
+ err = phylink_ethtool_ksettings_get(port_priv->mac->phylink,
|
||||
+ link_ksettings);
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
|
||||
err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
|
||||
port_priv->ethsw_data->dpsw_handle,
|
||||
@@ -99,9 +106,16 @@ dpaa2_switch_set_link_ksettings(struct n
|
||||
bool if_running;
|
||||
int err = 0, ret;
|
||||
|
||||
- if (dpaa2_switch_port_is_type_phy(port_priv))
|
||||
- return phylink_ethtool_ksettings_set(port_priv->mac->phylink,
|
||||
- link_ksettings);
|
||||
+ mutex_lock(&port_priv->mac_lock);
|
||||
+
|
||||
+ if (dpaa2_switch_port_is_type_phy(port_priv)) {
|
||||
+ err = phylink_ethtool_ksettings_set(port_priv->mac->phylink,
|
||||
+ link_ksettings);
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
|
||||
/* Interface needs to be down to change link settings */
|
||||
if_running = netif_running(netdev);
|
||||
@@ -189,8 +203,12 @@ static void dpaa2_switch_ethtool_get_sta
|
||||
dpaa2_switch_ethtool_counters[i].name, err);
|
||||
}
|
||||
|
||||
+ mutex_lock(&port_priv->mac_lock);
|
||||
+
|
||||
if (dpaa2_switch_port_has_mac(port_priv))
|
||||
dpaa2_mac_get_ethtool_stats(port_priv->mac, data + i);
|
||||
+
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
}
|
||||
|
||||
const struct ethtool_ops dpaa2_switch_port_ethtool_ops = {
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
|
||||
@@ -603,8 +603,11 @@ static int dpaa2_switch_port_link_state_
|
||||
|
||||
/* When we manage the MAC/PHY using phylink there is no need
|
||||
* to manually update the netif_carrier.
|
||||
+ * We can avoid locking because we are called from the "link changed"
|
||||
+ * IRQ handler, which is the same as the "endpoint changed" IRQ handler
|
||||
+ * (the writer to port_priv->mac), so we cannot race with it.
|
||||
*/
|
||||
- if (dpaa2_switch_port_is_type_phy(port_priv))
|
||||
+ if (dpaa2_mac_is_type_phy(port_priv->mac))
|
||||
return 0;
|
||||
|
||||
/* Interrupts are received even though no one issued an 'ifconfig up'
|
||||
@@ -684,6 +687,8 @@ static int dpaa2_switch_port_open(struct
|
||||
struct ethsw_core *ethsw = port_priv->ethsw_data;
|
||||
int err;
|
||||
|
||||
+ mutex_lock(&port_priv->mac_lock);
|
||||
+
|
||||
if (!dpaa2_switch_port_is_type_phy(port_priv)) {
|
||||
/* Explicitly set carrier off, otherwise
|
||||
* netif_carrier_ok() will return true and cause 'ip link show'
|
||||
@@ -697,6 +702,7 @@ static int dpaa2_switch_port_open(struct
|
||||
port_priv->ethsw_data->dpsw_handle,
|
||||
port_priv->idx);
|
||||
if (err) {
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
netdev_err(netdev, "dpsw_if_enable err %d\n", err);
|
||||
return err;
|
||||
}
|
||||
@@ -706,6 +712,8 @@ static int dpaa2_switch_port_open(struct
|
||||
if (dpaa2_switch_port_is_type_phy(port_priv))
|
||||
dpaa2_mac_start(port_priv->mac);
|
||||
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -715,6 +723,8 @@ static int dpaa2_switch_port_stop(struct
|
||||
struct ethsw_core *ethsw = port_priv->ethsw_data;
|
||||
int err;
|
||||
|
||||
+ mutex_lock(&port_priv->mac_lock);
|
||||
+
|
||||
if (dpaa2_switch_port_is_type_phy(port_priv)) {
|
||||
dpaa2_mac_stop(port_priv->mac);
|
||||
} else {
|
||||
@@ -722,6 +732,8 @@ static int dpaa2_switch_port_stop(struct
|
||||
netif_carrier_off(netdev);
|
||||
}
|
||||
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
+
|
||||
err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
|
||||
port_priv->ethsw_data->dpsw_handle,
|
||||
port_priv->idx);
|
||||
@@ -1461,7 +1473,9 @@ static int dpaa2_switch_port_connect_mac
|
||||
}
|
||||
}
|
||||
|
||||
+ mutex_lock(&port_priv->mac_lock);
|
||||
port_priv->mac = mac;
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1474,9 +1488,12 @@ err_free_mac:
|
||||
|
||||
static void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv)
|
||||
{
|
||||
- struct dpaa2_mac *mac = port_priv->mac;
|
||||
+ struct dpaa2_mac *mac;
|
||||
|
||||
+ mutex_lock(&port_priv->mac_lock);
|
||||
+ mac = port_priv->mac;
|
||||
port_priv->mac = NULL;
|
||||
+ mutex_unlock(&port_priv->mac_lock);
|
||||
|
||||
if (!mac)
|
||||
return;
|
||||
@@ -1495,6 +1512,7 @@ static irqreturn_t dpaa2_switch_irq0_han
|
||||
struct ethsw_port_priv *port_priv;
|
||||
u32 status = ~0;
|
||||
int err, if_id;
|
||||
+ bool had_mac;
|
||||
|
||||
err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
|
||||
DPSW_IRQ_INDEX_IF, &status);
|
||||
@@ -1513,7 +1531,12 @@ static irqreturn_t dpaa2_switch_irq0_han
|
||||
|
||||
if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) {
|
||||
rtnl_lock();
|
||||
- if (dpaa2_switch_port_has_mac(port_priv))
|
||||
+ /* We can avoid locking because the "endpoint changed" IRQ
|
||||
+ * handler is the only one who changes priv->mac at runtime,
|
||||
+ * so we are not racing with anyone.
|
||||
+ */
|
||||
+ had_mac = !!port_priv->mac;
|
||||
+ if (had_mac)
|
||||
dpaa2_switch_port_disconnect_mac(port_priv);
|
||||
else
|
||||
dpaa2_switch_port_connect_mac(port_priv);
|
||||
@@ -3250,6 +3273,8 @@ static int dpaa2_switch_probe_port(struc
|
||||
port_priv->netdev = port_netdev;
|
||||
port_priv->ethsw_data = ethsw;
|
||||
|
||||
+ mutex_init(&port_priv->mac_lock);
|
||||
+
|
||||
port_priv->idx = port_idx;
|
||||
port_priv->stp_state = BR_STATE_FORWARDING;
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
|
||||
@@ -161,6 +161,8 @@ struct ethsw_port_priv {
|
||||
|
||||
struct dpaa2_switch_filter_block *filter_block;
|
||||
struct dpaa2_mac *mac;
|
||||
+ /* Protects against changes to port_priv->mac */
|
||||
+ struct mutex mac_lock;
|
||||
};
|
||||
|
||||
/* Switch data */
|
||||
@@ -1,113 +0,0 @@
|
||||
From 4ea2faf5bb13d9ba9f07e996d495c4cbe34a4236 Mon Sep 17 00:00:00 2001
|
||||
From: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Date: Tue, 29 Nov 2022 16:12:21 +0200
|
||||
Subject: [PATCH 14/14] net: dpaa2-mac: move rtnl_lock() only around
|
||||
phylink_{,dis}connect_phy()
|
||||
|
||||
After the introduction of a private mac_lock that serializes access to
|
||||
priv->mac (and port_priv->mac in the switch), the only remaining purpose
|
||||
of rtnl_lock() is to satisfy the locking requirements of
|
||||
phylink_fwnode_phy_connect() and phylink_disconnect_phy().
|
||||
|
||||
But the functions these live in, dpaa2_mac_connect() and
|
||||
dpaa2_mac_disconnect(), have contradictory locking requirements.
|
||||
While phylink_fwnode_phy_connect() wants rtnl_lock() to be held,
|
||||
phylink_create() wants it to not be held.
|
||||
|
||||
Move the rtnl_lock() from top-level (in the dpaa2-eth and dpaa2-switch
|
||||
drivers) to only surround the phylink calls that require it, in the
|
||||
dpaa2-mac library code.
|
||||
|
||||
This is possible because dpaa2_mac_connect() and dpaa2_mac_disconnect()
|
||||
run unlocked, and there isn't any danger of an AB/BA deadlock between
|
||||
the rtnl_mutex and other private locks.
|
||||
|
||||
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
|
||||
Reviewed-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Tested-by: Ioana Ciornei <ioana.ciornei@nxp.com>
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c | 4 ----
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c | 5 +++++
|
||||
drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c | 4 ----
|
||||
3 files changed, 5 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
||||
@@ -4534,7 +4534,6 @@ static irqreturn_t dpni_irq0_handler_thr
|
||||
dpaa2_eth_set_mac_addr(netdev_priv(net_dev));
|
||||
dpaa2_eth_update_tx_fqids(priv);
|
||||
|
||||
- rtnl_lock();
|
||||
/* We can avoid locking because the "endpoint changed" IRQ
|
||||
* handler is the only one who changes priv->mac at runtime,
|
||||
* so we are not racing with anyone.
|
||||
@@ -4544,7 +4543,6 @@ static irqreturn_t dpni_irq0_handler_thr
|
||||
dpaa2_eth_disconnect_mac(priv);
|
||||
else
|
||||
dpaa2_eth_connect_mac(priv);
|
||||
- rtnl_unlock();
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
@@ -4863,9 +4861,7 @@ static int dpaa2_eth_remove(struct fsl_m
|
||||
else
|
||||
fsl_mc_free_irqs(ls_dev);
|
||||
|
||||
- rtnl_lock();
|
||||
dpaa2_eth_disconnect_mac(priv);
|
||||
- rtnl_unlock();
|
||||
dpaa2_eth_free_rings(priv);
|
||||
free_percpu(priv->fd);
|
||||
free_percpu(priv->sgt_cache);
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-mac.c
|
||||
@@ -428,7 +428,9 @@ int dpaa2_mac_connect(struct dpaa2_mac *
|
||||
}
|
||||
mac->phylink = phylink;
|
||||
|
||||
+ rtnl_lock();
|
||||
err = phylink_fwnode_phy_connect(mac->phylink, dpmac_node, 0);
|
||||
+ rtnl_unlock();
|
||||
if (err) {
|
||||
netdev_err(net_dev, "phylink_fwnode_phy_connect() = %d\n", err);
|
||||
goto err_phylink_destroy;
|
||||
@@ -446,7 +448,10 @@ err_pcs_destroy:
|
||||
|
||||
void dpaa2_mac_disconnect(struct dpaa2_mac *mac)
|
||||
{
|
||||
+ rtnl_lock();
|
||||
phylink_disconnect_phy(mac->phylink);
|
||||
+ rtnl_unlock();
|
||||
+
|
||||
phylink_destroy(mac->phylink);
|
||||
dpaa2_pcs_destroy(mac);
|
||||
of_phy_put(mac->serdes_phy);
|
||||
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
|
||||
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.c
|
||||
@@ -1530,7 +1530,6 @@ static irqreturn_t dpaa2_switch_irq0_han
|
||||
}
|
||||
|
||||
if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) {
|
||||
- rtnl_lock();
|
||||
/* We can avoid locking because the "endpoint changed" IRQ
|
||||
* handler is the only one who changes priv->mac at runtime,
|
||||
* so we are not racing with anyone.
|
||||
@@ -1540,7 +1539,6 @@ static irqreturn_t dpaa2_switch_irq0_han
|
||||
dpaa2_switch_port_disconnect_mac(port_priv);
|
||||
else
|
||||
dpaa2_switch_port_connect_mac(port_priv);
|
||||
- rtnl_unlock();
|
||||
}
|
||||
|
||||
out:
|
||||
@@ -2952,9 +2950,7 @@ static void dpaa2_switch_remove_port(str
|
||||
{
|
||||
struct ethsw_port_priv *port_priv = ethsw->ports[port_idx];
|
||||
|
||||
- rtnl_lock();
|
||||
dpaa2_switch_port_disconnect_mac(port_priv);
|
||||
- rtnl_unlock();
|
||||
free_netdev(port_priv->netdev);
|
||||
ethsw->ports[port_idx] = NULL;
|
||||
}
|
||||
@@ -10,8 +10,7 @@ BOARDNAME:=Microchip (Atmel AT91)
|
||||
FEATURES:=ext4 squashfs targz usbgadget ubifs
|
||||
SUBTARGETS:=sama7 sama5 sam9x
|
||||
|
||||
KERNEL_PATCHVER:=6.1
|
||||
KERNEL_TESTING_PATCHVER:=6.6
|
||||
KERNEL_PATCHVER:=6.6
|
||||
|
||||
include $(INCLUDE_DIR)/target.mk
|
||||
|
||||
|
||||
@@ -19,9 +19,7 @@ FAT32_BLOCKS:=$(shell echo \
|
||||
$$(($(AT91_SD_BOOT_PARTSIZE)*1024*1024/$(FAT32_BLOCK_SIZE))))
|
||||
|
||||
define Device/Default
|
||||
ifdef CONFIG_LINUX_6_6
|
||||
DEVICE_DTS_DIR := $(DTS_DIR)/microchip
|
||||
endif
|
||||
DEVICE_DTS_DIR := $(DTS_DIR)/microchip
|
||||
$(Device/default-nand)
|
||||
PROFILES := Default
|
||||
FILESYSTEMS := squashfs ubifs ext4
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
From 5d8ae2928f71f0f9c2c3f8f13d00ecec35649ad3 Mon Sep 17 00:00:00 2001
|
||||
From: Arnd Bergmann <arnd@arndb.de>
|
||||
Date: Thu, 15 Dec 2022 17:42:54 +0100
|
||||
Subject: [PATCH] pinctrl: at91: convert to NOIRQ_SYSTEM_SLEEP_PM_OPS
|
||||
|
||||
With the old SET_NOIRQ_SYSTEM_SLEEP_PM_OPS, some configs result in a
|
||||
build warning:
|
||||
|
||||
drivers/pinctrl/pinctrl-at91.c:1668:12: error: 'at91_gpio_resume' defined but not used [-Werror=unused-function]
|
||||
1668 | static int at91_gpio_resume(struct device *dev)
|
||||
| ^~~~~~~~~~~~~~~~
|
||||
drivers/pinctrl/pinctrl-at91.c:1650:12: error: 'at91_gpio_suspend' defined but not used [-Werror=unused-function]
|
||||
1650 | static int at91_gpio_suspend(struct device *dev)
|
||||
| ^~~~~~~~~~~~~~~~~
|
||||
|
||||
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
|
||||
Reviewed-by: Ryan Wanner <Ryan.Wanner@microchip.com>
|
||||
Link: https://lore.kernel.org/r/20221215164301.934805-1-arnd@kernel.org
|
||||
Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
|
||||
---
|
||||
drivers/pinctrl/pinctrl-at91.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/pinctrl/pinctrl-at91.c
|
||||
+++ b/drivers/pinctrl/pinctrl-at91.c
|
||||
@@ -1921,7 +1921,7 @@ err:
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops at91_gpio_pm_ops = {
|
||||
- SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(at91_gpio_suspend, at91_gpio_resume)
|
||||
+ NOIRQ_SYSTEM_SLEEP_PM_OPS(at91_gpio_suspend, at91_gpio_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver at91_gpio_driver = {
|
||||
@@ -1,341 +0,0 @@
|
||||
CONFIG_ALIGNMENT_TRAP=y
|
||||
CONFIG_ARCH_32BIT_OFF_T=y
|
||||
CONFIG_ARCH_AT91=y
|
||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||
CONFIG_ARCH_KEEP_MEMBLOCK=y
|
||||
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
|
||||
CONFIG_ARCH_MULTIPLATFORM=y
|
||||
# CONFIG_ARCH_MULTI_V4 is not set
|
||||
CONFIG_ARCH_MULTI_V4T=y
|
||||
CONFIG_ARCH_MULTI_V4_V5=y
|
||||
CONFIG_ARCH_MULTI_V5=y
|
||||
CONFIG_ARCH_NR_GPIO=0
|
||||
CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y
|
||||
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_ARCH_SPARSEMEM_ENABLE=y
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
CONFIG_ARM=y
|
||||
CONFIG_ARM_CPU_SUSPEND=y
|
||||
CONFIG_ARM_HAS_GROUP_RELOCS=y
|
||||
CONFIG_ARM_L1_CACHE_SHIFT=5
|
||||
CONFIG_ARM_PATCH_PHYS_VIRT=y
|
||||
CONFIG_ARM_THUMB=y
|
||||
CONFIG_ARM_UNWIND=y
|
||||
# CONFIG_AT91RM9200_WATCHDOG is not set
|
||||
CONFIG_AT91SAM9X_WATCHDOG=y
|
||||
# CONFIG_AT91_ADC is not set
|
||||
CONFIG_AT91_SAMA5D2_ADC=y
|
||||
CONFIG_AT91_SOC_ID=y
|
||||
# CONFIG_AT91_SOC_SFR is not set
|
||||
CONFIG_ATMEL_AIC5_IRQ=y
|
||||
CONFIG_ATMEL_AIC_IRQ=y
|
||||
CONFIG_ATMEL_CLOCKSOURCE_PIT=y
|
||||
CONFIG_ATMEL_CLOCKSOURCE_TCB=y
|
||||
CONFIG_ATMEL_EBI=y
|
||||
CONFIG_ATMEL_PIT=y
|
||||
CONFIG_ATMEL_PM=y
|
||||
CONFIG_ATMEL_SDRAMC=y
|
||||
CONFIG_ATMEL_SSC=y
|
||||
CONFIG_ATMEL_ST=y
|
||||
CONFIG_ATMEL_TCB_CLKSRC=y
|
||||
CONFIG_AT_HDMAC=y
|
||||
CONFIG_AT_XDMAC=y
|
||||
CONFIG_AUTO_ZRELADDR=y
|
||||
CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y
|
||||
CONFIG_BLK_PM=y
|
||||
CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y
|
||||
CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5"
|
||||
CONFIG_CC_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_COMMON_CLK=y
|
||||
CONFIG_COMMON_CLK_AT91=y
|
||||
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
|
||||
CONFIG_COMPAT_32BIT_TIME=y
|
||||
CONFIG_CONFIGFS_FS=y
|
||||
CONFIG_CPU_32v4T=y
|
||||
CONFIG_CPU_32v5=y
|
||||
CONFIG_CPU_ABRT_EV4T=y
|
||||
CONFIG_CPU_ABRT_EV5TJ=y
|
||||
CONFIG_CPU_ARM920T=y
|
||||
CONFIG_CPU_ARM926T=y
|
||||
# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
|
||||
CONFIG_CPU_CACHE_V4WT=y
|
||||
CONFIG_CPU_CACHE_VIVT=y
|
||||
CONFIG_CPU_COPY_V4WB=y
|
||||
CONFIG_CPU_CP15=y
|
||||
CONFIG_CPU_CP15_MMU=y
|
||||
# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
|
||||
CONFIG_CPU_LITTLE_ENDIAN=y
|
||||
CONFIG_CPU_NO_EFFICIENT_FFS=y
|
||||
CONFIG_CPU_PABRT_LEGACY=y
|
||||
CONFIG_CPU_PM=y
|
||||
CONFIG_CPU_THUMB_CAPABLE=y
|
||||
CONFIG_CPU_TLB_V4WBI=y
|
||||
CONFIG_CPU_USE_DOMAINS=y
|
||||
CONFIG_CRC16=y
|
||||
CONFIG_CRC7=y
|
||||
CONFIG_CRC_CCITT=y
|
||||
CONFIG_CRC_ITU_T=y
|
||||
CONFIG_CRYPTO_CRC32C=y
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_HASH_INFO=y
|
||||
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
|
||||
CONFIG_CRYPTO_LIB_SHA1=y
|
||||
CONFIG_CRYPTO_LIB_UTILS=y
|
||||
CONFIG_CRYPTO_LZO=y
|
||||
CONFIG_CRYPTO_RNG2=y
|
||||
CONFIG_CRYPTO_ZSTD=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_DMA_ENGINE=y
|
||||
CONFIG_DMA_OF=y
|
||||
CONFIG_DMA_OPS=y
|
||||
CONFIG_DTC=y
|
||||
CONFIG_EDAC_ATOMIC_SCRUB=y
|
||||
CONFIG_EDAC_SUPPORT=y
|
||||
CONFIG_EEPROM_AT24=y
|
||||
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_FIXED_PHY=y
|
||||
CONFIG_FIX_EARLYCON_MEM=y
|
||||
CONFIG_FREEZER=y
|
||||
CONFIG_FS_IOMAP=y
|
||||
CONFIG_FS_MBCACHE=y
|
||||
CONFIG_FWNODE_MDIO=y
|
||||
CONFIG_FW_CACHE=y
|
||||
CONFIG_FW_LOADER_PAGED_BUF=y
|
||||
CONFIG_FW_LOADER_SYSFS=y
|
||||
CONFIG_GCC10_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND=y
|
||||
CONFIG_GENERIC_ALLOCATOR=y
|
||||
CONFIG_GENERIC_ATOMIC64=y
|
||||
CONFIG_GENERIC_BUG=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CPU_AUTOPROBE=y
|
||||
CONFIG_GENERIC_EARLY_IOREMAP=y
|
||||
CONFIG_GENERIC_IDLE_POLL_SETUP=y
|
||||
CONFIG_GENERIC_IRQ_CHIP=y
|
||||
CONFIG_GENERIC_IRQ_MULTI_HANDLER=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
|
||||
CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GENERIC_PINCONF=y
|
||||
CONFIG_GENERIC_PINCTRL_GROUPS=y
|
||||
CONFIG_GENERIC_PINMUX_FUNCTIONS=y
|
||||
CONFIG_GENERIC_SCHED_CLOCK=y
|
||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
||||
CONFIG_GENERIC_STRNCPY_FROM_USER=y
|
||||
CONFIG_GENERIC_STRNLEN_USER=y
|
||||
CONFIG_GLOB=y
|
||||
CONFIG_GPIOLIB_IRQCHIP=y
|
||||
CONFIG_GPIO_CDEV=y
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT_MAP=y
|
||||
CONFIG_HW_RANDOM=y
|
||||
CONFIG_HW_RANDOM_ATMEL=y
|
||||
CONFIG_HZ=128
|
||||
CONFIG_HZ_FIXED=128
|
||||
CONFIG_HZ_PERIODIC=y
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_ALGOBIT=y
|
||||
CONFIG_I2C_AT91=y
|
||||
# CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL is not set
|
||||
CONFIG_I2C_BOARDINFO=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_I2C_COMPAT=y
|
||||
CONFIG_I2C_GPIO=y
|
||||
CONFIG_IIO=y
|
||||
CONFIG_IIO_BUFFER=y
|
||||
CONFIG_IIO_KFIFO_BUF=y
|
||||
CONFIG_IIO_TRIGGER=y
|
||||
CONFIG_IIO_TRIGGERED_BUFFER=y
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_INPUT=y
|
||||
CONFIG_IRQCHIP=y
|
||||
CONFIG_IRQSTACKS=y
|
||||
CONFIG_IRQ_DOMAIN=y
|
||||
CONFIG_IRQ_FORCED_THREADING=y
|
||||
CONFIG_IRQ_WORK=y
|
||||
CONFIG_JBD2=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_PWM=y
|
||||
CONFIG_LEDS_TRIGGER_CPU=y
|
||||
CONFIG_LEDS_TRIGGER_GPIO=y
|
||||
CONFIG_LIBFDT=y
|
||||
CONFIG_LOCK_DEBUGGING_SUPPORT=y
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
CONFIG_MACB=y
|
||||
CONFIG_MACB_USE_HWSTAMP=y
|
||||
# CONFIG_MCHP_EIC is not set
|
||||
CONFIG_MDIO_BUS=y
|
||||
CONFIG_MDIO_DEVICE=y
|
||||
CONFIG_MDIO_DEVRES=y
|
||||
CONFIG_MEMFD_CREATE=y
|
||||
CONFIG_MEMORY=y
|
||||
CONFIG_MFD_AT91_USART=y
|
||||
CONFIG_MFD_ATMEL_FLEXCOM=y
|
||||
CONFIG_MFD_ATMEL_HLCDC=y
|
||||
CONFIG_MFD_ATMEL_SMC=y
|
||||
CONFIG_MFD_CORE=y
|
||||
CONFIG_MFD_SYSCON=y
|
||||
CONFIG_MICREL_PHY=y
|
||||
CONFIG_MICROCHIP_CLOCKSOURCE_PIT64B=y
|
||||
CONFIG_MICROCHIP_PIT64B=y
|
||||
CONFIG_MIGRATION=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_MMC_BLOCK=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_OF_AT91=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
CONFIG_MODULES_USE_ELF_REL=y
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_MTD_DATAFLASH=y
|
||||
# CONFIG_MTD_DATAFLASH_OTP is not set
|
||||
# CONFIG_MTD_DATAFLASH_WRITE_VERIFY is not set
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_UBI=y
|
||||
CONFIG_MTD_UBI_BEB_LIMIT=20
|
||||
# CONFIG_MTD_UBI_BLOCK is not set
|
||||
CONFIG_MTD_UBI_FASTMAP=y
|
||||
CONFIG_MTD_UBI_GLUEBI=y
|
||||
CONFIG_MTD_UBI_WL_THRESHOLD=4096
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_NEED_KUSER_HELPERS=y
|
||||
CONFIG_NEED_PER_CPU_KM=y
|
||||
CONFIG_NET_PTP_CLASSIFY=y
|
||||
CONFIG_NET_SELFTESTS=y
|
||||
CONFIG_NLS=y
|
||||
CONFIG_NVMEM=y
|
||||
CONFIG_NVMEM_LAYOUTS=y
|
||||
# CONFIG_NVMEM_MICROCHIP_OTPC is not set
|
||||
CONFIG_NVMEM_SYSFS=y
|
||||
CONFIG_OF=y
|
||||
CONFIG_OF_ADDRESS=y
|
||||
CONFIG_OF_EARLY_FLATTREE=y
|
||||
CONFIG_OF_FLATTREE=y
|
||||
CONFIG_OF_GPIO=y
|
||||
CONFIG_OF_IRQ=y
|
||||
CONFIG_OF_KOBJ=y
|
||||
CONFIG_OF_MDIO=y
|
||||
CONFIG_OLD_SIGACTION=y
|
||||
CONFIG_OLD_SIGSUSPEND3=y
|
||||
CONFIG_PAGE_OFFSET=0xC0000000
|
||||
CONFIG_PAGE_POOL=y
|
||||
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
|
||||
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
|
||||
CONFIG_PAHOLE_HAS_LANG_EXCLUDE=y
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PGTABLE_LEVELS=2
|
||||
CONFIG_PHYLIB=y
|
||||
CONFIG_PHYLIB_LEDS=y
|
||||
CONFIG_PHYLINK=y
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_PINCTRL_AT91=y
|
||||
# CONFIG_PINCTRL_AT91PIO4 is not set
|
||||
CONFIG_PM=y
|
||||
CONFIG_PM_CLK=y
|
||||
CONFIG_PM_SLEEP=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_AT91_POWEROFF=y
|
||||
CONFIG_POWER_RESET_AT91_RESET=y
|
||||
CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_PPS=y
|
||||
CONFIG_PREEMPT_NONE_BUILD=y
|
||||
CONFIG_PTP_1588_CLOCK=y
|
||||
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=y
|
||||
CONFIG_PWM_ATMEL_HLCDC_PWM=y
|
||||
CONFIG_PWM_ATMEL_TCB=y
|
||||
CONFIG_PWM_SYSFS=y
|
||||
CONFIG_RANDSTRUCT_NONE=y
|
||||
CONFIG_RATIONAL=y
|
||||
CONFIG_REGMAP=y
|
||||
CONFIG_REGMAP_I2C=y
|
||||
CONFIG_REGMAP_MMIO=y
|
||||
CONFIG_REGULATOR=y
|
||||
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_AT91RM9200=y
|
||||
CONFIG_RTC_DRV_AT91SAM9=y
|
||||
CONFIG_RTC_I2C_AND_SPI=y
|
||||
CONFIG_RTC_MC146818_LIB=y
|
||||
CONFIG_SAMA5D4_WATCHDOG=y
|
||||
# CONFIG_SERIAL_8250 is not set
|
||||
CONFIG_SERIAL_ATMEL=y
|
||||
CONFIG_SERIAL_ATMEL_CONSOLE=y
|
||||
CONFIG_SERIAL_ATMEL_PDC=y
|
||||
# CONFIG_SERIAL_ATMEL_TTYAT is not set
|
||||
CONFIG_SERIAL_MCTRL_GPIO=y
|
||||
CONFIG_SGL_ALLOC=y
|
||||
CONFIG_SOC_AT91RM9200=y
|
||||
CONFIG_SOC_AT91SAM9=y
|
||||
CONFIG_SOC_BUS=y
|
||||
CONFIG_SOC_SAM9X60=y
|
||||
CONFIG_SOC_SAM_V4_V5=y
|
||||
CONFIG_SOFTIRQ_ON_OWN_STACK=y
|
||||
CONFIG_SPARSE_IRQ=y
|
||||
CONFIG_SPI=y
|
||||
# CONFIG_SPI_AT91_USART is not set
|
||||
CONFIG_SPI_ATMEL=y
|
||||
CONFIG_SPI_ATMEL_QUADSPI=y
|
||||
CONFIG_SPI_BITBANG=y
|
||||
CONFIG_SPI_GPIO=y
|
||||
CONFIG_SPI_MASTER=y
|
||||
CONFIG_SPI_MEM=y
|
||||
CONFIG_SPI_SPIDEV=y
|
||||
CONFIG_SPLIT_PTLOCK_CPUS=999999
|
||||
CONFIG_SRAM=y
|
||||
CONFIG_SRAM_EXEC=y
|
||||
CONFIG_SRCU=y
|
||||
CONFIG_SUSPEND=y
|
||||
CONFIG_SUSPEND_FREEZER=y
|
||||
CONFIG_SWPHY=y
|
||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
||||
CONFIG_THREAD_INFO_IN_TASK=y
|
||||
CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
CONFIG_TIMER_OF=y
|
||||
CONFIG_TIMER_PROBE=y
|
||||
CONFIG_TINY_SRCU=y
|
||||
CONFIG_UBIFS_FS=y
|
||||
CONFIG_UBIFS_FS_ADVANCED_COMPR=y
|
||||
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
|
||||
CONFIG_UNWINDER_ARM=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_ACM=y
|
||||
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
|
||||
# CONFIG_USB_AT91 is not set
|
||||
# CONFIG_USB_ATMEL_USBA is not set
|
||||
CONFIG_USB_COMMON=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_EHCI_HCD_AT91=y
|
||||
# CONFIG_USB_EHCI_HCD_PLATFORM is not set
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_USB_OHCI_HCD=y
|
||||
CONFIG_USB_OHCI_HCD_AT91=y
|
||||
CONFIG_USB_OHCI_HCD_PLATFORM=y
|
||||
CONFIG_USB_SERIAL=y
|
||||
# CONFIG_USB_SERIAL_CONSOLE is not set
|
||||
CONFIG_USB_SERIAL_FTDI_SIO=y
|
||||
CONFIG_USB_SERIAL_PL2303=y
|
||||
CONFIG_USB_SUPPORT=y
|
||||
CONFIG_USE_OF=y
|
||||
# CONFIG_VFP is not set
|
||||
# CONFIG_VIDEO_ATMEL_XISC is not set
|
||||
# CONFIG_VIDEO_MICROCHIP_CSI2DC is not set
|
||||
CONFIG_WATCHDOG_CORE=y
|
||||
CONFIG_XXHASH=y
|
||||
CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
CONFIG_ZLIB_DEFLATE=y
|
||||
CONFIG_ZLIB_INFLATE=y
|
||||
CONFIG_ZSTD_COMMON=y
|
||||
CONFIG_ZSTD_COMPRESS=y
|
||||
CONFIG_ZSTD_DECOMPRESS=y
|
||||
@@ -1,517 +0,0 @@
|
||||
CONFIG_ALIGNMENT_TRAP=y
|
||||
CONFIG_ARCH_32BIT_OFF_T=y
|
||||
CONFIG_ARCH_AT91=y
|
||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||
CONFIG_ARCH_KEEP_MEMBLOCK=y
|
||||
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
|
||||
CONFIG_ARCH_MULTIPLATFORM=y
|
||||
CONFIG_ARCH_MULTI_V6_V7=y
|
||||
CONFIG_ARCH_MULTI_V7=y
|
||||
CONFIG_ARCH_NR_GPIO=0
|
||||
CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y
|
||||
CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y
|
||||
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_ARCH_SPARSEMEM_ENABLE=y
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
CONFIG_ARM=y
|
||||
CONFIG_ARM_AT91_CPUIDLE=y
|
||||
CONFIG_ARM_CPU_SUSPEND=y
|
||||
CONFIG_ARM_HAS_GROUP_RELOCS=y
|
||||
CONFIG_ARM_HEAVY_MB=y
|
||||
CONFIG_ARM_L1_CACHE_SHIFT=6
|
||||
CONFIG_ARM_L1_CACHE_SHIFT_6=y
|
||||
CONFIG_ARM_PATCH_IDIV=y
|
||||
CONFIG_ARM_PATCH_PHYS_VIRT=y
|
||||
CONFIG_ARM_THUMB=y
|
||||
CONFIG_ARM_UNWIND=y
|
||||
CONFIG_ARM_VIRT_EXT=y
|
||||
CONFIG_AT91SAM9X_WATCHDOG=y
|
||||
CONFIG_AT91_ADC=y
|
||||
CONFIG_AT91_SAMA5D2_ADC=y
|
||||
CONFIG_AT91_SOC_ID=y
|
||||
# CONFIG_AT91_SOC_SFR is not set
|
||||
CONFIG_ATMEL_AIC5_IRQ=y
|
||||
# CONFIG_ATMEL_CLOCKSOURCE_PIT is not set
|
||||
CONFIG_ATMEL_CLOCKSOURCE_TCB=y
|
||||
CONFIG_ATMEL_EBI=y
|
||||
CONFIG_ATMEL_PM=y
|
||||
CONFIG_ATMEL_SDRAMC=y
|
||||
# CONFIG_ATMEL_SECURE_PM is not set
|
||||
CONFIG_ATMEL_SSC=y
|
||||
CONFIG_ATMEL_TCB_CLKSRC=y
|
||||
CONFIG_AT_HDMAC=y
|
||||
CONFIG_AT_XDMAC=y
|
||||
CONFIG_AUTO_ZRELADDR=y
|
||||
CONFIG_BACKLIGHT_CLASS_DEVICE=y
|
||||
CONFIG_BACKLIGHT_PWM=y
|
||||
CONFIG_BATTERY_ACT8945A=y
|
||||
CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_COUNT=4
|
||||
CONFIG_BLK_DEV_RAM_SIZE=8192
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
CONFIG_BLK_PM=y
|
||||
CONFIG_CACHE_L2X0=y
|
||||
CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y
|
||||
CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5"
|
||||
CONFIG_CC_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_CMA=y
|
||||
CONFIG_CMA_ALIGNMENT=8
|
||||
CONFIG_CMA_AREAS=7
|
||||
# CONFIG_CMA_DEBUG is not set
|
||||
# CONFIG_CMA_DEBUGFS is not set
|
||||
CONFIG_CMA_SIZE_MBYTES=16
|
||||
# CONFIG_CMA_SIZE_SEL_MAX is not set
|
||||
CONFIG_CMA_SIZE_SEL_MBYTES=y
|
||||
# CONFIG_CMA_SIZE_SEL_MIN is not set
|
||||
# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set
|
||||
# CONFIG_CMA_SYSFS is not set
|
||||
CONFIG_CMDLINE="console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw"
|
||||
CONFIG_CMDLINE_FROM_BOOTLOADER=y
|
||||
CONFIG_COMMON_CLK=y
|
||||
CONFIG_COMMON_CLK_AT91=y
|
||||
CONFIG_COMPACT_UNEVICTABLE_DEFAULT=1
|
||||
CONFIG_COMPAT_32BIT_TIME=y
|
||||
CONFIG_CONFIGFS_FS=y
|
||||
CONFIG_CONTIG_ALLOC=y
|
||||
CONFIG_COREDUMP=y
|
||||
CONFIG_CPU_32v6K=y
|
||||
CONFIG_CPU_32v7=y
|
||||
CONFIG_CPU_ABRT_EV7=y
|
||||
CONFIG_CPU_CACHE_V7=y
|
||||
CONFIG_CPU_CACHE_VIPT=y
|
||||
CONFIG_CPU_COPY_V6=y
|
||||
CONFIG_CPU_CP15=y
|
||||
CONFIG_CPU_CP15_MMU=y
|
||||
CONFIG_CPU_HAS_ASID=y
|
||||
CONFIG_CPU_IDLE=y
|
||||
CONFIG_CPU_IDLE_GOV_LADDER=y
|
||||
CONFIG_CPU_IDLE_GOV_MENU=y
|
||||
CONFIG_CPU_LITTLE_ENDIAN=y
|
||||
CONFIG_CPU_PABRT_V7=y
|
||||
CONFIG_CPU_PM=y
|
||||
CONFIG_CPU_SPECTRE=y
|
||||
CONFIG_CPU_THUMB_CAPABLE=y
|
||||
CONFIG_CPU_TLB_V7=y
|
||||
CONFIG_CPU_V7=y
|
||||
CONFIG_CRASH_CORE=y
|
||||
CONFIG_CRASH_DUMP=y
|
||||
CONFIG_CRC16=y
|
||||
# CONFIG_CRC32_SARWATE is not set
|
||||
CONFIG_CRC32_SLICEBY8=y
|
||||
CONFIG_CROSS_MEMORY_ATTACH=y
|
||||
CONFIG_CRYPTO_CRC32C=y
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_DRBG=y
|
||||
CONFIG_CRYPTO_DRBG_HMAC=y
|
||||
CONFIG_CRYPTO_DRBG_MENU=y
|
||||
CONFIG_CRYPTO_HASH_INFO=y
|
||||
CONFIG_CRYPTO_HMAC=y
|
||||
CONFIG_CRYPTO_JITTERENTROPY=y
|
||||
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
|
||||
CONFIG_CRYPTO_LIB_SHA1=y
|
||||
CONFIG_CRYPTO_LIB_SHA256=y
|
||||
CONFIG_CRYPTO_LIB_UTILS=y
|
||||
CONFIG_CRYPTO_LZO=y
|
||||
CONFIG_CRYPTO_RNG=y
|
||||
CONFIG_CRYPTO_RNG2=y
|
||||
CONFIG_CRYPTO_RNG_DEFAULT=y
|
||||
CONFIG_CRYPTO_SEQIV=y
|
||||
CONFIG_CRYPTO_SHA256=y
|
||||
CONFIG_CRYPTO_SHA512=y
|
||||
CONFIG_CRYPTO_ZSTD=y
|
||||
CONFIG_CURRENT_POINTER_IN_TPIDRURO=y
|
||||
CONFIG_DCACHE_WORD_ACCESS=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_LL_INCLUDE="mach/debug-macro.S"
|
||||
CONFIG_DEBUG_MEMORY_INIT=y
|
||||
CONFIG_DEBUG_USER=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_DMA_CMA=y
|
||||
CONFIG_DMA_ENGINE=y
|
||||
CONFIG_DMA_OF=y
|
||||
CONFIG_DMA_OPS=y
|
||||
CONFIG_DMA_SHARED_BUFFER=y
|
||||
CONFIG_DNOTIFY=y
|
||||
CONFIG_DRM=y
|
||||
CONFIG_DRM_ATMEL_HLCDC=y
|
||||
CONFIG_DRM_BRIDGE=y
|
||||
CONFIG_DRM_FBDEV_EMULATION=y
|
||||
CONFIG_DRM_FBDEV_OVERALLOC=100
|
||||
CONFIG_DRM_GEM_DMA_HELPER=y
|
||||
CONFIG_DRM_KMS_HELPER=y
|
||||
CONFIG_DRM_NOMODESET=y
|
||||
CONFIG_DRM_PANEL=y
|
||||
CONFIG_DRM_PANEL_BRIDGE=y
|
||||
CONFIG_DRM_PANEL_ORIENTATION_QUIRKS=y
|
||||
CONFIG_DRM_PANEL_SIMPLE=y
|
||||
CONFIG_DTC=y
|
||||
CONFIG_DVB_CORE=y
|
||||
CONFIG_EDAC_ATOMIC_SCRUB=y
|
||||
CONFIG_EDAC_SUPPORT=y
|
||||
CONFIG_EEPROM_AT24=y
|
||||
CONFIG_ELF_CORE=y
|
||||
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_FAT_FS=y
|
||||
CONFIG_FB=y
|
||||
CONFIG_FB_CFB_COPYAREA=y
|
||||
CONFIG_FB_CFB_FILLRECT=y
|
||||
CONFIG_FB_CFB_IMAGEBLIT=y
|
||||
CONFIG_FB_CMDLINE=y
|
||||
CONFIG_FB_DEFERRED_IO=y
|
||||
CONFIG_FB_SYS_COPYAREA=y
|
||||
CONFIG_FB_SYS_FILLRECT=y
|
||||
CONFIG_FB_SYS_FOPS=y
|
||||
CONFIG_FB_SYS_IMAGEBLIT=y
|
||||
CONFIG_FIXED_PHY=y
|
||||
CONFIG_FIX_EARLYCON_MEM=y
|
||||
CONFIG_FREEZER=y
|
||||
CONFIG_FS_IOMAP=y
|
||||
CONFIG_FS_MBCACHE=y
|
||||
CONFIG_FWNODE_MDIO=y
|
||||
CONFIG_FW_CACHE=y
|
||||
CONFIG_FW_LOADER_PAGED_BUF=y
|
||||
CONFIG_FW_LOADER_SYSFS=y
|
||||
CONFIG_GCC10_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND=y
|
||||
CONFIG_GENERIC_ALLOCATOR=y
|
||||
CONFIG_GENERIC_BUG=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CPU_AUTOPROBE=y
|
||||
CONFIG_GENERIC_CPU_VULNERABILITIES=y
|
||||
CONFIG_GENERIC_EARLY_IOREMAP=y
|
||||
CONFIG_GENERIC_GETTIMEOFDAY=y
|
||||
CONFIG_GENERIC_IDLE_POLL_SETUP=y
|
||||
CONFIG_GENERIC_IRQ_CHIP=y
|
||||
CONFIG_GENERIC_IRQ_MULTI_HANDLER=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
|
||||
CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GENERIC_PINCONF=y
|
||||
CONFIG_GENERIC_SCHED_CLOCK=y
|
||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
||||
CONFIG_GENERIC_STRNCPY_FROM_USER=y
|
||||
CONFIG_GENERIC_STRNLEN_USER=y
|
||||
CONFIG_GENERIC_TIME_VSYSCALL=y
|
||||
CONFIG_GENERIC_VDSO_32=y
|
||||
CONFIG_GLOB=y
|
||||
CONFIG_GPIOLIB_IRQCHIP=y
|
||||
CONFIG_GPIO_CDEV=y
|
||||
# CONFIG_HARDEN_BRANCH_HISTORY is not set
|
||||
# CONFIG_HARDEN_BRANCH_PREDICTOR is not set
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT_MAP=y
|
||||
CONFIG_HAVE_SMP=y
|
||||
CONFIG_HDMI=y
|
||||
CONFIG_HID=y
|
||||
CONFIG_HID_GENERIC=y
|
||||
CONFIG_HW_RANDOM=y
|
||||
CONFIG_HW_RANDOM_ATMEL=y
|
||||
CONFIG_HZ_FIXED=0
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_ALGOBIT=y
|
||||
CONFIG_I2C_AT91=y
|
||||
# CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL is not set
|
||||
CONFIG_I2C_BOARDINFO=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_I2C_COMPAT=y
|
||||
CONFIG_I2C_GPIO=y
|
||||
CONFIG_I2C_HELPER_AUTO=y
|
||||
CONFIG_IIO=y
|
||||
CONFIG_IIO_BUFFER=y
|
||||
CONFIG_IIO_KFIFO_BUF=y
|
||||
CONFIG_IIO_TRIGGER=y
|
||||
CONFIG_IIO_TRIGGERED_BUFFER=y
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_INPUT=y
|
||||
CONFIG_INPUT_EVDEV=y
|
||||
CONFIG_INPUT_KEYBOARD=y
|
||||
CONFIG_INPUT_LEDS=y
|
||||
CONFIG_INPUT_TOUCHSCREEN=y
|
||||
CONFIG_IRQCHIP=y
|
||||
CONFIG_IRQSTACKS=y
|
||||
CONFIG_IRQ_DOMAIN=y
|
||||
CONFIG_IRQ_FORCED_THREADING=y
|
||||
CONFIG_IRQ_WORK=y
|
||||
CONFIG_JBD2=y
|
||||
# CONFIG_JFFS2_FS is not set
|
||||
CONFIG_KCMP=y
|
||||
CONFIG_KERNEL_GZIP=y
|
||||
# CONFIG_KERNEL_XZ is not set
|
||||
CONFIG_KEXEC=y
|
||||
CONFIG_KEXEC_CORE=y
|
||||
CONFIG_KEYBOARD_GPIO=y
|
||||
CONFIG_KEYBOARD_QT1070=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LEDS_PWM=y
|
||||
CONFIG_LEDS_TRIGGER_CPU=y
|
||||
CONFIG_LEDS_TRIGGER_GPIO=y
|
||||
CONFIG_LIBFDT=y
|
||||
CONFIG_LOCALVERSION_AUTO=y
|
||||
CONFIG_LOCK_DEBUGGING_SUPPORT=y
|
||||
CONFIG_LOG_BUF_SHIFT=16
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
CONFIG_MACB=y
|
||||
CONFIG_MACB_USE_HWSTAMP=y
|
||||
CONFIG_MAGIC_SYSRQ=y
|
||||
# CONFIG_MCHP_EIC is not set
|
||||
CONFIG_MDIO_BUS=y
|
||||
CONFIG_MDIO_DEVICE=y
|
||||
CONFIG_MDIO_DEVRES=y
|
||||
CONFIG_MEDIA_ANALOG_TV_SUPPORT=y
|
||||
CONFIG_MEDIA_ATTACH=y
|
||||
CONFIG_MEDIA_CAMERA_SUPPORT=y
|
||||
CONFIG_MEDIA_DIGITAL_TV_SUPPORT=y
|
||||
CONFIG_MEDIA_PLATFORM_SUPPORT=y
|
||||
CONFIG_MEDIA_RADIO_SUPPORT=y
|
||||
CONFIG_MEDIA_SDR_SUPPORT=y
|
||||
CONFIG_MEDIA_SUPPORT=y
|
||||
CONFIG_MEDIA_TEST_SUPPORT=y
|
||||
CONFIG_MEDIA_TUNER=y
|
||||
CONFIG_MEDIA_USB_SUPPORT=y
|
||||
CONFIG_MEMFD_CREATE=y
|
||||
CONFIG_MEMORY=y
|
||||
CONFIG_MEMORY_ISOLATION=y
|
||||
CONFIG_MFD_ACT8945A=y
|
||||
CONFIG_MFD_AT91_USART=y
|
||||
CONFIG_MFD_ATMEL_FLEXCOM=y
|
||||
CONFIG_MFD_ATMEL_HLCDC=y
|
||||
CONFIG_MFD_ATMEL_SMC=y
|
||||
CONFIG_MFD_CORE=y
|
||||
CONFIG_MFD_SYSCON=y
|
||||
CONFIG_MICREL_PHY=y
|
||||
# CONFIG_MICROCHIP_CLOCKSOURCE_PIT64B is not set
|
||||
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
|
||||
CONFIG_MIGRATION=y
|
||||
CONFIG_MMC=y
|
||||
CONFIG_MMC_ATMELMCI=y
|
||||
CONFIG_MMC_BLOCK=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_OF_AT91=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
CONFIG_MODULES_USE_ELF_REL=y
|
||||
# CONFIG_MTD_CFI_AMDSTD is not set
|
||||
# CONFIG_MTD_CFI_INTELEXT is not set
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
|
||||
CONFIG_MTD_SPI_NOR=y
|
||||
CONFIG_MTD_UBI=y
|
||||
CONFIG_MTD_UBI_BEB_LIMIT=20
|
||||
# CONFIG_MTD_UBI_BLOCK is not set
|
||||
CONFIG_MTD_UBI_FASTMAP=y
|
||||
CONFIG_MTD_UBI_WL_THRESHOLD=4096
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_NEED_PER_CPU_KM=y
|
||||
# CONFIG_NEON is not set
|
||||
CONFIG_NET_PTP_CLASSIFY=y
|
||||
CONFIG_NET_SELFTESTS=y
|
||||
CONFIG_NLS=y
|
||||
CONFIG_NLS_CODEPAGE_437=y
|
||||
CONFIG_NLS_CODEPAGE_850=y
|
||||
CONFIG_NLS_ISO8859_1=y
|
||||
CONFIG_NLS_UTF8=y
|
||||
CONFIG_NO_HZ_COMMON=y
|
||||
CONFIG_NO_HZ_IDLE=y
|
||||
CONFIG_NVMEM=y
|
||||
CONFIG_NVMEM_LAYOUTS=y
|
||||
# CONFIG_NVMEM_MICROCHIP_OTPC is not set
|
||||
CONFIG_NVMEM_SYSFS=y
|
||||
CONFIG_OF=y
|
||||
CONFIG_OF_ADDRESS=y
|
||||
CONFIG_OF_EARLY_FLATTREE=y
|
||||
CONFIG_OF_FLATTREE=y
|
||||
CONFIG_OF_GPIO=y
|
||||
CONFIG_OF_IRQ=y
|
||||
CONFIG_OF_KOBJ=y
|
||||
CONFIG_OF_MDIO=y
|
||||
CONFIG_OLD_SIGACTION=y
|
||||
CONFIG_OLD_SIGSUSPEND3=y
|
||||
CONFIG_OUTER_CACHE=y
|
||||
CONFIG_OUTER_CACHE_SYNC=y
|
||||
CONFIG_PAGE_OFFSET=0xC0000000
|
||||
CONFIG_PAGE_POOL=y
|
||||
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
|
||||
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
|
||||
CONFIG_PAHOLE_HAS_LANG_EXCLUDE=y
|
||||
# CONFIG_PARTITION_ADVANCED is not set
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PGTABLE_LEVELS=2
|
||||
CONFIG_PHYLIB=y
|
||||
CONFIG_PHYLIB_LEDS=y
|
||||
CONFIG_PHYLINK=y
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_PINCTRL_AT91=y
|
||||
CONFIG_PINCTRL_AT91PIO4=y
|
||||
# CONFIG_PINCTRL_SINGLE is not set
|
||||
CONFIG_PM=y
|
||||
CONFIG_PM_CLK=y
|
||||
CONFIG_PM_SLEEP=y
|
||||
CONFIG_POWER_RESET=y
|
||||
CONFIG_POWER_RESET_AT91_POWEROFF=y
|
||||
CONFIG_POWER_RESET_AT91_RESET=y
|
||||
CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC=y
|
||||
CONFIG_POWER_SUPPLY=y
|
||||
CONFIG_PPS=y
|
||||
CONFIG_PREEMPT_NONE_BUILD=y
|
||||
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_PROC_VMCORE=y
|
||||
CONFIG_PTP_1588_CLOCK=y
|
||||
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=y
|
||||
CONFIG_PWM_ATMEL_HLCDC_PWM=y
|
||||
CONFIG_PWM_ATMEL_TCB=y
|
||||
CONFIG_PWM_SYSFS=y
|
||||
CONFIG_RANDSTRUCT_NONE=y
|
||||
CONFIG_RATIONAL=y
|
||||
CONFIG_REGMAP=y
|
||||
CONFIG_REGMAP_I2C=y
|
||||
CONFIG_REGMAP_MMIO=y
|
||||
CONFIG_REGMAP_SPI=y
|
||||
CONFIG_REGULATOR=y
|
||||
CONFIG_REGULATOR_ACT8865=y
|
||||
CONFIG_REGULATOR_ACT8945A=y
|
||||
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_AT91RM9200=y
|
||||
# CONFIG_RTC_DRV_AT91SAM9 is not set
|
||||
# CONFIG_RTC_DRV_CMOS is not set
|
||||
CONFIG_RTC_I2C_AND_SPI=y
|
||||
CONFIG_SAMA5D4_WATCHDOG=y
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_SCSI_COMMON=y
|
||||
# CONFIG_SCSI_LOWLEVEL is not set
|
||||
# CONFIG_SERIAL_8250 is not set
|
||||
CONFIG_SERIAL_ATMEL=y
|
||||
CONFIG_SERIAL_ATMEL_CONSOLE=y
|
||||
CONFIG_SERIAL_ATMEL_PDC=y
|
||||
# CONFIG_SERIAL_ATMEL_TTYAT is not set
|
||||
CONFIG_SERIAL_MCTRL_GPIO=y
|
||||
CONFIG_SGL_ALLOC=y
|
||||
CONFIG_SG_POOL=y
|
||||
CONFIG_SND=y
|
||||
CONFIG_SND_ARM=y
|
||||
# CONFIG_SND_AT73C213 is not set
|
||||
# CONFIG_SND_AT91_SOC_SAM9G20_WM8731 is not set
|
||||
# CONFIG_SND_AT91_SOC_SAM9X5_WM8731 is not set
|
||||
CONFIG_SND_ATMEL_SOC=y
|
||||
CONFIG_SND_ATMEL_SOC_CLASSD=y
|
||||
CONFIG_SND_ATMEL_SOC_DMA=y
|
||||
CONFIG_SND_ATMEL_SOC_I2S=y
|
||||
CONFIG_SND_ATMEL_SOC_PDC=y
|
||||
# CONFIG_SND_ATMEL_SOC_PDMIC is not set
|
||||
CONFIG_SND_ATMEL_SOC_SSC=y
|
||||
CONFIG_SND_ATMEL_SOC_SSC_DMA=y
|
||||
# CONFIG_SND_ATMEL_SOC_SSC_PDC is not set
|
||||
# CONFIG_SND_ATMEL_SOC_TSE850_PCM5142 is not set
|
||||
CONFIG_SND_ATMEL_SOC_WM8904=y
|
||||
# CONFIG_SND_COMPRESS_OFFLOAD is not set
|
||||
CONFIG_SND_DMAENGINE_PCM=y
|
||||
CONFIG_SND_JACK=y
|
||||
CONFIG_SND_JACK_INPUT_DEV=y
|
||||
# CONFIG_SND_MCHP_SOC_I2S_MCC is not set
|
||||
# CONFIG_SND_MCHP_SOC_PDMC is not set
|
||||
# CONFIG_SND_MCHP_SOC_SPDIFRX is not set
|
||||
# CONFIG_SND_MCHP_SOC_SPDIFTX is not set
|
||||
CONFIG_SND_PCM=y
|
||||
CONFIG_SND_PCM_TIMER=y
|
||||
CONFIG_SND_SOC=y
|
||||
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y
|
||||
CONFIG_SND_SOC_I2C_AND_SPI=y
|
||||
CONFIG_SND_SOC_MIKROE_PROTO=y
|
||||
CONFIG_SND_SOC_WM8731=y
|
||||
CONFIG_SND_SOC_WM8904=y
|
||||
CONFIG_SND_SPI=y
|
||||
CONFIG_SND_SUPPORT_OLD_API=y
|
||||
CONFIG_SND_TIMER=y
|
||||
CONFIG_SOC_BUS=y
|
||||
# CONFIG_SOC_LAN966 is not set
|
||||
CONFIG_SOC_SAMA5=y
|
||||
CONFIG_SOC_SAMA5D2=y
|
||||
CONFIG_SOC_SAMA5D3=y
|
||||
CONFIG_SOC_SAMA5D4=y
|
||||
# CONFIG_SOC_SAMA7G5 is not set
|
||||
CONFIG_SOC_SAM_V7=y
|
||||
CONFIG_SOFTIRQ_ON_OWN_STACK=y
|
||||
CONFIG_SOUND=y
|
||||
CONFIG_SOUND_OSS_CORE=y
|
||||
CONFIG_SPARSE_IRQ=y
|
||||
CONFIG_SPI=y
|
||||
# CONFIG_SPI_AT91_USART is not set
|
||||
CONFIG_SPI_ATMEL=y
|
||||
CONFIG_SPI_ATMEL_QUADSPI=y
|
||||
CONFIG_SPI_BITBANG=y
|
||||
CONFIG_SPI_GPIO=y
|
||||
CONFIG_SPI_MASTER=y
|
||||
CONFIG_SPI_MEM=y
|
||||
# CONFIG_SQUASHFS is not set
|
||||
CONFIG_SRAM=y
|
||||
CONFIG_SRAM_EXEC=y
|
||||
CONFIG_SRCU=y
|
||||
# CONFIG_STANDALONE is not set
|
||||
CONFIG_SUSPEND=y
|
||||
CONFIG_SUSPEND_FREEZER=y
|
||||
CONFIG_SWPHY=y
|
||||
# CONFIG_SWP_EMULATE is not set
|
||||
CONFIG_SYNC_FILE=y
|
||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
||||
CONFIG_THREAD_INFO_IN_TASK=y
|
||||
CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
CONFIG_TIMER_OF=y
|
||||
CONFIG_TIMER_PROBE=y
|
||||
CONFIG_TINY_SRCU=y
|
||||
CONFIG_TOUCHSCREEN_ATMEL_MXT=y
|
||||
CONFIG_UACCESS_WITH_MEMCPY=y
|
||||
CONFIG_UBIFS_FS=y
|
||||
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
|
||||
CONFIG_UNWINDER_ARM=y
|
||||
CONFIG_USB=y
|
||||
CONFIG_USB_ACM=y
|
||||
CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
|
||||
# CONFIG_USB_AT91 is not set
|
||||
# CONFIG_USB_ATMEL_USBA is not set
|
||||
CONFIG_USB_COMMON=y
|
||||
CONFIG_USB_EHCI_HCD=y
|
||||
CONFIG_USB_EHCI_HCD_AT91=y
|
||||
# CONFIG_USB_EHCI_HCD_PLATFORM is not set
|
||||
# CONFIG_USB_EHCI_ROOT_HUB_TT is not set
|
||||
CONFIG_USB_GADGET=y
|
||||
CONFIG_USB_HID=y
|
||||
CONFIG_USB_OHCI_HCD=y
|
||||
CONFIG_USB_OHCI_HCD_AT91=y
|
||||
CONFIG_USB_OHCI_HCD_PLATFORM=y
|
||||
# CONFIG_USB_PWC is not set
|
||||
CONFIG_USB_SERIAL=y
|
||||
# CONFIG_USB_SERIAL_CONSOLE is not set
|
||||
CONFIG_USB_SERIAL_FTDI_SIO=y
|
||||
CONFIG_USB_SERIAL_PL2303=y
|
||||
CONFIG_USB_STORAGE=y
|
||||
CONFIG_USB_SUPPORT=y
|
||||
CONFIG_USE_OF=y
|
||||
CONFIG_VFAT_FS=y
|
||||
CONFIG_VFP=y
|
||||
CONFIG_VFPv3=y
|
||||
# CONFIG_VIDEO_ATMEL_XISC is not set
|
||||
CONFIG_VIDEOMODE_HELPERS=y
|
||||
CONFIG_VIDEO_DEV=y
|
||||
# CONFIG_VIDEO_MICROCHIP_CSI2DC is not set
|
||||
CONFIG_VIDEO_V4L2_I2C=y
|
||||
CONFIG_VM_EVENT_COUNTERS=y
|
||||
CONFIG_WATCHDOG_CORE=y
|
||||
# CONFIG_WQ_POWER_EFFICIENT_DEFAULT is not set
|
||||
CONFIG_XXHASH=y
|
||||
CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
CONFIG_ZLIB_DEFLATE=y
|
||||
CONFIG_ZLIB_INFLATE=y
|
||||
CONFIG_ZSTD_COMMON=y
|
||||
CONFIG_ZSTD_COMPRESS=y
|
||||
CONFIG_ZSTD_DECOMPRESS=y
|
||||
@@ -1,424 +0,0 @@
|
||||
CONFIG_ALIGNMENT_TRAP=y
|
||||
# CONFIG_ALLOW_DEV_COREDUMP is not set
|
||||
CONFIG_ARCH_32BIT_OFF_T=y
|
||||
CONFIG_ARCH_AT91=y
|
||||
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
|
||||
CONFIG_ARCH_KEEP_MEMBLOCK=y
|
||||
CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
|
||||
CONFIG_ARCH_MULTIPLATFORM=y
|
||||
CONFIG_ARCH_MULTI_V6_V7=y
|
||||
CONFIG_ARCH_MULTI_V7=y
|
||||
CONFIG_ARCH_NR_GPIO=0
|
||||
CONFIG_ARCH_OPTIONAL_KERNEL_RWX=y
|
||||
CONFIG_ARCH_OPTIONAL_KERNEL_RWX_DEFAULT=y
|
||||
CONFIG_ARCH_SELECT_MEMORY_MODEL=y
|
||||
CONFIG_ARCH_SPARSEMEM_ENABLE=y
|
||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
|
||||
CONFIG_ARM=y
|
||||
CONFIG_ARM_GIC=y
|
||||
CONFIG_ARM_HAS_GROUP_RELOCS=y
|
||||
CONFIG_ARM_L1_CACHE_SHIFT=6
|
||||
CONFIG_ARM_L1_CACHE_SHIFT_6=y
|
||||
# CONFIG_ARM_PATCH_IDIV is not set
|
||||
CONFIG_ARM_PATCH_PHYS_VIRT=y
|
||||
CONFIG_ARM_THUMB=y
|
||||
CONFIG_ARM_UNWIND=y
|
||||
CONFIG_ARM_VIRT_EXT=y
|
||||
# CONFIG_AT91SAM9X_WATCHDOG is not set
|
||||
# CONFIG_AT91_ADC is not set
|
||||
CONFIG_AT91_SAMA5D2_ADC=y
|
||||
CONFIG_AT91_SOC_ID=y
|
||||
# CONFIG_AT91_SOC_SFR is not set
|
||||
CONFIG_ATMEL_CLOCKSOURCE_TCB=y
|
||||
# CONFIG_ATMEL_EBI is not set
|
||||
CONFIG_ATMEL_SDRAMC=y
|
||||
CONFIG_ATMEL_TCB_CLKSRC=y
|
||||
# CONFIG_AT_HDMAC is not set
|
||||
CONFIG_AT_XDMAC=y
|
||||
CONFIG_AUTO_ZRELADDR=y
|
||||
CONFIG_BINFMT_FLAT_ARGVP_ENVP_ON_STACK=y
|
||||
CONFIG_BLK_DEV_LOOP=y
|
||||
CONFIG_BLK_DEV_RAM=y
|
||||
CONFIG_BLK_DEV_RAM_COUNT=1
|
||||
CONFIG_BLK_DEV_RAM_SIZE=8192
|
||||
CONFIG_BLK_DEV_SD=y
|
||||
# CONFIG_CACHE_L2X0 is not set
|
||||
CONFIG_CAN=y
|
||||
CONFIG_CC_HAVE_STACKPROTECTOR_TLS=y
|
||||
CONFIG_CC_IMPLICIT_FALLTHROUGH="-Wimplicit-fallthrough=5"
|
||||
CONFIG_CC_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_CLONE_BACKWARDS=y
|
||||
CONFIG_CMA=y
|
||||
CONFIG_CMA_ALIGNMENT=9
|
||||
CONFIG_CMA_AREAS=7
|
||||
# CONFIG_CMA_DEBUG is not set
|
||||
# CONFIG_CMA_DEBUGFS is not set
|
||||
CONFIG_CMA_SIZE_MBYTES=256
|
||||
# CONFIG_CMA_SIZE_SEL_MAX is not set
|
||||
CONFIG_CMA_SIZE_SEL_MBYTES=y
|
||||
# CONFIG_CMA_SIZE_SEL_MIN is not set
|
||||
# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set
|
||||
# CONFIG_CMA_SYSFS is not set
|
||||
CONFIG_CMDLINE="console=ttyS0,115200 earlyprintk nocache ignore_loglevel"
|
||||
CONFIG_CMDLINE_FROM_BOOTLOADER=y
|
||||
CONFIG_COMMON_CLK=y
|
||||
CONFIG_COMMON_CLK_AT91=y
|
||||
# CONFIG_COMPACTION is not set
|
||||
CONFIG_COMPAT_32BIT_TIME=y
|
||||
CONFIG_CONFIGFS_FS=y
|
||||
CONFIG_CONSOLE_TRANSLATIONS=y
|
||||
CONFIG_CONTIG_ALLOC=y
|
||||
CONFIG_CPUFREQ_DT=y
|
||||
CONFIG_CPUFREQ_DT_PLATDEV=y
|
||||
CONFIG_CPU_32v6K=y
|
||||
CONFIG_CPU_32v7=y
|
||||
CONFIG_CPU_ABRT_EV7=y
|
||||
CONFIG_CPU_CACHE_V7=y
|
||||
CONFIG_CPU_CACHE_VIPT=y
|
||||
CONFIG_CPU_COPY_V6=y
|
||||
CONFIG_CPU_CP15=y
|
||||
CONFIG_CPU_CP15_MMU=y
|
||||
CONFIG_CPU_FREQ=y
|
||||
CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
|
||||
# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
|
||||
CONFIG_CPU_FREQ_GOV_ATTR_SET=y
|
||||
CONFIG_CPU_FREQ_GOV_COMMON=y
|
||||
CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
|
||||
CONFIG_CPU_FREQ_GOV_ONDEMAND=y
|
||||
CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
|
||||
CONFIG_CPU_FREQ_GOV_POWERSAVE=y
|
||||
CONFIG_CPU_FREQ_GOV_USERSPACE=y
|
||||
CONFIG_CPU_FREQ_STAT=y
|
||||
CONFIG_CPU_HAS_ASID=y
|
||||
CONFIG_CPU_LITTLE_ENDIAN=y
|
||||
CONFIG_CPU_PABRT_V7=y
|
||||
CONFIG_CPU_SPECTRE=y
|
||||
# CONFIG_CPU_SW_DOMAIN_PAN is not set
|
||||
CONFIG_CPU_THUMB_CAPABLE=y
|
||||
CONFIG_CPU_TLB_V7=y
|
||||
CONFIG_CPU_V7=y
|
||||
CONFIG_CRC16=y
|
||||
CONFIG_CRC_CCITT=y
|
||||
CONFIG_CRC_ITU_T=y
|
||||
CONFIG_CRYPTO_CMAC=y
|
||||
CONFIG_CRYPTO_CRC32C=y
|
||||
CONFIG_CRYPTO_DEFLATE=y
|
||||
CONFIG_CRYPTO_DRBG=y
|
||||
CONFIG_CRYPTO_DRBG_HMAC=y
|
||||
CONFIG_CRYPTO_DRBG_MENU=y
|
||||
CONFIG_CRYPTO_ECB=y
|
||||
CONFIG_CRYPTO_ECC=y
|
||||
CONFIG_CRYPTO_ECDH=y
|
||||
CONFIG_CRYPTO_HMAC=y
|
||||
CONFIG_CRYPTO_JITTERENTROPY=y
|
||||
CONFIG_CRYPTO_LIB_BLAKE2S_GENERIC=y
|
||||
CONFIG_CRYPTO_LIB_SHA1=y
|
||||
CONFIG_CRYPTO_LIB_SHA256=y
|
||||
CONFIG_CRYPTO_LIB_UTILS=y
|
||||
CONFIG_CRYPTO_LZO=y
|
||||
CONFIG_CRYPTO_RNG=y
|
||||
CONFIG_CRYPTO_RNG2=y
|
||||
CONFIG_CRYPTO_RNG_DEFAULT=y
|
||||
CONFIG_CRYPTO_SHA256=y
|
||||
CONFIG_CRYPTO_SHA512=y
|
||||
CONFIG_CURRENT_POINTER_IN_TPIDRURO=y
|
||||
CONFIG_DCACHE_WORD_ACCESS=y
|
||||
CONFIG_DEBUG_AT91_SAMA7G5_FLEXCOM3=y
|
||||
CONFIG_DEBUG_AT91_UART=y
|
||||
CONFIG_DEBUG_INFO=y
|
||||
CONFIG_DEBUG_LL=y
|
||||
CONFIG_DEBUG_LL_INCLUDE="debug/at91.S"
|
||||
CONFIG_DEBUG_UART_PHYS=0xe1824200
|
||||
CONFIG_DEBUG_UART_VIRT=0xe0824200
|
||||
CONFIG_DEBUG_USER=y
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_DEVTMPFS_MOUNT=y
|
||||
CONFIG_DMADEVICES=y
|
||||
CONFIG_DMATEST=y
|
||||
CONFIG_DMA_CMA=y
|
||||
CONFIG_DMA_ENGINE=y
|
||||
CONFIG_DMA_ENGINE_RAID=y
|
||||
CONFIG_DMA_OF=y
|
||||
CONFIG_DMA_OPS=y
|
||||
CONFIG_DTC=y
|
||||
CONFIG_DUMMY_CONSOLE=y
|
||||
CONFIG_EARLY_PRINTK=y
|
||||
CONFIG_EDAC_ATOMIC_SCRUB=y
|
||||
CONFIG_EDAC_SUPPORT=y
|
||||
CONFIG_EEPROM_AT24=y
|
||||
# CONFIG_EFI_PARTITION is not set
|
||||
CONFIG_EXCLUSIVE_SYSTEM_RAM=y
|
||||
CONFIG_EXT4_FS=y
|
||||
CONFIG_FANOTIFY=y
|
||||
CONFIG_FAT_FS=y
|
||||
CONFIG_FIXED_PHY=y
|
||||
CONFIG_FIX_EARLYCON_MEM=y
|
||||
CONFIG_FS_IOMAP=y
|
||||
CONFIG_FS_MBCACHE=y
|
||||
CONFIG_FWNODE_MDIO=y
|
||||
CONFIG_FW_LOADER_PAGED_BUF=y
|
||||
CONFIG_FW_LOADER_SYSFS=y
|
||||
CONFIG_GCC10_NO_ARRAY_BOUNDS=y
|
||||
CONFIG_GCC_ASM_GOTO_OUTPUT_WORKAROUND=y
|
||||
CONFIG_GENERIC_ALLOCATOR=y
|
||||
CONFIG_GENERIC_BUG=y
|
||||
CONFIG_GENERIC_CLOCKEVENTS=y
|
||||
CONFIG_GENERIC_CPU_AUTOPROBE=y
|
||||
CONFIG_GENERIC_CPU_VULNERABILITIES=y
|
||||
CONFIG_GENERIC_EARLY_IOREMAP=y
|
||||
CONFIG_GENERIC_GETTIMEOFDAY=y
|
||||
CONFIG_GENERIC_IDLE_POLL_SETUP=y
|
||||
CONFIG_GENERIC_IRQ_MULTI_HANDLER=y
|
||||
CONFIG_GENERIC_IRQ_SHOW=y
|
||||
CONFIG_GENERIC_IRQ_SHOW_LEVEL=y
|
||||
CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED=y
|
||||
CONFIG_GENERIC_PCI_IOMAP=y
|
||||
CONFIG_GENERIC_PINCONF=y
|
||||
CONFIG_GENERIC_PINCTRL_GROUPS=y
|
||||
CONFIG_GENERIC_PINMUX_FUNCTIONS=y
|
||||
CONFIG_GENERIC_SCHED_CLOCK=y
|
||||
CONFIG_GENERIC_SMP_IDLE_THREAD=y
|
||||
CONFIG_GENERIC_STRNCPY_FROM_USER=y
|
||||
CONFIG_GENERIC_STRNLEN_USER=y
|
||||
CONFIG_GENERIC_TIME_VSYSCALL=y
|
||||
CONFIG_GENERIC_VDSO_32=y
|
||||
CONFIG_GLOB=y
|
||||
CONFIG_GPIOLIB_IRQCHIP=y
|
||||
CONFIG_GPIO_CDEV=y
|
||||
CONFIG_GRACE_PERIOD=y
|
||||
# CONFIG_HARDEN_BRANCH_HISTORY is not set
|
||||
# CONFIG_HARDEN_BRANCH_PREDICTOR is not set
|
||||
CONFIG_HARDIRQS_SW_RESEND=y
|
||||
CONFIG_HAS_DMA=y
|
||||
CONFIG_HAS_IOMEM=y
|
||||
CONFIG_HAS_IOPORT_MAP=y
|
||||
CONFIG_HAVE_SMP=y
|
||||
CONFIG_HW_CONSOLE=y
|
||||
CONFIG_HW_RANDOM=y
|
||||
CONFIG_HZ_FIXED=0
|
||||
CONFIG_I2C=y
|
||||
CONFIG_I2C_AT91=y
|
||||
# CONFIG_I2C_AT91_SLAVE_EXPERIMENTAL is not set
|
||||
CONFIG_I2C_BOARDINFO=y
|
||||
CONFIG_I2C_CHARDEV=y
|
||||
CONFIG_IIO=y
|
||||
CONFIG_IIO_BUFFER=y
|
||||
CONFIG_IIO_CONFIGFS=y
|
||||
# CONFIG_IIO_HRTIMER_TRIGGER is not set
|
||||
CONFIG_IIO_KFIFO_BUF=y
|
||||
CONFIG_IIO_SW_TRIGGER=y
|
||||
# CONFIG_IIO_TIGHTLOOP_TRIGGER is not set
|
||||
CONFIG_IIO_TRIGGER=y
|
||||
CONFIG_IIO_TRIGGERED_BUFFER=y
|
||||
CONFIG_INITRAMFS_SOURCE=""
|
||||
CONFIG_INPUT=y
|
||||
CONFIG_IP_PNP=y
|
||||
# CONFIG_IP_PNP_BOOTP is not set
|
||||
CONFIG_IP_PNP_DHCP=y
|
||||
# CONFIG_IP_PNP_RARP is not set
|
||||
CONFIG_IRQCHIP=y
|
||||
CONFIG_IRQSTACKS=y
|
||||
CONFIG_IRQ_DOMAIN=y
|
||||
CONFIG_IRQ_DOMAIN_HIERARCHY=y
|
||||
CONFIG_IRQ_FORCED_THREADING=y
|
||||
CONFIG_IRQ_WORK=y
|
||||
CONFIG_JBD2=y
|
||||
CONFIG_LEDS_GPIO=y
|
||||
CONFIG_LIBFDT=y
|
||||
CONFIG_LOCKD=y
|
||||
CONFIG_LOCK_DEBUGGING_SUPPORT=y
|
||||
CONFIG_LOG_BUF_SHIFT=16
|
||||
CONFIG_LSM="N"
|
||||
CONFIG_LZO_COMPRESS=y
|
||||
CONFIG_LZO_DECOMPRESS=y
|
||||
CONFIG_MACB=y
|
||||
CONFIG_MACB_USE_HWSTAMP=y
|
||||
# CONFIG_MCHP_EIC is not set
|
||||
CONFIG_MDIO_BUS=y
|
||||
CONFIG_MDIO_DEVICE=y
|
||||
CONFIG_MDIO_DEVRES=y
|
||||
CONFIG_MEDIA_CAMERA_SUPPORT=y
|
||||
CONFIG_MEDIA_CONTROLLER=y
|
||||
CONFIG_MEDIA_PLATFORM_SUPPORT=y
|
||||
CONFIG_MEDIA_SUPPORT=y
|
||||
CONFIG_MEDIA_SUPPORT_FILTER=y
|
||||
CONFIG_MEMFD_CREATE=y
|
||||
CONFIG_MEMORY=y
|
||||
CONFIG_MEMORY_ISOLATION=y
|
||||
CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7
|
||||
CONFIG_MFD_AT91_USART=y
|
||||
CONFIG_MFD_ATMEL_FLEXCOM=y
|
||||
CONFIG_MFD_CORE=y
|
||||
CONFIG_MFD_SYSCON=y
|
||||
CONFIG_MICREL_PHY=y
|
||||
CONFIG_MICROCHIP_CLOCKSOURCE_PIT64B=y
|
||||
CONFIG_MICROCHIP_PIT64B=y
|
||||
CONFIG_MIGHT_HAVE_CACHE_L2X0=y
|
||||
CONFIG_MIGRATION=y
|
||||
CONFIG_MMC=y
|
||||
# CONFIG_MMC_ATMELMCI is not set
|
||||
CONFIG_MMC_BLOCK=y
|
||||
CONFIG_MMC_SDHCI=y
|
||||
CONFIG_MMC_SDHCI_OF_AT91=y
|
||||
CONFIG_MMC_SDHCI_PLTFM=y
|
||||
CONFIG_MODULES_USE_ELF_REL=y
|
||||
CONFIG_MODULE_FORCE_LOAD=y
|
||||
CONFIG_MODULE_FORCE_UNLOAD=y
|
||||
CONFIG_MTD_CMDLINE_PARTS=y
|
||||
CONFIG_NEED_DMA_MAP_STATE=y
|
||||
CONFIG_NEED_PER_CPU_KM=y
|
||||
CONFIG_NEON=y
|
||||
CONFIG_NET_PTP_CLASSIFY=y
|
||||
CONFIG_NET_SELFTESTS=y
|
||||
CONFIG_NET_SWITCHDEV=y
|
||||
CONFIG_NFS_FS=y
|
||||
CONFIG_NLS=y
|
||||
CONFIG_NLS_CODEPAGE_437=y
|
||||
CONFIG_NLS_CODEPAGE_850=y
|
||||
CONFIG_NLS_ISO8859_1=y
|
||||
CONFIG_NLS_UTF8=y
|
||||
CONFIG_NO_HZ_COMMON=y
|
||||
CONFIG_NO_HZ_IDLE=y
|
||||
CONFIG_NVMEM=y
|
||||
CONFIG_NVMEM_LAYOUTS=y
|
||||
# CONFIG_NVMEM_MICROCHIP_OTPC is not set
|
||||
CONFIG_NVMEM_SYSFS=y
|
||||
CONFIG_OF=y
|
||||
CONFIG_OF_ADDRESS=y
|
||||
CONFIG_OF_EARLY_FLATTREE=y
|
||||
CONFIG_OF_FLATTREE=y
|
||||
CONFIG_OF_GPIO=y
|
||||
CONFIG_OF_IRQ=y
|
||||
CONFIG_OF_KOBJ=y
|
||||
CONFIG_OF_MDIO=y
|
||||
CONFIG_OLD_SIGACTION=y
|
||||
CONFIG_OLD_SIGSUSPEND3=y
|
||||
CONFIG_PAGE_OFFSET=0xC0000000
|
||||
CONFIG_PAGE_POOL=y
|
||||
CONFIG_PAGE_SIZE_LESS_THAN_256KB=y
|
||||
CONFIG_PAGE_SIZE_LESS_THAN_64KB=y
|
||||
CONFIG_PAHOLE_HAS_LANG_EXCLUDE=y
|
||||
CONFIG_PCCARD=y
|
||||
CONFIG_PERF_USE_VMALLOC=y
|
||||
CONFIG_PGTABLE_LEVELS=2
|
||||
CONFIG_PHYLIB=y
|
||||
CONFIG_PHYLIB_LEDS=y
|
||||
CONFIG_PHYLINK=y
|
||||
CONFIG_PINCTRL=y
|
||||
CONFIG_PINCTRL_AT91=y
|
||||
CONFIG_PINCTRL_AT91PIO4=y
|
||||
CONFIG_PM_OPP=y
|
||||
CONFIG_POWER_RESET=y
|
||||
# CONFIG_POWER_RESET_AT91_POWEROFF is not set
|
||||
CONFIG_POWER_RESET_AT91_RESET=y
|
||||
CONFIG_POWER_RESET_AT91_SAMA5D2_SHDWC=y
|
||||
CONFIG_PPS=y
|
||||
CONFIG_PREEMPT_NONE_BUILD=y
|
||||
# CONFIG_PREVENT_FIRMWARE_BUILD is not set
|
||||
CONFIG_PRINTK_TIME=y
|
||||
CONFIG_PTP_1588_CLOCK=y
|
||||
CONFIG_PTP_1588_CLOCK_OPTIONAL=y
|
||||
CONFIG_PWM=y
|
||||
CONFIG_PWM_ATMEL=y
|
||||
CONFIG_PWM_SYSFS=y
|
||||
CONFIG_RANDSTRUCT_NONE=y
|
||||
CONFIG_RATIONAL=y
|
||||
CONFIG_REGMAP=y
|
||||
CONFIG_REGMAP_I2C=y
|
||||
CONFIG_REGMAP_MMIO=y
|
||||
CONFIG_REGMAP_SPI=y
|
||||
CONFIG_REGULATOR=y
|
||||
CONFIG_REGULATOR_FIXED_VOLTAGE=y
|
||||
CONFIG_REGULATOR_MCP16502=y
|
||||
CONFIG_ROOT_NFS=y
|
||||
CONFIG_RTC_CLASS=y
|
||||
CONFIG_RTC_DRV_AT91RM9200=y
|
||||
CONFIG_RTC_DRV_AT91SAM9=y
|
||||
CONFIG_RTC_I2C_AND_SPI=y
|
||||
CONFIG_RTC_MC146818_LIB=y
|
||||
# CONFIG_RUNTIME_TESTING_MENU is not set
|
||||
CONFIG_SAMA5D4_WATCHDOG=y
|
||||
CONFIG_SCSI=y
|
||||
CONFIG_SCSI_COMMON=y
|
||||
# CONFIG_SERIAL_8250 is not set
|
||||
CONFIG_SERIAL_ATMEL=y
|
||||
CONFIG_SERIAL_ATMEL_CONSOLE=y
|
||||
CONFIG_SERIAL_ATMEL_PDC=y
|
||||
# CONFIG_SERIAL_ATMEL_TTYAT is not set
|
||||
CONFIG_SERIAL_MCTRL_GPIO=y
|
||||
CONFIG_SGL_ALLOC=y
|
||||
CONFIG_SG_POOL=y
|
||||
CONFIG_SND=y
|
||||
CONFIG_SND_ATMEL_SOC=y
|
||||
# CONFIG_SND_ATMEL_SOC_CLASSD is not set
|
||||
# CONFIG_SND_ATMEL_SOC_I2S is not set
|
||||
# CONFIG_SND_ATMEL_SOC_PDMIC is not set
|
||||
# CONFIG_SND_COMPRESS_OFFLOAD is not set
|
||||
CONFIG_SND_DMAENGINE_PCM=y
|
||||
CONFIG_SND_JACK=y
|
||||
CONFIG_SND_JACK_INPUT_DEV=y
|
||||
CONFIG_SND_MCHP_SOC_I2S_MCC=y
|
||||
# CONFIG_SND_MCHP_SOC_PDMC is not set
|
||||
CONFIG_SND_MCHP_SOC_SPDIFRX=y
|
||||
CONFIG_SND_MCHP_SOC_SPDIFTX=y
|
||||
CONFIG_SND_PCM=y
|
||||
CONFIG_SND_SIMPLE_CARD=y
|
||||
CONFIG_SND_SIMPLE_CARD_UTILS=y
|
||||
CONFIG_SND_SOC=y
|
||||
CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y
|
||||
CONFIG_SND_SOC_I2C_AND_SPI=y
|
||||
# CONFIG_SND_SOC_MIKROE_PROTO is not set
|
||||
CONFIG_SND_SOC_PCM5102A=y
|
||||
CONFIG_SND_SOC_SPDIF=y
|
||||
CONFIG_SOC_BUS=y
|
||||
# CONFIG_SOC_LAN966 is not set
|
||||
# CONFIG_SOC_SAMA5D2 is not set
|
||||
# CONFIG_SOC_SAMA5D3 is not set
|
||||
# CONFIG_SOC_SAMA5D4 is not set
|
||||
CONFIG_SOC_SAMA7=y
|
||||
CONFIG_SOC_SAMA7G5=y
|
||||
CONFIG_SOC_SAM_V7=y
|
||||
CONFIG_SOFTIRQ_ON_OWN_STACK=y
|
||||
CONFIG_SOUND=y
|
||||
CONFIG_SOUND_OSS_CORE=y
|
||||
CONFIG_SPARSE_IRQ=y
|
||||
CONFIG_SPI=y
|
||||
# CONFIG_SPI_AT91_USART is not set
|
||||
CONFIG_SPI_ATMEL=y
|
||||
# CONFIG_SPI_ATMEL_QUADSPI is not set
|
||||
CONFIG_SPI_MASTER=y
|
||||
CONFIG_SPI_MEM=y
|
||||
CONFIG_SRCU=y
|
||||
CONFIG_STACKTRACE=y
|
||||
# CONFIG_STANDALONE is not set
|
||||
CONFIG_SUNRPC=y
|
||||
# CONFIG_SWAP is not set
|
||||
CONFIG_SWPHY=y
|
||||
# CONFIG_SWP_EMULATE is not set
|
||||
CONFIG_SYSFS_DEPRECATED=y
|
||||
CONFIG_SYSFS_DEPRECATED_V2=y
|
||||
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
|
||||
CONFIG_THREAD_INFO_IN_TASK=y
|
||||
CONFIG_TICK_CPU_ACCOUNTING=y
|
||||
CONFIG_TIMER_OF=y
|
||||
CONFIG_TIMER_PROBE=y
|
||||
CONFIG_TINY_SRCU=y
|
||||
CONFIG_UACCESS_WITH_MEMCPY=y
|
||||
CONFIG_UNCOMPRESS_INCLUDE="debug/uncompress.h"
|
||||
CONFIG_UNWINDER_ARM=y
|
||||
CONFIG_USE_OF=y
|
||||
CONFIG_VFAT_FS=y
|
||||
CONFIG_VFP=y
|
||||
CONFIG_VFPv3=y
|
||||
# CONFIG_VIDEO_ATMEL_XISC is not set
|
||||
CONFIG_VIDEO_DEV=y
|
||||
# CONFIG_VIDEO_MICROCHIP_CSI2DC is not set
|
||||
CONFIG_VIDEO_V4L2_I2C=y
|
||||
CONFIG_VT=y
|
||||
CONFIG_VT_CONSOLE=y
|
||||
# CONFIG_VT_HW_CONSOLE_BINDING is not set
|
||||
CONFIG_WATCHDOG_CORE=y
|
||||
CONFIG_ZBOOT_ROM_BSS=0x0
|
||||
CONFIG_ZBOOT_ROM_TEXT=0x0
|
||||
CONFIG_ZLIB_DEFLATE=y
|
||||
CONFIG_ZLIB_INFLATE=y
|
||||
@@ -1,352 +0,0 @@
|
||||
From 8c20e2eb5f2a0175b774134685e4d7bd93e85ff8 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Wed, 21 Dec 2022 21:18:59 -0700
|
||||
Subject: [PATCH 01/19] UPSTREAM: mm: multi-gen LRU: rename lru_gen_struct to
|
||||
lru_gen_folio
|
||||
|
||||
Patch series "mm: multi-gen LRU: memcg LRU", v3.
|
||||
|
||||
Overview
|
||||
========
|
||||
|
||||
An memcg LRU is a per-node LRU of memcgs. It is also an LRU of LRUs,
|
||||
since each node and memcg combination has an LRU of folios (see
|
||||
mem_cgroup_lruvec()).
|
||||
|
||||
Its goal is to improve the scalability of global reclaim, which is
|
||||
critical to system-wide memory overcommit in data centers. Note that
|
||||
memcg reclaim is currently out of scope.
|
||||
|
||||
Its memory bloat is a pointer to each lruvec and negligible to each
|
||||
pglist_data. In terms of traversing memcgs during global reclaim, it
|
||||
improves the best-case complexity from O(n) to O(1) and does not affect
|
||||
the worst-case complexity O(n). Therefore, on average, it has a sublinear
|
||||
complexity in contrast to the current linear complexity.
|
||||
|
||||
The basic structure of an memcg LRU can be understood by an analogy to
|
||||
the active/inactive LRU (of folios):
|
||||
1. It has the young and the old (generations), i.e., the counterparts
|
||||
to the active and the inactive;
|
||||
2. The increment of max_seq triggers promotion, i.e., the counterpart
|
||||
to activation;
|
||||
3. Other events trigger similar operations, e.g., offlining an memcg
|
||||
triggers demotion, i.e., the counterpart to deactivation.
|
||||
|
||||
In terms of global reclaim, it has two distinct features:
|
||||
1. Sharding, which allows each thread to start at a random memcg (in
|
||||
the old generation) and improves parallelism;
|
||||
2. Eventual fairness, which allows direct reclaim to bail out at will
|
||||
and reduces latency without affecting fairness over some time.
|
||||
|
||||
The commit message in patch 6 details the workflow:
|
||||
https://lore.kernel.org/r/20221222041905.2431096-7-yuzhao@google.com/
|
||||
|
||||
The following is a simple test to quickly verify its effectiveness.
|
||||
|
||||
Test design:
|
||||
1. Create multiple memcgs.
|
||||
2. Each memcg contains a job (fio).
|
||||
3. All jobs access the same amount of memory randomly.
|
||||
4. The system does not experience global memory pressure.
|
||||
5. Periodically write to the root memory.reclaim.
|
||||
|
||||
Desired outcome:
|
||||
1. All memcgs have similar pgsteal counts, i.e., stddev(pgsteal)
|
||||
over mean(pgsteal) is close to 0%.
|
||||
2. The total pgsteal is close to the total requested through
|
||||
memory.reclaim, i.e., sum(pgsteal) over sum(requested) is close
|
||||
to 100%.
|
||||
|
||||
Actual outcome [1]:
|
||||
MGLRU off MGLRU on
|
||||
stddev(pgsteal) / mean(pgsteal) 75% 20%
|
||||
sum(pgsteal) / sum(requested) 425% 95%
|
||||
|
||||
####################################################################
|
||||
MEMCGS=128
|
||||
|
||||
for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
|
||||
mkdir /sys/fs/cgroup/memcg$memcg
|
||||
done
|
||||
|
||||
start() {
|
||||
echo $BASHPID > /sys/fs/cgroup/memcg$memcg/cgroup.procs
|
||||
|
||||
fio -name=memcg$memcg --numjobs=1 --ioengine=mmap \
|
||||
--filename=/dev/zero --size=1920M --rw=randrw \
|
||||
--rate=64m,64m --random_distribution=random \
|
||||
--fadvise_hint=0 --time_based --runtime=10h \
|
||||
--group_reporting --minimal
|
||||
}
|
||||
|
||||
for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
|
||||
start &
|
||||
done
|
||||
|
||||
sleep 600
|
||||
|
||||
for ((i = 0; i < 600; i++)); do
|
||||
echo 256m >/sys/fs/cgroup/memory.reclaim
|
||||
sleep 6
|
||||
done
|
||||
|
||||
for ((memcg = 0; memcg < $MEMCGS; memcg++)); do
|
||||
grep "pgsteal " /sys/fs/cgroup/memcg$memcg/memory.stat
|
||||
done
|
||||
####################################################################
|
||||
|
||||
[1]: This was obtained from running the above script (touches less
|
||||
than 256GB memory) on an EPYC 7B13 with 512GB DRAM for over an
|
||||
hour.
|
||||
|
||||
This patch (of 8):
|
||||
|
||||
The new name lru_gen_folio will be more distinct from the coming
|
||||
lru_gen_memcg.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-1-yuzhao@google.com
|
||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-2-yuzhao@google.com
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Jonathan Corbet <corbet@lwn.net>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Cc: Michal Hocko <mhocko@kernel.org>
|
||||
Cc: Mike Rapoport <rppt@kernel.org>
|
||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
|
||||
Cc: Suren Baghdasaryan <surenb@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Bug: 274865848
|
||||
(cherry picked from commit 391655fe08d1f942359a11148aa9aaf3f99d6d6f)
|
||||
Change-Id: I7df67e0e2435ba28f10eaa57d28d98b61a9210a6
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
include/linux/mm_inline.h | 4 ++--
|
||||
include/linux/mmzone.h | 6 +++---
|
||||
mm/vmscan.c | 34 +++++++++++++++++-----------------
|
||||
mm/workingset.c | 4 ++--
|
||||
4 files changed, 24 insertions(+), 24 deletions(-)
|
||||
|
||||
--- a/include/linux/mm_inline.h
|
||||
+++ b/include/linux/mm_inline.h
|
||||
@@ -178,7 +178,7 @@ static inline void lru_gen_update_size(s
|
||||
int zone = folio_zonenum(folio);
|
||||
int delta = folio_nr_pages(folio);
|
||||
enum lru_list lru = type * LRU_INACTIVE_FILE;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
VM_WARN_ON_ONCE(old_gen != -1 && old_gen >= MAX_NR_GENS);
|
||||
VM_WARN_ON_ONCE(new_gen != -1 && new_gen >= MAX_NR_GENS);
|
||||
@@ -224,7 +224,7 @@ static inline bool lru_gen_add_folio(str
|
||||
int gen = folio_lru_gen(folio);
|
||||
int type = folio_is_file_lru(folio);
|
||||
int zone = folio_zonenum(folio);
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
VM_WARN_ON_ONCE_FOLIO(gen != -1, folio);
|
||||
|
||||
--- a/include/linux/mmzone.h
|
||||
+++ b/include/linux/mmzone.h
|
||||
@@ -404,7 +404,7 @@ enum {
|
||||
* The number of pages in each generation is eventually consistent and therefore
|
||||
* can be transiently negative when reset_batch_size() is pending.
|
||||
*/
|
||||
-struct lru_gen_struct {
|
||||
+struct lru_gen_folio {
|
||||
/* the aging increments the youngest generation number */
|
||||
unsigned long max_seq;
|
||||
/* the eviction increments the oldest generation numbers */
|
||||
@@ -461,7 +461,7 @@ struct lru_gen_mm_state {
|
||||
struct lru_gen_mm_walk {
|
||||
/* the lruvec under reclaim */
|
||||
struct lruvec *lruvec;
|
||||
- /* unstable max_seq from lru_gen_struct */
|
||||
+ /* unstable max_seq from lru_gen_folio */
|
||||
unsigned long max_seq;
|
||||
/* the next address within an mm to scan */
|
||||
unsigned long next_addr;
|
||||
@@ -524,7 +524,7 @@ struct lruvec {
|
||||
unsigned long flags;
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
/* evictable pages divided into generations */
|
||||
- struct lru_gen_struct lrugen;
|
||||
+ struct lru_gen_folio lrugen;
|
||||
/* to concurrently iterate lru_gen_mm_list */
|
||||
struct lru_gen_mm_state mm_state;
|
||||
#endif
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -3190,7 +3190,7 @@ static int get_nr_gens(struct lruvec *lr
|
||||
|
||||
static bool __maybe_unused seq_is_valid(struct lruvec *lruvec)
|
||||
{
|
||||
- /* see the comment on lru_gen_struct */
|
||||
+ /* see the comment on lru_gen_folio */
|
||||
return get_nr_gens(lruvec, LRU_GEN_FILE) >= MIN_NR_GENS &&
|
||||
get_nr_gens(lruvec, LRU_GEN_FILE) <= get_nr_gens(lruvec, LRU_GEN_ANON) &&
|
||||
get_nr_gens(lruvec, LRU_GEN_ANON) <= MAX_NR_GENS;
|
||||
@@ -3596,7 +3596,7 @@ struct ctrl_pos {
|
||||
static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain,
|
||||
struct ctrl_pos *pos)
|
||||
{
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
int hist = lru_hist_from_seq(lrugen->min_seq[type]);
|
||||
|
||||
pos->refaulted = lrugen->avg_refaulted[type][tier] +
|
||||
@@ -3611,7 +3611,7 @@ static void read_ctrl_pos(struct lruvec
|
||||
static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover)
|
||||
{
|
||||
int hist, tier;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
bool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS > 1;
|
||||
unsigned long seq = carryover ? lrugen->min_seq[type] : lrugen->max_seq + 1;
|
||||
|
||||
@@ -3688,7 +3688,7 @@ static int folio_update_gen(struct folio
|
||||
static int folio_inc_gen(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
|
||||
{
|
||||
int type = folio_is_file_lru(folio);
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
|
||||
unsigned long new_flags, old_flags = READ_ONCE(folio->flags);
|
||||
|
||||
@@ -3733,7 +3733,7 @@ static void update_batch_size(struct lru
|
||||
static void reset_batch_size(struct lruvec *lruvec, struct lru_gen_mm_walk *walk)
|
||||
{
|
||||
int gen, type, zone;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
walk->batched = 0;
|
||||
|
||||
@@ -4250,7 +4250,7 @@ static bool inc_min_seq(struct lruvec *l
|
||||
{
|
||||
int zone;
|
||||
int remaining = MAX_LRU_BATCH;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]);
|
||||
|
||||
if (type == LRU_GEN_ANON && !can_swap)
|
||||
@@ -4286,7 +4286,7 @@ static bool try_to_inc_min_seq(struct lr
|
||||
{
|
||||
int gen, type, zone;
|
||||
bool success = false;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
DEFINE_MIN_SEQ(lruvec);
|
||||
|
||||
VM_WARN_ON_ONCE(!seq_is_valid(lruvec));
|
||||
@@ -4307,7 +4307,7 @@ next:
|
||||
;
|
||||
}
|
||||
|
||||
- /* see the comment on lru_gen_struct */
|
||||
+ /* see the comment on lru_gen_folio */
|
||||
if (can_swap) {
|
||||
min_seq[LRU_GEN_ANON] = min(min_seq[LRU_GEN_ANON], min_seq[LRU_GEN_FILE]);
|
||||
min_seq[LRU_GEN_FILE] = max(min_seq[LRU_GEN_ANON], lrugen->min_seq[LRU_GEN_FILE]);
|
||||
@@ -4329,7 +4329,7 @@ static void inc_max_seq(struct lruvec *l
|
||||
{
|
||||
int prev, next;
|
||||
int type, zone;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
restart:
|
||||
spin_lock_irq(&lruvec->lru_lock);
|
||||
@@ -4389,7 +4389,7 @@ static bool try_to_inc_max_seq(struct lr
|
||||
bool success;
|
||||
struct lru_gen_mm_walk *walk;
|
||||
struct mm_struct *mm = NULL;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
VM_WARN_ON_ONCE(max_seq > READ_ONCE(lrugen->max_seq));
|
||||
|
||||
@@ -4454,7 +4454,7 @@ static bool should_run_aging(struct lruv
|
||||
unsigned long old = 0;
|
||||
unsigned long young = 0;
|
||||
unsigned long total = 0;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
|
||||
for (type = !can_swap; type < ANON_AND_FILE; type++) {
|
||||
@@ -4740,7 +4740,7 @@ static bool sort_folio(struct lruvec *lr
|
||||
int delta = folio_nr_pages(folio);
|
||||
int refs = folio_lru_refs(folio);
|
||||
int tier = lru_tier_from_refs(refs);
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
VM_WARN_ON_ONCE_FOLIO(gen >= MAX_NR_GENS, folio);
|
||||
|
||||
@@ -4848,7 +4848,7 @@ static int scan_folios(struct lruvec *lr
|
||||
int scanned = 0;
|
||||
int isolated = 0;
|
||||
int remaining = MAX_LRU_BATCH;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
|
||||
VM_WARN_ON_ONCE(!list_empty(list));
|
||||
@@ -5248,7 +5248,7 @@ done:
|
||||
|
||||
static bool __maybe_unused state_is_valid(struct lruvec *lruvec)
|
||||
{
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
if (lrugen->enabled) {
|
||||
enum lru_list lru;
|
||||
@@ -5530,7 +5530,7 @@ static void lru_gen_seq_show_full(struct
|
||||
int i;
|
||||
int type, tier;
|
||||
int hist = lru_hist_from_seq(seq);
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
for (tier = 0; tier < MAX_NR_TIERS; tier++) {
|
||||
seq_printf(m, " %10d", tier);
|
||||
@@ -5580,7 +5580,7 @@ static int lru_gen_seq_show(struct seq_f
|
||||
unsigned long seq;
|
||||
bool full = !debugfs_real_fops(m->file)->write;
|
||||
struct lruvec *lruvec = v;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
int nid = lruvec_pgdat(lruvec)->node_id;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
DEFINE_MAX_SEQ(lruvec);
|
||||
@@ -5834,7 +5834,7 @@ void lru_gen_init_lruvec(struct lruvec *
|
||||
{
|
||||
int i;
|
||||
int gen, type, zone;
|
||||
- struct lru_gen_struct *lrugen = &lruvec->lrugen;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
|
||||
lrugen->max_seq = MIN_NR_GENS + 1;
|
||||
lrugen->enabled = lru_gen_enabled();
|
||||
--- a/mm/workingset.c
|
||||
+++ b/mm/workingset.c
|
||||
@@ -223,7 +223,7 @@ static void *lru_gen_eviction(struct fol
|
||||
unsigned long token;
|
||||
unsigned long min_seq;
|
||||
struct lruvec *lruvec;
|
||||
- struct lru_gen_struct *lrugen;
|
||||
+ struct lru_gen_folio *lrugen;
|
||||
int type = folio_is_file_lru(folio);
|
||||
int delta = folio_nr_pages(folio);
|
||||
int refs = folio_lru_refs(folio);
|
||||
@@ -252,7 +252,7 @@ static void lru_gen_refault(struct folio
|
||||
unsigned long token;
|
||||
unsigned long min_seq;
|
||||
struct lruvec *lruvec;
|
||||
- struct lru_gen_struct *lrugen;
|
||||
+ struct lru_gen_folio *lrugen;
|
||||
struct mem_cgroup *memcg;
|
||||
struct pglist_data *pgdat;
|
||||
int type = folio_is_file_lru(folio);
|
||||
@@ -1,192 +0,0 @@
|
||||
From 14f9a7a15f3d1af351f30e0438fd747b7ac253b0 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Wed, 21 Dec 2022 21:19:01 -0700
|
||||
Subject: [PATCH 03/19] UPSTREAM: mm: multi-gen LRU: remove eviction fairness
|
||||
safeguard
|
||||
|
||||
Recall that the eviction consumes the oldest generation: first it
|
||||
bucket-sorts folios whose gen counters were updated by the aging and
|
||||
reclaims the rest; then it increments lrugen->min_seq.
|
||||
|
||||
The current eviction fairness safeguard for global reclaim has a
|
||||
dilemma: when there are multiple eligible memcgs, should it continue
|
||||
or stop upon meeting the reclaim goal? If it continues, it overshoots
|
||||
and increases direct reclaim latency; if it stops, it loses fairness
|
||||
between memcgs it has taken memory away from and those it has yet to.
|
||||
|
||||
With memcg LRU, the eviction, while ensuring eventual fairness, will
|
||||
stop upon meeting its goal. Therefore the current eviction fairness
|
||||
safeguard for global reclaim will not be needed.
|
||||
|
||||
Note that memcg LRU only applies to global reclaim. For memcg reclaim,
|
||||
the eviction will continue, even if it is overshooting. This becomes
|
||||
unconditional due to code simplification.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-4-yuzhao@google.com
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Jonathan Corbet <corbet@lwn.net>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Cc: Michal Hocko <mhocko@kernel.org>
|
||||
Cc: Mike Rapoport <rppt@kernel.org>
|
||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
|
||||
Cc: Suren Baghdasaryan <surenb@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Bug: 274865848
|
||||
(cherry picked from commit a579086c99ed70cc4bfc104348dbe3dd8f2787e6)
|
||||
Change-Id: I08ac1b3c90e29cafd0566785aaa4bcdb5db7d22c
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 81 +++++++++++++++--------------------------------------
|
||||
1 file changed, 23 insertions(+), 58 deletions(-)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -448,6 +448,11 @@ static bool cgroup_reclaim(struct scan_c
|
||||
return sc->target_mem_cgroup;
|
||||
}
|
||||
|
||||
+static bool global_reclaim(struct scan_control *sc)
|
||||
+{
|
||||
+ return !sc->target_mem_cgroup || mem_cgroup_is_root(sc->target_mem_cgroup);
|
||||
+}
|
||||
+
|
||||
/**
|
||||
* writeback_throttling_sane - is the usual dirty throttling mechanism available?
|
||||
* @sc: scan_control in question
|
||||
@@ -498,6 +503,11 @@ static bool cgroup_reclaim(struct scan_c
|
||||
return false;
|
||||
}
|
||||
|
||||
+static bool global_reclaim(struct scan_control *sc)
|
||||
+{
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
static bool writeback_throttling_sane(struct scan_control *sc)
|
||||
{
|
||||
return true;
|
||||
@@ -5005,8 +5015,7 @@ static int isolate_folios(struct lruvec
|
||||
return scanned;
|
||||
}
|
||||
|
||||
-static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swappiness,
|
||||
- bool *need_swapping)
|
||||
+static int evict_folios(struct lruvec *lruvec, struct scan_control *sc, int swappiness)
|
||||
{
|
||||
int type;
|
||||
int scanned;
|
||||
@@ -5094,9 +5103,6 @@ retry:
|
||||
goto retry;
|
||||
}
|
||||
|
||||
- if (need_swapping && type == LRU_GEN_ANON)
|
||||
- *need_swapping = true;
|
||||
-
|
||||
return scanned;
|
||||
}
|
||||
|
||||
@@ -5135,67 +5141,26 @@ done:
|
||||
return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
|
||||
}
|
||||
|
||||
-static bool should_abort_scan(struct lruvec *lruvec, unsigned long seq,
|
||||
- struct scan_control *sc, bool need_swapping)
|
||||
+static unsigned long get_nr_to_reclaim(struct scan_control *sc)
|
||||
{
|
||||
- int i;
|
||||
- DEFINE_MAX_SEQ(lruvec);
|
||||
-
|
||||
- if (!current_is_kswapd()) {
|
||||
- /* age each memcg at most once to ensure fairness */
|
||||
- if (max_seq - seq > 1)
|
||||
- return true;
|
||||
-
|
||||
- /* over-swapping can increase allocation latency */
|
||||
- if (sc->nr_reclaimed >= sc->nr_to_reclaim && need_swapping)
|
||||
- return true;
|
||||
-
|
||||
- /* give this thread a chance to exit and free its memory */
|
||||
- if (fatal_signal_pending(current)) {
|
||||
- sc->nr_reclaimed += MIN_LRU_BATCH;
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- if (cgroup_reclaim(sc))
|
||||
- return false;
|
||||
- } else if (sc->nr_reclaimed - sc->last_reclaimed < sc->nr_to_reclaim)
|
||||
- return false;
|
||||
-
|
||||
- /* keep scanning at low priorities to ensure fairness */
|
||||
- if (sc->priority > DEF_PRIORITY - 2)
|
||||
- return false;
|
||||
-
|
||||
- /*
|
||||
- * A minimum amount of work was done under global memory pressure. For
|
||||
- * kswapd, it may be overshooting. For direct reclaim, the allocation
|
||||
- * may succeed if all suitable zones are somewhat safe. In either case,
|
||||
- * it's better to stop now, and restart later if necessary.
|
||||
- */
|
||||
- for (i = 0; i <= sc->reclaim_idx; i++) {
|
||||
- unsigned long wmark;
|
||||
- struct zone *zone = lruvec_pgdat(lruvec)->node_zones + i;
|
||||
-
|
||||
- if (!managed_zone(zone))
|
||||
- continue;
|
||||
-
|
||||
- wmark = current_is_kswapd() ? high_wmark_pages(zone) : low_wmark_pages(zone);
|
||||
- if (wmark > zone_page_state(zone, NR_FREE_PAGES))
|
||||
- return false;
|
||||
- }
|
||||
+ /* don't abort memcg reclaim to ensure fairness */
|
||||
+ if (!global_reclaim(sc))
|
||||
+ return -1;
|
||||
|
||||
- sc->nr_reclaimed += MIN_LRU_BATCH;
|
||||
+ /* discount the previous progress for kswapd */
|
||||
+ if (current_is_kswapd())
|
||||
+ return sc->nr_to_reclaim + sc->last_reclaimed;
|
||||
|
||||
- return true;
|
||||
+ return max(sc->nr_to_reclaim, compact_gap(sc->order));
|
||||
}
|
||||
|
||||
static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
||||
{
|
||||
struct blk_plug plug;
|
||||
bool need_aging = false;
|
||||
- bool need_swapping = false;
|
||||
unsigned long scanned = 0;
|
||||
unsigned long reclaimed = sc->nr_reclaimed;
|
||||
- DEFINE_MAX_SEQ(lruvec);
|
||||
+ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
|
||||
|
||||
lru_add_drain();
|
||||
|
||||
@@ -5219,7 +5184,7 @@ static void lru_gen_shrink_lruvec(struct
|
||||
if (!nr_to_scan)
|
||||
goto done;
|
||||
|
||||
- delta = evict_folios(lruvec, sc, swappiness, &need_swapping);
|
||||
+ delta = evict_folios(lruvec, sc, swappiness);
|
||||
if (!delta)
|
||||
goto done;
|
||||
|
||||
@@ -5227,7 +5192,7 @@ static void lru_gen_shrink_lruvec(struct
|
||||
if (scanned >= nr_to_scan)
|
||||
break;
|
||||
|
||||
- if (should_abort_scan(lruvec, max_seq, sc, need_swapping))
|
||||
+ if (sc->nr_reclaimed >= nr_to_reclaim)
|
||||
break;
|
||||
|
||||
cond_resched();
|
||||
@@ -5677,7 +5642,7 @@ static int run_eviction(struct lruvec *l
|
||||
if (sc->nr_reclaimed >= nr_to_reclaim)
|
||||
return 0;
|
||||
|
||||
- if (!evict_folios(lruvec, sc, swappiness, NULL))
|
||||
+ if (!evict_folios(lruvec, sc, swappiness))
|
||||
return 0;
|
||||
|
||||
cond_resched();
|
||||
@@ -1,294 +0,0 @@
|
||||
From f3c93d2e37a3c56593d7ccf4f4bcf1b58426fdd8 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Wed, 21 Dec 2022 21:19:02 -0700
|
||||
Subject: [PATCH 04/19] BACKPORT: mm: multi-gen LRU: remove aging fairness
|
||||
safeguard
|
||||
|
||||
Recall that the aging produces the youngest generation: first it scans
|
||||
for accessed folios and updates their gen counters; then it increments
|
||||
lrugen->max_seq.
|
||||
|
||||
The current aging fairness safeguard for kswapd uses two passes to
|
||||
ensure the fairness to multiple eligible memcgs. On the first pass,
|
||||
which is shared with the eviction, it checks whether all eligible
|
||||
memcgs are low on cold folios. If so, it requires a second pass, on
|
||||
which it ages all those memcgs at the same time.
|
||||
|
||||
With memcg LRU, the aging, while ensuring eventual fairness, will run
|
||||
when necessary. Therefore the current aging fairness safeguard for
|
||||
kswapd will not be needed.
|
||||
|
||||
Note that memcg LRU only applies to global reclaim. For memcg reclaim,
|
||||
the aging can be unfair to different memcgs, i.e., their
|
||||
lrugen->max_seq can be incremented at different paces.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-5-yuzhao@google.com
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Jonathan Corbet <corbet@lwn.net>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Cc: Michal Hocko <mhocko@kernel.org>
|
||||
Cc: Mike Rapoport <rppt@kernel.org>
|
||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
|
||||
Cc: Suren Baghdasaryan <surenb@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Bug: 274865848
|
||||
(cherry picked from commit 7348cc91821b0cb24dfb00e578047f68299a50ab)
|
||||
[TJ: Resolved conflicts with older function signatures for
|
||||
min_cgroup_below_min / min_cgroup_below_low]
|
||||
Change-Id: I6e36ecfbaaefbc0a56d9a9d5d7cbe404ed7f57a5
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 126 ++++++++++++++++++++++++----------------------------
|
||||
1 file changed, 59 insertions(+), 67 deletions(-)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -136,7 +136,6 @@ struct scan_control {
|
||||
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
/* help kswapd make better choices among multiple memcgs */
|
||||
- unsigned int memcgs_need_aging:1;
|
||||
unsigned long last_reclaimed;
|
||||
#endif
|
||||
|
||||
@@ -4457,7 +4456,7 @@ done:
|
||||
return true;
|
||||
}
|
||||
|
||||
-static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsigned long *min_seq,
|
||||
+static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
|
||||
struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
|
||||
{
|
||||
int gen, type, zone;
|
||||
@@ -4466,6 +4465,13 @@ static bool should_run_aging(struct lruv
|
||||
unsigned long total = 0;
|
||||
struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
+ DEFINE_MIN_SEQ(lruvec);
|
||||
+
|
||||
+ /* whether this lruvec is completely out of cold folios */
|
||||
+ if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
|
||||
+ *nr_to_scan = 0;
|
||||
+ return true;
|
||||
+ }
|
||||
|
||||
for (type = !can_swap; type < ANON_AND_FILE; type++) {
|
||||
unsigned long seq;
|
||||
@@ -4494,8 +4500,6 @@ static bool should_run_aging(struct lruv
|
||||
* stalls when the number of generations reaches MIN_NR_GENS. Hence, the
|
||||
* ideal number of generations is MIN_NR_GENS+1.
|
||||
*/
|
||||
- if (min_seq[!can_swap] + MIN_NR_GENS > max_seq)
|
||||
- return true;
|
||||
if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
|
||||
return false;
|
||||
|
||||
@@ -4514,40 +4518,54 @@ static bool should_run_aging(struct lruv
|
||||
return false;
|
||||
}
|
||||
|
||||
-static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, unsigned long min_ttl)
|
||||
+static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
|
||||
{
|
||||
- bool need_aging;
|
||||
- unsigned long nr_to_scan;
|
||||
- int swappiness = get_swappiness(lruvec, sc);
|
||||
+ int gen, type, zone;
|
||||
+ unsigned long total = 0;
|
||||
+ bool can_swap = get_swappiness(lruvec, sc);
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
DEFINE_MAX_SEQ(lruvec);
|
||||
DEFINE_MIN_SEQ(lruvec);
|
||||
|
||||
- VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
|
||||
+ for (type = !can_swap; type < ANON_AND_FILE; type++) {
|
||||
+ unsigned long seq;
|
||||
|
||||
- mem_cgroup_calculate_protection(NULL, memcg);
|
||||
+ for (seq = min_seq[type]; seq <= max_seq; seq++) {
|
||||
+ gen = lru_gen_from_seq(seq);
|
||||
|
||||
- if (mem_cgroup_below_min(memcg))
|
||||
- return false;
|
||||
+ for (zone = 0; zone < MAX_NR_ZONES; zone++)
|
||||
+ total += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan);
|
||||
+ /* whether the size is big enough to be helpful */
|
||||
+ return mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
|
||||
+}
|
||||
|
||||
- if (min_ttl) {
|
||||
- int gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
|
||||
- unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
|
||||
+static bool lruvec_is_reclaimable(struct lruvec *lruvec, struct scan_control *sc,
|
||||
+ unsigned long min_ttl)
|
||||
+{
|
||||
+ int gen;
|
||||
+ unsigned long birth;
|
||||
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
+ DEFINE_MIN_SEQ(lruvec);
|
||||
|
||||
- if (time_is_after_jiffies(birth + min_ttl))
|
||||
- return false;
|
||||
+ VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
|
||||
|
||||
- /* the size is likely too small to be helpful */
|
||||
- if (!nr_to_scan && sc->priority != DEF_PRIORITY)
|
||||
- return false;
|
||||
- }
|
||||
+ /* see the comment on lru_gen_folio */
|
||||
+ gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
|
||||
+ birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
|
||||
|
||||
- if (need_aging)
|
||||
- try_to_inc_max_seq(lruvec, max_seq, sc, swappiness, false);
|
||||
+ if (time_is_after_jiffies(birth + min_ttl))
|
||||
+ return false;
|
||||
|
||||
- return true;
|
||||
+ if (!lruvec_is_sizable(lruvec, sc))
|
||||
+ return false;
|
||||
+
|
||||
+ mem_cgroup_calculate_protection(NULL, memcg);
|
||||
+
|
||||
+ return !mem_cgroup_below_min(memcg);
|
||||
}
|
||||
|
||||
/* to protect the working set of the last N jiffies */
|
||||
@@ -4556,46 +4574,32 @@ static unsigned long lru_gen_min_ttl __r
|
||||
static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc)
|
||||
{
|
||||
struct mem_cgroup *memcg;
|
||||
- bool success = false;
|
||||
unsigned long min_ttl = READ_ONCE(lru_gen_min_ttl);
|
||||
|
||||
VM_WARN_ON_ONCE(!current_is_kswapd());
|
||||
|
||||
sc->last_reclaimed = sc->nr_reclaimed;
|
||||
|
||||
- /*
|
||||
- * To reduce the chance of going into the aging path, which can be
|
||||
- * costly, optimistically skip it if the flag below was cleared in the
|
||||
- * eviction path. This improves the overall performance when multiple
|
||||
- * memcgs are available.
|
||||
- */
|
||||
- if (!sc->memcgs_need_aging) {
|
||||
- sc->memcgs_need_aging = true;
|
||||
+ /* check the order to exclude compaction-induced reclaim */
|
||||
+ if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY)
|
||||
return;
|
||||
- }
|
||||
-
|
||||
- set_mm_walk(pgdat);
|
||||
|
||||
memcg = mem_cgroup_iter(NULL, NULL, NULL);
|
||||
do {
|
||||
struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat);
|
||||
|
||||
- if (age_lruvec(lruvec, sc, min_ttl))
|
||||
- success = true;
|
||||
+ if (lruvec_is_reclaimable(lruvec, sc, min_ttl)) {
|
||||
+ mem_cgroup_iter_break(NULL, memcg);
|
||||
+ return;
|
||||
+ }
|
||||
|
||||
cond_resched();
|
||||
} while ((memcg = mem_cgroup_iter(NULL, memcg, NULL)));
|
||||
|
||||
- clear_mm_walk();
|
||||
-
|
||||
- /* check the order to exclude compaction-induced reclaim */
|
||||
- if (success || !min_ttl || sc->order)
|
||||
- return;
|
||||
-
|
||||
/*
|
||||
* The main goal is to OOM kill if every generation from all memcgs is
|
||||
* younger than min_ttl. However, another possibility is all memcgs are
|
||||
- * either below min or empty.
|
||||
+ * either too small or below min.
|
||||
*/
|
||||
if (mutex_trylock(&oom_lock)) {
|
||||
struct oom_control oc = {
|
||||
@@ -5112,33 +5116,27 @@ retry:
|
||||
* reclaim.
|
||||
*/
|
||||
static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
|
||||
- bool can_swap, bool *need_aging)
|
||||
+ bool can_swap)
|
||||
{
|
||||
unsigned long nr_to_scan;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
DEFINE_MAX_SEQ(lruvec);
|
||||
- DEFINE_MIN_SEQ(lruvec);
|
||||
|
||||
if (mem_cgroup_below_min(memcg) ||
|
||||
(mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim))
|
||||
return 0;
|
||||
|
||||
- *need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, can_swap, &nr_to_scan);
|
||||
- if (!*need_aging)
|
||||
+ if (!should_run_aging(lruvec, max_seq, sc, can_swap, &nr_to_scan))
|
||||
return nr_to_scan;
|
||||
|
||||
/* skip the aging path at the default priority */
|
||||
if (sc->priority == DEF_PRIORITY)
|
||||
- goto done;
|
||||
+ return nr_to_scan;
|
||||
|
||||
- /* leave the work to lru_gen_age_node() */
|
||||
- if (current_is_kswapd())
|
||||
- return 0;
|
||||
+ try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false);
|
||||
|
||||
- if (try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false))
|
||||
- return nr_to_scan;
|
||||
-done:
|
||||
- return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0;
|
||||
+ /* skip this lruvec as it's low on cold folios */
|
||||
+ return 0;
|
||||
}
|
||||
|
||||
static unsigned long get_nr_to_reclaim(struct scan_control *sc)
|
||||
@@ -5157,9 +5155,7 @@ static unsigned long get_nr_to_reclaim(s
|
||||
static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
||||
{
|
||||
struct blk_plug plug;
|
||||
- bool need_aging = false;
|
||||
unsigned long scanned = 0;
|
||||
- unsigned long reclaimed = sc->nr_reclaimed;
|
||||
unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
|
||||
|
||||
lru_add_drain();
|
||||
@@ -5180,13 +5176,13 @@ static void lru_gen_shrink_lruvec(struct
|
||||
else
|
||||
swappiness = 0;
|
||||
|
||||
- nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness, &need_aging);
|
||||
+ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
|
||||
if (!nr_to_scan)
|
||||
- goto done;
|
||||
+ break;
|
||||
|
||||
delta = evict_folios(lruvec, sc, swappiness);
|
||||
if (!delta)
|
||||
- goto done;
|
||||
+ break;
|
||||
|
||||
scanned += delta;
|
||||
if (scanned >= nr_to_scan)
|
||||
@@ -5198,10 +5194,6 @@ static void lru_gen_shrink_lruvec(struct
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
- /* see the comment in lru_gen_age_node() */
|
||||
- if (sc->nr_reclaimed - reclaimed >= MIN_LRU_BATCH && !need_aging)
|
||||
- sc->memcgs_need_aging = false;
|
||||
-done:
|
||||
clear_mm_walk();
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
@@ -1,166 +0,0 @@
|
||||
From eca3858631e0cbad2ca6e40f788892749428e4cb Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Wed, 21 Dec 2022 21:19:03 -0700
|
||||
Subject: [PATCH 05/19] UPSTREAM: mm: multi-gen LRU: shuffle should_run_aging()
|
||||
|
||||
Move should_run_aging() next to its only caller left.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-6-yuzhao@google.com
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Jonathan Corbet <corbet@lwn.net>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Cc: Michal Hocko <mhocko@kernel.org>
|
||||
Cc: Mike Rapoport <rppt@kernel.org>
|
||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
|
||||
Cc: Suren Baghdasaryan <surenb@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Bug: 274865848
|
||||
(cherry picked from commit 77d4459a4a1a472b7309e475f962dda87d950abd)
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
Change-Id: I3b0383fe16b93a783b4d8c0b3a0b325160392576
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 124 ++++++++++++++++++++++++++--------------------------
|
||||
1 file changed, 62 insertions(+), 62 deletions(-)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -4456,68 +4456,6 @@ done:
|
||||
return true;
|
||||
}
|
||||
|
||||
-static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
|
||||
- struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
|
||||
-{
|
||||
- int gen, type, zone;
|
||||
- unsigned long old = 0;
|
||||
- unsigned long young = 0;
|
||||
- unsigned long total = 0;
|
||||
- struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
- struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
- DEFINE_MIN_SEQ(lruvec);
|
||||
-
|
||||
- /* whether this lruvec is completely out of cold folios */
|
||||
- if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
|
||||
- *nr_to_scan = 0;
|
||||
- return true;
|
||||
- }
|
||||
-
|
||||
- for (type = !can_swap; type < ANON_AND_FILE; type++) {
|
||||
- unsigned long seq;
|
||||
-
|
||||
- for (seq = min_seq[type]; seq <= max_seq; seq++) {
|
||||
- unsigned long size = 0;
|
||||
-
|
||||
- gen = lru_gen_from_seq(seq);
|
||||
-
|
||||
- for (zone = 0; zone < MAX_NR_ZONES; zone++)
|
||||
- size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
|
||||
-
|
||||
- total += size;
|
||||
- if (seq == max_seq)
|
||||
- young += size;
|
||||
- else if (seq + MIN_NR_GENS == max_seq)
|
||||
- old += size;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- /* try to scrape all its memory if this memcg was deleted */
|
||||
- *nr_to_scan = mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
|
||||
-
|
||||
- /*
|
||||
- * The aging tries to be lazy to reduce the overhead, while the eviction
|
||||
- * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
|
||||
- * ideal number of generations is MIN_NR_GENS+1.
|
||||
- */
|
||||
- if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
|
||||
- return false;
|
||||
-
|
||||
- /*
|
||||
- * It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
|
||||
- * of the total number of pages for each generation. A reasonable range
|
||||
- * for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
|
||||
- * aging cares about the upper bound of hot pages, while the eviction
|
||||
- * cares about the lower bound of cold pages.
|
||||
- */
|
||||
- if (young * MIN_NR_GENS > total)
|
||||
- return true;
|
||||
- if (old * (MIN_NR_GENS + 2) < total)
|
||||
- return true;
|
||||
-
|
||||
- return false;
|
||||
-}
|
||||
-
|
||||
static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
|
||||
{
|
||||
int gen, type, zone;
|
||||
@@ -5110,6 +5048,68 @@ retry:
|
||||
return scanned;
|
||||
}
|
||||
|
||||
+static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq,
|
||||
+ struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan)
|
||||
+{
|
||||
+ int gen, type, zone;
|
||||
+ unsigned long old = 0;
|
||||
+ unsigned long young = 0;
|
||||
+ unsigned long total = 0;
|
||||
+ struct lru_gen_folio *lrugen = &lruvec->lrugen;
|
||||
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
+ DEFINE_MIN_SEQ(lruvec);
|
||||
+
|
||||
+ /* whether this lruvec is completely out of cold folios */
|
||||
+ if (min_seq[!can_swap] + MIN_NR_GENS > max_seq) {
|
||||
+ *nr_to_scan = 0;
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ for (type = !can_swap; type < ANON_AND_FILE; type++) {
|
||||
+ unsigned long seq;
|
||||
+
|
||||
+ for (seq = min_seq[type]; seq <= max_seq; seq++) {
|
||||
+ unsigned long size = 0;
|
||||
+
|
||||
+ gen = lru_gen_from_seq(seq);
|
||||
+
|
||||
+ for (zone = 0; zone < MAX_NR_ZONES; zone++)
|
||||
+ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L);
|
||||
+
|
||||
+ total += size;
|
||||
+ if (seq == max_seq)
|
||||
+ young += size;
|
||||
+ else if (seq + MIN_NR_GENS == max_seq)
|
||||
+ old += size;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /* try to scrape all its memory if this memcg was deleted */
|
||||
+ *nr_to_scan = mem_cgroup_online(memcg) ? (total >> sc->priority) : total;
|
||||
+
|
||||
+ /*
|
||||
+ * The aging tries to be lazy to reduce the overhead, while the eviction
|
||||
+ * stalls when the number of generations reaches MIN_NR_GENS. Hence, the
|
||||
+ * ideal number of generations is MIN_NR_GENS+1.
|
||||
+ */
|
||||
+ if (min_seq[!can_swap] + MIN_NR_GENS < max_seq)
|
||||
+ return false;
|
||||
+
|
||||
+ /*
|
||||
+ * It's also ideal to spread pages out evenly, i.e., 1/(MIN_NR_GENS+1)
|
||||
+ * of the total number of pages for each generation. A reasonable range
|
||||
+ * for this average portion is [1/MIN_NR_GENS, 1/(MIN_NR_GENS+2)]. The
|
||||
+ * aging cares about the upper bound of hot pages, while the eviction
|
||||
+ * cares about the lower bound of cold pages.
|
||||
+ */
|
||||
+ if (young * MIN_NR_GENS > total)
|
||||
+ return true;
|
||||
+ if (old * (MIN_NR_GENS + 2) < total)
|
||||
+ return true;
|
||||
+
|
||||
+ return false;
|
||||
+}
|
||||
+
|
||||
/*
|
||||
* For future optimizations:
|
||||
* 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg
|
||||
@@ -1,876 +0,0 @@
|
||||
From 8ee8571e47aa75221e5fbd4c9c7802fc4244c346 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Wed, 21 Dec 2022 21:19:04 -0700
|
||||
Subject: [PATCH 06/19] BACKPORT: mm: multi-gen LRU: per-node lru_gen_folio
|
||||
lists
|
||||
|
||||
For each node, memcgs are divided into two generations: the old and
|
||||
the young. For each generation, memcgs are randomly sharded into
|
||||
multiple bins to improve scalability. For each bin, an RCU hlist_nulls
|
||||
is virtually divided into three segments: the head, the tail and the
|
||||
default.
|
||||
|
||||
An onlining memcg is added to the tail of a random bin in the old
|
||||
generation. The eviction starts at the head of a random bin in the old
|
||||
generation. The per-node memcg generation counter, whose reminder (mod
|
||||
2) indexes the old generation, is incremented when all its bins become
|
||||
empty.
|
||||
|
||||
There are four operations:
|
||||
1. MEMCG_LRU_HEAD, which moves an memcg to the head of a random bin in
|
||||
its current generation (old or young) and updates its "seg" to
|
||||
"head";
|
||||
2. MEMCG_LRU_TAIL, which moves an memcg to the tail of a random bin in
|
||||
its current generation (old or young) and updates its "seg" to
|
||||
"tail";
|
||||
3. MEMCG_LRU_OLD, which moves an memcg to the head of a random bin in
|
||||
the old generation, updates its "gen" to "old" and resets its "seg"
|
||||
to "default";
|
||||
4. MEMCG_LRU_YOUNG, which moves an memcg to the tail of a random bin
|
||||
in the young generation, updates its "gen" to "young" and resets
|
||||
its "seg" to "default".
|
||||
|
||||
The events that trigger the above operations are:
|
||||
1. Exceeding the soft limit, which triggers MEMCG_LRU_HEAD;
|
||||
2. The first attempt to reclaim an memcg below low, which triggers
|
||||
MEMCG_LRU_TAIL;
|
||||
3. The first attempt to reclaim an memcg below reclaimable size
|
||||
threshold, which triggers MEMCG_LRU_TAIL;
|
||||
4. The second attempt to reclaim an memcg below reclaimable size
|
||||
threshold, which triggers MEMCG_LRU_YOUNG;
|
||||
5. Attempting to reclaim an memcg below min, which triggers
|
||||
MEMCG_LRU_YOUNG;
|
||||
6. Finishing the aging on the eviction path, which triggers
|
||||
MEMCG_LRU_YOUNG;
|
||||
7. Offlining an memcg, which triggers MEMCG_LRU_OLD.
|
||||
|
||||
Note that memcg LRU only applies to global reclaim, and the
|
||||
round-robin incrementing of their max_seq counters ensures the
|
||||
eventual fairness to all eligible memcgs. For memcg reclaim, it still
|
||||
relies on mem_cgroup_iter().
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-7-yuzhao@google.com
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Jonathan Corbet <corbet@lwn.net>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Cc: Michal Hocko <mhocko@kernel.org>
|
||||
Cc: Mike Rapoport <rppt@kernel.org>
|
||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
|
||||
Cc: Suren Baghdasaryan <surenb@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Bug: 274865848
|
||||
(cherry picked from commit e4dde56cd208674ce899b47589f263499e5b8cdc)
|
||||
[TJ: Resolved conflicts with older function signatures for
|
||||
min_cgroup_below_min / min_cgroup_below_low and includes]
|
||||
Change-Id: Idc8a0f635e035d72dd911f807d1224cb47cbd655
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
include/linux/memcontrol.h | 10 +
|
||||
include/linux/mm_inline.h | 17 ++
|
||||
include/linux/mmzone.h | 117 +++++++++++-
|
||||
mm/memcontrol.c | 16 ++
|
||||
mm/page_alloc.c | 1 +
|
||||
mm/vmscan.c | 374 +++++++++++++++++++++++++++++++++----
|
||||
6 files changed, 500 insertions(+), 35 deletions(-)
|
||||
|
||||
--- a/include/linux/memcontrol.h
|
||||
+++ b/include/linux/memcontrol.h
|
||||
@@ -795,6 +795,11 @@ static inline void obj_cgroup_put(struct
|
||||
percpu_ref_put(&objcg->refcnt);
|
||||
}
|
||||
|
||||
+static inline bool mem_cgroup_tryget(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+ return !memcg || css_tryget(&memcg->css);
|
||||
+}
|
||||
+
|
||||
static inline void mem_cgroup_put(struct mem_cgroup *memcg)
|
||||
{
|
||||
if (memcg)
|
||||
@@ -1295,6 +1300,11 @@ static inline void obj_cgroup_put(struct
|
||||
{
|
||||
}
|
||||
|
||||
+static inline bool mem_cgroup_tryget(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
static inline void mem_cgroup_put(struct mem_cgroup *memcg)
|
||||
{
|
||||
}
|
||||
--- a/include/linux/mm_inline.h
|
||||
+++ b/include/linux/mm_inline.h
|
||||
@@ -122,6 +122,18 @@ static inline bool lru_gen_in_fault(void
|
||||
return current->in_lru_fault;
|
||||
}
|
||||
|
||||
+#ifdef CONFIG_MEMCG
|
||||
+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
|
||||
+{
|
||||
+ return READ_ONCE(lruvec->lrugen.seg);
|
||||
+}
|
||||
+#else
|
||||
+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+#endif
|
||||
+
|
||||
static inline int lru_gen_from_seq(unsigned long seq)
|
||||
{
|
||||
return seq % MAX_NR_GENS;
|
||||
@@ -302,6 +314,11 @@ static inline bool lru_gen_in_fault(void
|
||||
return false;
|
||||
}
|
||||
|
||||
+static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
|
||||
{
|
||||
return false;
|
||||
--- a/include/linux/mmzone.h
|
||||
+++ b/include/linux/mmzone.h
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/list.h>
|
||||
+#include <linux/list_nulls.h>
|
||||
#include <linux/wait.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/cache.h>
|
||||
@@ -367,6 +368,15 @@ struct page_vma_mapped_walk;
|
||||
#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
|
||||
#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
|
||||
|
||||
+/* see the comment on MEMCG_NR_GENS */
|
||||
+enum {
|
||||
+ MEMCG_LRU_NOP,
|
||||
+ MEMCG_LRU_HEAD,
|
||||
+ MEMCG_LRU_TAIL,
|
||||
+ MEMCG_LRU_OLD,
|
||||
+ MEMCG_LRU_YOUNG,
|
||||
+};
|
||||
+
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
|
||||
enum {
|
||||
@@ -426,6 +436,14 @@ struct lru_gen_folio {
|
||||
atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS];
|
||||
/* whether the multi-gen LRU is enabled */
|
||||
bool enabled;
|
||||
+#ifdef CONFIG_MEMCG
|
||||
+ /* the memcg generation this lru_gen_folio belongs to */
|
||||
+ u8 gen;
|
||||
+ /* the list segment this lru_gen_folio belongs to */
|
||||
+ u8 seg;
|
||||
+ /* per-node lru_gen_folio list for global reclaim */
|
||||
+ struct hlist_nulls_node list;
|
||||
+#endif
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -479,12 +497,87 @@ void lru_gen_init_lruvec(struct lruvec *
|
||||
void lru_gen_look_around(struct page_vma_mapped_walk *pvmw);
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
+
|
||||
+/*
|
||||
+ * For each node, memcgs are divided into two generations: the old and the
|
||||
+ * young. For each generation, memcgs are randomly sharded into multiple bins
|
||||
+ * to improve scalability. For each bin, the hlist_nulls is virtually divided
|
||||
+ * into three segments: the head, the tail and the default.
|
||||
+ *
|
||||
+ * An onlining memcg is added to the tail of a random bin in the old generation.
|
||||
+ * The eviction starts at the head of a random bin in the old generation. The
|
||||
+ * per-node memcg generation counter, whose reminder (mod MEMCG_NR_GENS) indexes
|
||||
+ * the old generation, is incremented when all its bins become empty.
|
||||
+ *
|
||||
+ * There are four operations:
|
||||
+ * 1. MEMCG_LRU_HEAD, which moves an memcg to the head of a random bin in its
|
||||
+ * current generation (old or young) and updates its "seg" to "head";
|
||||
+ * 2. MEMCG_LRU_TAIL, which moves an memcg to the tail of a random bin in its
|
||||
+ * current generation (old or young) and updates its "seg" to "tail";
|
||||
+ * 3. MEMCG_LRU_OLD, which moves an memcg to the head of a random bin in the old
|
||||
+ * generation, updates its "gen" to "old" and resets its "seg" to "default";
|
||||
+ * 4. MEMCG_LRU_YOUNG, which moves an memcg to the tail of a random bin in the
|
||||
+ * young generation, updates its "gen" to "young" and resets its "seg" to
|
||||
+ * "default".
|
||||
+ *
|
||||
+ * The events that trigger the above operations are:
|
||||
+ * 1. Exceeding the soft limit, which triggers MEMCG_LRU_HEAD;
|
||||
+ * 2. The first attempt to reclaim an memcg below low, which triggers
|
||||
+ * MEMCG_LRU_TAIL;
|
||||
+ * 3. The first attempt to reclaim an memcg below reclaimable size threshold,
|
||||
+ * which triggers MEMCG_LRU_TAIL;
|
||||
+ * 4. The second attempt to reclaim an memcg below reclaimable size threshold,
|
||||
+ * which triggers MEMCG_LRU_YOUNG;
|
||||
+ * 5. Attempting to reclaim an memcg below min, which triggers MEMCG_LRU_YOUNG;
|
||||
+ * 6. Finishing the aging on the eviction path, which triggers MEMCG_LRU_YOUNG;
|
||||
+ * 7. Offlining an memcg, which triggers MEMCG_LRU_OLD.
|
||||
+ *
|
||||
+ * Note that memcg LRU only applies to global reclaim, and the round-robin
|
||||
+ * incrementing of their max_seq counters ensures the eventual fairness to all
|
||||
+ * eligible memcgs. For memcg reclaim, it still relies on mem_cgroup_iter().
|
||||
+ */
|
||||
+#define MEMCG_NR_GENS 2
|
||||
+#define MEMCG_NR_BINS 8
|
||||
+
|
||||
+struct lru_gen_memcg {
|
||||
+ /* the per-node memcg generation counter */
|
||||
+ unsigned long seq;
|
||||
+ /* each memcg has one lru_gen_folio per node */
|
||||
+ unsigned long nr_memcgs[MEMCG_NR_GENS];
|
||||
+ /* per-node lru_gen_folio list for global reclaim */
|
||||
+ struct hlist_nulls_head fifo[MEMCG_NR_GENS][MEMCG_NR_BINS];
|
||||
+ /* protects the above */
|
||||
+ spinlock_t lock;
|
||||
+};
|
||||
+
|
||||
+void lru_gen_init_pgdat(struct pglist_data *pgdat);
|
||||
+
|
||||
void lru_gen_init_memcg(struct mem_cgroup *memcg);
|
||||
void lru_gen_exit_memcg(struct mem_cgroup *memcg);
|
||||
-#endif
|
||||
+void lru_gen_online_memcg(struct mem_cgroup *memcg);
|
||||
+void lru_gen_offline_memcg(struct mem_cgroup *memcg);
|
||||
+void lru_gen_release_memcg(struct mem_cgroup *memcg);
|
||||
+void lru_gen_rotate_memcg(struct lruvec *lruvec, int op);
|
||||
+
|
||||
+#else /* !CONFIG_MEMCG */
|
||||
+
|
||||
+#define MEMCG_NR_GENS 1
|
||||
+
|
||||
+struct lru_gen_memcg {
|
||||
+};
|
||||
+
|
||||
+static inline void lru_gen_init_pgdat(struct pglist_data *pgdat)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+#endif /* CONFIG_MEMCG */
|
||||
|
||||
#else /* !CONFIG_LRU_GEN */
|
||||
|
||||
+static inline void lru_gen_init_pgdat(struct pglist_data *pgdat)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
static inline void lru_gen_init_lruvec(struct lruvec *lruvec)
|
||||
{
|
||||
}
|
||||
@@ -494,6 +587,7 @@ static inline void lru_gen_look_around(s
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
+
|
||||
static inline void lru_gen_init_memcg(struct mem_cgroup *memcg)
|
||||
{
|
||||
}
|
||||
@@ -501,7 +595,24 @@ static inline void lru_gen_init_memcg(st
|
||||
static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg)
|
||||
{
|
||||
}
|
||||
-#endif
|
||||
+
|
||||
+static inline void lru_gen_online_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+static inline void lru_gen_offline_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+static inline void lru_gen_release_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+static inline void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
+#endif /* CONFIG_MEMCG */
|
||||
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
@@ -1218,6 +1329,8 @@ typedef struct pglist_data {
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
/* kswap mm walk data */
|
||||
struct lru_gen_mm_walk mm_walk;
|
||||
+ /* lru_gen_folio list */
|
||||
+ struct lru_gen_memcg memcg_lru;
|
||||
#endif
|
||||
|
||||
CACHELINE_PADDING(_pad2_);
|
||||
--- a/mm/memcontrol.c
|
||||
+++ b/mm/memcontrol.c
|
||||
@@ -477,6 +477,16 @@ static void mem_cgroup_update_tree(struc
|
||||
struct mem_cgroup_per_node *mz;
|
||||
struct mem_cgroup_tree_per_node *mctz;
|
||||
|
||||
+ if (lru_gen_enabled()) {
|
||||
+ struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec;
|
||||
+
|
||||
+ /* see the comment on MEMCG_NR_GENS */
|
||||
+ if (soft_limit_excess(memcg) && lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
|
||||
+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
|
||||
+
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
mctz = soft_limit_tree.rb_tree_per_node[nid];
|
||||
if (!mctz)
|
||||
return;
|
||||
@@ -3524,6 +3534,9 @@ unsigned long mem_cgroup_soft_limit_recl
|
||||
struct mem_cgroup_tree_per_node *mctz;
|
||||
unsigned long excess;
|
||||
|
||||
+ if (lru_gen_enabled())
|
||||
+ return 0;
|
||||
+
|
||||
if (order > 0)
|
||||
return 0;
|
||||
|
||||
@@ -5390,6 +5403,7 @@ static int mem_cgroup_css_online(struct
|
||||
if (unlikely(mem_cgroup_is_root(memcg)))
|
||||
queue_delayed_work(system_unbound_wq, &stats_flush_dwork,
|
||||
2UL*HZ);
|
||||
+ lru_gen_online_memcg(memcg);
|
||||
return 0;
|
||||
offline_kmem:
|
||||
memcg_offline_kmem(memcg);
|
||||
@@ -5421,6 +5435,7 @@ static void mem_cgroup_css_offline(struc
|
||||
memcg_offline_kmem(memcg);
|
||||
reparent_shrinker_deferred(memcg);
|
||||
wb_memcg_offline(memcg);
|
||||
+ lru_gen_offline_memcg(memcg);
|
||||
|
||||
drain_all_stock(memcg);
|
||||
|
||||
@@ -5432,6 +5447,7 @@ static void mem_cgroup_css_released(stru
|
||||
struct mem_cgroup *memcg = mem_cgroup_from_css(css);
|
||||
|
||||
invalidate_reclaim_iterators(memcg);
|
||||
+ lru_gen_release_memcg(memcg);
|
||||
}
|
||||
|
||||
static void mem_cgroup_css_free(struct cgroup_subsys_state *css)
|
||||
--- a/mm/page_alloc.c
|
||||
+++ b/mm/page_alloc.c
|
||||
@@ -7956,6 +7956,7 @@ static void __init free_area_init_node(i
|
||||
pgdat_set_deferred_range(pgdat);
|
||||
|
||||
free_area_init_core(pgdat);
|
||||
+ lru_gen_init_pgdat(pgdat);
|
||||
}
|
||||
|
||||
static void __init free_area_init_memoryless_node(int nid)
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -54,6 +54,8 @@
|
||||
#include <linux/shmem_fs.h>
|
||||
#include <linux/ctype.h>
|
||||
#include <linux/debugfs.h>
|
||||
+#include <linux/rculist_nulls.h>
|
||||
+#include <linux/random.h>
|
||||
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/div64.h>
|
||||
@@ -134,11 +136,6 @@ struct scan_control {
|
||||
/* Always discard instead of demoting to lower tier memory */
|
||||
unsigned int no_demotion:1;
|
||||
|
||||
-#ifdef CONFIG_LRU_GEN
|
||||
- /* help kswapd make better choices among multiple memcgs */
|
||||
- unsigned long last_reclaimed;
|
||||
-#endif
|
||||
-
|
||||
/* Allocation order */
|
||||
s8 order;
|
||||
|
||||
@@ -3160,6 +3157,9 @@ DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_ca
|
||||
for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \
|
||||
for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++)
|
||||
|
||||
+#define get_memcg_gen(seq) ((seq) % MEMCG_NR_GENS)
|
||||
+#define get_memcg_bin(bin) ((bin) % MEMCG_NR_BINS)
|
||||
+
|
||||
static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid)
|
||||
{
|
||||
struct pglist_data *pgdat = NODE_DATA(nid);
|
||||
@@ -4442,8 +4442,7 @@ done:
|
||||
if (sc->priority <= DEF_PRIORITY - 2)
|
||||
wait_event_killable(lruvec->mm_state.wait,
|
||||
max_seq < READ_ONCE(lrugen->max_seq));
|
||||
-
|
||||
- return max_seq < READ_ONCE(lrugen->max_seq);
|
||||
+ return false;
|
||||
}
|
||||
|
||||
VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
|
||||
@@ -4516,8 +4515,6 @@ static void lru_gen_age_node(struct pgli
|
||||
|
||||
VM_WARN_ON_ONCE(!current_is_kswapd());
|
||||
|
||||
- sc->last_reclaimed = sc->nr_reclaimed;
|
||||
-
|
||||
/* check the order to exclude compaction-induced reclaim */
|
||||
if (!min_ttl || sc->order || sc->priority == DEF_PRIORITY)
|
||||
return;
|
||||
@@ -5115,8 +5112,7 @@ static bool should_run_aging(struct lruv
|
||||
* 1. Defer try_to_inc_max_seq() to workqueues to reduce latency for memcg
|
||||
* reclaim.
|
||||
*/
|
||||
-static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc,
|
||||
- bool can_swap)
|
||||
+static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, bool can_swap)
|
||||
{
|
||||
unsigned long nr_to_scan;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
@@ -5133,10 +5129,8 @@ static unsigned long get_nr_to_scan(stru
|
||||
if (sc->priority == DEF_PRIORITY)
|
||||
return nr_to_scan;
|
||||
|
||||
- try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false);
|
||||
-
|
||||
/* skip this lruvec as it's low on cold folios */
|
||||
- return 0;
|
||||
+ return try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, false) ? -1 : 0;
|
||||
}
|
||||
|
||||
static unsigned long get_nr_to_reclaim(struct scan_control *sc)
|
||||
@@ -5145,29 +5139,18 @@ static unsigned long get_nr_to_reclaim(s
|
||||
if (!global_reclaim(sc))
|
||||
return -1;
|
||||
|
||||
- /* discount the previous progress for kswapd */
|
||||
- if (current_is_kswapd())
|
||||
- return sc->nr_to_reclaim + sc->last_reclaimed;
|
||||
-
|
||||
return max(sc->nr_to_reclaim, compact_gap(sc->order));
|
||||
}
|
||||
|
||||
-static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
||||
+static bool try_to_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
||||
{
|
||||
- struct blk_plug plug;
|
||||
+ long nr_to_scan;
|
||||
unsigned long scanned = 0;
|
||||
unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
|
||||
|
||||
- lru_add_drain();
|
||||
-
|
||||
- blk_start_plug(&plug);
|
||||
-
|
||||
- set_mm_walk(lruvec_pgdat(lruvec));
|
||||
-
|
||||
while (true) {
|
||||
int delta;
|
||||
int swappiness;
|
||||
- unsigned long nr_to_scan;
|
||||
|
||||
if (sc->may_swap)
|
||||
swappiness = get_swappiness(lruvec, sc);
|
||||
@@ -5177,7 +5160,7 @@ static void lru_gen_shrink_lruvec(struct
|
||||
swappiness = 0;
|
||||
|
||||
nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
|
||||
- if (!nr_to_scan)
|
||||
+ if (nr_to_scan <= 0)
|
||||
break;
|
||||
|
||||
delta = evict_folios(lruvec, sc, swappiness);
|
||||
@@ -5194,10 +5177,251 @@ static void lru_gen_shrink_lruvec(struct
|
||||
cond_resched();
|
||||
}
|
||||
|
||||
+ /* whether try_to_inc_max_seq() was successful */
|
||||
+ return nr_to_scan < 0;
|
||||
+}
|
||||
+
|
||||
+static int shrink_one(struct lruvec *lruvec, struct scan_control *sc)
|
||||
+{
|
||||
+ bool success;
|
||||
+ unsigned long scanned = sc->nr_scanned;
|
||||
+ unsigned long reclaimed = sc->nr_reclaimed;
|
||||
+ int seg = lru_gen_memcg_seg(lruvec);
|
||||
+ struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
|
||||
+
|
||||
+ /* see the comment on MEMCG_NR_GENS */
|
||||
+ if (!lruvec_is_sizable(lruvec, sc))
|
||||
+ return seg != MEMCG_LRU_TAIL ? MEMCG_LRU_TAIL : MEMCG_LRU_YOUNG;
|
||||
+
|
||||
+ mem_cgroup_calculate_protection(NULL, memcg);
|
||||
+
|
||||
+ if (mem_cgroup_below_min(memcg))
|
||||
+ return MEMCG_LRU_YOUNG;
|
||||
+
|
||||
+ if (mem_cgroup_below_low(memcg)) {
|
||||
+ /* see the comment on MEMCG_NR_GENS */
|
||||
+ if (seg != MEMCG_LRU_TAIL)
|
||||
+ return MEMCG_LRU_TAIL;
|
||||
+
|
||||
+ memcg_memory_event(memcg, MEMCG_LOW);
|
||||
+ }
|
||||
+
|
||||
+ success = try_to_shrink_lruvec(lruvec, sc);
|
||||
+
|
||||
+ shrink_slab(sc->gfp_mask, pgdat->node_id, memcg, sc->priority);
|
||||
+
|
||||
+ if (!sc->proactive)
|
||||
+ vmpressure(sc->gfp_mask, memcg, false, sc->nr_scanned - scanned,
|
||||
+ sc->nr_reclaimed - reclaimed);
|
||||
+
|
||||
+ sc->nr_reclaimed += current->reclaim_state->reclaimed_slab;
|
||||
+ current->reclaim_state->reclaimed_slab = 0;
|
||||
+
|
||||
+ return success ? MEMCG_LRU_YOUNG : 0;
|
||||
+}
|
||||
+
|
||||
+#ifdef CONFIG_MEMCG
|
||||
+
|
||||
+static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
|
||||
+{
|
||||
+ int gen;
|
||||
+ int bin;
|
||||
+ int first_bin;
|
||||
+ struct lruvec *lruvec;
|
||||
+ struct lru_gen_folio *lrugen;
|
||||
+ const struct hlist_nulls_node *pos;
|
||||
+ int op = 0;
|
||||
+ struct mem_cgroup *memcg = NULL;
|
||||
+ unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
|
||||
+
|
||||
+ bin = first_bin = get_random_u32_below(MEMCG_NR_BINS);
|
||||
+restart:
|
||||
+ gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq));
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+
|
||||
+ hlist_nulls_for_each_entry_rcu(lrugen, pos, &pgdat->memcg_lru.fifo[gen][bin], list) {
|
||||
+ if (op)
|
||||
+ lru_gen_rotate_memcg(lruvec, op);
|
||||
+
|
||||
+ mem_cgroup_put(memcg);
|
||||
+
|
||||
+ lruvec = container_of(lrugen, struct lruvec, lrugen);
|
||||
+ memcg = lruvec_memcg(lruvec);
|
||||
+
|
||||
+ if (!mem_cgroup_tryget(memcg)) {
|
||||
+ op = 0;
|
||||
+ memcg = NULL;
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ rcu_read_unlock();
|
||||
+
|
||||
+ op = shrink_one(lruvec, sc);
|
||||
+
|
||||
+ if (sc->nr_reclaimed >= nr_to_reclaim)
|
||||
+ goto success;
|
||||
+
|
||||
+ rcu_read_lock();
|
||||
+ }
|
||||
+
|
||||
+ rcu_read_unlock();
|
||||
+
|
||||
+ /* restart if raced with lru_gen_rotate_memcg() */
|
||||
+ if (gen != get_nulls_value(pos))
|
||||
+ goto restart;
|
||||
+
|
||||
+ /* try the rest of the bins of the current generation */
|
||||
+ bin = get_memcg_bin(bin + 1);
|
||||
+ if (bin != first_bin)
|
||||
+ goto restart;
|
||||
+success:
|
||||
+ if (op)
|
||||
+ lru_gen_rotate_memcg(lruvec, op);
|
||||
+
|
||||
+ mem_cgroup_put(memcg);
|
||||
+}
|
||||
+
|
||||
+static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
||||
+{
|
||||
+ struct blk_plug plug;
|
||||
+
|
||||
+ VM_WARN_ON_ONCE(global_reclaim(sc));
|
||||
+
|
||||
+ lru_add_drain();
|
||||
+
|
||||
+ blk_start_plug(&plug);
|
||||
+
|
||||
+ set_mm_walk(lruvec_pgdat(lruvec));
|
||||
+
|
||||
+ if (try_to_shrink_lruvec(lruvec, sc))
|
||||
+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);
|
||||
+
|
||||
+ clear_mm_walk();
|
||||
+
|
||||
+ blk_finish_plug(&plug);
|
||||
+}
|
||||
+
|
||||
+#else /* !CONFIG_MEMCG */
|
||||
+
|
||||
+static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
|
||||
+{
|
||||
+ BUILD_BUG();
|
||||
+}
|
||||
+
|
||||
+static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
||||
+{
|
||||
+ BUILD_BUG();
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
+static void set_initial_priority(struct pglist_data *pgdat, struct scan_control *sc)
|
||||
+{
|
||||
+ int priority;
|
||||
+ unsigned long reclaimable;
|
||||
+ struct lruvec *lruvec = mem_cgroup_lruvec(NULL, pgdat);
|
||||
+
|
||||
+ if (sc->priority != DEF_PRIORITY || sc->nr_to_reclaim < MIN_LRU_BATCH)
|
||||
+ return;
|
||||
+ /*
|
||||
+ * Determine the initial priority based on ((total / MEMCG_NR_GENS) >>
|
||||
+ * priority) * reclaimed_to_scanned_ratio = nr_to_reclaim, where the
|
||||
+ * estimated reclaimed_to_scanned_ratio = inactive / total.
|
||||
+ */
|
||||
+ reclaimable = node_page_state(pgdat, NR_INACTIVE_FILE);
|
||||
+ if (get_swappiness(lruvec, sc))
|
||||
+ reclaimable += node_page_state(pgdat, NR_INACTIVE_ANON);
|
||||
+
|
||||
+ reclaimable /= MEMCG_NR_GENS;
|
||||
+
|
||||
+ /* round down reclaimable and round up sc->nr_to_reclaim */
|
||||
+ priority = fls_long(reclaimable) - 1 - fls_long(sc->nr_to_reclaim - 1);
|
||||
+
|
||||
+ sc->priority = clamp(priority, 0, DEF_PRIORITY);
|
||||
+}
|
||||
+
|
||||
+static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)
|
||||
+{
|
||||
+ struct blk_plug plug;
|
||||
+ unsigned long reclaimed = sc->nr_reclaimed;
|
||||
+
|
||||
+ VM_WARN_ON_ONCE(!global_reclaim(sc));
|
||||
+
|
||||
+ lru_add_drain();
|
||||
+
|
||||
+ blk_start_plug(&plug);
|
||||
+
|
||||
+ set_mm_walk(pgdat);
|
||||
+
|
||||
+ set_initial_priority(pgdat, sc);
|
||||
+
|
||||
+ if (current_is_kswapd())
|
||||
+ sc->nr_reclaimed = 0;
|
||||
+
|
||||
+ if (mem_cgroup_disabled())
|
||||
+ shrink_one(&pgdat->__lruvec, sc);
|
||||
+ else
|
||||
+ shrink_many(pgdat, sc);
|
||||
+
|
||||
+ if (current_is_kswapd())
|
||||
+ sc->nr_reclaimed += reclaimed;
|
||||
+
|
||||
clear_mm_walk();
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
+
|
||||
+ /* kswapd should never fail */
|
||||
+ pgdat->kswapd_failures = 0;
|
||||
+}
|
||||
+
|
||||
+#ifdef CONFIG_MEMCG
|
||||
+void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
|
||||
+{
|
||||
+ int seg;
|
||||
+ int old, new;
|
||||
+ int bin = get_random_u32_below(MEMCG_NR_BINS);
|
||||
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
|
||||
+
|
||||
+ spin_lock(&pgdat->memcg_lru.lock);
|
||||
+
|
||||
+ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
+
|
||||
+ seg = 0;
|
||||
+ new = old = lruvec->lrugen.gen;
|
||||
+
|
||||
+ /* see the comment on MEMCG_NR_GENS */
|
||||
+ if (op == MEMCG_LRU_HEAD)
|
||||
+ seg = MEMCG_LRU_HEAD;
|
||||
+ else if (op == MEMCG_LRU_TAIL)
|
||||
+ seg = MEMCG_LRU_TAIL;
|
||||
+ else if (op == MEMCG_LRU_OLD)
|
||||
+ new = get_memcg_gen(pgdat->memcg_lru.seq);
|
||||
+ else if (op == MEMCG_LRU_YOUNG)
|
||||
+ new = get_memcg_gen(pgdat->memcg_lru.seq + 1);
|
||||
+ else
|
||||
+ VM_WARN_ON_ONCE(true);
|
||||
+
|
||||
+ hlist_nulls_del_rcu(&lruvec->lrugen.list);
|
||||
+
|
||||
+ if (op == MEMCG_LRU_HEAD || op == MEMCG_LRU_OLD)
|
||||
+ hlist_nulls_add_head_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
|
||||
+ else
|
||||
+ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
|
||||
+
|
||||
+ pgdat->memcg_lru.nr_memcgs[old]--;
|
||||
+ pgdat->memcg_lru.nr_memcgs[new]++;
|
||||
+
|
||||
+ lruvec->lrugen.gen = new;
|
||||
+ WRITE_ONCE(lruvec->lrugen.seg, seg);
|
||||
+
|
||||
+ if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq))
|
||||
+ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
|
||||
+
|
||||
+ spin_unlock(&pgdat->memcg_lru.lock);
|
||||
}
|
||||
+#endif
|
||||
|
||||
/******************************************************************************
|
||||
* state change
|
||||
@@ -5655,11 +5879,11 @@ static int run_cmd(char cmd, int memcg_i
|
||||
|
||||
if (!mem_cgroup_disabled()) {
|
||||
rcu_read_lock();
|
||||
+
|
||||
memcg = mem_cgroup_from_id(memcg_id);
|
||||
-#ifdef CONFIG_MEMCG
|
||||
- if (memcg && !css_tryget(&memcg->css))
|
||||
+ if (!mem_cgroup_tryget(memcg))
|
||||
memcg = NULL;
|
||||
-#endif
|
||||
+
|
||||
rcu_read_unlock();
|
||||
|
||||
if (!memcg)
|
||||
@@ -5807,6 +6031,19 @@ void lru_gen_init_lruvec(struct lruvec *
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
+
|
||||
+void lru_gen_init_pgdat(struct pglist_data *pgdat)
|
||||
+{
|
||||
+ int i, j;
|
||||
+
|
||||
+ spin_lock_init(&pgdat->memcg_lru.lock);
|
||||
+
|
||||
+ for (i = 0; i < MEMCG_NR_GENS; i++) {
|
||||
+ for (j = 0; j < MEMCG_NR_BINS; j++)
|
||||
+ INIT_HLIST_NULLS_HEAD(&pgdat->memcg_lru.fifo[i][j], i);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
void lru_gen_init_memcg(struct mem_cgroup *memcg)
|
||||
{
|
||||
INIT_LIST_HEAD(&memcg->mm_list.fifo);
|
||||
@@ -5830,7 +6067,69 @@ void lru_gen_exit_memcg(struct mem_cgrou
|
||||
}
|
||||
}
|
||||
}
|
||||
-#endif
|
||||
+
|
||||
+void lru_gen_online_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+ int gen;
|
||||
+ int nid;
|
||||
+ int bin = get_random_u32_below(MEMCG_NR_BINS);
|
||||
+
|
||||
+ for_each_node(nid) {
|
||||
+ struct pglist_data *pgdat = NODE_DATA(nid);
|
||||
+ struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
+
|
||||
+ spin_lock(&pgdat->memcg_lru.lock);
|
||||
+
|
||||
+ VM_WARN_ON_ONCE(!hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
+
|
||||
+ gen = get_memcg_gen(pgdat->memcg_lru.seq);
|
||||
+
|
||||
+ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[gen][bin]);
|
||||
+ pgdat->memcg_lru.nr_memcgs[gen]++;
|
||||
+
|
||||
+ lruvec->lrugen.gen = gen;
|
||||
+
|
||||
+ spin_unlock(&pgdat->memcg_lru.lock);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void lru_gen_offline_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+ int nid;
|
||||
+
|
||||
+ for_each_node(nid) {
|
||||
+ struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
+
|
||||
+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void lru_gen_release_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+ int gen;
|
||||
+ int nid;
|
||||
+
|
||||
+ for_each_node(nid) {
|
||||
+ struct pglist_data *pgdat = NODE_DATA(nid);
|
||||
+ struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
+
|
||||
+ spin_lock(&pgdat->memcg_lru.lock);
|
||||
+
|
||||
+ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
+
|
||||
+ gen = lruvec->lrugen.gen;
|
||||
+
|
||||
+ hlist_nulls_del_rcu(&lruvec->lrugen.list);
|
||||
+ pgdat->memcg_lru.nr_memcgs[gen]--;
|
||||
+
|
||||
+ if (!pgdat->memcg_lru.nr_memcgs[gen] && gen == get_memcg_gen(pgdat->memcg_lru.seq))
|
||||
+ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
|
||||
+
|
||||
+ spin_unlock(&pgdat->memcg_lru.lock);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#endif /* CONFIG_MEMCG */
|
||||
|
||||
static int __init init_lru_gen(void)
|
||||
{
|
||||
@@ -5857,6 +6156,10 @@ static void lru_gen_shrink_lruvec(struct
|
||||
{
|
||||
}
|
||||
|
||||
+static void lru_gen_shrink_node(struct pglist_data *pgdat, struct scan_control *sc)
|
||||
+{
|
||||
+}
|
||||
+
|
||||
#endif /* CONFIG_LRU_GEN */
|
||||
|
||||
static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
||||
@@ -5870,7 +6173,7 @@ static void shrink_lruvec(struct lruvec
|
||||
bool proportional_reclaim;
|
||||
struct blk_plug plug;
|
||||
|
||||
- if (lru_gen_enabled()) {
|
||||
+ if (lru_gen_enabled() && !global_reclaim(sc)) {
|
||||
lru_gen_shrink_lruvec(lruvec, sc);
|
||||
return;
|
||||
}
|
||||
@@ -6113,6 +6416,11 @@ static void shrink_node(pg_data_t *pgdat
|
||||
struct lruvec *target_lruvec;
|
||||
bool reclaimable = false;
|
||||
|
||||
+ if (lru_gen_enabled() && global_reclaim(sc)) {
|
||||
+ lru_gen_shrink_node(pgdat, sc);
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat);
|
||||
|
||||
again:
|
||||
@@ -1,202 +0,0 @@
|
||||
From 11b14ee8cbbbebd8204609076a9327a1171cd253 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Wed, 21 Dec 2022 21:19:05 -0700
|
||||
Subject: [PATCH 07/19] BACKPORT: mm: multi-gen LRU: clarify scan_control flags
|
||||
|
||||
Among the flags in scan_control:
|
||||
1. sc->may_swap, which indicates swap constraint due to memsw.max, is
|
||||
supported as usual.
|
||||
2. sc->proactive, which indicates reclaim by memory.reclaim, may not
|
||||
opportunistically skip the aging path, since it is considered less
|
||||
latency sensitive.
|
||||
3. !(sc->gfp_mask & __GFP_IO), which indicates IO constraint, lowers
|
||||
swappiness to prioritize file LRU, since clean file folios are more
|
||||
likely to exist.
|
||||
4. sc->may_writepage and sc->may_unmap, which indicates opportunistic
|
||||
reclaim, are rejected, since unmapped clean folios are already
|
||||
prioritized. Scanning for more of them is likely futile and can
|
||||
cause high reclaim latency when there is a large number of memcgs.
|
||||
|
||||
The rest are handled by the existing code.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-8-yuzhao@google.com
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Jonathan Corbet <corbet@lwn.net>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Cc: Michal Hocko <mhocko@kernel.org>
|
||||
Cc: Mike Rapoport <rppt@kernel.org>
|
||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
|
||||
Cc: Suren Baghdasaryan <surenb@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Bug: 274865848
|
||||
(cherry picked from commit e9d4e1ee788097484606c32122f146d802a9c5fb)
|
||||
[TJ: Resolved conflict with older function signature for min_cgroup_below_min, and over
|
||||
cdded861182142ac4488a4d64c571107aeb77f53 ("ANDROID: MGLRU: Don't skip anon reclaim if swap low")]
|
||||
Change-Id: Ic2e779eaf4e91a3921831b4e2fa10c740dc59d50
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 55 +++++++++++++++++++++++++++--------------------------
|
||||
1 file changed, 28 insertions(+), 27 deletions(-)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -3185,6 +3185,9 @@ static int get_swappiness(struct lruvec
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
struct pglist_data *pgdat = lruvec_pgdat(lruvec);
|
||||
|
||||
+ if (!sc->may_swap)
|
||||
+ return 0;
|
||||
+
|
||||
if (!can_demote(pgdat->node_id, sc) &&
|
||||
mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH)
|
||||
return 0;
|
||||
@@ -4223,7 +4226,7 @@ static void walk_mm(struct lruvec *lruve
|
||||
} while (err == -EAGAIN);
|
||||
}
|
||||
|
||||
-static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat)
|
||||
+static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat, bool force_alloc)
|
||||
{
|
||||
struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk;
|
||||
|
||||
@@ -4231,7 +4234,7 @@ static struct lru_gen_mm_walk *set_mm_wa
|
||||
VM_WARN_ON_ONCE(walk);
|
||||
|
||||
walk = &pgdat->mm_walk;
|
||||
- } else if (!pgdat && !walk) {
|
||||
+ } else if (!walk && force_alloc) {
|
||||
VM_WARN_ON_ONCE(current_is_kswapd());
|
||||
|
||||
walk = kzalloc(sizeof(*walk), __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
|
||||
@@ -4419,7 +4422,7 @@ static bool try_to_inc_max_seq(struct lr
|
||||
goto done;
|
||||
}
|
||||
|
||||
- walk = set_mm_walk(NULL);
|
||||
+ walk = set_mm_walk(NULL, true);
|
||||
if (!walk) {
|
||||
success = iterate_mm_list_nowalk(lruvec, max_seq);
|
||||
goto done;
|
||||
@@ -4488,8 +4491,6 @@ static bool lruvec_is_reclaimable(struct
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
DEFINE_MIN_SEQ(lruvec);
|
||||
|
||||
- VM_WARN_ON_ONCE(sc->memcg_low_reclaim);
|
||||
-
|
||||
/* see the comment on lru_gen_folio */
|
||||
gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]);
|
||||
birth = READ_ONCE(lruvec->lrugen.timestamps[gen]);
|
||||
@@ -4753,12 +4754,8 @@ static bool isolate_folio(struct lruvec
|
||||
{
|
||||
bool success;
|
||||
|
||||
- /* unmapping inhibited */
|
||||
- if (!sc->may_unmap && folio_mapped(folio))
|
||||
- return false;
|
||||
-
|
||||
/* swapping inhibited */
|
||||
- if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) &&
|
||||
+ if (!(sc->gfp_mask & __GFP_IO) &&
|
||||
(folio_test_dirty(folio) ||
|
||||
(folio_test_anon(folio) && !folio_test_swapcache(folio))))
|
||||
return false;
|
||||
@@ -4857,9 +4854,8 @@ static int scan_folios(struct lruvec *lr
|
||||
__count_vm_events(PGSCAN_ANON + type, isolated);
|
||||
|
||||
/*
|
||||
- * There might not be eligible pages due to reclaim_idx, may_unmap and
|
||||
- * may_writepage. Check the remaining to prevent livelock if it's not
|
||||
- * making progress.
|
||||
+ * There might not be eligible folios due to reclaim_idx. Check the
|
||||
+ * remaining to prevent livelock if it's not making progress.
|
||||
*/
|
||||
return isolated || !remaining ? scanned : 0;
|
||||
}
|
||||
@@ -5118,8 +5114,7 @@ static long get_nr_to_scan(struct lruvec
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
DEFINE_MAX_SEQ(lruvec);
|
||||
|
||||
- if (mem_cgroup_below_min(memcg) ||
|
||||
- (mem_cgroup_below_low(memcg) && !sc->memcg_low_reclaim))
|
||||
+ if (mem_cgroup_below_min(memcg))
|
||||
return 0;
|
||||
|
||||
if (!should_run_aging(lruvec, max_seq, sc, can_swap, &nr_to_scan))
|
||||
@@ -5147,17 +5142,14 @@ static bool try_to_shrink_lruvec(struct
|
||||
long nr_to_scan;
|
||||
unsigned long scanned = 0;
|
||||
unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
|
||||
+ int swappiness = get_swappiness(lruvec, sc);
|
||||
+
|
||||
+ /* clean file folios are more likely to exist */
|
||||
+ if (swappiness && !(sc->gfp_mask & __GFP_IO))
|
||||
+ swappiness = 1;
|
||||
|
||||
while (true) {
|
||||
int delta;
|
||||
- int swappiness;
|
||||
-
|
||||
- if (sc->may_swap)
|
||||
- swappiness = get_swappiness(lruvec, sc);
|
||||
- else if (!cgroup_reclaim(sc) && get_swappiness(lruvec, sc))
|
||||
- swappiness = 1;
|
||||
- else
|
||||
- swappiness = 0;
|
||||
|
||||
nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness);
|
||||
if (nr_to_scan <= 0)
|
||||
@@ -5288,12 +5280,13 @@ static void lru_gen_shrink_lruvec(struct
|
||||
struct blk_plug plug;
|
||||
|
||||
VM_WARN_ON_ONCE(global_reclaim(sc));
|
||||
+ VM_WARN_ON_ONCE(!sc->may_writepage || !sc->may_unmap);
|
||||
|
||||
lru_add_drain();
|
||||
|
||||
blk_start_plug(&plug);
|
||||
|
||||
- set_mm_walk(lruvec_pgdat(lruvec));
|
||||
+ set_mm_walk(NULL, sc->proactive);
|
||||
|
||||
if (try_to_shrink_lruvec(lruvec, sc))
|
||||
lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG);
|
||||
@@ -5349,11 +5342,19 @@ static void lru_gen_shrink_node(struct p
|
||||
|
||||
VM_WARN_ON_ONCE(!global_reclaim(sc));
|
||||
|
||||
+ /*
|
||||
+ * Unmapped clean folios are already prioritized. Scanning for more of
|
||||
+ * them is likely futile and can cause high reclaim latency when there
|
||||
+ * is a large number of memcgs.
|
||||
+ */
|
||||
+ if (!sc->may_writepage || !sc->may_unmap)
|
||||
+ goto done;
|
||||
+
|
||||
lru_add_drain();
|
||||
|
||||
blk_start_plug(&plug);
|
||||
|
||||
- set_mm_walk(pgdat);
|
||||
+ set_mm_walk(pgdat, sc->proactive);
|
||||
|
||||
set_initial_priority(pgdat, sc);
|
||||
|
||||
@@ -5371,7 +5372,7 @@ static void lru_gen_shrink_node(struct p
|
||||
clear_mm_walk();
|
||||
|
||||
blk_finish_plug(&plug);
|
||||
-
|
||||
+done:
|
||||
/* kswapd should never fail */
|
||||
pgdat->kswapd_failures = 0;
|
||||
}
|
||||
@@ -5943,7 +5944,7 @@ static ssize_t lru_gen_seq_write(struct
|
||||
set_task_reclaim_state(current, &sc.reclaim_state);
|
||||
flags = memalloc_noreclaim_save();
|
||||
blk_start_plug(&plug);
|
||||
- if (!set_mm_walk(NULL)) {
|
||||
+ if (!set_mm_walk(NULL, true)) {
|
||||
err = -ENOMEM;
|
||||
goto done;
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
From 25887d48dff860751a06caa4188bfaf6bfb6e4b2 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Wed, 21 Dec 2022 21:19:06 -0700
|
||||
Subject: [PATCH 08/19] UPSTREAM: mm: multi-gen LRU: simplify
|
||||
arch_has_hw_pte_young() check
|
||||
|
||||
Scanning page tables when hardware does not set the accessed bit has
|
||||
no real use cases.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221222041905.2431096-9-yuzhao@google.com
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Jonathan Corbet <corbet@lwn.net>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Cc: Michal Hocko <mhocko@kernel.org>
|
||||
Cc: Mike Rapoport <rppt@kernel.org>
|
||||
Cc: Roman Gushchin <roman.gushchin@linux.dev>
|
||||
Cc: Suren Baghdasaryan <surenb@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Bug: 274865848
|
||||
(cherry picked from commit f386e9314025ea99dae639ed2032560a92081430)
|
||||
Change-Id: I84d97ab665b4e3bb862a9bc7d72f50dea7191a6b
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -4417,7 +4417,7 @@ static bool try_to_inc_max_seq(struct lr
|
||||
* handful of PTEs. Spreading the work out over a period of time usually
|
||||
* is less efficient, but it avoids bursty page faults.
|
||||
*/
|
||||
- if (!force_scan && !(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) {
|
||||
+ if (!arch_has_hw_pte_young() || !get_cap(LRU_GEN_MM_WALK)) {
|
||||
success = iterate_mm_list_nowalk(lruvec, max_seq);
|
||||
goto done;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
From 620b0ee94455e48d124414cd06d8a53f69fb6453 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Mon, 13 Feb 2023 00:53:22 -0700
|
||||
Subject: [PATCH 09/19] UPSTREAM: mm: multi-gen LRU: avoid futile retries
|
||||
|
||||
Recall that the per-node memcg LRU has two generations and they alternate
|
||||
when the last memcg (of a given node) is moved from one to the other.
|
||||
Each generation is also sharded into multiple bins to improve scalability.
|
||||
A reclaimer starts with a random bin (in the old generation) and, if it
|
||||
fails, it will retry, i.e., to try the rest of the bins.
|
||||
|
||||
If a reclaimer fails with the last memcg, it should move this memcg to the
|
||||
young generation first, which causes the generations to alternate, and
|
||||
then retry. Otherwise, the retries will be futile because all other bins
|
||||
are empty.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230213075322.1416966-1-yuzhao@google.com
|
||||
Fixes: e4dde56cd208 ("mm: multi-gen LRU: per-node lru_gen_folio lists")
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Reported-by: T.J. Mercier <tjmercier@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
Bug: 274865848
|
||||
(cherry picked from commit 9f550d78b40da21b4da515db4c37d8d7b12aa1a6)
|
||||
Change-Id: Ie92535676b005ec9e7987632b742fdde8d54436f
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 25 +++++++++++++++----------
|
||||
1 file changed, 15 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -5217,18 +5217,20 @@ static int shrink_one(struct lruvec *lru
|
||||
|
||||
static void shrink_many(struct pglist_data *pgdat, struct scan_control *sc)
|
||||
{
|
||||
+ int op;
|
||||
int gen;
|
||||
int bin;
|
||||
int first_bin;
|
||||
struct lruvec *lruvec;
|
||||
struct lru_gen_folio *lrugen;
|
||||
+ struct mem_cgroup *memcg;
|
||||
const struct hlist_nulls_node *pos;
|
||||
- int op = 0;
|
||||
- struct mem_cgroup *memcg = NULL;
|
||||
unsigned long nr_to_reclaim = get_nr_to_reclaim(sc);
|
||||
|
||||
bin = first_bin = get_random_u32_below(MEMCG_NR_BINS);
|
||||
restart:
|
||||
+ op = 0;
|
||||
+ memcg = NULL;
|
||||
gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq));
|
||||
|
||||
rcu_read_lock();
|
||||
@@ -5252,14 +5254,22 @@ restart:
|
||||
|
||||
op = shrink_one(lruvec, sc);
|
||||
|
||||
- if (sc->nr_reclaimed >= nr_to_reclaim)
|
||||
- goto success;
|
||||
-
|
||||
rcu_read_lock();
|
||||
+
|
||||
+ if (sc->nr_reclaimed >= nr_to_reclaim)
|
||||
+ break;
|
||||
}
|
||||
|
||||
rcu_read_unlock();
|
||||
|
||||
+ if (op)
|
||||
+ lru_gen_rotate_memcg(lruvec, op);
|
||||
+
|
||||
+ mem_cgroup_put(memcg);
|
||||
+
|
||||
+ if (sc->nr_reclaimed >= nr_to_reclaim)
|
||||
+ return;
|
||||
+
|
||||
/* restart if raced with lru_gen_rotate_memcg() */
|
||||
if (gen != get_nulls_value(pos))
|
||||
goto restart;
|
||||
@@ -5268,11 +5278,6 @@ restart:
|
||||
bin = get_memcg_bin(bin + 1);
|
||||
if (bin != first_bin)
|
||||
goto restart;
|
||||
-success:
|
||||
- if (op)
|
||||
- lru_gen_rotate_memcg(lruvec, op);
|
||||
-
|
||||
- mem_cgroup_put(memcg);
|
||||
}
|
||||
|
||||
static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc)
|
||||
@@ -1,191 +0,0 @@
|
||||
From 70d216c71ff5c5b17dd1da6294f97b91fb6aba7a Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Fri, 30 Dec 2022 14:52:51 -0700
|
||||
Subject: [PATCH 10/19] UPSTREAM: mm: add vma_has_recency()
|
||||
|
||||
Add vma_has_recency() to indicate whether a VMA may exhibit temporal
|
||||
locality that the LRU algorithm relies on.
|
||||
|
||||
This function returns false for VMAs marked by VM_SEQ_READ or
|
||||
VM_RAND_READ. While the former flag indicates linear access, i.e., a
|
||||
special case of spatial locality, both flags indicate a lack of temporal
|
||||
locality, i.e., the reuse of an area within a relatively small duration.
|
||||
|
||||
"Recency" is chosen over "locality" to avoid confusion between temporal
|
||||
and spatial localities.
|
||||
|
||||
Before this patch, the active/inactive LRU only ignored the accessed bit
|
||||
from VMAs marked by VM_SEQ_READ. After this patch, the active/inactive
|
||||
LRU and MGLRU share the same logic: they both ignore the accessed bit if
|
||||
vma_has_recency() returns false.
|
||||
|
||||
For the active/inactive LRU, the following fio test showed a [6, 8]%
|
||||
increase in IOPS when randomly accessing mapped files under memory
|
||||
pressure.
|
||||
|
||||
kb=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
|
||||
kb=$((kb - 8*1024*1024))
|
||||
|
||||
modprobe brd rd_nr=1 rd_size=$kb
|
||||
dd if=/dev/zero of=/dev/ram0 bs=1M
|
||||
|
||||
mkfs.ext4 /dev/ram0
|
||||
mount /dev/ram0 /mnt/
|
||||
swapoff -a
|
||||
|
||||
fio --name=test --directory=/mnt/ --ioengine=mmap --numjobs=8 \
|
||||
--size=8G --rw=randrw --time_based --runtime=10m \
|
||||
--group_reporting
|
||||
|
||||
The discussion that led to this patch is here [1]. Additional test
|
||||
results are available in that thread.
|
||||
|
||||
[1] https://lore.kernel.org/r/Y31s%2FK8T85jh05wH@google.com/
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221230215252.2628425-1-yuzhao@google.com
|
||||
Change-Id: I291dcb795197659e40e46539cd32b857677c34ad
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
|
||||
Cc: Andrea Righi <andrea.righi@canonical.com>
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit 8788f6781486769d9598dcaedc3fe0eb12fc3e59)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
include/linux/mm_inline.h | 8 ++++++++
|
||||
mm/memory.c | 7 +++----
|
||||
mm/rmap.c | 42 +++++++++++++++++----------------------
|
||||
mm/vmscan.c | 5 ++++-
|
||||
4 files changed, 33 insertions(+), 29 deletions(-)
|
||||
|
||||
--- a/include/linux/mm_inline.h
|
||||
+++ b/include/linux/mm_inline.h
|
||||
@@ -600,4 +600,12 @@ pte_install_uffd_wp_if_needed(struct vm_
|
||||
#endif
|
||||
}
|
||||
|
||||
+static inline bool vma_has_recency(struct vm_area_struct *vma)
|
||||
+{
|
||||
+ if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))
|
||||
+ return false;
|
||||
+
|
||||
+ return true;
|
||||
+}
|
||||
+
|
||||
#endif
|
||||
--- a/mm/memory.c
|
||||
+++ b/mm/memory.c
|
||||
@@ -1445,8 +1445,7 @@ again:
|
||||
force_flush = 1;
|
||||
set_page_dirty(page);
|
||||
}
|
||||
- if (pte_young(ptent) &&
|
||||
- likely(!(vma->vm_flags & VM_SEQ_READ)))
|
||||
+ if (pte_young(ptent) && likely(vma_has_recency(vma)))
|
||||
mark_page_accessed(page);
|
||||
}
|
||||
rss[mm_counter(page)]--;
|
||||
@@ -5218,8 +5217,8 @@ static inline void mm_account_fault(stru
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
static void lru_gen_enter_fault(struct vm_area_struct *vma)
|
||||
{
|
||||
- /* the LRU algorithm doesn't apply to sequential or random reads */
|
||||
- current->in_lru_fault = !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ));
|
||||
+ /* the LRU algorithm only applies to accesses with recency */
|
||||
+ current->in_lru_fault = vma_has_recency(vma);
|
||||
}
|
||||
|
||||
static void lru_gen_exit_fault(void)
|
||||
--- a/mm/rmap.c
|
||||
+++ b/mm/rmap.c
|
||||
@@ -823,25 +823,14 @@ static bool folio_referenced_one(struct
|
||||
}
|
||||
|
||||
if (pvmw.pte) {
|
||||
- if (lru_gen_enabled() && pte_young(*pvmw.pte) &&
|
||||
- !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))) {
|
||||
+ if (lru_gen_enabled() && pte_young(*pvmw.pte)) {
|
||||
lru_gen_look_around(&pvmw);
|
||||
referenced++;
|
||||
}
|
||||
|
||||
if (ptep_clear_flush_young_notify(vma, address,
|
||||
- pvmw.pte)) {
|
||||
- /*
|
||||
- * Don't treat a reference through
|
||||
- * a sequentially read mapping as such.
|
||||
- * If the folio has been used in another mapping,
|
||||
- * we will catch it; if this other mapping is
|
||||
- * already gone, the unmap path will have set
|
||||
- * the referenced flag or activated the folio.
|
||||
- */
|
||||
- if (likely(!(vma->vm_flags & VM_SEQ_READ)))
|
||||
- referenced++;
|
||||
- }
|
||||
+ pvmw.pte))
|
||||
+ referenced++;
|
||||
} else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) {
|
||||
if (pmdp_clear_flush_young_notify(vma, address,
|
||||
pvmw.pmd))
|
||||
@@ -875,7 +864,20 @@ static bool invalid_folio_referenced_vma
|
||||
struct folio_referenced_arg *pra = arg;
|
||||
struct mem_cgroup *memcg = pra->memcg;
|
||||
|
||||
- if (!mm_match_cgroup(vma->vm_mm, memcg))
|
||||
+ /*
|
||||
+ * Ignore references from this mapping if it has no recency. If the
|
||||
+ * folio has been used in another mapping, we will catch it; if this
|
||||
+ * other mapping is already gone, the unmap path will have set the
|
||||
+ * referenced flag or activated the folio in zap_pte_range().
|
||||
+ */
|
||||
+ if (!vma_has_recency(vma))
|
||||
+ return true;
|
||||
+
|
||||
+ /*
|
||||
+ * If we are reclaiming on behalf of a cgroup, skip counting on behalf
|
||||
+ * of references from different cgroups.
|
||||
+ */
|
||||
+ if (memcg && !mm_match_cgroup(vma->vm_mm, memcg))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -906,6 +908,7 @@ int folio_referenced(struct folio *folio
|
||||
.arg = (void *)&pra,
|
||||
.anon_lock = folio_lock_anon_vma_read,
|
||||
.try_lock = true,
|
||||
+ .invalid_vma = invalid_folio_referenced_vma,
|
||||
};
|
||||
|
||||
*vm_flags = 0;
|
||||
@@ -921,15 +924,6 @@ int folio_referenced(struct folio *folio
|
||||
return 1;
|
||||
}
|
||||
|
||||
- /*
|
||||
- * If we are reclaiming on behalf of a cgroup, skip
|
||||
- * counting on behalf of references from different
|
||||
- * cgroups
|
||||
- */
|
||||
- if (memcg) {
|
||||
- rwc.invalid_vma = invalid_folio_referenced_vma;
|
||||
- }
|
||||
-
|
||||
rmap_walk(folio, &rwc);
|
||||
*vm_flags = pra.vm_flags;
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -3778,7 +3778,10 @@ static int should_skip_vma(unsigned long
|
||||
if (is_vm_hugetlb_page(vma))
|
||||
return true;
|
||||
|
||||
- if (vma->vm_flags & (VM_LOCKED | VM_SPECIAL | VM_SEQ_READ | VM_RAND_READ))
|
||||
+ if (!vma_has_recency(vma))
|
||||
+ return true;
|
||||
+
|
||||
+ if (vma->vm_flags & (VM_LOCKED | VM_SPECIAL))
|
||||
return true;
|
||||
|
||||
if (vma == get_gate_vma(vma->vm_mm))
|
||||
@@ -1,129 +0,0 @@
|
||||
From 9ca4e437a24dfc4ec6c362f319eb9850b9eca497 Mon Sep 17 00:00:00 2001
|
||||
From: Yu Zhao <yuzhao@google.com>
|
||||
Date: Fri, 30 Dec 2022 14:52:52 -0700
|
||||
Subject: [PATCH 11/19] UPSTREAM: mm: support POSIX_FADV_NOREUSE
|
||||
|
||||
This patch adds POSIX_FADV_NOREUSE to vma_has_recency() so that the LRU
|
||||
algorithm can ignore access to mapped files marked by this flag.
|
||||
|
||||
The advantages of POSIX_FADV_NOREUSE are:
|
||||
1. Unlike MADV_SEQUENTIAL and MADV_RANDOM, it does not alter the
|
||||
default readahead behavior.
|
||||
2. Unlike MADV_SEQUENTIAL and MADV_RANDOM, it does not split VMAs and
|
||||
therefore does not take mmap_lock.
|
||||
3. Unlike MADV_COLD, setting it has a negligible cost, regardless of
|
||||
how many pages it affects.
|
||||
|
||||
Its limitations are:
|
||||
1. Like POSIX_FADV_RANDOM and POSIX_FADV_SEQUENTIAL, it currently does
|
||||
not support range. IOW, its scope is the entire file.
|
||||
2. It currently does not ignore access through file descriptors.
|
||||
Specifically, for the active/inactive LRU, given a file page shared
|
||||
by two users and one of them having set POSIX_FADV_NOREUSE on the
|
||||
file, this page will be activated upon the second user accessing
|
||||
it. This corner case can be covered by checking POSIX_FADV_NOREUSE
|
||||
before calling folio_mark_accessed() on the read path. But it is
|
||||
considered not worth the effort.
|
||||
|
||||
There have been a few attempts to support POSIX_FADV_NOREUSE, e.g., [1].
|
||||
This time the goal is to fill a niche: a few desktop applications, e.g.,
|
||||
large file transferring and video encoding/decoding, want fast file
|
||||
streaming with mmap() rather than direct IO. Among those applications, an
|
||||
SVT-AV1 regression was reported when running with MGLRU [2]. The
|
||||
following test can reproduce that regression.
|
||||
|
||||
kb=$(awk '/MemTotal/ { print $2 }' /proc/meminfo)
|
||||
kb=$((kb - 8*1024*1024))
|
||||
|
||||
modprobe brd rd_nr=1 rd_size=$kb
|
||||
dd if=/dev/zero of=/dev/ram0 bs=1M
|
||||
|
||||
mkfs.ext4 /dev/ram0
|
||||
mount /dev/ram0 /mnt/
|
||||
swapoff -a
|
||||
|
||||
fallocate -l 8G /mnt/swapfile
|
||||
mkswap /mnt/swapfile
|
||||
swapon /mnt/swapfile
|
||||
|
||||
wget http://ultravideo.cs.tut.fi/video/Bosphorus_3840x2160_120fps_420_8bit_YUV_Y4M.7z
|
||||
7z e -o/mnt/ Bosphorus_3840x2160_120fps_420_8bit_YUV_Y4M.7z
|
||||
SvtAv1EncApp --preset 12 -w 3840 -h 2160 \
|
||||
-i /mnt/Bosphorus_3840x2160.y4m
|
||||
|
||||
For MGLRU, the following change showed a [9-11]% increase in FPS,
|
||||
which makes it on par with the active/inactive LRU.
|
||||
|
||||
patch Source/App/EncApp/EbAppMain.c <<EOF
|
||||
31a32
|
||||
> #include <fcntl.h>
|
||||
35d35
|
||||
< #include <fcntl.h> /* _O_BINARY */
|
||||
117a118
|
||||
> posix_fadvise(config->mmap.fd, 0, 0, POSIX_FADV_NOREUSE);
|
||||
EOF
|
||||
|
||||
[1] https://lore.kernel.org/r/1308923350-7932-1-git-send-email-andrea@betterlinux.com/
|
||||
[2] https://openbenchmarking.org/result/2209259-PTS-MGLRU8GB57
|
||||
|
||||
Link: https://lkml.kernel.org/r/20221230215252.2628425-2-yuzhao@google.com
|
||||
Change-Id: I0b7f5f971d78014ea1ba44cee6a8ec902a4330d0
|
||||
Signed-off-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
|
||||
Cc: Andrea Righi <andrea.righi@canonical.com>
|
||||
Cc: Johannes Weiner <hannes@cmpxchg.org>
|
||||
Cc: Michael Larabel <Michael@MichaelLarabel.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit 17e810229cb3068b692fa078bd9b3a6527e0866a)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
include/linux/fs.h | 2 ++
|
||||
include/linux/mm_inline.h | 3 +++
|
||||
mm/fadvise.c | 5 ++++-
|
||||
3 files changed, 9 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/include/linux/fs.h
|
||||
+++ b/include/linux/fs.h
|
||||
@@ -166,6 +166,8 @@ typedef int (dio_iodone_t)(struct kiocb
|
||||
/* File supports DIRECT IO */
|
||||
#define FMODE_CAN_ODIRECT ((__force fmode_t)0x400000)
|
||||
|
||||
+#define FMODE_NOREUSE ((__force fmode_t)0x800000)
|
||||
+
|
||||
/* File was opened by fanotify and shouldn't generate fanotify events */
|
||||
#define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
|
||||
|
||||
--- a/include/linux/mm_inline.h
|
||||
+++ b/include/linux/mm_inline.h
|
||||
@@ -605,6 +605,9 @@ static inline bool vma_has_recency(struc
|
||||
if (vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))
|
||||
return false;
|
||||
|
||||
+ if (vma->vm_file && (vma->vm_file->f_mode & FMODE_NOREUSE))
|
||||
+ return false;
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
--- a/mm/fadvise.c
|
||||
+++ b/mm/fadvise.c
|
||||
@@ -80,7 +80,7 @@ int generic_fadvise(struct file *file, l
|
||||
case POSIX_FADV_NORMAL:
|
||||
file->f_ra.ra_pages = bdi->ra_pages;
|
||||
spin_lock(&file->f_lock);
|
||||
- file->f_mode &= ~FMODE_RANDOM;
|
||||
+ file->f_mode &= ~(FMODE_RANDOM | FMODE_NOREUSE);
|
||||
spin_unlock(&file->f_lock);
|
||||
break;
|
||||
case POSIX_FADV_RANDOM:
|
||||
@@ -107,6 +107,9 @@ int generic_fadvise(struct file *file, l
|
||||
force_page_cache_readahead(mapping, file, start_index, nrpages);
|
||||
break;
|
||||
case POSIX_FADV_NOREUSE:
|
||||
+ spin_lock(&file->f_lock);
|
||||
+ file->f_mode |= FMODE_NOREUSE;
|
||||
+ spin_unlock(&file->f_lock);
|
||||
break;
|
||||
case POSIX_FADV_DONTNEED:
|
||||
__filemap_fdatawrite_range(mapping, offset, endbyte,
|
||||
@@ -1,67 +0,0 @@
|
||||
From 1b5e4c317d80f4826eceb3781702d18d06b14394 Mon Sep 17 00:00:00 2001
|
||||
From: "T.J. Alumbaugh" <talumbau@google.com>
|
||||
Date: Wed, 18 Jan 2023 00:18:21 +0000
|
||||
Subject: [PATCH 12/19] UPSTREAM: mm: multi-gen LRU: section for working set
|
||||
protection
|
||||
|
||||
Patch series "mm: multi-gen LRU: improve".
|
||||
|
||||
This patch series improves a few MGLRU functions, collects related
|
||||
functions, and adds additional documentation.
|
||||
|
||||
This patch (of 7):
|
||||
|
||||
Add a section for working set protection in the code and the design doc.
|
||||
The admin doc already contains its usage.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-1-talumbau@google.com
|
||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-2-talumbau@google.com
|
||||
Change-Id: I65599075fd42951db7739a2ab7cee78516e157b3
|
||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
|
||||
Cc: Yu Zhao <yuzhao@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit 7b8144e63d84716f16a1b929e0c7e03ae5c4d5c1)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
Documentation/mm/multigen_lru.rst | 15 +++++++++++++++
|
||||
mm/vmscan.c | 4 ++++
|
||||
2 files changed, 19 insertions(+)
|
||||
|
||||
--- a/Documentation/mm/multigen_lru.rst
|
||||
+++ b/Documentation/mm/multigen_lru.rst
|
||||
@@ -141,6 +141,21 @@ loop has detected outlying refaults from
|
||||
this end, the feedback loop uses the first tier as the baseline, for
|
||||
the reason stated earlier.
|
||||
|
||||
+Working set protection
|
||||
+----------------------
|
||||
+Each generation is timestamped at birth. If ``lru_gen_min_ttl`` is
|
||||
+set, an ``lruvec`` is protected from the eviction when its oldest
|
||||
+generation was born within ``lru_gen_min_ttl`` milliseconds. In other
|
||||
+words, it prevents the working set of ``lru_gen_min_ttl`` milliseconds
|
||||
+from getting evicted. The OOM killer is triggered if this working set
|
||||
+cannot be kept in memory.
|
||||
+
|
||||
+This time-based approach has the following advantages:
|
||||
+
|
||||
+1. It is easier to configure because it is agnostic to applications
|
||||
+ and memory sizes.
|
||||
+2. It is more reliable because it is directly wired to the OOM killer.
|
||||
+
|
||||
Summary
|
||||
-------
|
||||
The multi-gen LRU can be disassembled into the following parts:
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -4461,6 +4461,10 @@ done:
|
||||
return true;
|
||||
}
|
||||
|
||||
+/******************************************************************************
|
||||
+ * working set protection
|
||||
+ ******************************************************************************/
|
||||
+
|
||||
static bool lruvec_is_sizable(struct lruvec *lruvec, struct scan_control *sc)
|
||||
{
|
||||
int gen, type, zone;
|
||||
@@ -1,57 +0,0 @@
|
||||
From 5ddf9d53d375e42af49b744bd7c2f8247c6bce15 Mon Sep 17 00:00:00 2001
|
||||
From: "T.J. Alumbaugh" <talumbau@google.com>
|
||||
Date: Wed, 18 Jan 2023 00:18:22 +0000
|
||||
Subject: [PATCH 13/19] UPSTREAM: mm: multi-gen LRU: section for rmap/PT walk
|
||||
feedback
|
||||
|
||||
Add a section for lru_gen_look_around() in the code and the design doc.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-3-talumbau@google.com
|
||||
Change-Id: I5097af63f61b3b69ec2abee6cdbdc33c296df213
|
||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
|
||||
Cc: Yu Zhao <yuzhao@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit db19a43d9b3a8876552f00f656008206ef9a5efa)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
Documentation/mm/multigen_lru.rst | 14 ++++++++++++++
|
||||
mm/vmscan.c | 4 ++++
|
||||
2 files changed, 18 insertions(+)
|
||||
|
||||
--- a/Documentation/mm/multigen_lru.rst
|
||||
+++ b/Documentation/mm/multigen_lru.rst
|
||||
@@ -156,6 +156,20 @@ This time-based approach has the followi
|
||||
and memory sizes.
|
||||
2. It is more reliable because it is directly wired to the OOM killer.
|
||||
|
||||
+Rmap/PT walk feedback
|
||||
+---------------------
|
||||
+Searching the rmap for PTEs mapping each page on an LRU list (to test
|
||||
+and clear the accessed bit) can be expensive because pages from
|
||||
+different VMAs (PA space) are not cache friendly to the rmap (VA
|
||||
+space). For workloads mostly using mapped pages, searching the rmap
|
||||
+can incur the highest CPU cost in the reclaim path.
|
||||
+
|
||||
+``lru_gen_look_around()`` exploits spatial locality to reduce the
|
||||
+trips into the rmap. It scans the adjacent PTEs of a young PTE and
|
||||
+promotes hot pages. If the scan was done cacheline efficiently, it
|
||||
+adds the PMD entry pointing to the PTE table to the Bloom filter. This
|
||||
+forms a feedback loop between the eviction and the aging.
|
||||
+
|
||||
Summary
|
||||
-------
|
||||
The multi-gen LRU can be disassembled into the following parts:
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -4555,6 +4555,10 @@ static void lru_gen_age_node(struct pgli
|
||||
}
|
||||
}
|
||||
|
||||
+/******************************************************************************
|
||||
+ * rmap/PT walk feedback
|
||||
+ ******************************************************************************/
|
||||
+
|
||||
/*
|
||||
* This function exploits spatial locality when shrink_folio_list() walks the
|
||||
* rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages. If
|
||||
@@ -1,243 +0,0 @@
|
||||
From 397624e12244ec038f51cb1f178ccb7a2ec562e5 Mon Sep 17 00:00:00 2001
|
||||
From: "T.J. Alumbaugh" <talumbau@google.com>
|
||||
Date: Wed, 18 Jan 2023 00:18:23 +0000
|
||||
Subject: [PATCH 14/19] UPSTREAM: mm: multi-gen LRU: section for Bloom filters
|
||||
|
||||
Move Bloom filters code into a dedicated section. Improve the design doc
|
||||
to explain Bloom filter usage and connection between aging and eviction in
|
||||
their use.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-4-talumbau@google.com
|
||||
Change-Id: I73e866f687c1ed9f5c8538086aa39408b79897db
|
||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
|
||||
Cc: Yu Zhao <yuzhao@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit ccbbbb85945d8f0255aa9dbc1b617017e2294f2c)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
Documentation/mm/multigen_lru.rst | 16 +++
|
||||
mm/vmscan.c | 180 +++++++++++++++---------------
|
||||
2 files changed, 108 insertions(+), 88 deletions(-)
|
||||
|
||||
--- a/Documentation/mm/multigen_lru.rst
|
||||
+++ b/Documentation/mm/multigen_lru.rst
|
||||
@@ -170,6 +170,22 @@ promotes hot pages. If the scan was done
|
||||
adds the PMD entry pointing to the PTE table to the Bloom filter. This
|
||||
forms a feedback loop between the eviction and the aging.
|
||||
|
||||
+Bloom Filters
|
||||
+-------------
|
||||
+Bloom filters are a space and memory efficient data structure for set
|
||||
+membership test, i.e., test if an element is not in the set or may be
|
||||
+in the set.
|
||||
+
|
||||
+In the eviction path, specifically, in ``lru_gen_look_around()``, if a
|
||||
+PMD has a sufficient number of hot pages, its address is placed in the
|
||||
+filter. In the aging path, set membership means that the PTE range
|
||||
+will be scanned for young pages.
|
||||
+
|
||||
+Note that Bloom filters are probabilistic on set membership. If a test
|
||||
+is false positive, the cost is an additional scan of a range of PTEs,
|
||||
+which may yield hot pages anyway. Parameters of the filter itself can
|
||||
+control the false positive rate in the limit.
|
||||
+
|
||||
Summary
|
||||
-------
|
||||
The multi-gen LRU can be disassembled into the following parts:
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -3209,6 +3209,98 @@ static bool __maybe_unused seq_is_valid(
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
+ * Bloom filters
|
||||
+ ******************************************************************************/
|
||||
+
|
||||
+/*
|
||||
+ * Bloom filters with m=1<<15, k=2 and the false positive rates of ~1/5 when
|
||||
+ * n=10,000 and ~1/2 when n=20,000, where, conventionally, m is the number of
|
||||
+ * bits in a bitmap, k is the number of hash functions and n is the number of
|
||||
+ * inserted items.
|
||||
+ *
|
||||
+ * Page table walkers use one of the two filters to reduce their search space.
|
||||
+ * To get rid of non-leaf entries that no longer have enough leaf entries, the
|
||||
+ * aging uses the double-buffering technique to flip to the other filter each
|
||||
+ * time it produces a new generation. For non-leaf entries that have enough
|
||||
+ * leaf entries, the aging carries them over to the next generation in
|
||||
+ * walk_pmd_range(); the eviction also report them when walking the rmap
|
||||
+ * in lru_gen_look_around().
|
||||
+ *
|
||||
+ * For future optimizations:
|
||||
+ * 1. It's not necessary to keep both filters all the time. The spare one can be
|
||||
+ * freed after the RCU grace period and reallocated if needed again.
|
||||
+ * 2. And when reallocating, it's worth scaling its size according to the number
|
||||
+ * of inserted entries in the other filter, to reduce the memory overhead on
|
||||
+ * small systems and false positives on large systems.
|
||||
+ * 3. Jenkins' hash function is an alternative to Knuth's.
|
||||
+ */
|
||||
+#define BLOOM_FILTER_SHIFT 15
|
||||
+
|
||||
+static inline int filter_gen_from_seq(unsigned long seq)
|
||||
+{
|
||||
+ return seq % NR_BLOOM_FILTERS;
|
||||
+}
|
||||
+
|
||||
+static void get_item_key(void *item, int *key)
|
||||
+{
|
||||
+ u32 hash = hash_ptr(item, BLOOM_FILTER_SHIFT * 2);
|
||||
+
|
||||
+ BUILD_BUG_ON(BLOOM_FILTER_SHIFT * 2 > BITS_PER_TYPE(u32));
|
||||
+
|
||||
+ key[0] = hash & (BIT(BLOOM_FILTER_SHIFT) - 1);
|
||||
+ key[1] = hash >> BLOOM_FILTER_SHIFT;
|
||||
+}
|
||||
+
|
||||
+static bool test_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
|
||||
+{
|
||||
+ int key[2];
|
||||
+ unsigned long *filter;
|
||||
+ int gen = filter_gen_from_seq(seq);
|
||||
+
|
||||
+ filter = READ_ONCE(lruvec->mm_state.filters[gen]);
|
||||
+ if (!filter)
|
||||
+ return true;
|
||||
+
|
||||
+ get_item_key(item, key);
|
||||
+
|
||||
+ return test_bit(key[0], filter) && test_bit(key[1], filter);
|
||||
+}
|
||||
+
|
||||
+static void update_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
|
||||
+{
|
||||
+ int key[2];
|
||||
+ unsigned long *filter;
|
||||
+ int gen = filter_gen_from_seq(seq);
|
||||
+
|
||||
+ filter = READ_ONCE(lruvec->mm_state.filters[gen]);
|
||||
+ if (!filter)
|
||||
+ return;
|
||||
+
|
||||
+ get_item_key(item, key);
|
||||
+
|
||||
+ if (!test_bit(key[0], filter))
|
||||
+ set_bit(key[0], filter);
|
||||
+ if (!test_bit(key[1], filter))
|
||||
+ set_bit(key[1], filter);
|
||||
+}
|
||||
+
|
||||
+static void reset_bloom_filter(struct lruvec *lruvec, unsigned long seq)
|
||||
+{
|
||||
+ unsigned long *filter;
|
||||
+ int gen = filter_gen_from_seq(seq);
|
||||
+
|
||||
+ filter = lruvec->mm_state.filters[gen];
|
||||
+ if (filter) {
|
||||
+ bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT),
|
||||
+ __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
|
||||
+ WRITE_ONCE(lruvec->mm_state.filters[gen], filter);
|
||||
+}
|
||||
+
|
||||
+/******************************************************************************
|
||||
* mm_struct list
|
||||
******************************************************************************/
|
||||
|
||||
@@ -3333,94 +3425,6 @@ void lru_gen_migrate_mm(struct mm_struct
|
||||
}
|
||||
#endif
|
||||
|
||||
-/*
|
||||
- * Bloom filters with m=1<<15, k=2 and the false positive rates of ~1/5 when
|
||||
- * n=10,000 and ~1/2 when n=20,000, where, conventionally, m is the number of
|
||||
- * bits in a bitmap, k is the number of hash functions and n is the number of
|
||||
- * inserted items.
|
||||
- *
|
||||
- * Page table walkers use one of the two filters to reduce their search space.
|
||||
- * To get rid of non-leaf entries that no longer have enough leaf entries, the
|
||||
- * aging uses the double-buffering technique to flip to the other filter each
|
||||
- * time it produces a new generation. For non-leaf entries that have enough
|
||||
- * leaf entries, the aging carries them over to the next generation in
|
||||
- * walk_pmd_range(); the eviction also report them when walking the rmap
|
||||
- * in lru_gen_look_around().
|
||||
- *
|
||||
- * For future optimizations:
|
||||
- * 1. It's not necessary to keep both filters all the time. The spare one can be
|
||||
- * freed after the RCU grace period and reallocated if needed again.
|
||||
- * 2. And when reallocating, it's worth scaling its size according to the number
|
||||
- * of inserted entries in the other filter, to reduce the memory overhead on
|
||||
- * small systems and false positives on large systems.
|
||||
- * 3. Jenkins' hash function is an alternative to Knuth's.
|
||||
- */
|
||||
-#define BLOOM_FILTER_SHIFT 15
|
||||
-
|
||||
-static inline int filter_gen_from_seq(unsigned long seq)
|
||||
-{
|
||||
- return seq % NR_BLOOM_FILTERS;
|
||||
-}
|
||||
-
|
||||
-static void get_item_key(void *item, int *key)
|
||||
-{
|
||||
- u32 hash = hash_ptr(item, BLOOM_FILTER_SHIFT * 2);
|
||||
-
|
||||
- BUILD_BUG_ON(BLOOM_FILTER_SHIFT * 2 > BITS_PER_TYPE(u32));
|
||||
-
|
||||
- key[0] = hash & (BIT(BLOOM_FILTER_SHIFT) - 1);
|
||||
- key[1] = hash >> BLOOM_FILTER_SHIFT;
|
||||
-}
|
||||
-
|
||||
-static void reset_bloom_filter(struct lruvec *lruvec, unsigned long seq)
|
||||
-{
|
||||
- unsigned long *filter;
|
||||
- int gen = filter_gen_from_seq(seq);
|
||||
-
|
||||
- filter = lruvec->mm_state.filters[gen];
|
||||
- if (filter) {
|
||||
- bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT));
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT),
|
||||
- __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN);
|
||||
- WRITE_ONCE(lruvec->mm_state.filters[gen], filter);
|
||||
-}
|
||||
-
|
||||
-static void update_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
|
||||
-{
|
||||
- int key[2];
|
||||
- unsigned long *filter;
|
||||
- int gen = filter_gen_from_seq(seq);
|
||||
-
|
||||
- filter = READ_ONCE(lruvec->mm_state.filters[gen]);
|
||||
- if (!filter)
|
||||
- return;
|
||||
-
|
||||
- get_item_key(item, key);
|
||||
-
|
||||
- if (!test_bit(key[0], filter))
|
||||
- set_bit(key[0], filter);
|
||||
- if (!test_bit(key[1], filter))
|
||||
- set_bit(key[1], filter);
|
||||
-}
|
||||
-
|
||||
-static bool test_bloom_filter(struct lruvec *lruvec, unsigned long seq, void *item)
|
||||
-{
|
||||
- int key[2];
|
||||
- unsigned long *filter;
|
||||
- int gen = filter_gen_from_seq(seq);
|
||||
-
|
||||
- filter = READ_ONCE(lruvec->mm_state.filters[gen]);
|
||||
- if (!filter)
|
||||
- return true;
|
||||
-
|
||||
- get_item_key(item, key);
|
||||
-
|
||||
- return test_bit(key[0], filter) && test_bit(key[1], filter);
|
||||
-}
|
||||
-
|
||||
static void reset_mm_stats(struct lruvec *lruvec, struct lru_gen_mm_walk *walk, bool last)
|
||||
{
|
||||
int i;
|
||||
@@ -1,427 +0,0 @@
|
||||
From 48c916b812652f9453be5bd45a703728926d41ca Mon Sep 17 00:00:00 2001
|
||||
From: "T.J. Alumbaugh" <talumbau@google.com>
|
||||
Date: Wed, 18 Jan 2023 00:18:24 +0000
|
||||
Subject: [PATCH 15/19] UPSTREAM: mm: multi-gen LRU: section for memcg LRU
|
||||
|
||||
Move memcg LRU code into a dedicated section. Improve the design doc to
|
||||
outline its architecture.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-5-talumbau@google.com
|
||||
Change-Id: Id252e420cff7a858acb098cf2b3642da5c40f602
|
||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
|
||||
Cc: Yu Zhao <yuzhao@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit 36c7b4db7c942ae9e1b111f0c6b468c8b2e33842)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
Documentation/mm/multigen_lru.rst | 33 +++-
|
||||
include/linux/mm_inline.h | 17 --
|
||||
include/linux/mmzone.h | 13 +-
|
||||
mm/memcontrol.c | 8 +-
|
||||
mm/vmscan.c | 250 +++++++++++++++++-------------
|
||||
5 files changed, 178 insertions(+), 143 deletions(-)
|
||||
|
||||
--- a/Documentation/mm/multigen_lru.rst
|
||||
+++ b/Documentation/mm/multigen_lru.rst
|
||||
@@ -186,9 +186,40 @@ is false positive, the cost is an additi
|
||||
which may yield hot pages anyway. Parameters of the filter itself can
|
||||
control the false positive rate in the limit.
|
||||
|
||||
+Memcg LRU
|
||||
+---------
|
||||
+An memcg LRU is a per-node LRU of memcgs. It is also an LRU of LRUs,
|
||||
+since each node and memcg combination has an LRU of folios (see
|
||||
+``mem_cgroup_lruvec()``). Its goal is to improve the scalability of
|
||||
+global reclaim, which is critical to system-wide memory overcommit in
|
||||
+data centers. Note that memcg LRU only applies to global reclaim.
|
||||
+
|
||||
+The basic structure of an memcg LRU can be understood by an analogy to
|
||||
+the active/inactive LRU (of folios):
|
||||
+
|
||||
+1. It has the young and the old (generations), i.e., the counterparts
|
||||
+ to the active and the inactive;
|
||||
+2. The increment of ``max_seq`` triggers promotion, i.e., the
|
||||
+ counterpart to activation;
|
||||
+3. Other events trigger similar operations, e.g., offlining an memcg
|
||||
+ triggers demotion, i.e., the counterpart to deactivation.
|
||||
+
|
||||
+In terms of global reclaim, it has two distinct features:
|
||||
+
|
||||
+1. Sharding, which allows each thread to start at a random memcg (in
|
||||
+ the old generation) and improves parallelism;
|
||||
+2. Eventual fairness, which allows direct reclaim to bail out at will
|
||||
+ and reduces latency without affecting fairness over some time.
|
||||
+
|
||||
+In terms of traversing memcgs during global reclaim, it improves the
|
||||
+best-case complexity from O(n) to O(1) and does not affect the
|
||||
+worst-case complexity O(n). Therefore, on average, it has a sublinear
|
||||
+complexity.
|
||||
+
|
||||
Summary
|
||||
-------
|
||||
-The multi-gen LRU can be disassembled into the following parts:
|
||||
+The multi-gen LRU (of folios) can be disassembled into the following
|
||||
+parts:
|
||||
|
||||
* Generations
|
||||
* Rmap walks
|
||||
--- a/include/linux/mm_inline.h
|
||||
+++ b/include/linux/mm_inline.h
|
||||
@@ -122,18 +122,6 @@ static inline bool lru_gen_in_fault(void
|
||||
return current->in_lru_fault;
|
||||
}
|
||||
|
||||
-#ifdef CONFIG_MEMCG
|
||||
-static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
|
||||
-{
|
||||
- return READ_ONCE(lruvec->lrugen.seg);
|
||||
-}
|
||||
-#else
|
||||
-static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
|
||||
-{
|
||||
- return 0;
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
static inline int lru_gen_from_seq(unsigned long seq)
|
||||
{
|
||||
return seq % MAX_NR_GENS;
|
||||
@@ -314,11 +302,6 @@ static inline bool lru_gen_in_fault(void
|
||||
return false;
|
||||
}
|
||||
|
||||
-static inline int lru_gen_memcg_seg(struct lruvec *lruvec)
|
||||
-{
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static inline bool lru_gen_add_folio(struct lruvec *lruvec, struct folio *folio, bool reclaiming)
|
||||
{
|
||||
return false;
|
||||
--- a/include/linux/mmzone.h
|
||||
+++ b/include/linux/mmzone.h
|
||||
@@ -368,15 +368,6 @@ struct page_vma_mapped_walk;
|
||||
#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF)
|
||||
#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF)
|
||||
|
||||
-/* see the comment on MEMCG_NR_GENS */
|
||||
-enum {
|
||||
- MEMCG_LRU_NOP,
|
||||
- MEMCG_LRU_HEAD,
|
||||
- MEMCG_LRU_TAIL,
|
||||
- MEMCG_LRU_OLD,
|
||||
- MEMCG_LRU_YOUNG,
|
||||
-};
|
||||
-
|
||||
#ifdef CONFIG_LRU_GEN
|
||||
|
||||
enum {
|
||||
@@ -557,7 +548,7 @@ void lru_gen_exit_memcg(struct mem_cgrou
|
||||
void lru_gen_online_memcg(struct mem_cgroup *memcg);
|
||||
void lru_gen_offline_memcg(struct mem_cgroup *memcg);
|
||||
void lru_gen_release_memcg(struct mem_cgroup *memcg);
|
||||
-void lru_gen_rotate_memcg(struct lruvec *lruvec, int op);
|
||||
+void lru_gen_soft_reclaim(struct lruvec *lruvec);
|
||||
|
||||
#else /* !CONFIG_MEMCG */
|
||||
|
||||
@@ -608,7 +599,7 @@ static inline void lru_gen_release_memcg
|
||||
{
|
||||
}
|
||||
|
||||
-static inline void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
|
||||
+static inline void lru_gen_soft_reclaim(struct lruvec *lruvec)
|
||||
{
|
||||
}
|
||||
|
||||
--- a/mm/memcontrol.c
|
||||
+++ b/mm/memcontrol.c
|
||||
@@ -478,12 +478,8 @@ static void mem_cgroup_update_tree(struc
|
||||
struct mem_cgroup_tree_per_node *mctz;
|
||||
|
||||
if (lru_gen_enabled()) {
|
||||
- struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec;
|
||||
-
|
||||
- /* see the comment on MEMCG_NR_GENS */
|
||||
- if (soft_limit_excess(memcg) && lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
|
||||
- lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
|
||||
-
|
||||
+ if (soft_limit_excess(memcg))
|
||||
+ lru_gen_soft_reclaim(&memcg->nodeinfo[nid]->lruvec);
|
||||
return;
|
||||
}
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -4692,6 +4692,148 @@ void lru_gen_look_around(struct page_vma
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
+ * memcg LRU
|
||||
+ ******************************************************************************/
|
||||
+
|
||||
+/* see the comment on MEMCG_NR_GENS */
|
||||
+enum {
|
||||
+ MEMCG_LRU_NOP,
|
||||
+ MEMCG_LRU_HEAD,
|
||||
+ MEMCG_LRU_TAIL,
|
||||
+ MEMCG_LRU_OLD,
|
||||
+ MEMCG_LRU_YOUNG,
|
||||
+};
|
||||
+
|
||||
+#ifdef CONFIG_MEMCG
|
||||
+
|
||||
+static int lru_gen_memcg_seg(struct lruvec *lruvec)
|
||||
+{
|
||||
+ return READ_ONCE(lruvec->lrugen.seg);
|
||||
+}
|
||||
+
|
||||
+static void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
|
||||
+{
|
||||
+ int seg;
|
||||
+ int old, new;
|
||||
+ int bin = get_random_u32_below(MEMCG_NR_BINS);
|
||||
+ struct pglist_data *pgdat = lruvec_pgdat(lruvec);
|
||||
+
|
||||
+ spin_lock(&pgdat->memcg_lru.lock);
|
||||
+
|
||||
+ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
+
|
||||
+ seg = 0;
|
||||
+ new = old = lruvec->lrugen.gen;
|
||||
+
|
||||
+ /* see the comment on MEMCG_NR_GENS */
|
||||
+ if (op == MEMCG_LRU_HEAD)
|
||||
+ seg = MEMCG_LRU_HEAD;
|
||||
+ else if (op == MEMCG_LRU_TAIL)
|
||||
+ seg = MEMCG_LRU_TAIL;
|
||||
+ else if (op == MEMCG_LRU_OLD)
|
||||
+ new = get_memcg_gen(pgdat->memcg_lru.seq);
|
||||
+ else if (op == MEMCG_LRU_YOUNG)
|
||||
+ new = get_memcg_gen(pgdat->memcg_lru.seq + 1);
|
||||
+ else
|
||||
+ VM_WARN_ON_ONCE(true);
|
||||
+
|
||||
+ hlist_nulls_del_rcu(&lruvec->lrugen.list);
|
||||
+
|
||||
+ if (op == MEMCG_LRU_HEAD || op == MEMCG_LRU_OLD)
|
||||
+ hlist_nulls_add_head_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
|
||||
+ else
|
||||
+ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
|
||||
+
|
||||
+ pgdat->memcg_lru.nr_memcgs[old]--;
|
||||
+ pgdat->memcg_lru.nr_memcgs[new]++;
|
||||
+
|
||||
+ lruvec->lrugen.gen = new;
|
||||
+ WRITE_ONCE(lruvec->lrugen.seg, seg);
|
||||
+
|
||||
+ if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq))
|
||||
+ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
|
||||
+
|
||||
+ spin_unlock(&pgdat->memcg_lru.lock);
|
||||
+}
|
||||
+
|
||||
+void lru_gen_online_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+ int gen;
|
||||
+ int nid;
|
||||
+ int bin = get_random_u32_below(MEMCG_NR_BINS);
|
||||
+
|
||||
+ for_each_node(nid) {
|
||||
+ struct pglist_data *pgdat = NODE_DATA(nid);
|
||||
+ struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
+
|
||||
+ spin_lock(&pgdat->memcg_lru.lock);
|
||||
+
|
||||
+ VM_WARN_ON_ONCE(!hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
+
|
||||
+ gen = get_memcg_gen(pgdat->memcg_lru.seq);
|
||||
+
|
||||
+ hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[gen][bin]);
|
||||
+ pgdat->memcg_lru.nr_memcgs[gen]++;
|
||||
+
|
||||
+ lruvec->lrugen.gen = gen;
|
||||
+
|
||||
+ spin_unlock(&pgdat->memcg_lru.lock);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void lru_gen_offline_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+ int nid;
|
||||
+
|
||||
+ for_each_node(nid) {
|
||||
+ struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
+
|
||||
+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void lru_gen_release_memcg(struct mem_cgroup *memcg)
|
||||
+{
|
||||
+ int gen;
|
||||
+ int nid;
|
||||
+
|
||||
+ for_each_node(nid) {
|
||||
+ struct pglist_data *pgdat = NODE_DATA(nid);
|
||||
+ struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
+
|
||||
+ spin_lock(&pgdat->memcg_lru.lock);
|
||||
+
|
||||
+ VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
+
|
||||
+ gen = lruvec->lrugen.gen;
|
||||
+
|
||||
+ hlist_nulls_del_rcu(&lruvec->lrugen.list);
|
||||
+ pgdat->memcg_lru.nr_memcgs[gen]--;
|
||||
+
|
||||
+ if (!pgdat->memcg_lru.nr_memcgs[gen] && gen == get_memcg_gen(pgdat->memcg_lru.seq))
|
||||
+ WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
|
||||
+
|
||||
+ spin_unlock(&pgdat->memcg_lru.lock);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+void lru_gen_soft_reclaim(struct lruvec *lruvec)
|
||||
+{
|
||||
+ /* see the comment on MEMCG_NR_GENS */
|
||||
+ if (lru_gen_memcg_seg(lruvec) != MEMCG_LRU_HEAD)
|
||||
+ lru_gen_rotate_memcg(lruvec, MEMCG_LRU_HEAD);
|
||||
+}
|
||||
+
|
||||
+#else /* !CONFIG_MEMCG */
|
||||
+
|
||||
+static int lru_gen_memcg_seg(struct lruvec *lruvec)
|
||||
+{
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#endif
|
||||
+
|
||||
+/******************************************************************************
|
||||
* the eviction
|
||||
******************************************************************************/
|
||||
|
||||
@@ -5397,53 +5539,6 @@ done:
|
||||
pgdat->kswapd_failures = 0;
|
||||
}
|
||||
|
||||
-#ifdef CONFIG_MEMCG
|
||||
-void lru_gen_rotate_memcg(struct lruvec *lruvec, int op)
|
||||
-{
|
||||
- int seg;
|
||||
- int old, new;
|
||||
- int bin = get_random_u32_below(MEMCG_NR_BINS);
|
||||
- struct pglist_data *pgdat = lruvec_pgdat(lruvec);
|
||||
-
|
||||
- spin_lock(&pgdat->memcg_lru.lock);
|
||||
-
|
||||
- VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
-
|
||||
- seg = 0;
|
||||
- new = old = lruvec->lrugen.gen;
|
||||
-
|
||||
- /* see the comment on MEMCG_NR_GENS */
|
||||
- if (op == MEMCG_LRU_HEAD)
|
||||
- seg = MEMCG_LRU_HEAD;
|
||||
- else if (op == MEMCG_LRU_TAIL)
|
||||
- seg = MEMCG_LRU_TAIL;
|
||||
- else if (op == MEMCG_LRU_OLD)
|
||||
- new = get_memcg_gen(pgdat->memcg_lru.seq);
|
||||
- else if (op == MEMCG_LRU_YOUNG)
|
||||
- new = get_memcg_gen(pgdat->memcg_lru.seq + 1);
|
||||
- else
|
||||
- VM_WARN_ON_ONCE(true);
|
||||
-
|
||||
- hlist_nulls_del_rcu(&lruvec->lrugen.list);
|
||||
-
|
||||
- if (op == MEMCG_LRU_HEAD || op == MEMCG_LRU_OLD)
|
||||
- hlist_nulls_add_head_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
|
||||
- else
|
||||
- hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[new][bin]);
|
||||
-
|
||||
- pgdat->memcg_lru.nr_memcgs[old]--;
|
||||
- pgdat->memcg_lru.nr_memcgs[new]++;
|
||||
-
|
||||
- lruvec->lrugen.gen = new;
|
||||
- WRITE_ONCE(lruvec->lrugen.seg, seg);
|
||||
-
|
||||
- if (!pgdat->memcg_lru.nr_memcgs[old] && old == get_memcg_gen(pgdat->memcg_lru.seq))
|
||||
- WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
|
||||
-
|
||||
- spin_unlock(&pgdat->memcg_lru.lock);
|
||||
-}
|
||||
-#endif
|
||||
-
|
||||
/******************************************************************************
|
||||
* state change
|
||||
******************************************************************************/
|
||||
@@ -6089,67 +6184,6 @@ void lru_gen_exit_memcg(struct mem_cgrou
|
||||
}
|
||||
}
|
||||
|
||||
-void lru_gen_online_memcg(struct mem_cgroup *memcg)
|
||||
-{
|
||||
- int gen;
|
||||
- int nid;
|
||||
- int bin = get_random_u32_below(MEMCG_NR_BINS);
|
||||
-
|
||||
- for_each_node(nid) {
|
||||
- struct pglist_data *pgdat = NODE_DATA(nid);
|
||||
- struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
-
|
||||
- spin_lock(&pgdat->memcg_lru.lock);
|
||||
-
|
||||
- VM_WARN_ON_ONCE(!hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
-
|
||||
- gen = get_memcg_gen(pgdat->memcg_lru.seq);
|
||||
-
|
||||
- hlist_nulls_add_tail_rcu(&lruvec->lrugen.list, &pgdat->memcg_lru.fifo[gen][bin]);
|
||||
- pgdat->memcg_lru.nr_memcgs[gen]++;
|
||||
-
|
||||
- lruvec->lrugen.gen = gen;
|
||||
-
|
||||
- spin_unlock(&pgdat->memcg_lru.lock);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-void lru_gen_offline_memcg(struct mem_cgroup *memcg)
|
||||
-{
|
||||
- int nid;
|
||||
-
|
||||
- for_each_node(nid) {
|
||||
- struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
-
|
||||
- lru_gen_rotate_memcg(lruvec, MEMCG_LRU_OLD);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-void lru_gen_release_memcg(struct mem_cgroup *memcg)
|
||||
-{
|
||||
- int gen;
|
||||
- int nid;
|
||||
-
|
||||
- for_each_node(nid) {
|
||||
- struct pglist_data *pgdat = NODE_DATA(nid);
|
||||
- struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
-
|
||||
- spin_lock(&pgdat->memcg_lru.lock);
|
||||
-
|
||||
- VM_WARN_ON_ONCE(hlist_nulls_unhashed(&lruvec->lrugen.list));
|
||||
-
|
||||
- gen = lruvec->lrugen.gen;
|
||||
-
|
||||
- hlist_nulls_del_rcu(&lruvec->lrugen.list);
|
||||
- pgdat->memcg_lru.nr_memcgs[gen]--;
|
||||
-
|
||||
- if (!pgdat->memcg_lru.nr_memcgs[gen] && gen == get_memcg_gen(pgdat->memcg_lru.seq))
|
||||
- WRITE_ONCE(pgdat->memcg_lru.seq, pgdat->memcg_lru.seq + 1);
|
||||
-
|
||||
- spin_unlock(&pgdat->memcg_lru.lock);
|
||||
- }
|
||||
-}
|
||||
-
|
||||
#endif /* CONFIG_MEMCG */
|
||||
|
||||
static int __init init_lru_gen(void)
|
||||
@@ -1,40 +0,0 @@
|
||||
From bec433f29537652ed054148edfd7e2183ddcf7c3 Mon Sep 17 00:00:00 2001
|
||||
From: "T.J. Alumbaugh" <talumbau@google.com>
|
||||
Date: Wed, 18 Jan 2023 00:18:25 +0000
|
||||
Subject: [PATCH 16/19] UPSTREAM: mm: multi-gen LRU: improve
|
||||
lru_gen_exit_memcg()
|
||||
|
||||
Add warnings and poison ->next.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-6-talumbau@google.com
|
||||
Change-Id: I53de9e04c1ae941e122b33cd45d2bbb5f34aae0c
|
||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
|
||||
Cc: Yu Zhao <yuzhao@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit 37cc99979d04cca677c0ad5c0acd1149ec165d1b)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -6171,12 +6171,17 @@ void lru_gen_exit_memcg(struct mem_cgrou
|
||||
int i;
|
||||
int nid;
|
||||
|
||||
+ VM_WARN_ON_ONCE(!list_empty(&memcg->mm_list.fifo));
|
||||
+
|
||||
for_each_node(nid) {
|
||||
struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
|
||||
+ VM_WARN_ON_ONCE(lruvec->mm_state.nr_walkers);
|
||||
VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0,
|
||||
sizeof(lruvec->lrugen.nr_pages)));
|
||||
|
||||
+ lruvec->lrugen.list.next = LIST_POISON1;
|
||||
+
|
||||
for (i = 0; i < NR_BLOOM_FILTERS; i++) {
|
||||
bitmap_free(lruvec->mm_state.filters[i]);
|
||||
lruvec->mm_state.filters[i] = NULL;
|
||||
@@ -1,135 +0,0 @@
|
||||
From fc0e3b06e0f19917b7ecad7967a72f61d4743644 Mon Sep 17 00:00:00 2001
|
||||
From: "T.J. Alumbaugh" <talumbau@google.com>
|
||||
Date: Wed, 18 Jan 2023 00:18:26 +0000
|
||||
Subject: [PATCH 17/19] UPSTREAM: mm: multi-gen LRU: improve walk_pmd_range()
|
||||
|
||||
Improve readability of walk_pmd_range() and walk_pmd_range_locked().
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-7-talumbau@google.com
|
||||
Change-Id: Ia084fbf53fe989673b7804ca8ca520af12d7d52a
|
||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
|
||||
Cc: Yu Zhao <yuzhao@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit b5ff4133617d0eced35b685da0bd0929dd9fabb7)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 40 ++++++++++++++++++++--------------------
|
||||
1 file changed, 20 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -3980,8 +3980,8 @@ restart:
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)
|
||||
-static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area_struct *vma,
|
||||
- struct mm_walk *args, unsigned long *bitmap, unsigned long *start)
|
||||
+static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area_struct *vma,
|
||||
+ struct mm_walk *args, unsigned long *bitmap, unsigned long *first)
|
||||
{
|
||||
int i;
|
||||
pmd_t *pmd;
|
||||
@@ -3994,18 +3994,19 @@ static void walk_pmd_range_locked(pud_t
|
||||
VM_WARN_ON_ONCE(pud_leaf(*pud));
|
||||
|
||||
/* try to batch at most 1+MIN_LRU_BATCH+1 entries */
|
||||
- if (*start == -1) {
|
||||
- *start = next;
|
||||
+ if (*first == -1) {
|
||||
+ *first = addr;
|
||||
+ bitmap_zero(bitmap, MIN_LRU_BATCH);
|
||||
return;
|
||||
}
|
||||
|
||||
- i = next == -1 ? 0 : pmd_index(next) - pmd_index(*start);
|
||||
+ i = addr == -1 ? 0 : pmd_index(addr) - pmd_index(*first);
|
||||
if (i && i <= MIN_LRU_BATCH) {
|
||||
__set_bit(i - 1, bitmap);
|
||||
return;
|
||||
}
|
||||
|
||||
- pmd = pmd_offset(pud, *start);
|
||||
+ pmd = pmd_offset(pud, *first);
|
||||
|
||||
ptl = pmd_lockptr(args->mm, pmd);
|
||||
if (!spin_trylock(ptl))
|
||||
@@ -4016,15 +4017,16 @@ static void walk_pmd_range_locked(pud_t
|
||||
do {
|
||||
unsigned long pfn;
|
||||
struct folio *folio;
|
||||
- unsigned long addr = i ? (*start & PMD_MASK) + i * PMD_SIZE : *start;
|
||||
+
|
||||
+ /* don't round down the first address */
|
||||
+ addr = i ? (*first & PMD_MASK) + i * PMD_SIZE : *first;
|
||||
|
||||
pfn = get_pmd_pfn(pmd[i], vma, addr);
|
||||
if (pfn == -1)
|
||||
goto next;
|
||||
|
||||
if (!pmd_trans_huge(pmd[i])) {
|
||||
- if (arch_has_hw_nonleaf_pmd_young() &&
|
||||
- get_cap(LRU_GEN_NONLEAF_YOUNG))
|
||||
+ if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG))
|
||||
pmdp_test_and_clear_young(vma, addr, pmd + i);
|
||||
goto next;
|
||||
}
|
||||
@@ -4053,12 +4055,11 @@ next:
|
||||
arch_leave_lazy_mmu_mode();
|
||||
spin_unlock(ptl);
|
||||
done:
|
||||
- *start = -1;
|
||||
- bitmap_zero(bitmap, MIN_LRU_BATCH);
|
||||
+ *first = -1;
|
||||
}
|
||||
#else
|
||||
-static void walk_pmd_range_locked(pud_t *pud, unsigned long next, struct vm_area_struct *vma,
|
||||
- struct mm_walk *args, unsigned long *bitmap, unsigned long *start)
|
||||
+static void walk_pmd_range_locked(pud_t *pud, unsigned long addr, struct vm_area_struct *vma,
|
||||
+ struct mm_walk *args, unsigned long *bitmap, unsigned long *first)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
@@ -4071,9 +4072,9 @@ static void walk_pmd_range(pud_t *pud, u
|
||||
unsigned long next;
|
||||
unsigned long addr;
|
||||
struct vm_area_struct *vma;
|
||||
- unsigned long pos = -1;
|
||||
+ unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)];
|
||||
+ unsigned long first = -1;
|
||||
struct lru_gen_mm_walk *walk = args->private;
|
||||
- unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
|
||||
|
||||
VM_WARN_ON_ONCE(pud_leaf(*pud));
|
||||
|
||||
@@ -4115,18 +4116,17 @@ restart:
|
||||
if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat))
|
||||
continue;
|
||||
|
||||
- walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
|
||||
+ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
walk->mm_stats[MM_NONLEAF_TOTAL]++;
|
||||
|
||||
- if (arch_has_hw_nonleaf_pmd_young() &&
|
||||
- get_cap(LRU_GEN_NONLEAF_YOUNG)) {
|
||||
+ if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG)) {
|
||||
if (!pmd_young(val))
|
||||
continue;
|
||||
|
||||
- walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos);
|
||||
+ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &first);
|
||||
}
|
||||
|
||||
if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i))
|
||||
@@ -4143,7 +4143,7 @@ restart:
|
||||
update_bloom_filter(walk->lruvec, walk->max_seq + 1, pmd + i);
|
||||
}
|
||||
|
||||
- walk_pmd_range_locked(pud, -1, vma, args, bitmap, &pos);
|
||||
+ walk_pmd_range_locked(pud, -1, vma, args, bitmap, &first);
|
||||
|
||||
if (i < PTRS_PER_PMD && get_next_vma(PUD_MASK, PMD_SIZE, args, &start, &end))
|
||||
goto restart;
|
||||
@@ -1,148 +0,0 @@
|
||||
From e604c3ccb4dfbdde2467fccef9bb36170a392695 Mon Sep 17 00:00:00 2001
|
||||
From: "T.J. Alumbaugh" <talumbau@google.com>
|
||||
Date: Wed, 18 Jan 2023 00:18:27 +0000
|
||||
Subject: [PATCH 18/19] UPSTREAM: mm: multi-gen LRU: simplify
|
||||
lru_gen_look_around()
|
||||
|
||||
Update the folio generation in place with or without
|
||||
current->reclaim_state->mm_walk. The LRU lock is held for longer, if
|
||||
mm_walk is NULL and the number of folios to update is more than
|
||||
PAGEVEC_SIZE.
|
||||
|
||||
This causes a measurable regression from the LRU lock contention during a
|
||||
microbencmark. But a tiny regression is not worth the complexity.
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230118001827.1040870-8-talumbau@google.com
|
||||
Change-Id: I9ce18b4f4062e6c1c13c98ece9422478eb8e1846
|
||||
Signed-off-by: T.J. Alumbaugh <talumbau@google.com>
|
||||
Cc: Yu Zhao <yuzhao@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
(cherry picked from commit abf086721a2f1e6897c57796f7268df1b194c750)
|
||||
Bug: 274865848
|
||||
Signed-off-by: T.J. Mercier <tjmercier@google.com>
|
||||
---
|
||||
mm/vmscan.c | 73 +++++++++++++++++------------------------------------
|
||||
1 file changed, 23 insertions(+), 50 deletions(-)
|
||||
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -4573,13 +4573,12 @@ static void lru_gen_age_node(struct pgli
|
||||
void lru_gen_look_around(struct page_vma_mapped_walk *pvmw)
|
||||
{
|
||||
int i;
|
||||
- pte_t *pte;
|
||||
unsigned long start;
|
||||
unsigned long end;
|
||||
- unsigned long addr;
|
||||
struct lru_gen_mm_walk *walk;
|
||||
int young = 0;
|
||||
- unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {};
|
||||
+ pte_t *pte = pvmw->pte;
|
||||
+ unsigned long addr = pvmw->address;
|
||||
struct folio *folio = pfn_folio(pvmw->pfn);
|
||||
struct mem_cgroup *memcg = folio_memcg(folio);
|
||||
struct pglist_data *pgdat = folio_pgdat(folio);
|
||||
@@ -4596,25 +4595,28 @@ void lru_gen_look_around(struct page_vma
|
||||
/* avoid taking the LRU lock under the PTL when possible */
|
||||
walk = current->reclaim_state ? current->reclaim_state->mm_walk : NULL;
|
||||
|
||||
- start = max(pvmw->address & PMD_MASK, pvmw->vma->vm_start);
|
||||
- end = min(pvmw->address | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1;
|
||||
+ start = max(addr & PMD_MASK, pvmw->vma->vm_start);
|
||||
+ end = min(addr | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1;
|
||||
|
||||
if (end - start > MIN_LRU_BATCH * PAGE_SIZE) {
|
||||
- if (pvmw->address - start < MIN_LRU_BATCH * PAGE_SIZE / 2)
|
||||
+ if (addr - start < MIN_LRU_BATCH * PAGE_SIZE / 2)
|
||||
end = start + MIN_LRU_BATCH * PAGE_SIZE;
|
||||
- else if (end - pvmw->address < MIN_LRU_BATCH * PAGE_SIZE / 2)
|
||||
+ else if (end - addr < MIN_LRU_BATCH * PAGE_SIZE / 2)
|
||||
start = end - MIN_LRU_BATCH * PAGE_SIZE;
|
||||
else {
|
||||
- start = pvmw->address - MIN_LRU_BATCH * PAGE_SIZE / 2;
|
||||
- end = pvmw->address + MIN_LRU_BATCH * PAGE_SIZE / 2;
|
||||
+ start = addr - MIN_LRU_BATCH * PAGE_SIZE / 2;
|
||||
+ end = addr + MIN_LRU_BATCH * PAGE_SIZE / 2;
|
||||
}
|
||||
}
|
||||
|
||||
- pte = pvmw->pte - (pvmw->address - start) / PAGE_SIZE;
|
||||
+ /* folio_update_gen() requires stable folio_memcg() */
|
||||
+ if (!mem_cgroup_trylock_pages(memcg))
|
||||
+ return;
|
||||
|
||||
- rcu_read_lock();
|
||||
arch_enter_lazy_mmu_mode();
|
||||
|
||||
+ pte -= (addr - start) / PAGE_SIZE;
|
||||
+
|
||||
for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) {
|
||||
unsigned long pfn;
|
||||
|
||||
@@ -4639,56 +4641,27 @@ void lru_gen_look_around(struct page_vma
|
||||
!folio_test_swapcache(folio)))
|
||||
folio_mark_dirty(folio);
|
||||
|
||||
+ if (walk) {
|
||||
+ old_gen = folio_update_gen(folio, new_gen);
|
||||
+ if (old_gen >= 0 && old_gen != new_gen)
|
||||
+ update_batch_size(walk, folio, old_gen, new_gen);
|
||||
+
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
old_gen = folio_lru_gen(folio);
|
||||
if (old_gen < 0)
|
||||
folio_set_referenced(folio);
|
||||
else if (old_gen != new_gen)
|
||||
- __set_bit(i, bitmap);
|
||||
+ folio_activate(folio);
|
||||
}
|
||||
|
||||
arch_leave_lazy_mmu_mode();
|
||||
- rcu_read_unlock();
|
||||
+ mem_cgroup_unlock_pages();
|
||||
|
||||
/* feedback from rmap walkers to page table walkers */
|
||||
if (suitable_to_scan(i, young))
|
||||
update_bloom_filter(lruvec, max_seq, pvmw->pmd);
|
||||
-
|
||||
- if (!walk && bitmap_weight(bitmap, MIN_LRU_BATCH) < PAGEVEC_SIZE) {
|
||||
- for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
|
||||
- folio = pfn_folio(pte_pfn(pte[i]));
|
||||
- folio_activate(folio);
|
||||
- }
|
||||
- return;
|
||||
- }
|
||||
-
|
||||
- /* folio_update_gen() requires stable folio_memcg() */
|
||||
- if (!mem_cgroup_trylock_pages(memcg))
|
||||
- return;
|
||||
-
|
||||
- if (!walk) {
|
||||
- spin_lock_irq(&lruvec->lru_lock);
|
||||
- new_gen = lru_gen_from_seq(lruvec->lrugen.max_seq);
|
||||
- }
|
||||
-
|
||||
- for_each_set_bit(i, bitmap, MIN_LRU_BATCH) {
|
||||
- folio = pfn_folio(pte_pfn(pte[i]));
|
||||
- if (folio_memcg_rcu(folio) != memcg)
|
||||
- continue;
|
||||
-
|
||||
- old_gen = folio_update_gen(folio, new_gen);
|
||||
- if (old_gen < 0 || old_gen == new_gen)
|
||||
- continue;
|
||||
-
|
||||
- if (walk)
|
||||
- update_batch_size(walk, folio, old_gen, new_gen);
|
||||
- else
|
||||
- lru_gen_update_size(lruvec, folio, old_gen, new_gen);
|
||||
- }
|
||||
-
|
||||
- if (!walk)
|
||||
- spin_unlock_irq(&lruvec->lru_lock);
|
||||
-
|
||||
- mem_cgroup_unlock_pages();
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -1,273 +0,0 @@
|
||||
From 418038c22452df38cde519cc8c662bb15139764a Mon Sep 17 00:00:00 2001
|
||||
From: Kalesh Singh <kaleshsingh@google.com>
|
||||
Date: Thu, 13 Apr 2023 14:43:26 -0700
|
||||
Subject: [PATCH 19/19] mm: Multi-gen LRU: remove wait_event_killable()
|
||||
|
||||
Android 14 and later default to MGLRU [1] and field telemetry showed
|
||||
occasional long tail latency (>100ms) in the reclaim path.
|
||||
|
||||
Tracing revealed priority inversion in the reclaim path. In
|
||||
try_to_inc_max_seq(), when high priority tasks were blocked on
|
||||
wait_event_killable(), the preemption of the low priority task to call
|
||||
wake_up_all() caused those high priority tasks to wait longer than
|
||||
necessary. In general, this problem is not different from others of its
|
||||
kind, e.g., one caused by mutex_lock(). However, it is specific to MGLRU
|
||||
because it introduced the new wait queue lruvec->mm_state.wait.
|
||||
|
||||
The purpose of this new wait queue is to avoid the thundering herd
|
||||
problem. If many direct reclaimers rush into try_to_inc_max_seq(), only
|
||||
one can succeed, i.e., the one to wake up the rest, and the rest who
|
||||
failed might cause premature OOM kills if they do not wait. So far there
|
||||
is no evidence supporting this scenario, based on how often the wait has
|
||||
been hit. And this begs the question how useful the wait queue is in
|
||||
practice.
|
||||
|
||||
Based on Minchan's recommendation, which is in line with his commit
|
||||
6d4675e60135 ("mm: don't be stuck to rmap lock on reclaim path") and the
|
||||
rest of the MGLRU code which also uses trylock when possible, remove the
|
||||
wait queue.
|
||||
|
||||
[1] https://android-review.googlesource.com/q/I7ed7fbfd6ef9ce10053347528125dd98c39e50bf
|
||||
|
||||
Link: https://lkml.kernel.org/r/20230413214326.2147568-1-kaleshsingh@google.com
|
||||
Fixes: bd74fdaea146 ("mm: multi-gen LRU: support page table walks")
|
||||
Signed-off-by: Kalesh Singh <kaleshsingh@google.com>
|
||||
Suggested-by: Minchan Kim <minchan@kernel.org>
|
||||
Reported-by: Wei Wang <wvw@google.com>
|
||||
Acked-by: Yu Zhao <yuzhao@google.com>
|
||||
Cc: Minchan Kim <minchan@kernel.org>
|
||||
Cc: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
|
||||
Cc: Oleksandr Natalenko <oleksandr@natalenko.name>
|
||||
Cc: Suleiman Souhlal <suleiman@google.com>
|
||||
Cc: Suren Baghdasaryan <surenb@google.com>
|
||||
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
|
||||
---
|
||||
include/linux/mmzone.h | 8 +--
|
||||
mm/vmscan.c | 112 +++++++++++++++--------------------------
|
||||
2 files changed, 42 insertions(+), 78 deletions(-)
|
||||
|
||||
--- a/include/linux/mmzone.h
|
||||
+++ b/include/linux/mmzone.h
|
||||
@@ -453,18 +453,14 @@ enum {
|
||||
struct lru_gen_mm_state {
|
||||
/* set to max_seq after each iteration */
|
||||
unsigned long seq;
|
||||
- /* where the current iteration continues (inclusive) */
|
||||
+ /* where the current iteration continues after */
|
||||
struct list_head *head;
|
||||
- /* where the last iteration ended (exclusive) */
|
||||
+ /* where the last iteration ended before */
|
||||
struct list_head *tail;
|
||||
- /* to wait for the last page table walker to finish */
|
||||
- struct wait_queue_head wait;
|
||||
/* Bloom filters flip after each iteration */
|
||||
unsigned long *filters[NR_BLOOM_FILTERS];
|
||||
/* the mm stats for debugging */
|
||||
unsigned long stats[NR_HIST_GENS][NR_MM_STATS];
|
||||
- /* the number of concurrent page table walkers */
|
||||
- int nr_walkers;
|
||||
};
|
||||
|
||||
struct lru_gen_mm_walk {
|
||||
--- a/mm/vmscan.c
|
||||
+++ b/mm/vmscan.c
|
||||
@@ -3371,18 +3371,13 @@ void lru_gen_del_mm(struct mm_struct *mm
|
||||
if (!lruvec)
|
||||
continue;
|
||||
|
||||
- /* where the last iteration ended (exclusive) */
|
||||
+ /* where the current iteration continues after */
|
||||
+ if (lruvec->mm_state.head == &mm->lru_gen.list)
|
||||
+ lruvec->mm_state.head = lruvec->mm_state.head->prev;
|
||||
+
|
||||
+ /* where the last iteration ended before */
|
||||
if (lruvec->mm_state.tail == &mm->lru_gen.list)
|
||||
lruvec->mm_state.tail = lruvec->mm_state.tail->next;
|
||||
-
|
||||
- /* where the current iteration continues (inclusive) */
|
||||
- if (lruvec->mm_state.head != &mm->lru_gen.list)
|
||||
- continue;
|
||||
-
|
||||
- lruvec->mm_state.head = lruvec->mm_state.head->next;
|
||||
- /* the deletion ends the current iteration */
|
||||
- if (lruvec->mm_state.head == &mm_list->fifo)
|
||||
- WRITE_ONCE(lruvec->mm_state.seq, lruvec->mm_state.seq + 1);
|
||||
}
|
||||
|
||||
list_del_init(&mm->lru_gen.list);
|
||||
@@ -3478,68 +3473,54 @@ static bool iterate_mm_list(struct lruve
|
||||
struct mm_struct **iter)
|
||||
{
|
||||
bool first = false;
|
||||
- bool last = true;
|
||||
+ bool last = false;
|
||||
struct mm_struct *mm = NULL;
|
||||
struct mem_cgroup *memcg = lruvec_memcg(lruvec);
|
||||
struct lru_gen_mm_list *mm_list = get_mm_list(memcg);
|
||||
struct lru_gen_mm_state *mm_state = &lruvec->mm_state;
|
||||
|
||||
/*
|
||||
- * There are four interesting cases for this page table walker:
|
||||
- * 1. It tries to start a new iteration of mm_list with a stale max_seq;
|
||||
- * there is nothing left to do.
|
||||
- * 2. It's the first of the current generation, and it needs to reset
|
||||
- * the Bloom filter for the next generation.
|
||||
- * 3. It reaches the end of mm_list, and it needs to increment
|
||||
- * mm_state->seq; the iteration is done.
|
||||
- * 4. It's the last of the current generation, and it needs to reset the
|
||||
- * mm stats counters for the next generation.
|
||||
+ * mm_state->seq is incremented after each iteration of mm_list. There
|
||||
+ * are three interesting cases for this page table walker:
|
||||
+ * 1. It tries to start a new iteration with a stale max_seq: there is
|
||||
+ * nothing left to do.
|
||||
+ * 2. It started the next iteration: it needs to reset the Bloom filter
|
||||
+ * so that a fresh set of PTE tables can be recorded.
|
||||
+ * 3. It ended the current iteration: it needs to reset the mm stats
|
||||
+ * counters and tell its caller to increment max_seq.
|
||||
*/
|
||||
spin_lock(&mm_list->lock);
|
||||
|
||||
VM_WARN_ON_ONCE(mm_state->seq + 1 < walk->max_seq);
|
||||
- VM_WARN_ON_ONCE(*iter && mm_state->seq > walk->max_seq);
|
||||
- VM_WARN_ON_ONCE(*iter && !mm_state->nr_walkers);
|
||||
|
||||
- if (walk->max_seq <= mm_state->seq) {
|
||||
- if (!*iter)
|
||||
- last = false;
|
||||
+ if (walk->max_seq <= mm_state->seq)
|
||||
goto done;
|
||||
- }
|
||||
|
||||
- if (!mm_state->nr_walkers) {
|
||||
- VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo);
|
||||
+ if (!mm_state->head)
|
||||
+ mm_state->head = &mm_list->fifo;
|
||||
|
||||
- mm_state->head = mm_list->fifo.next;
|
||||
+ if (mm_state->head == &mm_list->fifo)
|
||||
first = true;
|
||||
- }
|
||||
-
|
||||
- while (!mm && mm_state->head != &mm_list->fifo) {
|
||||
- mm = list_entry(mm_state->head, struct mm_struct, lru_gen.list);
|
||||
|
||||
+ do {
|
||||
mm_state->head = mm_state->head->next;
|
||||
+ if (mm_state->head == &mm_list->fifo) {
|
||||
+ WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
|
||||
+ last = true;
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
/* force scan for those added after the last iteration */
|
||||
- if (!mm_state->tail || mm_state->tail == &mm->lru_gen.list) {
|
||||
- mm_state->tail = mm_state->head;
|
||||
+ if (!mm_state->tail || mm_state->tail == mm_state->head) {
|
||||
+ mm_state->tail = mm_state->head->next;
|
||||
walk->force_scan = true;
|
||||
}
|
||||
|
||||
+ mm = list_entry(mm_state->head, struct mm_struct, lru_gen.list);
|
||||
if (should_skip_mm(mm, walk))
|
||||
mm = NULL;
|
||||
- }
|
||||
-
|
||||
- if (mm_state->head == &mm_list->fifo)
|
||||
- WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
|
||||
+ } while (!mm);
|
||||
done:
|
||||
- if (*iter && !mm)
|
||||
- mm_state->nr_walkers--;
|
||||
- if (!*iter && mm)
|
||||
- mm_state->nr_walkers++;
|
||||
-
|
||||
- if (mm_state->nr_walkers)
|
||||
- last = false;
|
||||
-
|
||||
if (*iter || last)
|
||||
reset_mm_stats(lruvec, walk, last);
|
||||
|
||||
@@ -3567,9 +3548,9 @@ static bool iterate_mm_list_nowalk(struc
|
||||
|
||||
VM_WARN_ON_ONCE(mm_state->seq + 1 < max_seq);
|
||||
|
||||
- if (max_seq > mm_state->seq && !mm_state->nr_walkers) {
|
||||
- VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo);
|
||||
-
|
||||
+ if (max_seq > mm_state->seq) {
|
||||
+ mm_state->head = NULL;
|
||||
+ mm_state->tail = NULL;
|
||||
WRITE_ONCE(mm_state->seq, mm_state->seq + 1);
|
||||
reset_mm_stats(lruvec, NULL, true);
|
||||
success = true;
|
||||
@@ -4172,10 +4153,6 @@ restart:
|
||||
|
||||
walk_pmd_range(&val, addr, next, args);
|
||||
|
||||
- /* a racy check to curtail the waiting time */
|
||||
- if (wq_has_sleeper(&walk->lruvec->mm_state.wait))
|
||||
- return 1;
|
||||
-
|
||||
if (need_resched() || walk->batched >= MAX_LRU_BATCH) {
|
||||
end = (addr | ~PUD_MASK) + 1;
|
||||
goto done;
|
||||
@@ -4208,8 +4185,14 @@ static void walk_mm(struct lruvec *lruve
|
||||
walk->next_addr = FIRST_USER_ADDRESS;
|
||||
|
||||
do {
|
||||
+ DEFINE_MAX_SEQ(lruvec);
|
||||
+
|
||||
err = -EBUSY;
|
||||
|
||||
+ /* another thread might have called inc_max_seq() */
|
||||
+ if (walk->max_seq != max_seq)
|
||||
+ break;
|
||||
+
|
||||
/* folio_update_gen() requires stable folio_memcg() */
|
||||
if (!mem_cgroup_trylock_pages(memcg))
|
||||
break;
|
||||
@@ -4444,25 +4427,12 @@ static bool try_to_inc_max_seq(struct lr
|
||||
success = iterate_mm_list(lruvec, walk, &mm);
|
||||
if (mm)
|
||||
walk_mm(lruvec, mm, walk);
|
||||
-
|
||||
- cond_resched();
|
||||
} while (mm);
|
||||
done:
|
||||
- if (!success) {
|
||||
- if (sc->priority <= DEF_PRIORITY - 2)
|
||||
- wait_event_killable(lruvec->mm_state.wait,
|
||||
- max_seq < READ_ONCE(lrugen->max_seq));
|
||||
- return false;
|
||||
- }
|
||||
+ if (success)
|
||||
+ inc_max_seq(lruvec, can_swap, force_scan);
|
||||
|
||||
- VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq));
|
||||
-
|
||||
- inc_max_seq(lruvec, can_swap, force_scan);
|
||||
- /* either this sees any waiters or they will see updated max_seq */
|
||||
- if (wq_has_sleeper(&lruvec->mm_state.wait))
|
||||
- wake_up_all(&lruvec->mm_state.wait);
|
||||
-
|
||||
- return true;
|
||||
+ return success;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
@@ -6116,7 +6086,6 @@ void lru_gen_init_lruvec(struct lruvec *
|
||||
INIT_LIST_HEAD(&lrugen->folios[gen][type][zone]);
|
||||
|
||||
lruvec->mm_state.seq = MIN_NR_GENS;
|
||||
- init_waitqueue_head(&lruvec->mm_state.wait);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MEMCG
|
||||
@@ -6149,7 +6118,6 @@ void lru_gen_exit_memcg(struct mem_cgrou
|
||||
for_each_node(nid) {
|
||||
struct lruvec *lruvec = get_lruvec(memcg, nid);
|
||||
|
||||
- VM_WARN_ON_ONCE(lruvec->mm_state.nr_walkers);
|
||||
VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0,
|
||||
sizeof(lruvec->lrugen.nr_pages)));
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
From e2192de59e457aef8d1f055a452131f0b3e5d097 Mon Sep 17 00:00:00 2001
|
||||
From: Johannes Berg <johannes.berg@intel.com>
|
||||
Date: Wed, 18 Jan 2023 14:26:53 +0100
|
||||
Subject: [PATCH] bitfield: add FIELD_PREP_CONST()
|
||||
|
||||
Neither FIELD_PREP() nor *_encode_bits() can be used
|
||||
in constant contexts (such as initializers), but we
|
||||
don't want to define shift constants for all masks
|
||||
just for use in initializers, and having checks that
|
||||
the values fit is also useful.
|
||||
|
||||
Therefore, add FIELD_PREP_CONST() which is a smaller
|
||||
version of FIELD_PREP() that can only take constant
|
||||
arguments and has less friendly (but not less strict)
|
||||
error checks, and expands to a constant value.
|
||||
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
Link: https://lore.kernel.org/r/20230118142652.53f20593504b.Iaeea0aee77a6493d70e573b4aa55c91c00e01e4b@changeid
|
||||
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
|
||||
---
|
||||
include/linux/bitfield.h | 26 ++++++++++++++++++++++++++
|
||||
1 file changed, 26 insertions(+)
|
||||
|
||||
--- a/include/linux/bitfield.h
|
||||
+++ b/include/linux/bitfield.h
|
||||
@@ -115,6 +115,32 @@
|
||||
((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask); \
|
||||
})
|
||||
|
||||
+#define __BF_CHECK_POW2(n) BUILD_BUG_ON_ZERO(((n) & ((n) - 1)) != 0)
|
||||
+
|
||||
+/**
|
||||
+ * FIELD_PREP_CONST() - prepare a constant bitfield element
|
||||
+ * @_mask: shifted mask defining the field's length and position
|
||||
+ * @_val: value to put in the field
|
||||
+ *
|
||||
+ * FIELD_PREP_CONST() masks and shifts up the value. The result should
|
||||
+ * be combined with other fields of the bitfield using logical OR.
|
||||
+ *
|
||||
+ * Unlike FIELD_PREP() this is a constant expression and can therefore
|
||||
+ * be used in initializers. Error checking is less comfortable for this
|
||||
+ * version, and non-constant masks cannot be used.
|
||||
+ */
|
||||
+#define FIELD_PREP_CONST(_mask, _val) \
|
||||
+ ( \
|
||||
+ /* mask must be non-zero */ \
|
||||
+ BUILD_BUG_ON_ZERO((_mask) == 0) + \
|
||||
+ /* check if value fits */ \
|
||||
+ BUILD_BUG_ON_ZERO(~((_mask) >> __bf_shf(_mask)) & (_val)) + \
|
||||
+ /* check if mask is contiguous */ \
|
||||
+ __BF_CHECK_POW2((_mask) + (1ULL << __bf_shf(_mask))) + \
|
||||
+ /* and create the value */ \
|
||||
+ (((typeof(_mask))(_val) << __bf_shf(_mask)) & (_mask)) \
|
||||
+ )
|
||||
+
|
||||
/**
|
||||
* FIELD_GET() - extract a bitfield element
|
||||
* @_mask: shifted mask defining the field's length and position
|
||||
@@ -1,63 +0,0 @@
|
||||
From 579aee9fc594af94c242068c011b0233563d4bbf Mon Sep 17 00:00:00 2001
|
||||
From: Stephen Rothwell <sfr@canb.auug.org.au>
|
||||
Date: Mon, 10 Oct 2022 16:57:21 +1100
|
||||
Subject: [PATCH] powerpc: suppress some linker warnings in recent linker
|
||||
versions
|
||||
|
||||
This is a follow on from commit
|
||||
|
||||
0d362be5b142 ("Makefile: link with -z noexecstack --no-warn-rwx-segments")
|
||||
|
||||
for arch/powerpc/boot to address wanrings like:
|
||||
|
||||
ld: warning: opal-calls.o: missing .note.GNU-stack section implies executable stack
|
||||
ld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker
|
||||
ld: warning: arch/powerpc/boot/zImage.epapr has a LOAD segment with RWX permissions
|
||||
|
||||
This fixes issue https://github.com/linuxppc/issues/issues/417
|
||||
|
||||
Signed-off-by: Stephen Rothwell <sfr@canb.auug.org.au>
|
||||
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
|
||||
Link: https://lore.kernel.org/r/20221010165721.106267e6@canb.auug.org.au
|
||||
---
|
||||
arch/powerpc/boot/wrapper | 15 ++++++++++++++-
|
||||
1 file changed, 14 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/arch/powerpc/boot/wrapper
|
||||
+++ b/arch/powerpc/boot/wrapper
|
||||
@@ -215,6 +215,11 @@ ld_version()
|
||||
}'
|
||||
}
|
||||
|
||||
+ld_is_lld()
|
||||
+{
|
||||
+ ${CROSS}ld -V 2>&1 | grep -q LLD
|
||||
+}
|
||||
+
|
||||
# Do not include PT_INTERP segment when linking pie. Non-pie linking
|
||||
# just ignores this option.
|
||||
LD_VERSION=$(${CROSS}ld --version | ld_version)
|
||||
@@ -223,6 +228,14 @@ if [ "$LD_VERSION" -ge "$LD_NO_DL_MIN_VE
|
||||
nodl="--no-dynamic-linker"
|
||||
fi
|
||||
|
||||
+# suppress some warnings in recent ld versions
|
||||
+nowarn="-z noexecstack"
|
||||
+if ! ld_is_lld; then
|
||||
+ if [ "$LD_VERSION" -ge "$(echo 2.39 | ld_version)" ]; then
|
||||
+ nowarn="$nowarn --no-warn-rwx-segments"
|
||||
+ fi
|
||||
+fi
|
||||
+
|
||||
platformo=$object/"$platform".o
|
||||
lds=$object/zImage.lds
|
||||
ext=strip
|
||||
@@ -504,7 +517,7 @@ if [ "$platform" != "miboot" ]; then
|
||||
text_start="-Ttext $link_address"
|
||||
fi
|
||||
#link everything
|
||||
- ${CROSS}ld -m $format -T $lds $text_start $pie $nodl $rodynamic $notext -o "$ofile" $map \
|
||||
+ ${CROSS}ld -m $format -T $lds $text_start $pie $nodl $nowarn $rodynamic $notext -o "$ofile" $map \
|
||||
$platformo $tmp $object/wrapper.a
|
||||
rm $tmp
|
||||
fi
|
||||
@@ -1,161 +0,0 @@
|
||||
From 66a5c40f60f5d88ad8d47ba6a4ba05892853fa1f Mon Sep 17 00:00:00 2001
|
||||
From: Tanzir Hasan <tanzirh@google.com>
|
||||
Date: Tue, 26 Dec 2023 18:00:00 +0000
|
||||
Subject: [PATCH] kernel.h: removed REPEAT_BYTE from kernel.h
|
||||
|
||||
This patch creates wordpart.h and includes it in asm/word-at-a-time.h
|
||||
for all architectures. WORD_AT_A_TIME_CONSTANTS depends on kernel.h
|
||||
because of REPEAT_BYTE. Moving this to another header and including it
|
||||
where necessary allows us to not include the bloated kernel.h. Making
|
||||
this implicit dependency on REPEAT_BYTE explicit allows for later
|
||||
improvements in the lib/string.c inclusion list.
|
||||
|
||||
Suggested-by: Al Viro <viro@zeniv.linux.org.uk>
|
||||
Suggested-by: Andy Shevchenko <andy.shevchenko@gmail.com>
|
||||
Signed-off-by: Tanzir Hasan <tanzirh@google.com>
|
||||
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
|
||||
Link: https://lore.kernel.org/r/20231226-libstringheader-v6-1-80aa08c7652c@google.com
|
||||
Signed-off-by: Kees Cook <keescook@chromium.org>
|
||||
---
|
||||
arch/arm/include/asm/word-at-a-time.h | 3 ++-
|
||||
arch/arm64/include/asm/word-at-a-time.h | 3 ++-
|
||||
arch/powerpc/include/asm/word-at-a-time.h | 4 ++--
|
||||
arch/riscv/include/asm/word-at-a-time.h | 3 ++-
|
||||
arch/s390/include/asm/word-at-a-time.h | 3 ++-
|
||||
arch/sh/include/asm/word-at-a-time.h | 2 ++
|
||||
arch/x86/include/asm/word-at-a-time.h | 3 ++-
|
||||
arch/x86/kvm/mmu/mmu.c | 1 +
|
||||
fs/namei.c | 2 +-
|
||||
include/asm-generic/word-at-a-time.h | 3 ++-
|
||||
include/linux/kernel.h | 8 --------
|
||||
include/linux/wordpart.h | 13 +++++++++++++
|
||||
12 files changed, 31 insertions(+), 17 deletions(-)
|
||||
create mode 100644 include/linux/wordpart.h
|
||||
|
||||
--- a/arch/arm/include/asm/word-at-a-time.h
|
||||
+++ b/arch/arm/include/asm/word-at-a-time.h
|
||||
@@ -8,7 +8,8 @@
|
||||
* Little-endian word-at-a-time zero byte handling.
|
||||
* Heavily based on the x86 algorithm.
|
||||
*/
|
||||
-#include <linux/kernel.h>
|
||||
+#include <linux/bitops.h>
|
||||
+#include <linux/wordpart.h>
|
||||
|
||||
struct word_at_a_time {
|
||||
const unsigned long one_bits, high_bits;
|
||||
--- a/arch/arm64/include/asm/word-at-a-time.h
|
||||
+++ b/arch/arm64/include/asm/word-at-a-time.h
|
||||
@@ -9,7 +9,8 @@
|
||||
|
||||
#ifndef __AARCH64EB__
|
||||
|
||||
-#include <linux/kernel.h>
|
||||
+#include <linux/bitops.h>
|
||||
+#include <linux/wordpart.h>
|
||||
|
||||
struct word_at_a_time {
|
||||
const unsigned long one_bits, high_bits;
|
||||
--- a/arch/powerpc/include/asm/word-at-a-time.h
|
||||
+++ b/arch/powerpc/include/asm/word-at-a-time.h
|
||||
@@ -4,8 +4,8 @@
|
||||
/*
|
||||
* Word-at-a-time interfaces for PowerPC.
|
||||
*/
|
||||
-
|
||||
-#include <linux/kernel.h>
|
||||
+#include <linux/bitops.h>
|
||||
+#include <linux/wordpart.h>
|
||||
#include <asm/asm-compat.h>
|
||||
#include <asm/extable.h>
|
||||
|
||||
--- a/arch/sh/include/asm/word-at-a-time.h
|
||||
+++ b/arch/sh/include/asm/word-at-a-time.h
|
||||
@@ -5,6 +5,8 @@
|
||||
#ifdef CONFIG_CPU_BIG_ENDIAN
|
||||
# include <asm-generic/word-at-a-time.h>
|
||||
#else
|
||||
+#include <linux/bitops.h>
|
||||
+#include <linux/wordpart.h>
|
||||
/*
|
||||
* Little-endian version cribbed from x86.
|
||||
*/
|
||||
--- a/arch/x86/include/asm/word-at-a-time.h
|
||||
+++ b/arch/x86/include/asm/word-at-a-time.h
|
||||
@@ -2,7 +2,8 @@
|
||||
#ifndef _ASM_WORD_AT_A_TIME_H
|
||||
#define _ASM_WORD_AT_A_TIME_H
|
||||
|
||||
-#include <linux/kernel.h>
|
||||
+#include <linux/bitops.h>
|
||||
+#include <linux/wordpart.h>
|
||||
|
||||
/*
|
||||
* This is largely generic for little-endian machines, but the
|
||||
--- a/arch/x86/kvm/mmu/mmu.c
|
||||
+++ b/arch/x86/kvm/mmu/mmu.c
|
||||
@@ -44,6 +44,7 @@
|
||||
#include <linux/kern_levels.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/kthread.h>
|
||||
+#include <linux/wordpart.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/memtype.h>
|
||||
--- a/fs/namei.c
|
||||
+++ b/fs/namei.c
|
||||
@@ -17,8 +17,8 @@
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/export.h>
|
||||
-#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
+#include <linux/wordpart.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/namei.h>
|
||||
#include <linux/pagemap.h>
|
||||
--- a/include/asm-generic/word-at-a-time.h
|
||||
+++ b/include/asm-generic/word-at-a-time.h
|
||||
@@ -2,7 +2,8 @@
|
||||
#ifndef _ASM_WORD_AT_A_TIME_H
|
||||
#define _ASM_WORD_AT_A_TIME_H
|
||||
|
||||
-#include <linux/kernel.h>
|
||||
+#include <linux/bitops.h>
|
||||
+#include <linux/wordpart.h>
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#ifdef __BIG_ENDIAN
|
||||
--- a/include/linux/kernel.h
|
||||
+++ b/include/linux/kernel.h
|
||||
@@ -36,14 +36,6 @@
|
||||
|
||||
#define STACK_MAGIC 0xdeadbeef
|
||||
|
||||
-/**
|
||||
- * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
|
||||
- * @x: value to repeat
|
||||
- *
|
||||
- * NOTE: @x is not checked for > 0xff; larger values produce odd results.
|
||||
- */
|
||||
-#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
|
||||
-
|
||||
/* generic data direction definitions */
|
||||
#define READ 0
|
||||
#define WRITE 1
|
||||
--- /dev/null
|
||||
+++ b/include/linux/wordpart.h
|
||||
@@ -0,0 +1,13 @@
|
||||
+/* SPDX-License-Identifier: GPL-2.0 */
|
||||
+
|
||||
+#ifndef _LINUX_WORDPART_H
|
||||
+#define _LINUX_WORDPART_H
|
||||
+/**
|
||||
+ * REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
|
||||
+ * @x: value to repeat
|
||||
+ *
|
||||
+ * NOTE: @x is not checked for > 0xff; larger values produce odd results.
|
||||
+ */
|
||||
+#define REPEAT_BYTE(x) ((~0ul / 0xff) * (x))
|
||||
+
|
||||
+#endif // _LINUX_WORDPART_H
|
||||
@@ -1,107 +0,0 @@
|
||||
From adeb04362d74188c1e22ccb824b15a0a7b3de2f4 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
Date: Wed, 14 Feb 2024 19:26:32 +0200
|
||||
Subject: [PATCH] kernel.h: Move upper_*_bits() and lower_*_bits() to
|
||||
wordpart.h
|
||||
|
||||
The wordpart.h header is collecting APIs related to the handling
|
||||
parts of the word (usually in byte granularity). The upper_*_bits()
|
||||
and lower_*_bits() are good candidates to be moved to there.
|
||||
|
||||
This helps to clean up header dependency hell with regard to kernel.h
|
||||
as the latter gathers completely unrelated stuff together and slows
|
||||
down compilation (especially when it's included into other header).
|
||||
|
||||
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
Link: https://lore.kernel.org/r/20240214172752.3605073-1-andriy.shevchenko@linux.intel.com
|
||||
Reviewed-by: Randy Dunlap <rdunlap@infradead.org>
|
||||
Signed-off-by: Kees Cook <keescook@chromium.org>
|
||||
---
|
||||
include/linux/kernel.h | 30 ++----------------------------
|
||||
include/linux/wordpart.h | 29 +++++++++++++++++++++++++++++
|
||||
2 files changed, 31 insertions(+), 28 deletions(-)
|
||||
|
||||
--- a/include/linux/kernel.h
|
||||
+++ b/include/linux/kernel.h
|
||||
@@ -30,6 +30,8 @@
|
||||
#include <linux/build_bug.h>
|
||||
#include <linux/static_call_types.h>
|
||||
#include <linux/instruction_pointer.h>
|
||||
+#include <linux/wordpart.h>
|
||||
+
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
#include <uapi/linux/kernel.h>
|
||||
@@ -55,34 +57,6 @@
|
||||
} \
|
||||
)
|
||||
|
||||
-/**
|
||||
- * upper_32_bits - return bits 32-63 of a number
|
||||
- * @n: the number we're accessing
|
||||
- *
|
||||
- * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
|
||||
- * the "right shift count >= width of type" warning when that quantity is
|
||||
- * 32-bits.
|
||||
- */
|
||||
-#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
|
||||
-
|
||||
-/**
|
||||
- * lower_32_bits - return bits 0-31 of a number
|
||||
- * @n: the number we're accessing
|
||||
- */
|
||||
-#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
|
||||
-
|
||||
-/**
|
||||
- * upper_16_bits - return bits 16-31 of a number
|
||||
- * @n: the number we're accessing
|
||||
- */
|
||||
-#define upper_16_bits(n) ((u16)((n) >> 16))
|
||||
-
|
||||
-/**
|
||||
- * lower_16_bits - return bits 0-15 of a number
|
||||
- * @n: the number we're accessing
|
||||
- */
|
||||
-#define lower_16_bits(n) ((u16)((n) & 0xffff))
|
||||
-
|
||||
struct completion;
|
||||
struct user;
|
||||
|
||||
--- a/include/linux/wordpart.h
|
||||
+++ b/include/linux/wordpart.h
|
||||
@@ -2,6 +2,35 @@
|
||||
|
||||
#ifndef _LINUX_WORDPART_H
|
||||
#define _LINUX_WORDPART_H
|
||||
+
|
||||
+/**
|
||||
+ * upper_32_bits - return bits 32-63 of a number
|
||||
+ * @n: the number we're accessing
|
||||
+ *
|
||||
+ * A basic shift-right of a 64- or 32-bit quantity. Use this to suppress
|
||||
+ * the "right shift count >= width of type" warning when that quantity is
|
||||
+ * 32-bits.
|
||||
+ */
|
||||
+#define upper_32_bits(n) ((u32)(((n) >> 16) >> 16))
|
||||
+
|
||||
+/**
|
||||
+ * lower_32_bits - return bits 0-31 of a number
|
||||
+ * @n: the number we're accessing
|
||||
+ */
|
||||
+#define lower_32_bits(n) ((u32)((n) & 0xffffffff))
|
||||
+
|
||||
+/**
|
||||
+ * upper_16_bits - return bits 16-31 of a number
|
||||
+ * @n: the number we're accessing
|
||||
+ */
|
||||
+#define upper_16_bits(n) ((u16)((n) >> 16))
|
||||
+
|
||||
+/**
|
||||
+ * lower_16_bits - return bits 0-15 of a number
|
||||
+ * @n: the number we're accessing
|
||||
+ */
|
||||
+#define lower_16_bits(n) ((u16)((n) & 0xffff))
|
||||
+
|
||||
/**
|
||||
* REPEAT_BYTE - repeat the value @x multiple times as an unsigned long value
|
||||
* @x: value to repeat
|
||||
@@ -1,36 +0,0 @@
|
||||
From 8e7daa85641c9559c113f6b217bdc923397de77c Mon Sep 17 00:00:00 2001
|
||||
From: William Zhang <william.zhang@broadcom.com>
|
||||
Date: Thu, 22 Feb 2024 19:47:58 -0800
|
||||
Subject: [PATCH] mtd: rawnand: brcmnand: Support write protection setting from
|
||||
dts
|
||||
|
||||
The write protection feature is controlled by the module parameter wp_on
|
||||
with default set to enabled. But not all the board use this feature
|
||||
especially in BCMBCA broadband board. And module parameter is not
|
||||
sufficient as different board can have different option. Add a device
|
||||
tree property and allow this feature to be configured through the board
|
||||
dts on per board basis.
|
||||
|
||||
Signed-off-by: William Zhang <william.zhang@broadcom.com>
|
||||
Reviewed-by: Florian Fainelli <florian.fainelli@broadcom.com>
|
||||
Reviewed-by: Kamal Dasu <kamal.dasu@broadcom.com>
|
||||
Reviewed-by: David Regan <dregan@broadcom.com>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20240223034758.13753-14-william.zhang@broadcom.com
|
||||
---
|
||||
drivers/mtd/nand/raw/brcmnand/brcmnand.c | 4 ++++
|
||||
1 file changed, 4 insertions(+)
|
||||
|
||||
--- a/drivers/mtd/nand/raw/brcmnand/brcmnand.c
|
||||
+++ b/drivers/mtd/nand/raw/brcmnand/brcmnand.c
|
||||
@@ -3188,6 +3188,10 @@ int brcmnand_probe(struct platform_devic
|
||||
/* Disable XOR addressing */
|
||||
brcmnand_rmw_reg(ctrl, BRCMNAND_CS_XOR, 0xff, 0, 0);
|
||||
|
||||
+ /* Check if the board connects the WP pin */
|
||||
+ if (of_property_read_bool(dn, "brcm,wp-not-connected"))
|
||||
+ wp_on = 0;
|
||||
+
|
||||
if (ctrl->features & BRCMNAND_HAS_WP) {
|
||||
/* Permanently disable write protection */
|
||||
if (wp_on == 2)
|
||||
@@ -1,65 +0,0 @@
|
||||
From 63db0cb35e1cb3b3c134906d1062f65513fdda2d Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
|
||||
Date: Tue, 4 Oct 2022 10:37:09 +0200
|
||||
Subject: [PATCH] mtd: core: simplify (a bit) code find partition-matching
|
||||
dynamic OF node
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
1. Don't hardcode "partition-" string twice
|
||||
2. Use simpler logic & use ->name to avoid of_property_read_string()
|
||||
3. Use mtd_get_of_node() helper
|
||||
|
||||
Cc: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-1-zajec5@gmail.com
|
||||
---
|
||||
drivers/mtd/mtdcore.c | 16 +++++++---------
|
||||
1 file changed, 7 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/mtdcore.c
|
||||
+++ b/drivers/mtd/mtdcore.c
|
||||
@@ -551,18 +551,16 @@ static void mtd_check_of_node(struct mtd
|
||||
struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
|
||||
const char *pname, *prefix = "partition-";
|
||||
int plen, mtd_name_len, offset, prefix_len;
|
||||
- struct mtd_info *parent;
|
||||
bool found = false;
|
||||
|
||||
/* Check if MTD already has a device node */
|
||||
- if (dev_of_node(&mtd->dev))
|
||||
+ if (mtd_get_of_node(mtd))
|
||||
return;
|
||||
|
||||
/* Check if a partitions node exist */
|
||||
if (!mtd_is_partition(mtd))
|
||||
return;
|
||||
- parent = mtd->parent;
|
||||
- parent_dn = of_node_get(dev_of_node(&parent->dev));
|
||||
+ parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
|
||||
if (!parent_dn)
|
||||
return;
|
||||
|
||||
@@ -575,15 +573,15 @@ static void mtd_check_of_node(struct mtd
|
||||
|
||||
/* Search if a partition is defined with the same name */
|
||||
for_each_child_of_node(partitions, mtd_dn) {
|
||||
- offset = 0;
|
||||
-
|
||||
/* Skip partition with no/wrong prefix */
|
||||
- if (!of_node_name_prefix(mtd_dn, "partition-"))
|
||||
+ if (!of_node_name_prefix(mtd_dn, prefix))
|
||||
continue;
|
||||
|
||||
/* Label have priority. Check that first */
|
||||
- if (of_property_read_string(mtd_dn, "label", &pname)) {
|
||||
- of_property_read_string(mtd_dn, "name", &pname);
|
||||
+ if (!of_property_read_string(mtd_dn, "label", &pname)) {
|
||||
+ offset = 0;
|
||||
+ } else {
|
||||
+ pname = mtd_dn->name;
|
||||
offset = prefix_len;
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
From ddb8cefb7af288950447ca6eeeafb09977dab56f Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
|
||||
Date: Tue, 4 Oct 2022 10:37:10 +0200
|
||||
Subject: [PATCH] mtd: core: try to find OF node for every MTD partition
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
So far this feature was limited to the top-level "nvmem-cells" node.
|
||||
There are multiple parsers creating partitions and subpartitions
|
||||
dynamically. Extend that code to handle them too.
|
||||
|
||||
This allows finding partition-* node for every MTD (sub)partition.
|
||||
|
||||
Random example:
|
||||
|
||||
partitions {
|
||||
compatible = "brcm,bcm947xx-cfe-partitions";
|
||||
|
||||
partition-firmware {
|
||||
compatible = "brcm,trx";
|
||||
|
||||
partition-loader {
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
Cc: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20221004083710.27704-2-zajec5@gmail.com
|
||||
---
|
||||
drivers/mtd/mtdcore.c | 18 ++++++------------
|
||||
1 file changed, 6 insertions(+), 12 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/mtdcore.c
|
||||
+++ b/drivers/mtd/mtdcore.c
|
||||
@@ -551,20 +551,22 @@ static void mtd_check_of_node(struct mtd
|
||||
struct device_node *partitions, *parent_dn, *mtd_dn = NULL;
|
||||
const char *pname, *prefix = "partition-";
|
||||
int plen, mtd_name_len, offset, prefix_len;
|
||||
- bool found = false;
|
||||
|
||||
/* Check if MTD already has a device node */
|
||||
if (mtd_get_of_node(mtd))
|
||||
return;
|
||||
|
||||
- /* Check if a partitions node exist */
|
||||
if (!mtd_is_partition(mtd))
|
||||
return;
|
||||
+
|
||||
parent_dn = of_node_get(mtd_get_of_node(mtd->parent));
|
||||
if (!parent_dn)
|
||||
return;
|
||||
|
||||
- partitions = of_get_child_by_name(parent_dn, "partitions");
|
||||
+ if (mtd_is_partition(mtd->parent))
|
||||
+ partitions = of_node_get(parent_dn);
|
||||
+ else
|
||||
+ partitions = of_get_child_by_name(parent_dn, "partitions");
|
||||
if (!partitions)
|
||||
goto exit_parent;
|
||||
|
||||
@@ -588,19 +590,11 @@ static void mtd_check_of_node(struct mtd
|
||||
plen = strlen(pname) - offset;
|
||||
if (plen == mtd_name_len &&
|
||||
!strncmp(mtd->name, pname + offset, plen)) {
|
||||
- found = true;
|
||||
+ mtd_set_of_node(mtd, mtd_dn);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
- if (!found)
|
||||
- goto exit_partitions;
|
||||
-
|
||||
- /* Set of_node only for nvmem */
|
||||
- if (of_device_is_compatible(mtd_dn, "nvmem-cells"))
|
||||
- mtd_set_of_node(mtd, mtd_dn);
|
||||
-
|
||||
-exit_partitions:
|
||||
of_node_put(partitions);
|
||||
exit_parent:
|
||||
of_node_put(parent_dn);
|
||||
@@ -1,47 +0,0 @@
|
||||
From 26422ac78e9d8767bd4aabfbae616b15edbf6a1b Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
|
||||
Date: Sat, 22 Oct 2022 23:13:18 +0200
|
||||
Subject: [PATCH] mtd: core: set ROOT_DEV for partitions marked as rootfs in DT
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This adds support for "linux,rootfs" binding that is used to mark flash
|
||||
partition containing rootfs. It's useful for devices using device tree
|
||||
that don't have bootloader passing root info in cmdline.
|
||||
|
||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20221022211318.32009-2-zajec5@gmail.com
|
||||
---
|
||||
drivers/mtd/mtdcore.c | 12 ++++++++++++
|
||||
1 file changed, 12 insertions(+)
|
||||
|
||||
--- a/drivers/mtd/mtdcore.c
|
||||
+++ b/drivers/mtd/mtdcore.c
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <linux/leds.h>
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/nvmem-provider.h>
|
||||
+#include <linux/root_dev.h>
|
||||
|
||||
#include <linux/mtd/mtd.h>
|
||||
#include <linux/mtd/partitions.h>
|
||||
@@ -737,6 +738,17 @@ int add_mtd_device(struct mtd_info *mtd)
|
||||
not->add(mtd);
|
||||
|
||||
mutex_unlock(&mtd_table_mutex);
|
||||
+
|
||||
+ if (of_find_property(mtd_get_of_node(mtd), "linux,rootfs", NULL)) {
|
||||
+ if (IS_BUILTIN(CONFIG_MTD)) {
|
||||
+ pr_info("mtd: setting mtd%d (%s) as root device\n", mtd->index, mtd->name);
|
||||
+ ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, mtd->index);
|
||||
+ } else {
|
||||
+ pr_warn("mtd: can't set mtd%d (%s) as root device - mtd must be builtin\n",
|
||||
+ mtd->index, mtd->name);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* We _know_ we aren't being removed, because
|
||||
our caller is still holding us here. So none
|
||||
of this try_ nonsense, and no bitching about it
|
||||
@@ -1,117 +0,0 @@
|
||||
From 773bbe10449731c9525457873e0c2342e5cf883b Mon Sep 17 00:00:00 2001
|
||||
From: Michael Walle <michael@walle.cc>
|
||||
Date: Thu, 11 Aug 2022 00:06:53 +0200
|
||||
Subject: [PATCH] mtd: spi-nor: add generic flash driver
|
||||
|
||||
Our SFDP parsing is everything we need to support all basic operations
|
||||
of a flash device. If the flash isn't found in our in-kernel flash
|
||||
database, gracefully fall back to a driver described solely by its SFDP
|
||||
tables.
|
||||
|
||||
Signed-off-by: Michael Walle <michael@walle.cc>
|
||||
Signed-off-by: Tudor Ambarus <tudor.ambarus@microchip.com>
|
||||
Tested-by: Tudor Ambarus <tudor.ambarus@microchip.com>
|
||||
Reviewed-by: Takahiro Kuwano <Takahiro.Kuwano@infineon.com>
|
||||
Link: https://lore.kernel.org/r/20220810220654.1297699-7-michael@walle.cc
|
||||
---
|
||||
drivers/mtd/spi-nor/core.c | 26 ++++++++++++++++++++++++--
|
||||
drivers/mtd/spi-nor/core.h | 1 +
|
||||
drivers/mtd/spi-nor/sfdp.c | 27 +++++++++++++++++++++++++++
|
||||
3 files changed, 52 insertions(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/spi-nor/core.c
|
||||
+++ b/drivers/mtd/spi-nor/core.c
|
||||
@@ -1636,6 +1636,16 @@ static const struct spi_nor_manufacturer
|
||||
&spi_nor_xmc,
|
||||
};
|
||||
|
||||
+static const struct flash_info spi_nor_generic_flash = {
|
||||
+ .name = "spi-nor-generic",
|
||||
+ /*
|
||||
+ * JESD216 rev A doesn't specify the page size, therefore we need a
|
||||
+ * sane default.
|
||||
+ */
|
||||
+ .page_size = 256,
|
||||
+ .parse_sfdp = true,
|
||||
+};
|
||||
+
|
||||
static const struct flash_info *spi_nor_match_id(struct spi_nor *nor,
|
||||
const u8 *id)
|
||||
{
|
||||
@@ -1669,6 +1679,14 @@ static const struct flash_info *spi_nor_
|
||||
}
|
||||
|
||||
info = spi_nor_match_id(nor, id);
|
||||
+
|
||||
+ /* Fallback to a generic flash described only by its SFDP data. */
|
||||
+ if (!info) {
|
||||
+ ret = spi_nor_check_sfdp_signature(nor);
|
||||
+ if (!ret)
|
||||
+ info = &spi_nor_generic_flash;
|
||||
+ }
|
||||
+
|
||||
if (!info) {
|
||||
dev_err(nor->dev, "unrecognized JEDEC id bytes: %*ph\n",
|
||||
SPI_NOR_MAX_ID_LEN, id);
|
||||
@@ -2105,8 +2123,12 @@ static int spi_nor_select_pp(struct spi_
|
||||
* spi_nor_select_uniform_erase() - select optimum uniform erase type
|
||||
* @map: the erase map of the SPI NOR
|
||||
* @wanted_size: the erase type size to search for. Contains the value of
|
||||
- * info->sector_size or of the "small sector" size in case
|
||||
- * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined.
|
||||
+ * info->sector_size, the "small sector" size in case
|
||||
+ * CONFIG_MTD_SPI_NOR_USE_4K_SECTORS is defined or 0 if
|
||||
+ * there is no information about the sector size. The
|
||||
+ * latter is the case if the flash parameters are parsed
|
||||
+ * solely by SFDP, then the largest supported erase type
|
||||
+ * is selected.
|
||||
*
|
||||
* Once the optimum uniform sector erase command is found, disable all the
|
||||
* other.
|
||||
--- a/drivers/mtd/spi-nor/core.h
|
||||
+++ b/drivers/mtd/spi-nor/core.h
|
||||
@@ -708,6 +708,8 @@ int spi_nor_controller_ops_read_reg(stru
|
||||
int spi_nor_controller_ops_write_reg(struct spi_nor *nor, u8 opcode,
|
||||
const u8 *buf, size_t len);
|
||||
|
||||
+int spi_nor_check_sfdp_signature(struct spi_nor *nor);
|
||||
+
|
||||
static inline struct spi_nor *mtd_to_spi_nor(struct mtd_info *mtd)
|
||||
{
|
||||
return container_of(mtd, struct spi_nor, mtd);
|
||||
--- a/drivers/mtd/spi-nor/sfdp.c
|
||||
+++ b/drivers/mtd/spi-nor/sfdp.c
|
||||
@@ -1250,6 +1250,33 @@ static void spi_nor_post_sfdp_fixups(str
|
||||
}
|
||||
|
||||
/**
|
||||
+ * spi_nor_check_sfdp_signature() - check for a valid SFDP signature
|
||||
+ * @nor: pointer to a 'struct spi_nor'
|
||||
+ *
|
||||
+ * Used to detect if the flash supports the RDSFDP command as well as the
|
||||
+ * presence of a valid SFDP table.
|
||||
+ *
|
||||
+ * Return: 0 on success, -errno otherwise.
|
||||
+ */
|
||||
+int spi_nor_check_sfdp_signature(struct spi_nor *nor)
|
||||
+{
|
||||
+ u32 signature;
|
||||
+ int err;
|
||||
+
|
||||
+ /* Get the SFDP header. */
|
||||
+ err = spi_nor_read_sfdp_dma_unsafe(nor, 0, sizeof(signature),
|
||||
+ &signature);
|
||||
+ if (err < 0)
|
||||
+ return err;
|
||||
+
|
||||
+ /* Check the SFDP signature. */
|
||||
+ if (le32_to_cpu(signature) != SFDP_SIGNATURE)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* spi_nor_parse_sfdp() - parse the Serial Flash Discoverable Parameters.
|
||||
* @nor: pointer to a 'struct spi_nor'
|
||||
*
|
||||
@@ -1,32 +0,0 @@
|
||||
From 6c9d1fd52956c3148e847a214bae9102b1811de5 Mon Sep 17 00:00:00 2001
|
||||
From: Vincent Tremblay <vincent@vtremblay.dev>
|
||||
Date: Tue, 27 Dec 2022 09:10:08 -0500
|
||||
Subject: [PATCH] spidev: Add Silicon Labs SI3210 device compatible
|
||||
|
||||
Add compatible string for Silicon Labs SI3210 device.
|
||||
|
||||
Signed-off-by: Vincent Tremblay <vincent@vtremblay.dev>
|
||||
Link: https://lore.kernel.org/r/20221227141011.111410-2-vincent@vtremblay.dev
|
||||
Signed-off-by: Mark Brown <broonie@kernel.org>
|
||||
---
|
||||
drivers/spi/spidev.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
--- a/drivers/spi/spidev.c
|
||||
+++ b/drivers/spi/spidev.c
|
||||
@@ -702,6 +702,7 @@ static const struct spi_device_id spidev
|
||||
{ .name = "spi-petra" },
|
||||
{ .name = "spi-authenta" },
|
||||
{ .name = "em3581" },
|
||||
+ { .name = "si3210" },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(spi, spidev_spi_ids);
|
||||
@@ -730,6 +731,7 @@ static const struct of_device_id spidev_
|
||||
{ .compatible = "rohm,dh2228fv", .data = &spidev_of_check },
|
||||
{ .compatible = "semtech,sx1301", .data = &spidev_of_check },
|
||||
{ .compatible = "silabs,em3581", .data = &spidev_of_check },
|
||||
+ { .compatible = "silabs,si3210", .data = &spidev_of_check },
|
||||
{},
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, spidev_dt_ids);
|
||||
@@ -1,229 +0,0 @@
|
||||
From aec4d5f5ffd0f0092bd9dc21ea90e0bc237d4b74 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= <rafal@milecki.pl>
|
||||
Date: Sat, 15 Oct 2022 11:29:50 +0200
|
||||
Subject: [PATCH] mtd: parsers: add TP-Link SafeLoader partitions table parser
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
This parser deals with most TP-Link home routers. It reads info about
|
||||
partitions and registers them in the MTD subsystem.
|
||||
|
||||
Example from TP-Link Archer C5 V2:
|
||||
|
||||
spi-nor spi0.0: s25fl128s1 (16384 Kbytes)
|
||||
15 tplink-safeloader partitions found on MTD device spi0.0
|
||||
Creating 15 MTD partitions on "spi0.0":
|
||||
0x000000000000-0x000000040000 : "fs-uboot"
|
||||
0x000000040000-0x000000440000 : "os-image"
|
||||
0x000000440000-0x000000e40000 : "rootfs"
|
||||
0x000000e40000-0x000000e40200 : "default-mac"
|
||||
0x000000e40200-0x000000e40400 : "pin"
|
||||
0x000000e40400-0x000000e40600 : "product-info"
|
||||
0x000000e50000-0x000000e60000 : "partition-table"
|
||||
0x000000e60000-0x000000e60200 : "soft-version"
|
||||
0x000000e61000-0x000000e70000 : "support-list"
|
||||
0x000000e70000-0x000000e80000 : "profile"
|
||||
0x000000e80000-0x000000e90000 : "default-config"
|
||||
0x000000e90000-0x000000ee0000 : "user-config"
|
||||
0x000000ee0000-0x000000fe0000 : "log"
|
||||
0x000000fe0000-0x000000ff0000 : "radio_bk"
|
||||
0x000000ff0000-0x000001000000 : "radio"
|
||||
|
||||
Signed-off-by: Rafał Miłecki <rafal@milecki.pl>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20221015092950.27467-2-zajec5@gmail.com
|
||||
---
|
||||
drivers/mtd/parsers/Kconfig | 15 +++
|
||||
drivers/mtd/parsers/Makefile | 1 +
|
||||
drivers/mtd/parsers/tplink_safeloader.c | 150 ++++++++++++++++++++++++
|
||||
3 files changed, 166 insertions(+)
|
||||
create mode 100644 drivers/mtd/parsers/tplink_safeloader.c
|
||||
|
||||
--- a/drivers/mtd/parsers/Kconfig
|
||||
+++ b/drivers/mtd/parsers/Kconfig
|
||||
@@ -123,6 +123,21 @@ config MTD_AFS_PARTS
|
||||
for your particular device. It won't happen automatically. The
|
||||
'physmap' map driver (CONFIG_MTD_PHYSMAP) does this, for example.
|
||||
|
||||
+config MTD_PARSER_TPLINK_SAFELOADER
|
||||
+ tristate "TP-Link Safeloader partitions parser"
|
||||
+ depends on MTD && (ARCH_BCM_5301X || ATH79 || SOC_MT7620 || SOC_MT7621 || COMPILE_TEST)
|
||||
+ help
|
||||
+ TP-Link home routers use flash partitions to store various data. Info
|
||||
+ about flash space layout is stored in a partitions table using a
|
||||
+ custom ASCII-based format.
|
||||
+
|
||||
+ That format was first found in devices with SafeLoader bootloader and
|
||||
+ was named after it. Later it was adapted to CFE and U-Boot
|
||||
+ bootloaders.
|
||||
+
|
||||
+ This driver reads partitions table, parses it and creates MTD
|
||||
+ partitions.
|
||||
+
|
||||
config MTD_PARSER_TRX
|
||||
tristate "Parser for TRX format partitions"
|
||||
depends on MTD && (BCM47XX || ARCH_BCM_5301X || ARCH_MEDIATEK || RALINK || COMPILE_TEST)
|
||||
--- a/drivers/mtd/parsers/Makefile
|
||||
+++ b/drivers/mtd/parsers/Makefile
|
||||
@@ -10,6 +10,7 @@ ofpart-$(CONFIG_MTD_OF_PARTS_BCM4908) +=
|
||||
ofpart-$(CONFIG_MTD_OF_PARTS_LINKSYS_NS)+= ofpart_linksys_ns.o
|
||||
obj-$(CONFIG_MTD_PARSER_IMAGETAG) += parser_imagetag.o
|
||||
obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
|
||||
+obj-$(CONFIG_MTD_PARSER_TPLINK_SAFELOADER) += tplink_safeloader.o
|
||||
obj-$(CONFIG_MTD_PARSER_TRX) += parser_trx.o
|
||||
obj-$(CONFIG_MTD_SERCOMM_PARTS) += scpart.o
|
||||
obj-$(CONFIG_MTD_SHARPSL_PARTS) += sharpslpart.o
|
||||
--- /dev/null
|
||||
+++ b/drivers/mtd/parsers/tplink_safeloader.c
|
||||
@@ -0,0 +1,150 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * Copyright © 2022 Rafał Miłecki <rafal@milecki.pl>
|
||||
+ */
|
||||
+
|
||||
+#include <linux/kernel.h>
|
||||
+#include <linux/module.h>
|
||||
+#include <linux/mtd/mtd.h>
|
||||
+#include <linux/mtd/partitions.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/slab.h>
|
||||
+
|
||||
+#define TPLINK_SAFELOADER_DATA_OFFSET 4
|
||||
+#define TPLINK_SAFELOADER_MAX_PARTS 32
|
||||
+
|
||||
+struct safeloader_cmn_header {
|
||||
+ __be32 size;
|
||||
+ uint32_t unused;
|
||||
+} __packed;
|
||||
+
|
||||
+static void *mtd_parser_tplink_safeloader_read_table(struct mtd_info *mtd)
|
||||
+{
|
||||
+ struct safeloader_cmn_header hdr;
|
||||
+ struct device_node *np;
|
||||
+ size_t bytes_read;
|
||||
+ size_t offset;
|
||||
+ size_t size;
|
||||
+ char *buf;
|
||||
+ int err;
|
||||
+
|
||||
+ np = mtd_get_of_node(mtd);
|
||||
+ if (mtd_is_partition(mtd))
|
||||
+ of_node_get(np);
|
||||
+ else
|
||||
+ np = of_get_child_by_name(np, "partitions");
|
||||
+
|
||||
+ if (of_property_read_u32(np, "partitions-table-offset", (u32 *)&offset)) {
|
||||
+ pr_err("Failed to get partitions table offset\n");
|
||||
+ goto err_put;
|
||||
+ }
|
||||
+
|
||||
+ err = mtd_read(mtd, offset, sizeof(hdr), &bytes_read, (uint8_t *)&hdr);
|
||||
+ if (err && !mtd_is_bitflip(err)) {
|
||||
+ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset);
|
||||
+ goto err_put;
|
||||
+ }
|
||||
+
|
||||
+ size = be32_to_cpu(hdr.size);
|
||||
+
|
||||
+ buf = kmalloc(size + 1, GFP_KERNEL);
|
||||
+ if (!buf)
|
||||
+ goto err_put;
|
||||
+
|
||||
+ err = mtd_read(mtd, offset + sizeof(hdr), size, &bytes_read, buf);
|
||||
+ if (err && !mtd_is_bitflip(err)) {
|
||||
+ pr_err("Failed to read from %s at 0x%zx\n", mtd->name, offset + sizeof(hdr));
|
||||
+ goto err_kfree;
|
||||
+ }
|
||||
+
|
||||
+ buf[size] = '\0';
|
||||
+
|
||||
+ of_node_put(np);
|
||||
+
|
||||
+ return buf;
|
||||
+
|
||||
+err_kfree:
|
||||
+ kfree(buf);
|
||||
+err_put:
|
||||
+ of_node_put(np);
|
||||
+ return NULL;
|
||||
+}
|
||||
+
|
||||
+static int mtd_parser_tplink_safeloader_parse(struct mtd_info *mtd,
|
||||
+ const struct mtd_partition **pparts,
|
||||
+ struct mtd_part_parser_data *data)
|
||||
+{
|
||||
+ struct mtd_partition *parts;
|
||||
+ char name[65];
|
||||
+ size_t offset;
|
||||
+ size_t bytes;
|
||||
+ char *buf;
|
||||
+ int idx;
|
||||
+ int err;
|
||||
+
|
||||
+ parts = kcalloc(TPLINK_SAFELOADER_MAX_PARTS, sizeof(*parts), GFP_KERNEL);
|
||||
+ if (!parts) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto err_out;
|
||||
+ }
|
||||
+
|
||||
+ buf = mtd_parser_tplink_safeloader_read_table(mtd);
|
||||
+ if (!buf) {
|
||||
+ err = -ENOENT;
|
||||
+ goto err_out;
|
||||
+ }
|
||||
+
|
||||
+ for (idx = 0, offset = TPLINK_SAFELOADER_DATA_OFFSET;
|
||||
+ idx < TPLINK_SAFELOADER_MAX_PARTS &&
|
||||
+ sscanf(buf + offset, "partition %64s base 0x%llx size 0x%llx%zn\n",
|
||||
+ name, &parts[idx].offset, &parts[idx].size, &bytes) == 3;
|
||||
+ idx++, offset += bytes + 1) {
|
||||
+ parts[idx].name = kstrdup(name, GFP_KERNEL);
|
||||
+ if (!parts[idx].name) {
|
||||
+ err = -ENOMEM;
|
||||
+ goto err_free;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (idx == TPLINK_SAFELOADER_MAX_PARTS)
|
||||
+ pr_warn("Reached maximum number of partitions!\n");
|
||||
+
|
||||
+ kfree(buf);
|
||||
+
|
||||
+ *pparts = parts;
|
||||
+
|
||||
+ return idx;
|
||||
+
|
||||
+err_free:
|
||||
+ for (idx -= 1; idx >= 0; idx--)
|
||||
+ kfree(parts[idx].name);
|
||||
+err_out:
|
||||
+ return err;
|
||||
+};
|
||||
+
|
||||
+static void mtd_parser_tplink_safeloader_cleanup(const struct mtd_partition *pparts,
|
||||
+ int nr_parts)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < nr_parts; i++)
|
||||
+ kfree(pparts[i].name);
|
||||
+
|
||||
+ kfree(pparts);
|
||||
+}
|
||||
+
|
||||
+static const struct of_device_id mtd_parser_tplink_safeloader_of_match_table[] = {
|
||||
+ { .compatible = "tplink,safeloader-partitions" },
|
||||
+ {},
|
||||
+};
|
||||
+MODULE_DEVICE_TABLE(of, mtd_parser_tplink_safeloader_of_match_table);
|
||||
+
|
||||
+static struct mtd_part_parser mtd_parser_tplink_safeloader = {
|
||||
+ .parse_fn = mtd_parser_tplink_safeloader_parse,
|
||||
+ .cleanup = mtd_parser_tplink_safeloader_cleanup,
|
||||
+ .name = "tplink-safeloader",
|
||||
+ .of_match_table = mtd_parser_tplink_safeloader_of_match_table,
|
||||
+};
|
||||
+module_mtd_part_parser(mtd_parser_tplink_safeloader);
|
||||
+
|
||||
+MODULE_LICENSE("GPL");
|
||||
@@ -1,35 +0,0 @@
|
||||
From ebed787a0becb9354f0a23620a5130cccd6c730c Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 19 Jan 2023 03:45:43 +0000
|
||||
Subject: [PATCH] mtd: spinand: macronix: use scratch buffer for DMA operation
|
||||
|
||||
The mx35lf1ge4ab_get_eccsr() function uses an SPI DMA operation to
|
||||
read the eccsr, hence the buffer should not be on stack. Since commit
|
||||
380583227c0c7f ("spi: spi-mem: Add extra sanity checks on the op param")
|
||||
the kernel emmits a warning and blocks such operations.
|
||||
|
||||
Use the scratch buffer to get eccsr instead of trying to directly read
|
||||
into a stack-allocated variable.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Reviewed-by: Dhruva Gole <d-gole@ti.com>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/Y8i85zM0u4XdM46z@makrotopia.org
|
||||
---
|
||||
drivers/mtd/nand/spi/macronix.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/mtd/nand/spi/macronix.c
|
||||
+++ b/drivers/mtd/nand/spi/macronix.c
|
||||
@@ -83,9 +83,10 @@ static int mx35lf1ge4ab_ecc_get_status(s
|
||||
* in order to avoid forcing the wear-leveling layer to move
|
||||
* data around if it's not necessary.
|
||||
*/
|
||||
- if (mx35lf1ge4ab_get_eccsr(spinand, &eccsr))
|
||||
+ if (mx35lf1ge4ab_get_eccsr(spinand, spinand->scratchbuf))
|
||||
return nanddev_get_ecc_conf(nand)->strength;
|
||||
|
||||
+ eccsr = *spinand->scratchbuf;
|
||||
if (WARN_ON(eccsr > nanddev_get_ecc_conf(nand)->strength ||
|
||||
!eccsr))
|
||||
return nanddev_get_ecc_conf(nand)->strength;
|
||||
@@ -1,33 +0,0 @@
|
||||
From 1ecf9e390452e73a362ea7fbde8f3f0db83de856 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 22 Dec 2022 19:33:04 +0000
|
||||
Subject: [PATCH] mtd: ubi: wire-up parent MTD device
|
||||
|
||||
Wire up the device parent pointer of UBI devices to their lower MTD
|
||||
device, typically an MTD partition or whole-chip device.
|
||||
|
||||
The most noticeable change is that in sysfs, previously ubi devices
|
||||
would be could in /sys/devices/virtual/ubi while after this change they
|
||||
would be correctly attached to their parent MTD device, e.g.
|
||||
|
||||
/sys/devices/platform/1100d000.spi/spi_master/spi1/spi1.0/mtd/mtd2/ubi0.
|
||||
|
||||
Locating UBI devices using /sys/class/ubi/ of course still works as
|
||||
well.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Signed-off-by: Richard Weinberger <richard@nod.at>
|
||||
---
|
||||
drivers/mtd/ubi/build.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
--- a/drivers/mtd/ubi/build.c
|
||||
+++ b/drivers/mtd/ubi/build.c
|
||||
@@ -929,6 +929,7 @@ int ubi_attach_mtd_dev(struct mtd_info *
|
||||
ubi->dev.release = dev_release;
|
||||
ubi->dev.class = &ubi_class;
|
||||
ubi->dev.groups = ubi_dev_groups;
|
||||
+ ubi->dev.parent = &mtd->dev;
|
||||
|
||||
ubi->mtd = mtd;
|
||||
ubi->ubi_num = ubi_num;
|
||||
@@ -1,49 +0,0 @@
|
||||
From 05b8773ca33253ea562be145cf3145b05ef19f86 Mon Sep 17 00:00:00 2001
|
||||
From: Daniel Golle <daniel@makrotopia.org>
|
||||
Date: Thu, 22 Dec 2022 19:33:31 +0000
|
||||
Subject: [PATCH] mtd: ubi: block: wire-up device parent
|
||||
|
||||
ubiblock devices were previously only identifyable by their name, but
|
||||
not connected to their parent UBI volume device e.g. in sysfs.
|
||||
Properly parent ubiblock device as descendant of a UBI volume device
|
||||
to reflect device model hierachy.
|
||||
|
||||
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
|
||||
Signed-off-by: Richard Weinberger <richard@nod.at>
|
||||
---
|
||||
drivers/mtd/ubi/block.c | 2 +-
|
||||
drivers/mtd/ubi/kapi.c | 1 +
|
||||
include/linux/mtd/ubi.h | 1 +
|
||||
3 files changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/mtd/ubi/block.c
|
||||
+++ b/drivers/mtd/ubi/block.c
|
||||
@@ -452,7 +452,7 @@ int ubiblock_create(struct ubi_volume_in
|
||||
list_add_tail(&dev->list, &ubiblock_devices);
|
||||
|
||||
/* Must be the last step: anyone can call file ops from now on */
|
||||
- ret = add_disk(dev->gd);
|
||||
+ ret = device_add_disk(vi->dev, dev->gd, NULL);
|
||||
if (ret)
|
||||
goto out_destroy_wq;
|
||||
|
||||
--- a/drivers/mtd/ubi/kapi.c
|
||||
+++ b/drivers/mtd/ubi/kapi.c
|
||||
@@ -79,6 +79,7 @@ void ubi_do_get_volume_info(struct ubi_d
|
||||
vi->name_len = vol->name_len;
|
||||
vi->name = vol->name;
|
||||
vi->cdev = vol->cdev.dev;
|
||||
+ vi->dev = &vol->dev;
|
||||
}
|
||||
|
||||
/**
|
||||
--- a/include/linux/mtd/ubi.h
|
||||
+++ b/include/linux/mtd/ubi.h
|
||||
@@ -110,6 +110,7 @@ struct ubi_volume_info {
|
||||
int name_len;
|
||||
const char *name;
|
||||
dev_t cdev;
|
||||
+ struct device *dev;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1,47 +0,0 @@
|
||||
From 281f7a6c1a33fffcde32001bacbb4f672140fbf9 Mon Sep 17 00:00:00 2001
|
||||
From: Michael Walle <michael@walle.cc>
|
||||
Date: Wed, 8 Mar 2023 09:20:21 +0100
|
||||
Subject: [PATCH] mtd: core: prepare mtd_otp_nvmem_add() to handle
|
||||
-EPROBE_DEFER
|
||||
|
||||
NVMEM soon will get the ability for nvmem layouts and these might
|
||||
not be ready when nvmem_register() is called and thus it might
|
||||
return -EPROBE_DEFER. Don't print the error message in this case.
|
||||
|
||||
Signed-off-by: Michael Walle <michael@walle.cc>
|
||||
Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
|
||||
Link: https://lore.kernel.org/linux-mtd/20230308082021.870459-4-michael@walle.cc
|
||||
---
|
||||
drivers/mtd/mtdcore.c | 7 +++----
|
||||
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||
|
||||
--- a/drivers/mtd/mtdcore.c
|
||||
+++ b/drivers/mtd/mtdcore.c
|
||||
@@ -955,8 +955,8 @@ static int mtd_otp_nvmem_add(struct mtd_
|
||||
nvmem = mtd_otp_nvmem_register(mtd, "user-otp", size,
|
||||
mtd_nvmem_user_otp_reg_read);
|
||||
if (IS_ERR(nvmem)) {
|
||||
- dev_err(dev, "Failed to register OTP NVMEM device\n");
|
||||
- return PTR_ERR(nvmem);
|
||||
+ err = PTR_ERR(nvmem);
|
||||
+ goto err;
|
||||
}
|
||||
mtd->otp_user_nvmem = nvmem;
|
||||
}
|
||||
@@ -973,7 +973,6 @@ static int mtd_otp_nvmem_add(struct mtd_
|
||||
nvmem = mtd_otp_nvmem_register(mtd, "factory-otp", size,
|
||||
mtd_nvmem_fact_otp_reg_read);
|
||||
if (IS_ERR(nvmem)) {
|
||||
- dev_err(dev, "Failed to register OTP NVMEM device\n");
|
||||
err = PTR_ERR(nvmem);
|
||||
goto err;
|
||||
}
|
||||
@@ -985,7 +984,7 @@ static int mtd_otp_nvmem_add(struct mtd_
|
||||
|
||||
err:
|
||||
nvmem_unregister(mtd->otp_user_nvmem);
|
||||
- return err;
|
||||
+ return dev_err_probe(dev, err, "Failed to register OTP NVMEM device\n");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1,107 +0,0 @@
|
||||
From: Richard Gobert <richardbgobert@gmail.com>
|
||||
Date: Wed, 3 Jan 2024 15:44:21 +0100
|
||||
Subject: [PATCH] net: gro: parse ipv6 ext headers without frag0 invalidation
|
||||
|
||||
The existing code always pulls the IPv6 header and sets the transport
|
||||
offset initially. Then optionally again pulls any extension headers in
|
||||
ipv6_gso_pull_exthdrs and sets the transport offset again on return from
|
||||
that call. skb->data is set at the start of the first extension header
|
||||
before calling ipv6_gso_pull_exthdrs, and must disable the frag0
|
||||
optimization because that function uses pskb_may_pull/pskb_pull instead of
|
||||
skb_gro_ helpers. It sets the GRO offset to the TCP header with
|
||||
skb_gro_pull and sets the transport header. Then returns skb->data to its
|
||||
position before this block.
|
||||
|
||||
This commit introduces a new helper function - ipv6_gro_pull_exthdrs -
|
||||
which is used in ipv6_gro_receive to pull ipv6 ext headers instead of
|
||||
ipv6_gso_pull_exthdrs. Thus, there is no modification of skb->data, all
|
||||
operations use skb_gro_* helpers, and the frag0 fast path can be taken for
|
||||
IPv6 packets with ext headers.
|
||||
|
||||
Signed-off-by: Richard Gobert <richardbgobert@gmail.com>
|
||||
Reviewed-by: Willem de Bruijn <willemb@google.com>
|
||||
Reviewed-by: David Ahern <dsahern@kernel.org>
|
||||
Reviewed-by: Eric Dumazet <edumazet@google.com>
|
||||
Link: https://lore.kernel.org/r/504130f6-b56c-4dcc-882c-97942c59f5b7@gmail.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
|
||||
--- a/net/ipv6/ip6_offload.c
|
||||
+++ b/net/ipv6/ip6_offload.c
|
||||
@@ -36,6 +36,40 @@
|
||||
INDIRECT_CALL_L4(cb, f2, f1, head, skb); \
|
||||
})
|
||||
|
||||
+static int ipv6_gro_pull_exthdrs(struct sk_buff *skb, int off, int proto)
|
||||
+{
|
||||
+ const struct net_offload *ops = NULL;
|
||||
+ struct ipv6_opt_hdr *opth;
|
||||
+
|
||||
+ for (;;) {
|
||||
+ int len;
|
||||
+
|
||||
+ ops = rcu_dereference(inet6_offloads[proto]);
|
||||
+
|
||||
+ if (unlikely(!ops))
|
||||
+ break;
|
||||
+
|
||||
+ if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))
|
||||
+ break;
|
||||
+
|
||||
+ opth = skb_gro_header(skb, off + sizeof(*opth), off);
|
||||
+ if (unlikely(!opth))
|
||||
+ break;
|
||||
+
|
||||
+ len = ipv6_optlen(opth);
|
||||
+
|
||||
+ opth = skb_gro_header(skb, off + len, off);
|
||||
+ if (unlikely(!opth))
|
||||
+ break;
|
||||
+ proto = opth->nexthdr;
|
||||
+
|
||||
+ off += len;
|
||||
+ }
|
||||
+
|
||||
+ skb_gro_pull(skb, off - skb_network_offset(skb));
|
||||
+ return proto;
|
||||
+}
|
||||
+
|
||||
static int ipv6_gso_pull_exthdrs(struct sk_buff *skb, int proto)
|
||||
{
|
||||
const struct net_offload *ops = NULL;
|
||||
@@ -224,28 +258,25 @@ INDIRECT_CALLABLE_SCOPE struct sk_buff *
|
||||
goto out;
|
||||
|
||||
skb_set_network_header(skb, off);
|
||||
- skb_gro_pull(skb, sizeof(*iph));
|
||||
- skb_set_transport_header(skb, skb_gro_offset(skb));
|
||||
|
||||
- flush += ntohs(iph->payload_len) != skb_gro_len(skb);
|
||||
+ flush += ntohs(iph->payload_len) != skb->len - hlen;
|
||||
|
||||
proto = iph->nexthdr;
|
||||
ops = rcu_dereference(inet6_offloads[proto]);
|
||||
if (!ops || !ops->callbacks.gro_receive) {
|
||||
- pskb_pull(skb, skb_gro_offset(skb));
|
||||
- skb_gro_frag0_invalidate(skb);
|
||||
- proto = ipv6_gso_pull_exthdrs(skb, proto);
|
||||
- skb_gro_pull(skb, -skb_transport_offset(skb));
|
||||
- skb_reset_transport_header(skb);
|
||||
- __skb_push(skb, skb_gro_offset(skb));
|
||||
+ proto = ipv6_gro_pull_exthdrs(skb, hlen, proto);
|
||||
|
||||
ops = rcu_dereference(inet6_offloads[proto]);
|
||||
if (!ops || !ops->callbacks.gro_receive)
|
||||
goto out;
|
||||
|
||||
- iph = ipv6_hdr(skb);
|
||||
+ iph = skb_gro_network_header(skb);
|
||||
+ } else {
|
||||
+ skb_gro_pull(skb, sizeof(*iph));
|
||||
}
|
||||
|
||||
+ skb_set_transport_header(skb, skb_gro_offset(skb));
|
||||
+
|
||||
NAPI_GRO_CB(skb)->proto = proto;
|
||||
|
||||
flush--;
|
||||
@@ -1,41 +0,0 @@
|
||||
From 7390609b0121a1b982c5ecdfcd72dc328e5784ee Mon Sep 17 00:00:00 2001
|
||||
From: Michael Walle <michael@walle.cc>
|
||||
Date: Mon, 6 Feb 2023 13:43:42 +0000
|
||||
Subject: [PATCH] net: add helper eth_addr_add()
|
||||
|
||||
Add a helper to add an offset to a ethernet address. This comes in handy
|
||||
if you have a base ethernet address for multiple interfaces.
|
||||
|
||||
Signed-off-by: Michael Walle <michael@walle.cc>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Acked-by: Jakub Kicinski <kuba@kernel.org>
|
||||
Signed-off-by: Srinivas Kandagatla <srinivas.kandagatla@linaro.org>
|
||||
Link: https://lore.kernel.org/r/20230206134356.839737-9-srinivas.kandagatla@linaro.org
|
||||
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
---
|
||||
include/linux/etherdevice.h | 14 ++++++++++++++
|
||||
1 file changed, 14 insertions(+)
|
||||
|
||||
--- a/include/linux/etherdevice.h
|
||||
+++ b/include/linux/etherdevice.h
|
||||
@@ -508,6 +508,20 @@ static inline void eth_addr_inc(u8 *addr
|
||||
}
|
||||
|
||||
/**
|
||||
+ * eth_addr_add() - Add (or subtract) an offset to/from the given MAC address.
|
||||
+ *
|
||||
+ * @offset: Offset to add.
|
||||
+ * @addr: Pointer to a six-byte array containing Ethernet address to increment.
|
||||
+ */
|
||||
+static inline void eth_addr_add(u8 *addr, long offset)
|
||||
+{
|
||||
+ u64 u = ether_addr_to_u64(addr);
|
||||
+
|
||||
+ u += offset;
|
||||
+ u64_to_ether_addr(u, addr);
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* is_etherdev_addr - Tell if given Ethernet address belongs to the device.
|
||||
* @dev: Pointer to a device structure
|
||||
* @addr: Pointer to a six-byte array containing the Ethernet address
|
||||
@@ -1,79 +0,0 @@
|
||||
From fece7a8c65d1476b901b969a07b2979e1b459e66 Mon Sep 17 00:00:00 2001
|
||||
From: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
|
||||
Date: Fri, 28 Oct 2022 21:04:50 +0530
|
||||
Subject: [PATCH] net: wwan: t7xx: use union to group port type specific data
|
||||
|
||||
Use union inside t7xx_port to group port type specific data members.
|
||||
|
||||
Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
|
||||
Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/wwan/t7xx/t7xx_port.h | 6 +++++-
|
||||
drivers/net/wwan/t7xx/t7xx_port_wwan.c | 16 ++++++++--------
|
||||
2 files changed, 13 insertions(+), 9 deletions(-)
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port.h
|
||||
@@ -99,7 +99,6 @@ struct t7xx_port_conf {
|
||||
struct t7xx_port {
|
||||
/* Members not initialized in definition */
|
||||
const struct t7xx_port_conf *port_conf;
|
||||
- struct wwan_port *wwan_port;
|
||||
struct t7xx_pci_dev *t7xx_dev;
|
||||
struct device *dev;
|
||||
u16 seq_nums[2]; /* TX/RX sequence numbers */
|
||||
@@ -122,6 +121,11 @@ struct t7xx_port {
|
||||
int rx_length_th;
|
||||
bool chan_enable;
|
||||
struct task_struct *thread;
|
||||
+ union {
|
||||
+ struct {
|
||||
+ struct wwan_port *wwan_port;
|
||||
+ } wwan;
|
||||
+ };
|
||||
};
|
||||
|
||||
struct sk_buff *t7xx_port_alloc_skb(int payload);
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c
|
||||
@@ -109,12 +109,12 @@ static int t7xx_port_wwan_init(struct t7
|
||||
|
||||
static void t7xx_port_wwan_uninit(struct t7xx_port *port)
|
||||
{
|
||||
- if (!port->wwan_port)
|
||||
+ if (!port->wwan.wwan_port)
|
||||
return;
|
||||
|
||||
port->rx_length_th = 0;
|
||||
- wwan_remove_port(port->wwan_port);
|
||||
- port->wwan_port = NULL;
|
||||
+ wwan_remove_port(port->wwan.wwan_port);
|
||||
+ port->wwan.wwan_port = NULL;
|
||||
}
|
||||
|
||||
static int t7xx_port_wwan_recv_skb(struct t7xx_port *port, struct sk_buff *skb)
|
||||
@@ -129,7 +129,7 @@ static int t7xx_port_wwan_recv_skb(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
- wwan_port_rx(port->wwan_port, skb);
|
||||
+ wwan_port_rx(port->wwan.wwan_port, skb);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -158,10 +158,10 @@ static void t7xx_port_wwan_md_state_noti
|
||||
if (state != MD_STATE_READY)
|
||||
return;
|
||||
|
||||
- if (!port->wwan_port) {
|
||||
- port->wwan_port = wwan_create_port(port->dev, port_conf->port_type,
|
||||
- &wwan_ops, port);
|
||||
- if (IS_ERR(port->wwan_port))
|
||||
+ if (!port->wwan.wwan_port) {
|
||||
+ port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type,
|
||||
+ &wwan_ops, port);
|
||||
+ if (IS_ERR(port->wwan.wwan_port))
|
||||
dev_err(port->dev, "Unable to create WWWAN port %s", port_conf->name);
|
||||
}
|
||||
}
|
||||
@@ -1,237 +0,0 @@
|
||||
From 3349e4a48acb0923fa98d2beac82a833a76116cb Mon Sep 17 00:00:00 2001
|
||||
From: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
|
||||
Date: Fri, 28 Oct 2022 21:05:34 +0530
|
||||
Subject: [PATCH] net: wwan: t7xx: Add port for modem logging
|
||||
|
||||
The Modem Logging (MDL) port provides an interface to collect modem
|
||||
logs for debugging purposes. MDL is supported by the relay interface,
|
||||
and the mtk_t7xx port infrastructure. MDL allows user-space apps to
|
||||
control logging via mbim command and to collect logs via the relay
|
||||
interface, while port infrastructure facilitates communication between
|
||||
the driver and the modem.
|
||||
|
||||
Signed-off-by: Moises Veleta <moises.veleta@linux.intel.com>
|
||||
Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
|
||||
Signed-off-by: Devegowda Chandrashekar <chandrashekar.devegowda@intel.com>
|
||||
Acked-by: Ricardo Martinez <ricardo.martinez@linux.intel.com>
|
||||
Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/wwan/Kconfig | 1 +
|
||||
drivers/net/wwan/t7xx/Makefile | 3 +
|
||||
drivers/net/wwan/t7xx/t7xx_hif_cldma.c | 2 +
|
||||
drivers/net/wwan/t7xx/t7xx_pci.h | 3 +
|
||||
drivers/net/wwan/t7xx/t7xx_port.h | 3 +
|
||||
drivers/net/wwan/t7xx/t7xx_port_proxy.c | 12 +++
|
||||
drivers/net/wwan/t7xx/t7xx_port_proxy.h | 4 +
|
||||
drivers/net/wwan/t7xx/t7xx_port_trace.c | 116 ++++++++++++++++++++++++
|
||||
8 files changed, 144 insertions(+)
|
||||
create mode 100644 drivers/net/wwan/t7xx/t7xx_port_trace.c
|
||||
|
||||
--- a/drivers/net/wwan/Kconfig
|
||||
+++ b/drivers/net/wwan/Kconfig
|
||||
@@ -108,6 +108,7 @@ config IOSM
|
||||
config MTK_T7XX
|
||||
tristate "MediaTek PCIe 5G WWAN modem T7xx device"
|
||||
depends on PCI
|
||||
+ select RELAY if WWAN_DEBUGFS
|
||||
help
|
||||
Enables MediaTek PCIe based 5G WWAN modem (T7xx series) device.
|
||||
Adapts WWAN framework and provides network interface like wwan0
|
||||
--- a/drivers/net/wwan/t7xx/Makefile
|
||||
+++ b/drivers/net/wwan/t7xx/Makefile
|
||||
@@ -16,3 +16,6 @@ mtk_t7xx-y:= t7xx_pci.o \
|
||||
t7xx_hif_dpmaif_rx.o \
|
||||
t7xx_dpmaif.o \
|
||||
t7xx_netdev.o
|
||||
+
|
||||
+mtk_t7xx-$(CONFIG_WWAN_DEBUGFS) += \
|
||||
+ t7xx_port_trace.o \
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
|
||||
@@ -1019,6 +1019,8 @@ static int t7xx_cldma_late_init(struct c
|
||||
dev_err(md_ctrl->dev, "control TX ring init fail\n");
|
||||
goto err_free_tx_ring;
|
||||
}
|
||||
+
|
||||
+ md_ctrl->tx_ring[i].pkt_size = CLDMA_MTU;
|
||||
}
|
||||
|
||||
for (j = 0; j < CLDMA_RXQ_NUM; j++) {
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_pci.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_pci.h
|
||||
@@ -79,6 +79,9 @@ struct t7xx_pci_dev {
|
||||
spinlock_t md_pm_lock; /* Protects PCI resource lock */
|
||||
unsigned int sleep_disable_count;
|
||||
struct completion sleep_lock_acquire;
|
||||
+#ifdef CONFIG_WWAN_DEBUGFS
|
||||
+ struct dentry *debugfs_dir;
|
||||
+#endif
|
||||
};
|
||||
|
||||
enum t7xx_pm_id {
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port.h
|
||||
@@ -125,6 +125,9 @@ struct t7xx_port {
|
||||
struct {
|
||||
struct wwan_port *wwan_port;
|
||||
} wwan;
|
||||
+ struct {
|
||||
+ struct rchan *relaych;
|
||||
+ } log;
|
||||
};
|
||||
};
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port_proxy.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.c
|
||||
@@ -70,6 +70,18 @@ static const struct t7xx_port_conf t7xx_
|
||||
.name = "MBIM",
|
||||
.port_type = WWAN_PORT_MBIM,
|
||||
}, {
|
||||
+#ifdef CONFIG_WWAN_DEBUGFS
|
||||
+ .tx_ch = PORT_CH_MD_LOG_TX,
|
||||
+ .rx_ch = PORT_CH_MD_LOG_RX,
|
||||
+ .txq_index = 7,
|
||||
+ .rxq_index = 7,
|
||||
+ .txq_exp_index = 7,
|
||||
+ .rxq_exp_index = 7,
|
||||
+ .path_id = CLDMA_ID_MD,
|
||||
+ .ops = &t7xx_trace_port_ops,
|
||||
+ .name = "mdlog",
|
||||
+ }, {
|
||||
+#endif
|
||||
.tx_ch = PORT_CH_CONTROL_TX,
|
||||
.rx_ch = PORT_CH_CONTROL_RX,
|
||||
.txq_index = Q_IDX_CTRL,
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port_proxy.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.h
|
||||
@@ -87,6 +87,10 @@ struct ctrl_msg_header {
|
||||
extern struct port_ops wwan_sub_port_ops;
|
||||
extern struct port_ops ctl_port_ops;
|
||||
|
||||
+#ifdef CONFIG_WWAN_DEBUGFS
|
||||
+extern struct port_ops t7xx_trace_port_ops;
|
||||
+#endif
|
||||
+
|
||||
void t7xx_port_proxy_reset(struct port_proxy *port_prox);
|
||||
void t7xx_port_proxy_uninit(struct port_proxy *port_prox);
|
||||
int t7xx_port_proxy_init(struct t7xx_modem *md);
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port_trace.c
|
||||
@@ -0,0 +1,116 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0-only
|
||||
+/*
|
||||
+ * Copyright (C) 2022 Intel Corporation.
|
||||
+ */
|
||||
+
|
||||
+#include <linux/debugfs.h>
|
||||
+#include <linux/relay.h>
|
||||
+#include <linux/skbuff.h>
|
||||
+#include <linux/wwan.h>
|
||||
+
|
||||
+#include "t7xx_port.h"
|
||||
+#include "t7xx_port_proxy.h"
|
||||
+#include "t7xx_state_monitor.h"
|
||||
+
|
||||
+#define T7XX_TRC_SUB_BUFF_SIZE 131072
|
||||
+#define T7XX_TRC_N_SUB_BUFF 32
|
||||
+
|
||||
+static struct dentry *t7xx_trace_create_buf_file_handler(const char *filename,
|
||||
+ struct dentry *parent,
|
||||
+ umode_t mode,
|
||||
+ struct rchan_buf *buf,
|
||||
+ int *is_global)
|
||||
+{
|
||||
+ *is_global = 1;
|
||||
+ return debugfs_create_file(filename, mode, parent, buf,
|
||||
+ &relay_file_operations);
|
||||
+}
|
||||
+
|
||||
+static int t7xx_trace_remove_buf_file_handler(struct dentry *dentry)
|
||||
+{
|
||||
+ debugfs_remove(dentry);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int t7xx_trace_subbuf_start_handler(struct rchan_buf *buf, void *subbuf,
|
||||
+ void *prev_subbuf, size_t prev_padding)
|
||||
+{
|
||||
+ if (relay_buf_full(buf)) {
|
||||
+ pr_err_ratelimited("Relay_buf full dropping traces");
|
||||
+ return 0;
|
||||
+ }
|
||||
+
|
||||
+ return 1;
|
||||
+}
|
||||
+
|
||||
+static struct rchan_callbacks relay_callbacks = {
|
||||
+ .subbuf_start = t7xx_trace_subbuf_start_handler,
|
||||
+ .create_buf_file = t7xx_trace_create_buf_file_handler,
|
||||
+ .remove_buf_file = t7xx_trace_remove_buf_file_handler,
|
||||
+};
|
||||
+
|
||||
+static void t7xx_trace_port_uninit(struct t7xx_port *port)
|
||||
+{
|
||||
+ struct dentry *debugfs_dir = port->t7xx_dev->debugfs_dir;
|
||||
+ struct rchan *relaych = port->log.relaych;
|
||||
+
|
||||
+ if (!relaych)
|
||||
+ return;
|
||||
+
|
||||
+ relay_close(relaych);
|
||||
+ debugfs_remove_recursive(debugfs_dir);
|
||||
+}
|
||||
+
|
||||
+static int t7xx_trace_port_recv_skb(struct t7xx_port *port, struct sk_buff *skb)
|
||||
+{
|
||||
+ struct rchan *relaych = port->log.relaych;
|
||||
+
|
||||
+ if (!relaych)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ relay_write(relaych, skb->data, skb->len);
|
||||
+ dev_kfree_skb(skb);
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static void t7xx_port_trace_md_state_notify(struct t7xx_port *port, unsigned int state)
|
||||
+{
|
||||
+ struct rchan *relaych = port->log.relaych;
|
||||
+ struct dentry *debugfs_wwan_dir;
|
||||
+ struct dentry *debugfs_dir;
|
||||
+
|
||||
+ if (state != MD_STATE_READY || relaych)
|
||||
+ return;
|
||||
+
|
||||
+ debugfs_wwan_dir = wwan_get_debugfs_dir(port->dev);
|
||||
+ if (IS_ERR(debugfs_wwan_dir))
|
||||
+ return;
|
||||
+
|
||||
+ debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, debugfs_wwan_dir);
|
||||
+ if (IS_ERR_OR_NULL(debugfs_dir)) {
|
||||
+ wwan_put_debugfs_dir(debugfs_wwan_dir);
|
||||
+ dev_err(port->dev, "Unable to create debugfs for trace");
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ relaych = relay_open("relay_ch", debugfs_dir, T7XX_TRC_SUB_BUFF_SIZE,
|
||||
+ T7XX_TRC_N_SUB_BUFF, &relay_callbacks, NULL);
|
||||
+ if (!relaych)
|
||||
+ goto err_rm_debugfs_dir;
|
||||
+
|
||||
+ wwan_put_debugfs_dir(debugfs_wwan_dir);
|
||||
+ port->log.relaych = relaych;
|
||||
+ port->t7xx_dev->debugfs_dir = debugfs_dir;
|
||||
+ return;
|
||||
+
|
||||
+err_rm_debugfs_dir:
|
||||
+ debugfs_remove_recursive(debugfs_dir);
|
||||
+ wwan_put_debugfs_dir(debugfs_wwan_dir);
|
||||
+ dev_err(port->dev, "Unable to create trace port %s", port->port_conf->name);
|
||||
+}
|
||||
+
|
||||
+struct port_ops t7xx_trace_port_ops = {
|
||||
+ .recv_skb = t7xx_trace_port_recv_skb,
|
||||
+ .uninit = t7xx_trace_port_uninit,
|
||||
+ .md_state_notify = t7xx_port_trace_md_state_notify,
|
||||
+};
|
||||
@@ -1,31 +0,0 @@
|
||||
From c053d7b6bdcb45780036b32be6a950f71a78bf52 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Ilpo=20J=C3=A4rvinen?= <ilpo.jarvinen@linux.intel.com>
|
||||
Date: Thu, 3 Nov 2022 14:48:28 +0530
|
||||
Subject: [PATCH] net: wwan: t7xx: Use needed_headroom instead of
|
||||
hard_header_len
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
hard_header_len is used by gro_list_prepare() but on Rx, there
|
||||
is no header so use needed_headroom instead.
|
||||
|
||||
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
|
||||
Signed-off-by: Sreehari Kancharla <sreehari.kancharla@linux.intel.com>
|
||||
Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/wwan/t7xx/t7xx_netdev.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_netdev.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_netdev.c
|
||||
@@ -161,7 +161,7 @@ static void t7xx_ccmni_post_stop(struct
|
||||
|
||||
static void t7xx_ccmni_wwan_setup(struct net_device *dev)
|
||||
{
|
||||
- dev->hard_header_len += sizeof(struct ccci_header);
|
||||
+ dev->needed_headroom += sizeof(struct ccci_header);
|
||||
|
||||
dev->mtu = ETH_DATA_LEN;
|
||||
dev->max_mtu = CCMNI_MTU_MAX;
|
||||
@@ -1,652 +0,0 @@
|
||||
From 5545b7b9f294de7f95ec6a7cb1de0db52296001c Mon Sep 17 00:00:00 2001
|
||||
From: Haijun Liu <haijun.liu@mediatek.com>
|
||||
Date: Thu, 3 Nov 2022 14:48:29 +0530
|
||||
Subject: [PATCH] net: wwan: t7xx: Add NAPI support
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Replace the work queue based RX flow with a NAPI implementation
|
||||
Remove rx_thread and dpmaif_rxq_work.
|
||||
Enable GRO on RX path.
|
||||
Introduce dummy network device. its responsibility is
|
||||
- Binds one NAPI object for each DL HW queue and acts as
|
||||
the agent of all those network devices.
|
||||
- Use NAPI object to poll DL packets.
|
||||
- Helps to dispatch each packet to the network interface.
|
||||
|
||||
Signed-off-by: Haijun Liu <haijun.liu@mediatek.com>
|
||||
Co-developed-by: Sreehari Kancharla <sreehari.kancharla@linux.intel.com>
|
||||
Signed-off-by: Sreehari Kancharla <sreehari.kancharla@linux.intel.com>
|
||||
Signed-off-by: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
|
||||
Acked-by: Ricardo Martinez <ricardo.martinez@linux.intel.com>
|
||||
Acked-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
|
||||
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h | 14 +-
|
||||
drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c | 218 +++++++--------------
|
||||
drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h | 1 +
|
||||
drivers/net/wwan/t7xx/t7xx_netdev.c | 89 ++++++++-
|
||||
drivers/net/wwan/t7xx/t7xx_netdev.h | 5 +
|
||||
5 files changed, 161 insertions(+), 166 deletions(-)
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.h
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/mm_types.h>
|
||||
+#include <linux/netdevice.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/spinlock.h>
|
||||
@@ -109,20 +110,14 @@ struct dpmaif_rx_queue {
|
||||
struct dpmaif_bat_request *bat_req;
|
||||
struct dpmaif_bat_request *bat_frag;
|
||||
|
||||
- wait_queue_head_t rx_wq;
|
||||
- struct task_struct *rx_thread;
|
||||
- struct sk_buff_head skb_list;
|
||||
- unsigned int skb_list_max_len;
|
||||
-
|
||||
- struct workqueue_struct *worker;
|
||||
- struct work_struct dpmaif_rxq_work;
|
||||
-
|
||||
atomic_t rx_processing;
|
||||
|
||||
struct dpmaif_ctrl *dpmaif_ctrl;
|
||||
unsigned int expect_pit_seq;
|
||||
unsigned int pit_remain_release_cnt;
|
||||
struct dpmaif_cur_rx_skb_info rx_data_info;
|
||||
+ struct napi_struct napi;
|
||||
+ bool sleep_lock_pending;
|
||||
};
|
||||
|
||||
struct dpmaif_tx_queue {
|
||||
@@ -168,7 +163,8 @@ enum dpmaif_txq_state {
|
||||
struct dpmaif_callbacks {
|
||||
void (*state_notify)(struct t7xx_pci_dev *t7xx_dev,
|
||||
enum dpmaif_txq_state state, int txq_number);
|
||||
- void (*recv_skb)(struct t7xx_pci_dev *t7xx_dev, struct sk_buff *skb);
|
||||
+ void (*recv_skb)(struct t7xx_ccmni_ctrl *ccmni_ctlb, struct sk_buff *skb,
|
||||
+ struct napi_struct *napi);
|
||||
};
|
||||
|
||||
struct dpmaif_ctrl {
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
|
||||
@@ -45,6 +45,7 @@
|
||||
#include "t7xx_dpmaif.h"
|
||||
#include "t7xx_hif_dpmaif.h"
|
||||
#include "t7xx_hif_dpmaif_rx.h"
|
||||
+#include "t7xx_netdev.h"
|
||||
#include "t7xx_pci.h"
|
||||
|
||||
#define DPMAIF_BAT_COUNT 8192
|
||||
@@ -76,43 +77,6 @@ static unsigned int t7xx_normal_pit_bid(
|
||||
return value;
|
||||
}
|
||||
|
||||
-static int t7xx_dpmaif_net_rx_push_thread(void *arg)
|
||||
-{
|
||||
- struct dpmaif_rx_queue *q = arg;
|
||||
- struct dpmaif_ctrl *hif_ctrl;
|
||||
- struct dpmaif_callbacks *cb;
|
||||
-
|
||||
- hif_ctrl = q->dpmaif_ctrl;
|
||||
- cb = hif_ctrl->callbacks;
|
||||
-
|
||||
- while (!kthread_should_stop()) {
|
||||
- struct sk_buff *skb;
|
||||
- unsigned long flags;
|
||||
-
|
||||
- if (skb_queue_empty(&q->skb_list)) {
|
||||
- if (wait_event_interruptible(q->rx_wq,
|
||||
- !skb_queue_empty(&q->skb_list) ||
|
||||
- kthread_should_stop()))
|
||||
- continue;
|
||||
-
|
||||
- if (kthread_should_stop())
|
||||
- break;
|
||||
- }
|
||||
-
|
||||
- spin_lock_irqsave(&q->skb_list.lock, flags);
|
||||
- skb = __skb_dequeue(&q->skb_list);
|
||||
- spin_unlock_irqrestore(&q->skb_list.lock, flags);
|
||||
-
|
||||
- if (!skb)
|
||||
- continue;
|
||||
-
|
||||
- cb->recv_skb(hif_ctrl->t7xx_dev, skb);
|
||||
- cond_resched();
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
static int t7xx_dpmaif_update_bat_wr_idx(struct dpmaif_ctrl *dpmaif_ctrl,
|
||||
const unsigned int q_num, const unsigned int bat_cnt)
|
||||
{
|
||||
@@ -726,21 +690,10 @@ static int t7xx_dpmaifq_rx_notify_hw(str
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static void t7xx_dpmaif_rx_skb_enqueue(struct dpmaif_rx_queue *rxq, struct sk_buff *skb)
|
||||
-{
|
||||
- unsigned long flags;
|
||||
-
|
||||
- spin_lock_irqsave(&rxq->skb_list.lock, flags);
|
||||
- if (rxq->skb_list.qlen < rxq->skb_list_max_len)
|
||||
- __skb_queue_tail(&rxq->skb_list, skb);
|
||||
- else
|
||||
- dev_kfree_skb_any(skb);
|
||||
- spin_unlock_irqrestore(&rxq->skb_list.lock, flags);
|
||||
-}
|
||||
-
|
||||
static void t7xx_dpmaif_rx_skb(struct dpmaif_rx_queue *rxq,
|
||||
struct dpmaif_cur_rx_skb_info *skb_info)
|
||||
{
|
||||
+ struct dpmaif_ctrl *dpmaif_ctrl = rxq->dpmaif_ctrl;
|
||||
struct sk_buff *skb = skb_info->cur_skb;
|
||||
struct t7xx_skb_cb *skb_cb;
|
||||
u8 netif_id;
|
||||
@@ -758,11 +711,11 @@ static void t7xx_dpmaif_rx_skb(struct dp
|
||||
skb_cb = T7XX_SKB_CB(skb);
|
||||
skb_cb->netif_idx = netif_id;
|
||||
skb_cb->rx_pkt_type = skb_info->pkt_type;
|
||||
- t7xx_dpmaif_rx_skb_enqueue(rxq, skb);
|
||||
+ dpmaif_ctrl->callbacks->recv_skb(dpmaif_ctrl->t7xx_dev->ccmni_ctlb, skb, &rxq->napi);
|
||||
}
|
||||
|
||||
static int t7xx_dpmaif_rx_start(struct dpmaif_rx_queue *rxq, const unsigned int pit_cnt,
|
||||
- const unsigned long timeout)
|
||||
+ const unsigned int budget, int *once_more)
|
||||
{
|
||||
unsigned int cur_pit, pit_len, rx_cnt, recv_skb_cnt = 0;
|
||||
struct device *dev = rxq->dpmaif_ctrl->dev;
|
||||
@@ -777,13 +730,14 @@ static int t7xx_dpmaif_rx_start(struct d
|
||||
struct dpmaif_pit *pkt_info;
|
||||
u32 val;
|
||||
|
||||
- if (!skb_info->msg_pit_received && time_after_eq(jiffies, timeout))
|
||||
+ if (!skb_info->msg_pit_received && recv_skb_cnt >= budget)
|
||||
break;
|
||||
|
||||
pkt_info = (struct dpmaif_pit *)rxq->pit_base + cur_pit;
|
||||
if (t7xx_dpmaif_check_pit_seq(rxq, pkt_info)) {
|
||||
dev_err_ratelimited(dev, "RXQ%u checks PIT SEQ fail\n", rxq->index);
|
||||
- return -EAGAIN;
|
||||
+ *once_more = 1;
|
||||
+ return recv_skb_cnt;
|
||||
}
|
||||
|
||||
val = FIELD_GET(PD_PIT_PACKET_TYPE, le32_to_cpu(pkt_info->header));
|
||||
@@ -817,12 +771,7 @@ static int t7xx_dpmaif_rx_start(struct d
|
||||
}
|
||||
|
||||
memset(skb_info, 0, sizeof(*skb_info));
|
||||
-
|
||||
recv_skb_cnt++;
|
||||
- if (!(recv_skb_cnt & DPMAIF_RX_PUSH_THRESHOLD_MASK)) {
|
||||
- wake_up_all(&rxq->rx_wq);
|
||||
- recv_skb_cnt = 0;
|
||||
- }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -837,16 +786,13 @@ static int t7xx_dpmaif_rx_start(struct d
|
||||
}
|
||||
}
|
||||
|
||||
- if (recv_skb_cnt)
|
||||
- wake_up_all(&rxq->rx_wq);
|
||||
-
|
||||
if (!ret)
|
||||
ret = t7xx_dpmaifq_rx_notify_hw(rxq);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- return rx_cnt;
|
||||
+ return recv_skb_cnt;
|
||||
}
|
||||
|
||||
static unsigned int t7xx_dpmaifq_poll_pit(struct dpmaif_rx_queue *rxq)
|
||||
@@ -863,53 +809,30 @@ static unsigned int t7xx_dpmaifq_poll_pi
|
||||
return pit_cnt;
|
||||
}
|
||||
|
||||
-static int t7xx_dpmaif_rx_data_collect(struct dpmaif_ctrl *dpmaif_ctrl,
|
||||
- const unsigned int q_num, const unsigned int budget)
|
||||
+static int t7xx_dpmaif_napi_rx_data_collect(struct dpmaif_ctrl *dpmaif_ctrl,
|
||||
+ const unsigned int q_num,
|
||||
+ const unsigned int budget, int *once_more)
|
||||
{
|
||||
struct dpmaif_rx_queue *rxq = &dpmaif_ctrl->rxq[q_num];
|
||||
- unsigned long time_limit;
|
||||
unsigned int cnt;
|
||||
+ int ret = 0;
|
||||
|
||||
- time_limit = jiffies + msecs_to_jiffies(DPMAIF_WQ_TIME_LIMIT_MS);
|
||||
-
|
||||
- while ((cnt = t7xx_dpmaifq_poll_pit(rxq))) {
|
||||
- unsigned int rd_cnt;
|
||||
- int real_cnt;
|
||||
-
|
||||
- rd_cnt = min(cnt, budget);
|
||||
-
|
||||
- real_cnt = t7xx_dpmaif_rx_start(rxq, rd_cnt, time_limit);
|
||||
- if (real_cnt < 0)
|
||||
- return real_cnt;
|
||||
-
|
||||
- if (real_cnt < cnt)
|
||||
- return -EAGAIN;
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
+ cnt = t7xx_dpmaifq_poll_pit(rxq);
|
||||
+ if (!cnt)
|
||||
+ return ret;
|
||||
|
||||
-static void t7xx_dpmaif_do_rx(struct dpmaif_ctrl *dpmaif_ctrl, struct dpmaif_rx_queue *rxq)
|
||||
-{
|
||||
- struct dpmaif_hw_info *hw_info = &dpmaif_ctrl->hw_info;
|
||||
- int ret;
|
||||
+ ret = t7xx_dpmaif_rx_start(rxq, cnt, budget, once_more);
|
||||
+ if (ret < 0)
|
||||
+ dev_err(dpmaif_ctrl->dev, "dlq%u rx ERR:%d\n", rxq->index, ret);
|
||||
|
||||
- ret = t7xx_dpmaif_rx_data_collect(dpmaif_ctrl, rxq->index, rxq->budget);
|
||||
- if (ret < 0) {
|
||||
- /* Try one more time */
|
||||
- queue_work(rxq->worker, &rxq->dpmaif_rxq_work);
|
||||
- t7xx_dpmaif_clr_ip_busy_sts(hw_info);
|
||||
- } else {
|
||||
- t7xx_dpmaif_clr_ip_busy_sts(hw_info);
|
||||
- t7xx_dpmaif_dlq_unmask_rx_done(hw_info, rxq->index);
|
||||
- }
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
-static void t7xx_dpmaif_rxq_work(struct work_struct *work)
|
||||
+int t7xx_dpmaif_napi_rx_poll(struct napi_struct *napi, const int budget)
|
||||
{
|
||||
- struct dpmaif_rx_queue *rxq = container_of(work, struct dpmaif_rx_queue, dpmaif_rxq_work);
|
||||
- struct dpmaif_ctrl *dpmaif_ctrl = rxq->dpmaif_ctrl;
|
||||
- int ret;
|
||||
+ struct dpmaif_rx_queue *rxq = container_of(napi, struct dpmaif_rx_queue, napi);
|
||||
+ struct t7xx_pci_dev *t7xx_dev = rxq->dpmaif_ctrl->t7xx_dev;
|
||||
+ int ret, once_more = 0, work_done = 0;
|
||||
|
||||
atomic_set(&rxq->rx_processing, 1);
|
||||
/* Ensure rx_processing is changed to 1 before actually begin RX flow */
|
||||
@@ -917,22 +840,52 @@ static void t7xx_dpmaif_rxq_work(struct
|
||||
|
||||
if (!rxq->que_started) {
|
||||
atomic_set(&rxq->rx_processing, 0);
|
||||
- dev_err(dpmaif_ctrl->dev, "Work RXQ: %d has not been started\n", rxq->index);
|
||||
- return;
|
||||
+ dev_err(rxq->dpmaif_ctrl->dev, "Work RXQ: %d has not been started\n", rxq->index);
|
||||
+ return work_done;
|
||||
}
|
||||
|
||||
- ret = pm_runtime_resume_and_get(dpmaif_ctrl->dev);
|
||||
- if (ret < 0 && ret != -EACCES)
|
||||
- return;
|
||||
+ if (!rxq->sleep_lock_pending) {
|
||||
+ pm_runtime_get_noresume(rxq->dpmaif_ctrl->dev);
|
||||
+ t7xx_pci_disable_sleep(t7xx_dev);
|
||||
+ }
|
||||
+
|
||||
+ ret = try_wait_for_completion(&t7xx_dev->sleep_lock_acquire);
|
||||
+ if (!ret) {
|
||||
+ napi_complete_done(napi, work_done);
|
||||
+ rxq->sleep_lock_pending = true;
|
||||
+ napi_reschedule(napi);
|
||||
+ return work_done;
|
||||
+ }
|
||||
+
|
||||
+ rxq->sleep_lock_pending = false;
|
||||
+ while (work_done < budget) {
|
||||
+ int each_budget = budget - work_done;
|
||||
+ int rx_cnt = t7xx_dpmaif_napi_rx_data_collect(rxq->dpmaif_ctrl, rxq->index,
|
||||
+ each_budget, &once_more);
|
||||
+ if (rx_cnt > 0)
|
||||
+ work_done += rx_cnt;
|
||||
+ else
|
||||
+ break;
|
||||
+ }
|
||||
|
||||
- t7xx_pci_disable_sleep(dpmaif_ctrl->t7xx_dev);
|
||||
- if (t7xx_pci_sleep_disable_complete(dpmaif_ctrl->t7xx_dev))
|
||||
- t7xx_dpmaif_do_rx(dpmaif_ctrl, rxq);
|
||||
+ if (once_more) {
|
||||
+ napi_gro_flush(napi, false);
|
||||
+ work_done = budget;
|
||||
+ t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
|
||||
+ } else if (work_done < budget) {
|
||||
+ napi_complete_done(napi, work_done);
|
||||
+ t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
|
||||
+ t7xx_dpmaif_dlq_unmask_rx_done(&rxq->dpmaif_ctrl->hw_info, rxq->index);
|
||||
+ } else {
|
||||
+ t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
|
||||
+ }
|
||||
|
||||
- t7xx_pci_enable_sleep(dpmaif_ctrl->t7xx_dev);
|
||||
- pm_runtime_mark_last_busy(dpmaif_ctrl->dev);
|
||||
- pm_runtime_put_autosuspend(dpmaif_ctrl->dev);
|
||||
+ t7xx_pci_enable_sleep(rxq->dpmaif_ctrl->t7xx_dev);
|
||||
+ pm_runtime_mark_last_busy(rxq->dpmaif_ctrl->dev);
|
||||
+ pm_runtime_put_noidle(rxq->dpmaif_ctrl->dev);
|
||||
atomic_set(&rxq->rx_processing, 0);
|
||||
+
|
||||
+ return work_done;
|
||||
}
|
||||
|
||||
void t7xx_dpmaif_irq_rx_done(struct dpmaif_ctrl *dpmaif_ctrl, const unsigned int que_mask)
|
||||
@@ -947,7 +900,7 @@ void t7xx_dpmaif_irq_rx_done(struct dpma
|
||||
}
|
||||
|
||||
rxq = &dpmaif_ctrl->rxq[qno];
|
||||
- queue_work(rxq->worker, &rxq->dpmaif_rxq_work);
|
||||
+ napi_schedule(&rxq->napi);
|
||||
}
|
||||
|
||||
static void t7xx_dpmaif_base_free(const struct dpmaif_ctrl *dpmaif_ctrl,
|
||||
@@ -1082,50 +1035,14 @@ int t7xx_dpmaif_rxq_init(struct dpmaif_r
|
||||
int ret;
|
||||
|
||||
ret = t7xx_dpmaif_rx_alloc(queue);
|
||||
- if (ret < 0) {
|
||||
+ if (ret < 0)
|
||||
dev_err(queue->dpmaif_ctrl->dev, "Failed to allocate RX buffers: %d\n", ret);
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- INIT_WORK(&queue->dpmaif_rxq_work, t7xx_dpmaif_rxq_work);
|
||||
-
|
||||
- queue->worker = alloc_workqueue("dpmaif_rx%d_worker",
|
||||
- WQ_UNBOUND | WQ_MEM_RECLAIM | WQ_HIGHPRI, 1, queue->index);
|
||||
- if (!queue->worker) {
|
||||
- ret = -ENOMEM;
|
||||
- goto err_free_rx_buffer;
|
||||
- }
|
||||
-
|
||||
- init_waitqueue_head(&queue->rx_wq);
|
||||
- skb_queue_head_init(&queue->skb_list);
|
||||
- queue->skb_list_max_len = queue->bat_req->pkt_buf_sz;
|
||||
- queue->rx_thread = kthread_run(t7xx_dpmaif_net_rx_push_thread,
|
||||
- queue, "dpmaif_rx%d_push", queue->index);
|
||||
-
|
||||
- ret = PTR_ERR_OR_ZERO(queue->rx_thread);
|
||||
- if (ret)
|
||||
- goto err_free_workqueue;
|
||||
-
|
||||
- return 0;
|
||||
-
|
||||
-err_free_workqueue:
|
||||
- destroy_workqueue(queue->worker);
|
||||
-
|
||||
-err_free_rx_buffer:
|
||||
- t7xx_dpmaif_rx_buf_free(queue);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void t7xx_dpmaif_rxq_free(struct dpmaif_rx_queue *queue)
|
||||
{
|
||||
- if (queue->worker)
|
||||
- destroy_workqueue(queue->worker);
|
||||
-
|
||||
- if (queue->rx_thread)
|
||||
- kthread_stop(queue->rx_thread);
|
||||
-
|
||||
- skb_queue_purge(&queue->skb_list);
|
||||
t7xx_dpmaif_rx_buf_free(queue);
|
||||
}
|
||||
|
||||
@@ -1188,8 +1105,6 @@ void t7xx_dpmaif_rx_stop(struct dpmaif_c
|
||||
struct dpmaif_rx_queue *rxq = &dpmaif_ctrl->rxq[i];
|
||||
int timeout, value;
|
||||
|
||||
- flush_work(&rxq->dpmaif_rxq_work);
|
||||
-
|
||||
timeout = readx_poll_timeout_atomic(atomic_read, &rxq->rx_processing, value,
|
||||
!value, 0, DPMAIF_CHECK_INIT_TIMEOUT_US);
|
||||
if (timeout)
|
||||
@@ -1205,7 +1120,6 @@ static void t7xx_dpmaif_stop_rxq(struct
|
||||
{
|
||||
int cnt, j = 0;
|
||||
|
||||
- flush_work(&rxq->dpmaif_rxq_work);
|
||||
rxq->que_started = false;
|
||||
|
||||
do {
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.h
|
||||
@@ -112,5 +112,6 @@ int t7xx_dpmaif_bat_alloc(const struct d
|
||||
const enum bat_type buf_type);
|
||||
void t7xx_dpmaif_bat_free(const struct dpmaif_ctrl *dpmaif_ctrl,
|
||||
struct dpmaif_bat_request *bat_req);
|
||||
+int t7xx_dpmaif_napi_rx_poll(struct napi_struct *napi, const int budget);
|
||||
|
||||
#endif /* __T7XX_HIF_DPMA_RX_H__ */
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_netdev.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_netdev.c
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/if_arp.h>
|
||||
#include <linux/if_ether.h>
|
||||
+#include <linux/ip.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdev_features.h>
|
||||
@@ -29,6 +30,7 @@
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wwan.h>
|
||||
+#include <net/ipv6.h>
|
||||
#include <net/pkt_sched.h>
|
||||
|
||||
#include "t7xx_hif_dpmaif_rx.h"
|
||||
@@ -39,13 +41,47 @@
|
||||
#include "t7xx_state_monitor.h"
|
||||
|
||||
#define IP_MUX_SESSION_DEFAULT 0
|
||||
+#define SBD_PACKET_TYPE_MASK GENMASK(7, 4)
|
||||
+
|
||||
+static void t7xx_ccmni_enable_napi(struct t7xx_ccmni_ctrl *ctlb)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ if (ctlb->is_napi_en)
|
||||
+ return;
|
||||
+
|
||||
+ for (i = 0; i < RXQ_NUM; i++) {
|
||||
+ napi_enable(ctlb->napi[i]);
|
||||
+ napi_schedule(ctlb->napi[i]);
|
||||
+ }
|
||||
+ ctlb->is_napi_en = true;
|
||||
+}
|
||||
+
|
||||
+static void t7xx_ccmni_disable_napi(struct t7xx_ccmni_ctrl *ctlb)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ if (!ctlb->is_napi_en)
|
||||
+ return;
|
||||
+
|
||||
+ for (i = 0; i < RXQ_NUM; i++) {
|
||||
+ napi_synchronize(ctlb->napi[i]);
|
||||
+ napi_disable(ctlb->napi[i]);
|
||||
+ }
|
||||
+
|
||||
+ ctlb->is_napi_en = false;
|
||||
+}
|
||||
|
||||
static int t7xx_ccmni_open(struct net_device *dev)
|
||||
{
|
||||
struct t7xx_ccmni *ccmni = wwan_netdev_drvpriv(dev);
|
||||
+ struct t7xx_ccmni_ctrl *ccmni_ctl = ccmni->ctlb;
|
||||
|
||||
netif_carrier_on(dev);
|
||||
netif_tx_start_all_queues(dev);
|
||||
+ if (!atomic_fetch_inc(&ccmni_ctl->napi_usr_refcnt))
|
||||
+ t7xx_ccmni_enable_napi(ccmni_ctl);
|
||||
+
|
||||
atomic_inc(&ccmni->usage);
|
||||
return 0;
|
||||
}
|
||||
@@ -53,8 +89,12 @@ static int t7xx_ccmni_open(struct net_de
|
||||
static int t7xx_ccmni_close(struct net_device *dev)
|
||||
{
|
||||
struct t7xx_ccmni *ccmni = wwan_netdev_drvpriv(dev);
|
||||
+ struct t7xx_ccmni_ctrl *ccmni_ctl = ccmni->ctlb;
|
||||
|
||||
atomic_dec(&ccmni->usage);
|
||||
+ if (atomic_dec_and_test(&ccmni_ctl->napi_usr_refcnt))
|
||||
+ t7xx_ccmni_disable_napi(ccmni_ctl);
|
||||
+
|
||||
netif_carrier_off(dev);
|
||||
netif_tx_disable(dev);
|
||||
return 0;
|
||||
@@ -127,6 +167,9 @@ static void t7xx_ccmni_start(struct t7xx
|
||||
netif_carrier_on(ccmni->dev);
|
||||
}
|
||||
}
|
||||
+
|
||||
+ if (atomic_read(&ctlb->napi_usr_refcnt))
|
||||
+ t7xx_ccmni_enable_napi(ctlb);
|
||||
}
|
||||
|
||||
static void t7xx_ccmni_pre_stop(struct t7xx_ccmni_ctrl *ctlb)
|
||||
@@ -149,6 +192,9 @@ static void t7xx_ccmni_post_stop(struct
|
||||
struct t7xx_ccmni *ccmni;
|
||||
int i;
|
||||
|
||||
+ if (atomic_read(&ctlb->napi_usr_refcnt))
|
||||
+ t7xx_ccmni_disable_napi(ctlb);
|
||||
+
|
||||
for (i = 0; i < ctlb->nic_dev_num; i++) {
|
||||
ccmni = ctlb->ccmni_inst[i];
|
||||
if (!ccmni)
|
||||
@@ -183,6 +229,9 @@ static void t7xx_ccmni_wwan_setup(struct
|
||||
dev->features |= NETIF_F_RXCSUM;
|
||||
dev->hw_features |= NETIF_F_RXCSUM;
|
||||
|
||||
+ dev->features |= NETIF_F_GRO;
|
||||
+ dev->hw_features |= NETIF_F_GRO;
|
||||
+
|
||||
dev->needs_free_netdev = true;
|
||||
|
||||
dev->type = ARPHRD_NONE;
|
||||
@@ -190,6 +239,34 @@ static void t7xx_ccmni_wwan_setup(struct
|
||||
dev->netdev_ops = &ccmni_netdev_ops;
|
||||
}
|
||||
|
||||
+static void t7xx_init_netdev_napi(struct t7xx_ccmni_ctrl *ctlb)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ /* one HW, but shared with multiple net devices,
|
||||
+ * so add a dummy device for NAPI.
|
||||
+ */
|
||||
+ init_dummy_netdev(&ctlb->dummy_dev);
|
||||
+ atomic_set(&ctlb->napi_usr_refcnt, 0);
|
||||
+ ctlb->is_napi_en = false;
|
||||
+
|
||||
+ for (i = 0; i < RXQ_NUM; i++) {
|
||||
+ ctlb->napi[i] = &ctlb->hif_ctrl->rxq[i].napi;
|
||||
+ netif_napi_add_weight(&ctlb->dummy_dev, ctlb->napi[i], t7xx_dpmaif_napi_rx_poll,
|
||||
+ NIC_NAPI_POLL_BUDGET);
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void t7xx_uninit_netdev_napi(struct t7xx_ccmni_ctrl *ctlb)
|
||||
+{
|
||||
+ int i;
|
||||
+
|
||||
+ for (i = 0; i < RXQ_NUM; i++) {
|
||||
+ netif_napi_del(ctlb->napi[i]);
|
||||
+ ctlb->napi[i] = NULL;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
static int t7xx_ccmni_wwan_newlink(void *ctxt, struct net_device *dev, u32 if_id,
|
||||
struct netlink_ext_ack *extack)
|
||||
{
|
||||
@@ -311,7 +388,8 @@ static void init_md_status_notifier(stru
|
||||
t7xx_fsm_notifier_register(t7xx_dev->md, md_status_notifier);
|
||||
}
|
||||
|
||||
-static void t7xx_ccmni_recv_skb(struct t7xx_pci_dev *t7xx_dev, struct sk_buff *skb)
|
||||
+static void t7xx_ccmni_recv_skb(struct t7xx_ccmni_ctrl *ccmni_ctlb, struct sk_buff *skb,
|
||||
+ struct napi_struct *napi)
|
||||
{
|
||||
struct t7xx_skb_cb *skb_cb;
|
||||
struct net_device *net_dev;
|
||||
@@ -321,23 +399,22 @@ static void t7xx_ccmni_recv_skb(struct t
|
||||
|
||||
skb_cb = T7XX_SKB_CB(skb);
|
||||
netif_id = skb_cb->netif_idx;
|
||||
- ccmni = t7xx_dev->ccmni_ctlb->ccmni_inst[netif_id];
|
||||
+ ccmni = ccmni_ctlb->ccmni_inst[netif_id];
|
||||
if (!ccmni) {
|
||||
dev_kfree_skb(skb);
|
||||
return;
|
||||
}
|
||||
|
||||
net_dev = ccmni->dev;
|
||||
- skb->dev = net_dev;
|
||||
-
|
||||
pkt_type = skb_cb->rx_pkt_type;
|
||||
+ skb->dev = net_dev;
|
||||
if (pkt_type == PKT_TYPE_IP6)
|
||||
skb->protocol = htons(ETH_P_IPV6);
|
||||
else
|
||||
skb->protocol = htons(ETH_P_IP);
|
||||
|
||||
skb_len = skb->len;
|
||||
- netif_rx(skb);
|
||||
+ napi_gro_receive(napi, skb);
|
||||
net_dev->stats.rx_packets++;
|
||||
net_dev->stats.rx_bytes += skb_len;
|
||||
}
|
||||
@@ -404,6 +481,7 @@ int t7xx_ccmni_init(struct t7xx_pci_dev
|
||||
if (!ctlb->hif_ctrl)
|
||||
return -ENOMEM;
|
||||
|
||||
+ t7xx_init_netdev_napi(ctlb);
|
||||
init_md_status_notifier(t7xx_dev);
|
||||
return 0;
|
||||
}
|
||||
@@ -419,5 +497,6 @@ void t7xx_ccmni_exit(struct t7xx_pci_dev
|
||||
ctlb->wwan_is_registered = false;
|
||||
}
|
||||
|
||||
+ t7xx_uninit_netdev_napi(ctlb);
|
||||
t7xx_dpmaif_hif_exit(ctlb->hif_ctrl);
|
||||
}
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_netdev.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_netdev.h
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#define CCMNI_NETDEV_WDT_TO (1 * HZ)
|
||||
#define CCMNI_MTU_MAX 3000
|
||||
+#define NIC_NAPI_POLL_BUDGET 128
|
||||
|
||||
struct t7xx_ccmni {
|
||||
u8 index;
|
||||
@@ -47,6 +48,10 @@ struct t7xx_ccmni_ctrl {
|
||||
unsigned int md_sta;
|
||||
struct t7xx_fsm_notifier md_status_notify;
|
||||
bool wwan_is_registered;
|
||||
+ struct net_device dummy_dev;
|
||||
+ struct napi_struct *napi[RXQ_NUM];
|
||||
+ atomic_t napi_usr_refcnt;
|
||||
+ bool is_napi_en;
|
||||
};
|
||||
|
||||
int t7xx_ccmni_init(struct t7xx_pci_dev *t7xx_dev);
|
||||
@@ -1,154 +0,0 @@
|
||||
From 364d0221f1788e5225006ba7a0026e5968431c29 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Kornel=20Dul=C4=99ba?= <mindal@semihalf.com>
|
||||
Date: Thu, 26 Jan 2023 13:25:34 +0000
|
||||
Subject: [PATCH] net: wwan: t7xx: Fix Runtime PM resume sequence
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
Resume device before calling napi_schedule, instead of doing in the napi
|
||||
poll routine. Polling is done in softrq context. We can't call the PM
|
||||
resume logic from there as it's blocking and not irq safe.
|
||||
In order to make it work modify the interrupt handler to be run from irq
|
||||
handler thread.
|
||||
|
||||
Fixes: 5545b7b9f294 ("net: wwan: t7xx: Add NAPI support")
|
||||
Signed-off-by: Kornel Dulęba <mindal@semihalf.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c | 11 +++++++-
|
||||
drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c | 29 +++++++++++++++-------
|
||||
drivers/net/wwan/t7xx/t7xx_netdev.c | 16 +++++++++++-
|
||||
3 files changed, 45 insertions(+), 11 deletions(-)
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif.c
|
||||
@@ -152,6 +152,15 @@ static irqreturn_t t7xx_dpmaif_isr_handl
|
||||
}
|
||||
|
||||
t7xx_pcie_mac_clear_int(dpmaif_ctrl->t7xx_dev, isr_para->pcie_int);
|
||||
+
|
||||
+ return IRQ_WAKE_THREAD;
|
||||
+}
|
||||
+
|
||||
+static irqreturn_t t7xx_dpmaif_isr_thread(int irq, void *data)
|
||||
+{
|
||||
+ struct dpmaif_isr_para *isr_para = data;
|
||||
+ struct dpmaif_ctrl *dpmaif_ctrl = isr_para->dpmaif_ctrl;
|
||||
+
|
||||
t7xx_dpmaif_irq_cb(isr_para);
|
||||
t7xx_pcie_mac_set_int(dpmaif_ctrl->t7xx_dev, isr_para->pcie_int);
|
||||
return IRQ_HANDLED;
|
||||
@@ -188,7 +197,7 @@ static void t7xx_dpmaif_register_pcie_ir
|
||||
t7xx_pcie_mac_clear_int(t7xx_dev, int_type);
|
||||
|
||||
t7xx_dev->intr_handler[int_type] = t7xx_dpmaif_isr_handler;
|
||||
- t7xx_dev->intr_thread[int_type] = NULL;
|
||||
+ t7xx_dev->intr_thread[int_type] = t7xx_dpmaif_isr_thread;
|
||||
t7xx_dev->callback_param[int_type] = isr_para;
|
||||
|
||||
t7xx_pcie_mac_clear_int_status(t7xx_dev, int_type);
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_rx.c
|
||||
@@ -840,14 +840,13 @@ int t7xx_dpmaif_napi_rx_poll(struct napi
|
||||
|
||||
if (!rxq->que_started) {
|
||||
atomic_set(&rxq->rx_processing, 0);
|
||||
+ pm_runtime_put_autosuspend(rxq->dpmaif_ctrl->dev);
|
||||
dev_err(rxq->dpmaif_ctrl->dev, "Work RXQ: %d has not been started\n", rxq->index);
|
||||
return work_done;
|
||||
}
|
||||
|
||||
- if (!rxq->sleep_lock_pending) {
|
||||
- pm_runtime_get_noresume(rxq->dpmaif_ctrl->dev);
|
||||
+ if (!rxq->sleep_lock_pending)
|
||||
t7xx_pci_disable_sleep(t7xx_dev);
|
||||
- }
|
||||
|
||||
ret = try_wait_for_completion(&t7xx_dev->sleep_lock_acquire);
|
||||
if (!ret) {
|
||||
@@ -876,22 +875,22 @@ int t7xx_dpmaif_napi_rx_poll(struct napi
|
||||
napi_complete_done(napi, work_done);
|
||||
t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
|
||||
t7xx_dpmaif_dlq_unmask_rx_done(&rxq->dpmaif_ctrl->hw_info, rxq->index);
|
||||
+ t7xx_pci_enable_sleep(rxq->dpmaif_ctrl->t7xx_dev);
|
||||
+ pm_runtime_mark_last_busy(rxq->dpmaif_ctrl->dev);
|
||||
+ pm_runtime_put_autosuspend(rxq->dpmaif_ctrl->dev);
|
||||
+ atomic_set(&rxq->rx_processing, 0);
|
||||
} else {
|
||||
t7xx_dpmaif_clr_ip_busy_sts(&rxq->dpmaif_ctrl->hw_info);
|
||||
}
|
||||
|
||||
- t7xx_pci_enable_sleep(rxq->dpmaif_ctrl->t7xx_dev);
|
||||
- pm_runtime_mark_last_busy(rxq->dpmaif_ctrl->dev);
|
||||
- pm_runtime_put_noidle(rxq->dpmaif_ctrl->dev);
|
||||
- atomic_set(&rxq->rx_processing, 0);
|
||||
-
|
||||
return work_done;
|
||||
}
|
||||
|
||||
void t7xx_dpmaif_irq_rx_done(struct dpmaif_ctrl *dpmaif_ctrl, const unsigned int que_mask)
|
||||
{
|
||||
struct dpmaif_rx_queue *rxq;
|
||||
- int qno;
|
||||
+ struct dpmaif_ctrl *ctrl;
|
||||
+ int qno, ret;
|
||||
|
||||
qno = ffs(que_mask) - 1;
|
||||
if (qno < 0 || qno > DPMAIF_RXQ_NUM - 1) {
|
||||
@@ -900,6 +899,18 @@ void t7xx_dpmaif_irq_rx_done(struct dpma
|
||||
}
|
||||
|
||||
rxq = &dpmaif_ctrl->rxq[qno];
|
||||
+ ctrl = rxq->dpmaif_ctrl;
|
||||
+ /* We need to make sure that the modem has been resumed before
|
||||
+ * calling napi. This can't be done inside the polling function
|
||||
+ * as we could be blocked waiting for device to be resumed,
|
||||
+ * which can't be done from softirq context the poll function
|
||||
+ * is running in.
|
||||
+ */
|
||||
+ ret = pm_runtime_resume_and_get(ctrl->dev);
|
||||
+ if (ret < 0 && ret != -EACCES) {
|
||||
+ dev_err(ctrl->dev, "Failed to resume device: %d\n", ret);
|
||||
+ return;
|
||||
+ }
|
||||
napi_schedule(&rxq->napi);
|
||||
}
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_netdev.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_netdev.c
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/netdev_features.h>
|
||||
#include <linux/netdevice.h>
|
||||
+#include <linux/pm_runtime.h>
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/wwan.h>
|
||||
@@ -45,12 +46,25 @@
|
||||
|
||||
static void t7xx_ccmni_enable_napi(struct t7xx_ccmni_ctrl *ctlb)
|
||||
{
|
||||
- int i;
|
||||
+ struct dpmaif_ctrl *ctrl;
|
||||
+ int i, ret;
|
||||
+
|
||||
+ ctrl = ctlb->hif_ctrl;
|
||||
|
||||
if (ctlb->is_napi_en)
|
||||
return;
|
||||
|
||||
for (i = 0; i < RXQ_NUM; i++) {
|
||||
+ /* The usage count has to be bumped every time before calling
|
||||
+ * napi_schedule. It will be decresed in the poll routine,
|
||||
+ * right after napi_complete_done is called.
|
||||
+ */
|
||||
+ ret = pm_runtime_resume_and_get(ctrl->dev);
|
||||
+ if (ret < 0) {
|
||||
+ dev_err(ctrl->dev, "Failed to resume device: %d\n",
|
||||
+ ret);
|
||||
+ return;
|
||||
+ }
|
||||
napi_enable(ctlb->napi[i]);
|
||||
napi_schedule(ctlb->napi[i]);
|
||||
}
|
||||
@@ -1,300 +0,0 @@
|
||||
From 36bd28c1cb0dbf48645cfe43159907fb3253b33a Mon Sep 17 00:00:00 2001
|
||||
From: haozhe chang <haozhe.chang@mediatek.com>
|
||||
Date: Thu, 16 Mar 2023 17:58:20 +0800
|
||||
Subject: [PATCH] wwan: core: Support slicing in port TX flow of WWAN subsystem
|
||||
|
||||
wwan_port_fops_write inputs the SKB parameter to the TX callback of
|
||||
the WWAN device driver. However, the WWAN device (e.g., t7xx) may
|
||||
have an MTU less than the size of SKB, causing the TX buffer to be
|
||||
sliced and copied once more in the WWAN device driver.
|
||||
|
||||
This patch implements the slicing in the WWAN subsystem and gives
|
||||
the WWAN devices driver the option to slice(by frag_len) or not. By
|
||||
doing so, the additional memory copy is reduced.
|
||||
|
||||
Meanwhile, this patch gives WWAN devices driver the option to reserve
|
||||
headroom in fragments for the device-specific metadata.
|
||||
|
||||
Signed-off-by: haozhe chang <haozhe.chang@mediatek.com>
|
||||
Reviewed-by: Loic Poulain <loic.poulain@linaro.org>
|
||||
Link: https://lore.kernel.org/r/20230316095826.181904-1-haozhe.chang@mediatek.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/wwan/iosm/iosm_ipc_port.c | 3 +-
|
||||
drivers/net/wwan/mhi_wwan_ctrl.c | 2 +-
|
||||
drivers/net/wwan/rpmsg_wwan_ctrl.c | 2 +-
|
||||
drivers/net/wwan/t7xx/t7xx_port_wwan.c | 36 ++++++++--------
|
||||
drivers/net/wwan/wwan_core.c | 58 +++++++++++++++++++-------
|
||||
drivers/net/wwan/wwan_hwsim.c | 2 +-
|
||||
drivers/usb/class/cdc-wdm.c | 3 +-
|
||||
include/linux/wwan.h | 11 +++++
|
||||
8 files changed, 81 insertions(+), 36 deletions(-)
|
||||
|
||||
--- a/drivers/net/wwan/iosm/iosm_ipc_port.c
|
||||
+++ b/drivers/net/wwan/iosm/iosm_ipc_port.c
|
||||
@@ -63,7 +63,8 @@ struct iosm_cdev *ipc_port_init(struct i
|
||||
ipc_port->ipc_imem = ipc_imem;
|
||||
|
||||
ipc_port->iosm_port = wwan_create_port(ipc_port->dev, port_type,
|
||||
- &ipc_wwan_ctrl_ops, ipc_port);
|
||||
+ &ipc_wwan_ctrl_ops, NULL,
|
||||
+ ipc_port);
|
||||
|
||||
return ipc_port;
|
||||
}
|
||||
--- a/drivers/net/wwan/mhi_wwan_ctrl.c
|
||||
+++ b/drivers/net/wwan/mhi_wwan_ctrl.c
|
||||
@@ -237,7 +237,7 @@ static int mhi_wwan_ctrl_probe(struct mh
|
||||
|
||||
/* Register as a wwan port, id->driver_data contains wwan port type */
|
||||
port = wwan_create_port(&cntrl->mhi_dev->dev, id->driver_data,
|
||||
- &wwan_pops, mhiwwan);
|
||||
+ &wwan_pops, NULL, mhiwwan);
|
||||
if (IS_ERR(port)) {
|
||||
kfree(mhiwwan);
|
||||
return PTR_ERR(port);
|
||||
--- a/drivers/net/wwan/rpmsg_wwan_ctrl.c
|
||||
+++ b/drivers/net/wwan/rpmsg_wwan_ctrl.c
|
||||
@@ -129,7 +129,7 @@ static int rpmsg_wwan_ctrl_probe(struct
|
||||
|
||||
/* Register as a wwan port, id.driver_data contains wwan port type */
|
||||
port = wwan_create_port(parent, rpdev->id.driver_data,
|
||||
- &rpmsg_wwan_pops, rpwwan);
|
||||
+ &rpmsg_wwan_pops, NULL, rpwwan);
|
||||
if (IS_ERR(port))
|
||||
return PTR_ERR(port);
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port_wwan.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port_wwan.c
|
||||
@@ -54,13 +54,13 @@ static void t7xx_port_ctrl_stop(struct w
|
||||
static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb)
|
||||
{
|
||||
struct t7xx_port *port_private = wwan_port_get_drvdata(port);
|
||||
- size_t len, offset, chunk_len = 0, txq_mtu = CLDMA_MTU;
|
||||
const struct t7xx_port_conf *port_conf;
|
||||
+ struct sk_buff *cur = skb, *cloned;
|
||||
struct t7xx_fsm_ctl *ctl;
|
||||
enum md_state md_state;
|
||||
+ int cnt = 0, ret;
|
||||
|
||||
- len = skb->len;
|
||||
- if (!len || !port_private->chan_enable)
|
||||
+ if (!port_private->chan_enable)
|
||||
return -EINVAL;
|
||||
|
||||
port_conf = port_private->port_conf;
|
||||
@@ -72,23 +72,21 @@ static int t7xx_port_ctrl_tx(struct wwan
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
- for (offset = 0; offset < len; offset += chunk_len) {
|
||||
- struct sk_buff *skb_ccci;
|
||||
- int ret;
|
||||
-
|
||||
- chunk_len = min(len - offset, txq_mtu - sizeof(struct ccci_header));
|
||||
- skb_ccci = t7xx_port_alloc_skb(chunk_len);
|
||||
- if (!skb_ccci)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- skb_put_data(skb_ccci, skb->data + offset, chunk_len);
|
||||
- ret = t7xx_port_send_skb(port_private, skb_ccci, 0, 0);
|
||||
+ while (cur) {
|
||||
+ cloned = skb_clone(cur, GFP_KERNEL);
|
||||
+ cloned->len = skb_headlen(cur);
|
||||
+ ret = t7xx_port_send_skb(port_private, cloned, 0, 0);
|
||||
if (ret) {
|
||||
- dev_kfree_skb_any(skb_ccci);
|
||||
+ dev_kfree_skb(cloned);
|
||||
dev_err(port_private->dev, "Write error on %s port, %d\n",
|
||||
port_conf->name, ret);
|
||||
- return ret;
|
||||
+ return cnt ? cnt + ret : ret;
|
||||
}
|
||||
+ cnt += cur->len;
|
||||
+ if (cur == skb)
|
||||
+ cur = skb_shinfo(skb)->frag_list;
|
||||
+ else
|
||||
+ cur = cur->next;
|
||||
}
|
||||
|
||||
dev_kfree_skb(skb);
|
||||
@@ -154,13 +152,17 @@ static int t7xx_port_wwan_disable_chl(st
|
||||
static void t7xx_port_wwan_md_state_notify(struct t7xx_port *port, unsigned int state)
|
||||
{
|
||||
const struct t7xx_port_conf *port_conf = port->port_conf;
|
||||
+ unsigned int header_len = sizeof(struct ccci_header);
|
||||
+ struct wwan_port_caps caps;
|
||||
|
||||
if (state != MD_STATE_READY)
|
||||
return;
|
||||
|
||||
if (!port->wwan.wwan_port) {
|
||||
+ caps.frag_len = CLDMA_MTU - header_len;
|
||||
+ caps.headroom_len = header_len;
|
||||
port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type,
|
||||
- &wwan_ops, port);
|
||||
+ &wwan_ops, &caps, port);
|
||||
if (IS_ERR(port->wwan.wwan_port))
|
||||
dev_err(port->dev, "Unable to create WWWAN port %s", port_conf->name);
|
||||
}
|
||||
--- a/drivers/net/wwan/wwan_core.c
|
||||
+++ b/drivers/net/wwan/wwan_core.c
|
||||
@@ -67,6 +67,8 @@ struct wwan_device {
|
||||
* @rxq: Buffer inbound queue
|
||||
* @waitqueue: The waitqueue for port fops (read/write/poll)
|
||||
* @data_lock: Port specific data access serialization
|
||||
+ * @headroom_len: SKB reserved headroom size
|
||||
+ * @frag_len: Length to fragment packet
|
||||
* @at_data: AT port specific data
|
||||
*/
|
||||
struct wwan_port {
|
||||
@@ -79,6 +81,8 @@ struct wwan_port {
|
||||
struct sk_buff_head rxq;
|
||||
wait_queue_head_t waitqueue;
|
||||
struct mutex data_lock; /* Port specific data access serialization */
|
||||
+ size_t headroom_len;
|
||||
+ size_t frag_len;
|
||||
union {
|
||||
struct {
|
||||
struct ktermios termios;
|
||||
@@ -422,6 +426,7 @@ static int __wwan_port_dev_assign_name(s
|
||||
struct wwan_port *wwan_create_port(struct device *parent,
|
||||
enum wwan_port_type type,
|
||||
const struct wwan_port_ops *ops,
|
||||
+ struct wwan_port_caps *caps,
|
||||
void *drvdata)
|
||||
{
|
||||
struct wwan_device *wwandev;
|
||||
@@ -455,6 +460,8 @@ struct wwan_port *wwan_create_port(struc
|
||||
|
||||
port->type = type;
|
||||
port->ops = ops;
|
||||
+ port->frag_len = caps ? caps->frag_len : SIZE_MAX;
|
||||
+ port->headroom_len = caps ? caps->headroom_len : 0;
|
||||
mutex_init(&port->ops_lock);
|
||||
skb_queue_head_init(&port->rxq);
|
||||
init_waitqueue_head(&port->waitqueue);
|
||||
@@ -698,30 +705,53 @@ static ssize_t wwan_port_fops_read(struc
|
||||
static ssize_t wwan_port_fops_write(struct file *filp, const char __user *buf,
|
||||
size_t count, loff_t *offp)
|
||||
{
|
||||
+ struct sk_buff *skb, *head = NULL, *tail = NULL;
|
||||
struct wwan_port *port = filp->private_data;
|
||||
- struct sk_buff *skb;
|
||||
+ size_t frag_len, remain = count;
|
||||
int ret;
|
||||
|
||||
ret = wwan_wait_tx(port, !!(filp->f_flags & O_NONBLOCK));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- skb = alloc_skb(count, GFP_KERNEL);
|
||||
- if (!skb)
|
||||
- return -ENOMEM;
|
||||
-
|
||||
- if (copy_from_user(skb_put(skb, count), buf, count)) {
|
||||
- kfree_skb(skb);
|
||||
- return -EFAULT;
|
||||
- }
|
||||
+ do {
|
||||
+ frag_len = min(remain, port->frag_len);
|
||||
+ skb = alloc_skb(frag_len + port->headroom_len, GFP_KERNEL);
|
||||
+ if (!skb) {
|
||||
+ ret = -ENOMEM;
|
||||
+ goto freeskb;
|
||||
+ }
|
||||
+ skb_reserve(skb, port->headroom_len);
|
||||
+
|
||||
+ if (!head) {
|
||||
+ head = skb;
|
||||
+ } else if (!tail) {
|
||||
+ skb_shinfo(head)->frag_list = skb;
|
||||
+ tail = skb;
|
||||
+ } else {
|
||||
+ tail->next = skb;
|
||||
+ tail = skb;
|
||||
+ }
|
||||
+
|
||||
+ if (copy_from_user(skb_put(skb, frag_len), buf + count - remain, frag_len)) {
|
||||
+ ret = -EFAULT;
|
||||
+ goto freeskb;
|
||||
+ }
|
||||
+
|
||||
+ if (skb != head) {
|
||||
+ head->data_len += skb->len;
|
||||
+ head->len += skb->len;
|
||||
+ head->truesize += skb->truesize;
|
||||
+ }
|
||||
+ } while (remain -= frag_len);
|
||||
|
||||
- ret = wwan_port_op_tx(port, skb, !!(filp->f_flags & O_NONBLOCK));
|
||||
- if (ret) {
|
||||
- kfree_skb(skb);
|
||||
- return ret;
|
||||
- }
|
||||
+ ret = wwan_port_op_tx(port, head, !!(filp->f_flags & O_NONBLOCK));
|
||||
+ if (!ret)
|
||||
+ return count;
|
||||
|
||||
- return count;
|
||||
+freeskb:
|
||||
+ kfree_skb(head);
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static __poll_t wwan_port_fops_poll(struct file *filp, poll_table *wait)
|
||||
--- a/drivers/net/wwan/wwan_hwsim.c
|
||||
+++ b/drivers/net/wwan/wwan_hwsim.c
|
||||
@@ -205,7 +205,7 @@ static struct wwan_hwsim_port *wwan_hwsi
|
||||
|
||||
port->wwan = wwan_create_port(&dev->dev, WWAN_PORT_AT,
|
||||
&wwan_hwsim_port_ops,
|
||||
- port);
|
||||
+ NULL, port);
|
||||
if (IS_ERR(port->wwan)) {
|
||||
err = PTR_ERR(port->wwan);
|
||||
goto err_free_port;
|
||||
--- a/drivers/usb/class/cdc-wdm.c
|
||||
+++ b/drivers/usb/class/cdc-wdm.c
|
||||
@@ -933,7 +933,8 @@ static void wdm_wwan_init(struct wdm_dev
|
||||
return;
|
||||
}
|
||||
|
||||
- port = wwan_create_port(&intf->dev, desc->wwanp_type, &wdm_wwan_port_ops, desc);
|
||||
+ port = wwan_create_port(&intf->dev, desc->wwanp_type, &wdm_wwan_port_ops,
|
||||
+ NULL, desc);
|
||||
if (IS_ERR(port)) {
|
||||
dev_err(&intf->dev, "%s: Unable to create WWAN port\n",
|
||||
dev_name(intf->usb_dev));
|
||||
--- a/include/linux/wwan.h
|
||||
+++ b/include/linux/wwan.h
|
||||
@@ -62,11 +62,21 @@ struct wwan_port_ops {
|
||||
poll_table *wait);
|
||||
};
|
||||
|
||||
+/** struct wwan_port_caps - The WWAN port capbilities
|
||||
+ * @frag_len: WWAN port TX fragments length
|
||||
+ * @headroom_len: WWAN port TX fragments reserved headroom length
|
||||
+ */
|
||||
+struct wwan_port_caps {
|
||||
+ size_t frag_len;
|
||||
+ unsigned int headroom_len;
|
||||
+};
|
||||
+
|
||||
/**
|
||||
* wwan_create_port - Add a new WWAN port
|
||||
* @parent: Device to use as parent and shared by all WWAN ports
|
||||
* @type: WWAN port type
|
||||
* @ops: WWAN port operations
|
||||
+ * @caps: WWAN port capabilities
|
||||
* @drvdata: Pointer to caller driver data
|
||||
*
|
||||
* Allocate and register a new WWAN port. The port will be automatically exposed
|
||||
@@ -84,6 +94,7 @@ struct wwan_port_ops {
|
||||
struct wwan_port *wwan_create_port(struct device *parent,
|
||||
enum wwan_port_type type,
|
||||
const struct wwan_port_ops *ops,
|
||||
+ struct wwan_port_caps *caps,
|
||||
void *drvdata);
|
||||
|
||||
/**
|
||||
@@ -1,121 +0,0 @@
|
||||
From 72b1fe6cc6523908bfc339d07d18cb0f3469a643 Mon Sep 17 00:00:00 2001
|
||||
From: Tejun Heo <tj@kernel.org>
|
||||
Date: Thu, 25 May 2023 12:15:29 -1000
|
||||
Subject: [PATCH] net: wwan: t7xx: Use alloc_ordered_workqueue() to create
|
||||
ordered workqueues
|
||||
|
||||
BACKGROUND
|
||||
==========
|
||||
|
||||
When multiple work items are queued to a workqueue, their execution order
|
||||
doesn't match the queueing order. They may get executed in any order and
|
||||
simultaneously. When fully serialized execution - one by one in the queueing
|
||||
order - is needed, an ordered workqueue should be used which can be created
|
||||
with alloc_ordered_workqueue().
|
||||
|
||||
However, alloc_ordered_workqueue() was a later addition. Before it, an
|
||||
ordered workqueue could be obtained by creating an UNBOUND workqueue with
|
||||
@max_active==1. This originally was an implementation side-effect which was
|
||||
broken by 4c16bd327c74 ("workqueue: restore WQ_UNBOUND/max_active==1 to be
|
||||
ordered"). Because there were users that depended on the ordered execution,
|
||||
5c0338c68706 ("workqueue: restore WQ_UNBOUND/max_active==1 to be ordered")
|
||||
made workqueue allocation path to implicitly promote UNBOUND workqueues w/
|
||||
@max_active==1 to ordered workqueues.
|
||||
|
||||
While this has worked okay, overloading the UNBOUND allocation interface
|
||||
this way creates other issues. It's difficult to tell whether a given
|
||||
workqueue actually needs to be ordered and users that legitimately want a
|
||||
min concurrency level wq unexpectedly gets an ordered one instead. With
|
||||
planned UNBOUND workqueue updates to improve execution locality and more
|
||||
prevalence of chiplet designs which can benefit from such improvements, this
|
||||
isn't a state we wanna be in forever.
|
||||
|
||||
This patch series audits all callsites that create an UNBOUND workqueue w/
|
||||
@max_active==1 and converts them to alloc_ordered_workqueue() as necessary.
|
||||
|
||||
WHAT TO LOOK FOR
|
||||
================
|
||||
|
||||
The conversions are from
|
||||
|
||||
alloc_workqueue(WQ_UNBOUND | flags, 1, args..)
|
||||
|
||||
to
|
||||
|
||||
alloc_ordered_workqueue(flags, args...)
|
||||
|
||||
which don't cause any functional changes. If you know that fully ordered
|
||||
execution is not necessary, please let me know. I'll drop the conversion and
|
||||
instead add a comment noting the fact to reduce confusion while conversion
|
||||
is in progress.
|
||||
|
||||
If you aren't fully sure, it's completely fine to let the conversion
|
||||
through. The behavior will stay exactly the same and we can always
|
||||
reconsider later.
|
||||
|
||||
As there are follow-up workqueue core changes, I'd really appreciate if the
|
||||
patch can be routed through the workqueue tree w/ your acks. Thanks.
|
||||
|
||||
Signed-off-by: Tejun Heo <tj@kernel.org>
|
||||
Cc: Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
|
||||
Cc: Intel Corporation <linuxwwan@intel.com>
|
||||
Cc: Chiranjeevi Rapolu <chiranjeevi.rapolu@linux.intel.com>
|
||||
Cc: Liu Haijun <haijun.liu@mediatek.com>
|
||||
Cc: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
|
||||
Cc: Ricardo Martinez <ricardo.martinez@linux.intel.com>
|
||||
Cc: Loic Poulain <loic.poulain@linaro.org>
|
||||
Cc: Sergey Ryazanov <ryazanov.s.a@gmail.com>
|
||||
Cc: Johannes Berg <johannes@sipsolutions.net>
|
||||
Cc: "David S. Miller" <davem@davemloft.net>
|
||||
Cc: Eric Dumazet <edumazet@google.com>
|
||||
Cc: Jakub Kicinski <kuba@kernel.org>
|
||||
Cc: Paolo Abeni <pabeni@redhat.com>
|
||||
Cc: netdev@vger.kernel.org
|
||||
---
|
||||
drivers/net/wwan/t7xx/t7xx_hif_cldma.c | 13 +++++++------
|
||||
drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c | 5 +++--
|
||||
2 files changed, 10 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
|
||||
@@ -1294,9 +1294,9 @@ int t7xx_cldma_init(struct cldma_ctrl *m
|
||||
for (i = 0; i < CLDMA_TXQ_NUM; i++) {
|
||||
md_cd_queue_struct_init(&md_ctrl->txq[i], md_ctrl, MTK_TX, i);
|
||||
md_ctrl->txq[i].worker =
|
||||
- alloc_workqueue("md_hif%d_tx%d_worker",
|
||||
- WQ_UNBOUND | WQ_MEM_RECLAIM | (i ? 0 : WQ_HIGHPRI),
|
||||
- 1, md_ctrl->hif_id, i);
|
||||
+ alloc_ordered_workqueue("md_hif%d_tx%d_worker",
|
||||
+ WQ_MEM_RECLAIM | (i ? 0 : WQ_HIGHPRI),
|
||||
+ md_ctrl->hif_id, i);
|
||||
if (!md_ctrl->txq[i].worker)
|
||||
goto err_workqueue;
|
||||
|
||||
@@ -1307,9 +1307,10 @@ int t7xx_cldma_init(struct cldma_ctrl *m
|
||||
md_cd_queue_struct_init(&md_ctrl->rxq[i], md_ctrl, MTK_RX, i);
|
||||
INIT_WORK(&md_ctrl->rxq[i].cldma_work, t7xx_cldma_rx_done);
|
||||
|
||||
- md_ctrl->rxq[i].worker = alloc_workqueue("md_hif%d_rx%d_worker",
|
||||
- WQ_UNBOUND | WQ_MEM_RECLAIM,
|
||||
- 1, md_ctrl->hif_id, i);
|
||||
+ md_ctrl->rxq[i].worker =
|
||||
+ alloc_ordered_workqueue("md_hif%d_rx%d_worker",
|
||||
+ WQ_MEM_RECLAIM,
|
||||
+ md_ctrl->hif_id, i);
|
||||
if (!md_ctrl->rxq[i].worker)
|
||||
goto err_workqueue;
|
||||
}
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_dpmaif_tx.c
|
||||
@@ -618,8 +618,9 @@ int t7xx_dpmaif_txq_init(struct dpmaif_t
|
||||
return ret;
|
||||
}
|
||||
|
||||
- txq->worker = alloc_workqueue("md_dpmaif_tx%d_worker", WQ_UNBOUND | WQ_MEM_RECLAIM |
|
||||
- (txq->index ? 0 : WQ_HIGHPRI), 1, txq->index);
|
||||
+ txq->worker = alloc_ordered_workqueue("md_dpmaif_tx%d_worker",
|
||||
+ WQ_MEM_RECLAIM | (txq->index ? 0 : WQ_HIGHPRI),
|
||||
+ txq->index);
|
||||
if (!txq->worker)
|
||||
return -ENOMEM;
|
||||
|
||||
@@ -1,482 +0,0 @@
|
||||
From ba2274dcfda859b8a27193e68ad37bfe4da28ddc Mon Sep 17 00:00:00 2001
|
||||
From: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
|
||||
Date: Tue, 11 Jul 2023 08:28:13 +0200
|
||||
Subject: [PATCH] net: wwan: t7xx: Add AP CLDMA
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
At this moment with the current status, t7xx is not functional due to
|
||||
problems like this after connection, if there is no activity:
|
||||
[ 57.370534] mtk_t7xx 0000:72:00.0: [PM] SAP suspend error: -110
|
||||
[ 57.370581] mtk_t7xx 0000:72:00.0: can't suspend
|
||||
(t7xx_pci_pm_runtime_suspend [mtk_t7xx] returned -110)
|
||||
because after this, the traffic no longer works.
|
||||
|
||||
The complete series 'net: wwan: t7xx: fw flashing & coredump support'
|
||||
was reverted because of issues with the pci implementation.
|
||||
In order to have at least the modem working, it would be enough if just
|
||||
the first commit of the series is re-applied:
|
||||
d20ef656f994 net: wwan: t7xx: Add AP CLDMA
|
||||
With that, the Application Processor would be controlled, correctly
|
||||
suspended and the commented problems would be fixed (I am testing here
|
||||
like this with no related issue).
|
||||
|
||||
This commit is independent of the others and not related to the
|
||||
commented pci implementation for the new features: fw flashing and
|
||||
coredump collection.
|
||||
|
||||
Use v2 patch version of d20ef656f994 as JinJian Song suggests
|
||||
(https://patchwork.kernel.org/project/netdevbpf/patch/20230105154215.198828-1-m.chetan.kumar@linux.intel.com/).
|
||||
|
||||
Original text from the commit that would be re-applied:
|
||||
|
||||
d20ef656f994 net: wwan: t7xx: Add AP CLDMA
|
||||
Author: Haijun Liu <haijun.liu@mediatek.com>
|
||||
Date: Tue Aug 16 09:53:28 2022 +0530
|
||||
|
||||
The t7xx device contains two Cross Layer DMA (CLDMA) interfaces to
|
||||
communicate with AP and Modem processors respectively. So far only
|
||||
MD-CLDMA was being used, this patch enables AP-CLDMA.
|
||||
|
||||
Rename small Application Processor (sAP) to AP.
|
||||
|
||||
Signed-off-by: Haijun Liu <haijun.liu@mediatek.com>
|
||||
Co-developed-by: Madhusmita Sahu <madhusmita.sahu@intel.com>
|
||||
Signed-off-by: Madhusmita Sahu <madhusmita.sahu@intel.com>
|
||||
Signed-off-by: Moises Veleta <moises.veleta@linux.intel.com>
|
||||
Signed-off-by: Devegowda Chandrashekar <chandrashekar.devegowda@intel.com>
|
||||
Signed-off-by: M Chetan Kumar <m.chetan.kumar@linux.intel.com>
|
||||
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
|
||||
Reviewed-by: Sergey Ryazanov <ryazanov.s.a@gmail.com>
|
||||
Reviewed-by: Jesse Brandeburg <jesse.brandeburg@intel.com>
|
||||
|
||||
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
|
||||
Reviewed-by: Simon Horman <simon.horman@corigine.com>
|
||||
Link: https://lore.kernel.org/r/20230711062817.6108-1-jtornosm@redhat.com
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/wwan/t7xx/t7xx_hif_cldma.c | 17 +++--
|
||||
drivers/net/wwan/t7xx/t7xx_hif_cldma.h | 2 +-
|
||||
drivers/net/wwan/t7xx/t7xx_mhccif.h | 1 +
|
||||
drivers/net/wwan/t7xx/t7xx_modem_ops.c | 76 +++++++++++++++++-----
|
||||
drivers/net/wwan/t7xx/t7xx_modem_ops.h | 2 +
|
||||
drivers/net/wwan/t7xx/t7xx_port.h | 6 +-
|
||||
drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c | 8 ++-
|
||||
drivers/net/wwan/t7xx/t7xx_port_proxy.c | 18 ++++-
|
||||
drivers/net/wwan/t7xx/t7xx_reg.h | 2 +-
|
||||
drivers/net/wwan/t7xx/t7xx_state_monitor.c | 13 +++-
|
||||
drivers/net/wwan/t7xx/t7xx_state_monitor.h | 2 +
|
||||
11 files changed, 116 insertions(+), 31 deletions(-)
|
||||
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.c
|
||||
@@ -1067,13 +1067,18 @@ static void t7xx_hw_info_init(struct cld
|
||||
struct t7xx_cldma_hw *hw_info = &md_ctrl->hw_info;
|
||||
u32 phy_ao_base, phy_pd_base;
|
||||
|
||||
- if (md_ctrl->hif_id != CLDMA_ID_MD)
|
||||
- return;
|
||||
-
|
||||
- phy_ao_base = CLDMA1_AO_BASE;
|
||||
- phy_pd_base = CLDMA1_PD_BASE;
|
||||
- hw_info->phy_interrupt_id = CLDMA1_INT;
|
||||
hw_info->hw_mode = MODE_BIT_64;
|
||||
+
|
||||
+ if (md_ctrl->hif_id == CLDMA_ID_MD) {
|
||||
+ phy_ao_base = CLDMA1_AO_BASE;
|
||||
+ phy_pd_base = CLDMA1_PD_BASE;
|
||||
+ hw_info->phy_interrupt_id = CLDMA1_INT;
|
||||
+ } else {
|
||||
+ phy_ao_base = CLDMA0_AO_BASE;
|
||||
+ phy_pd_base = CLDMA0_PD_BASE;
|
||||
+ hw_info->phy_interrupt_id = CLDMA0_INT;
|
||||
+ }
|
||||
+
|
||||
hw_info->ap_ao_base = t7xx_pcie_addr_transfer(pbase->pcie_ext_reg_base,
|
||||
pbase->pcie_dev_reg_trsl_addr, phy_ao_base);
|
||||
hw_info->ap_pdn_base = t7xx_pcie_addr_transfer(pbase->pcie_ext_reg_base,
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_hif_cldma.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_hif_cldma.h
|
||||
@@ -34,7 +34,7 @@
|
||||
/**
|
||||
* enum cldma_id - Identifiers for CLDMA HW units.
|
||||
* @CLDMA_ID_MD: Modem control channel.
|
||||
- * @CLDMA_ID_AP: Application Processor control channel (not used at the moment).
|
||||
+ * @CLDMA_ID_AP: Application Processor control channel.
|
||||
* @CLDMA_NUM: Number of CLDMA HW units available.
|
||||
*/
|
||||
enum cldma_id {
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_mhccif.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_mhccif.h
|
||||
@@ -25,6 +25,7 @@
|
||||
D2H_INT_EXCEPTION_CLEARQ_DONE | \
|
||||
D2H_INT_EXCEPTION_ALLQ_RESET | \
|
||||
D2H_INT_PORT_ENUM | \
|
||||
+ D2H_INT_ASYNC_AP_HK | \
|
||||
D2H_INT_ASYNC_MD_HK)
|
||||
|
||||
void t7xx_mhccif_mask_set(struct t7xx_pci_dev *t7xx_dev, u32 val);
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_modem_ops.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.c
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "t7xx_state_monitor.h"
|
||||
|
||||
#define RT_ID_MD_PORT_ENUM 0
|
||||
+#define RT_ID_AP_PORT_ENUM 1
|
||||
/* Modem feature query identification code - "ICCC" */
|
||||
#define MD_FEATURE_QUERY_ID 0x49434343
|
||||
|
||||
@@ -298,6 +299,7 @@ static void t7xx_md_exception(struct t7x
|
||||
}
|
||||
|
||||
t7xx_cldma_exception(md->md_ctrl[CLDMA_ID_MD], stage);
|
||||
+ t7xx_cldma_exception(md->md_ctrl[CLDMA_ID_AP], stage);
|
||||
|
||||
if (stage == HIF_EX_INIT)
|
||||
t7xx_mhccif_h2d_swint_trigger(t7xx_dev, H2D_CH_EXCEPTION_ACK);
|
||||
@@ -426,7 +428,7 @@ static int t7xx_parse_host_rt_data(struc
|
||||
if (ft_spt_st != MTK_FEATURE_MUST_BE_SUPPORTED)
|
||||
return -EINVAL;
|
||||
|
||||
- if (i == RT_ID_MD_PORT_ENUM)
|
||||
+ if (i == RT_ID_MD_PORT_ENUM || i == RT_ID_AP_PORT_ENUM)
|
||||
t7xx_port_enum_msg_handler(ctl->md, rt_feature->data);
|
||||
}
|
||||
|
||||
@@ -456,12 +458,12 @@ static int t7xx_core_reset(struct t7xx_m
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static void t7xx_core_hk_handler(struct t7xx_modem *md, struct t7xx_fsm_ctl *ctl,
|
||||
+static void t7xx_core_hk_handler(struct t7xx_modem *md, struct t7xx_sys_info *core_info,
|
||||
+ struct t7xx_fsm_ctl *ctl,
|
||||
enum t7xx_fsm_event_state event_id,
|
||||
enum t7xx_fsm_event_state err_detect)
|
||||
{
|
||||
struct t7xx_fsm_event *event = NULL, *event_next;
|
||||
- struct t7xx_sys_info *core_info = &md->core_md;
|
||||
struct device *dev = &md->t7xx_dev->pdev->dev;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
@@ -531,19 +533,33 @@ static void t7xx_md_hk_wq(struct work_st
|
||||
t7xx_cldma_start(md->md_ctrl[CLDMA_ID_MD]);
|
||||
t7xx_fsm_broadcast_state(ctl, MD_STATE_WAITING_FOR_HS2);
|
||||
md->core_md.handshake_ongoing = true;
|
||||
- t7xx_core_hk_handler(md, ctl, FSM_EVENT_MD_HS2, FSM_EVENT_MD_HS2_EXIT);
|
||||
+ t7xx_core_hk_handler(md, &md->core_md, ctl, FSM_EVENT_MD_HS2, FSM_EVENT_MD_HS2_EXIT);
|
||||
+}
|
||||
+
|
||||
+static void t7xx_ap_hk_wq(struct work_struct *work)
|
||||
+{
|
||||
+ struct t7xx_modem *md = container_of(work, struct t7xx_modem, ap_handshake_work);
|
||||
+ struct t7xx_fsm_ctl *ctl = md->fsm_ctl;
|
||||
+
|
||||
+ /* Clear the HS2 EXIT event appended in t7xx_core_reset(). */
|
||||
+ t7xx_fsm_clr_event(ctl, FSM_EVENT_AP_HS2_EXIT);
|
||||
+ t7xx_cldma_stop(md->md_ctrl[CLDMA_ID_AP]);
|
||||
+ t7xx_cldma_switch_cfg(md->md_ctrl[CLDMA_ID_AP]);
|
||||
+ t7xx_cldma_start(md->md_ctrl[CLDMA_ID_AP]);
|
||||
+ md->core_ap.handshake_ongoing = true;
|
||||
+ t7xx_core_hk_handler(md, &md->core_ap, ctl, FSM_EVENT_AP_HS2, FSM_EVENT_AP_HS2_EXIT);
|
||||
}
|
||||
|
||||
void t7xx_md_event_notify(struct t7xx_modem *md, enum md_event_id evt_id)
|
||||
{
|
||||
struct t7xx_fsm_ctl *ctl = md->fsm_ctl;
|
||||
- void __iomem *mhccif_base;
|
||||
unsigned int int_sta;
|
||||
unsigned long flags;
|
||||
|
||||
switch (evt_id) {
|
||||
case FSM_PRE_START:
|
||||
- t7xx_mhccif_mask_clr(md->t7xx_dev, D2H_INT_PORT_ENUM);
|
||||
+ t7xx_mhccif_mask_clr(md->t7xx_dev, D2H_INT_PORT_ENUM | D2H_INT_ASYNC_MD_HK |
|
||||
+ D2H_INT_ASYNC_AP_HK);
|
||||
break;
|
||||
|
||||
case FSM_START:
|
||||
@@ -556,16 +572,26 @@ void t7xx_md_event_notify(struct t7xx_mo
|
||||
ctl->exp_flg = true;
|
||||
md->exp_id &= ~D2H_INT_EXCEPTION_INIT;
|
||||
md->exp_id &= ~D2H_INT_ASYNC_MD_HK;
|
||||
+ md->exp_id &= ~D2H_INT_ASYNC_AP_HK;
|
||||
} else if (ctl->exp_flg) {
|
||||
md->exp_id &= ~D2H_INT_ASYNC_MD_HK;
|
||||
- } else if (md->exp_id & D2H_INT_ASYNC_MD_HK) {
|
||||
- queue_work(md->handshake_wq, &md->handshake_work);
|
||||
- md->exp_id &= ~D2H_INT_ASYNC_MD_HK;
|
||||
- mhccif_base = md->t7xx_dev->base_addr.mhccif_rc_base;
|
||||
- iowrite32(D2H_INT_ASYNC_MD_HK, mhccif_base + REG_EP2RC_SW_INT_ACK);
|
||||
- t7xx_mhccif_mask_set(md->t7xx_dev, D2H_INT_ASYNC_MD_HK);
|
||||
+ md->exp_id &= ~D2H_INT_ASYNC_AP_HK;
|
||||
} else {
|
||||
- t7xx_mhccif_mask_clr(md->t7xx_dev, D2H_INT_ASYNC_MD_HK);
|
||||
+ void __iomem *mhccif_base = md->t7xx_dev->base_addr.mhccif_rc_base;
|
||||
+
|
||||
+ if (md->exp_id & D2H_INT_ASYNC_MD_HK) {
|
||||
+ queue_work(md->handshake_wq, &md->handshake_work);
|
||||
+ md->exp_id &= ~D2H_INT_ASYNC_MD_HK;
|
||||
+ iowrite32(D2H_INT_ASYNC_MD_HK, mhccif_base + REG_EP2RC_SW_INT_ACK);
|
||||
+ t7xx_mhccif_mask_set(md->t7xx_dev, D2H_INT_ASYNC_MD_HK);
|
||||
+ }
|
||||
+
|
||||
+ if (md->exp_id & D2H_INT_ASYNC_AP_HK) {
|
||||
+ queue_work(md->handshake_wq, &md->ap_handshake_work);
|
||||
+ md->exp_id &= ~D2H_INT_ASYNC_AP_HK;
|
||||
+ iowrite32(D2H_INT_ASYNC_AP_HK, mhccif_base + REG_EP2RC_SW_INT_ACK);
|
||||
+ t7xx_mhccif_mask_set(md->t7xx_dev, D2H_INT_ASYNC_AP_HK);
|
||||
+ }
|
||||
}
|
||||
spin_unlock_irqrestore(&md->exp_lock, flags);
|
||||
|
||||
@@ -578,6 +604,7 @@ void t7xx_md_event_notify(struct t7xx_mo
|
||||
|
||||
case FSM_READY:
|
||||
t7xx_mhccif_mask_set(md->t7xx_dev, D2H_INT_ASYNC_MD_HK);
|
||||
+ t7xx_mhccif_mask_set(md->t7xx_dev, D2H_INT_ASYNC_AP_HK);
|
||||
break;
|
||||
|
||||
default:
|
||||
@@ -629,6 +656,12 @@ static struct t7xx_modem *t7xx_md_alloc(
|
||||
md->core_md.feature_set[RT_ID_MD_PORT_ENUM] &= ~FEATURE_MSK;
|
||||
md->core_md.feature_set[RT_ID_MD_PORT_ENUM] |=
|
||||
FIELD_PREP(FEATURE_MSK, MTK_FEATURE_MUST_BE_SUPPORTED);
|
||||
+
|
||||
+ INIT_WORK(&md->ap_handshake_work, t7xx_ap_hk_wq);
|
||||
+ md->core_ap.feature_set[RT_ID_AP_PORT_ENUM] &= ~FEATURE_MSK;
|
||||
+ md->core_ap.feature_set[RT_ID_AP_PORT_ENUM] |=
|
||||
+ FIELD_PREP(FEATURE_MSK, MTK_FEATURE_MUST_BE_SUPPORTED);
|
||||
+
|
||||
return md;
|
||||
}
|
||||
|
||||
@@ -640,6 +673,7 @@ int t7xx_md_reset(struct t7xx_pci_dev *t
|
||||
md->exp_id = 0;
|
||||
t7xx_fsm_reset(md);
|
||||
t7xx_cldma_reset(md->md_ctrl[CLDMA_ID_MD]);
|
||||
+ t7xx_cldma_reset(md->md_ctrl[CLDMA_ID_AP]);
|
||||
t7xx_port_proxy_reset(md->port_prox);
|
||||
md->md_init_finish = true;
|
||||
return t7xx_core_reset(md);
|
||||
@@ -669,6 +703,10 @@ int t7xx_md_init(struct t7xx_pci_dev *t7
|
||||
if (ret)
|
||||
goto err_destroy_hswq;
|
||||
|
||||
+ ret = t7xx_cldma_alloc(CLDMA_ID_AP, t7xx_dev);
|
||||
+ if (ret)
|
||||
+ goto err_destroy_hswq;
|
||||
+
|
||||
ret = t7xx_fsm_init(md);
|
||||
if (ret)
|
||||
goto err_destroy_hswq;
|
||||
@@ -681,12 +719,16 @@ int t7xx_md_init(struct t7xx_pci_dev *t7
|
||||
if (ret)
|
||||
goto err_uninit_ccmni;
|
||||
|
||||
- ret = t7xx_port_proxy_init(md);
|
||||
+ ret = t7xx_cldma_init(md->md_ctrl[CLDMA_ID_AP]);
|
||||
if (ret)
|
||||
goto err_uninit_md_cldma;
|
||||
|
||||
+ ret = t7xx_port_proxy_init(md);
|
||||
+ if (ret)
|
||||
+ goto err_uninit_ap_cldma;
|
||||
+
|
||||
ret = t7xx_fsm_append_cmd(md->fsm_ctl, FSM_CMD_START, 0);
|
||||
- if (ret) /* fsm_uninit flushes cmd queue */
|
||||
+ if (ret) /* t7xx_fsm_uninit() flushes cmd queue */
|
||||
goto err_uninit_proxy;
|
||||
|
||||
t7xx_md_sys_sw_init(t7xx_dev);
|
||||
@@ -696,6 +738,9 @@ int t7xx_md_init(struct t7xx_pci_dev *t7
|
||||
err_uninit_proxy:
|
||||
t7xx_port_proxy_uninit(md->port_prox);
|
||||
|
||||
+err_uninit_ap_cldma:
|
||||
+ t7xx_cldma_exit(md->md_ctrl[CLDMA_ID_AP]);
|
||||
+
|
||||
err_uninit_md_cldma:
|
||||
t7xx_cldma_exit(md->md_ctrl[CLDMA_ID_MD]);
|
||||
|
||||
@@ -722,6 +767,7 @@ void t7xx_md_exit(struct t7xx_pci_dev *t
|
||||
|
||||
t7xx_fsm_append_cmd(md->fsm_ctl, FSM_CMD_PRE_STOP, FSM_CMD_FLAG_WAIT_FOR_COMPLETION);
|
||||
t7xx_port_proxy_uninit(md->port_prox);
|
||||
+ t7xx_cldma_exit(md->md_ctrl[CLDMA_ID_AP]);
|
||||
t7xx_cldma_exit(md->md_ctrl[CLDMA_ID_MD]);
|
||||
t7xx_ccmni_exit(t7xx_dev);
|
||||
t7xx_fsm_uninit(md);
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_modem_ops.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_modem_ops.h
|
||||
@@ -66,10 +66,12 @@ struct t7xx_modem {
|
||||
struct cldma_ctrl *md_ctrl[CLDMA_NUM];
|
||||
struct t7xx_pci_dev *t7xx_dev;
|
||||
struct t7xx_sys_info core_md;
|
||||
+ struct t7xx_sys_info core_ap;
|
||||
bool md_init_finish;
|
||||
bool rgu_irq_asserted;
|
||||
struct workqueue_struct *handshake_wq;
|
||||
struct work_struct handshake_work;
|
||||
+ struct work_struct ap_handshake_work;
|
||||
struct t7xx_fsm_ctl *fsm_ctl;
|
||||
struct port_proxy *port_prox;
|
||||
unsigned int exp_id;
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port.h
|
||||
@@ -36,9 +36,13 @@
|
||||
/* Channel ID and Message ID definitions.
|
||||
* The channel number consists of peer_id(15:12) , channel_id(11:0)
|
||||
* peer_id:
|
||||
- * 0:reserved, 1: to sAP, 2: to MD
|
||||
+ * 0:reserved, 1: to AP, 2: to MD
|
||||
*/
|
||||
enum port_ch {
|
||||
+ /* to AP */
|
||||
+ PORT_CH_AP_CONTROL_RX = 0x1000,
|
||||
+ PORT_CH_AP_CONTROL_TX = 0x1001,
|
||||
+
|
||||
/* to MD */
|
||||
PORT_CH_CONTROL_RX = 0x2000,
|
||||
PORT_CH_CONTROL_TX = 0x2001,
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c
|
||||
@@ -167,8 +167,12 @@ static int control_msg_handler(struct t7
|
||||
case CTL_ID_HS2_MSG:
|
||||
skb_pull(skb, sizeof(*ctrl_msg_h));
|
||||
|
||||
- if (port_conf->rx_ch == PORT_CH_CONTROL_RX) {
|
||||
- ret = t7xx_fsm_append_event(ctl, FSM_EVENT_MD_HS2, skb->data,
|
||||
+ if (port_conf->rx_ch == PORT_CH_CONTROL_RX ||
|
||||
+ port_conf->rx_ch == PORT_CH_AP_CONTROL_RX) {
|
||||
+ int event = port_conf->rx_ch == PORT_CH_CONTROL_RX ?
|
||||
+ FSM_EVENT_MD_HS2 : FSM_EVENT_AP_HS2;
|
||||
+
|
||||
+ ret = t7xx_fsm_append_event(ctl, event, skb->data,
|
||||
le32_to_cpu(ctrl_msg_h->data_length));
|
||||
if (ret)
|
||||
dev_err(port->dev, "Failed to append Handshake 2 event");
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_port_proxy.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_port_proxy.c
|
||||
@@ -48,7 +48,7 @@
|
||||
i < (proxy)->port_count; \
|
||||
i++, (p) = &(proxy)->ports[i])
|
||||
|
||||
-static const struct t7xx_port_conf t7xx_md_port_conf[] = {
|
||||
+static const struct t7xx_port_conf t7xx_port_conf[] = {
|
||||
{
|
||||
.tx_ch = PORT_CH_UART2_TX,
|
||||
.rx_ch = PORT_CH_UART2_RX,
|
||||
@@ -89,6 +89,14 @@ static const struct t7xx_port_conf t7xx_
|
||||
.path_id = CLDMA_ID_MD,
|
||||
.ops = &ctl_port_ops,
|
||||
.name = "t7xx_ctrl",
|
||||
+ }, {
|
||||
+ .tx_ch = PORT_CH_AP_CONTROL_TX,
|
||||
+ .rx_ch = PORT_CH_AP_CONTROL_RX,
|
||||
+ .txq_index = Q_IDX_CTRL,
|
||||
+ .rxq_index = Q_IDX_CTRL,
|
||||
+ .path_id = CLDMA_ID_AP,
|
||||
+ .ops = &ctl_port_ops,
|
||||
+ .name = "t7xx_ap_ctrl",
|
||||
},
|
||||
};
|
||||
|
||||
@@ -428,6 +436,9 @@ static void t7xx_proxy_init_all_ports(st
|
||||
if (port_conf->tx_ch == PORT_CH_CONTROL_TX)
|
||||
md->core_md.ctl_port = port;
|
||||
|
||||
+ if (port_conf->tx_ch == PORT_CH_AP_CONTROL_TX)
|
||||
+ md->core_ap.ctl_port = port;
|
||||
+
|
||||
port->t7xx_dev = md->t7xx_dev;
|
||||
port->dev = &md->t7xx_dev->pdev->dev;
|
||||
spin_lock_init(&port->port_update_lock);
|
||||
@@ -442,7 +453,7 @@ static void t7xx_proxy_init_all_ports(st
|
||||
|
||||
static int t7xx_proxy_alloc(struct t7xx_modem *md)
|
||||
{
|
||||
- unsigned int port_count = ARRAY_SIZE(t7xx_md_port_conf);
|
||||
+ unsigned int port_count = ARRAY_SIZE(t7xx_port_conf);
|
||||
struct device *dev = &md->t7xx_dev->pdev->dev;
|
||||
struct port_proxy *port_prox;
|
||||
int i;
|
||||
@@ -456,7 +467,7 @@ static int t7xx_proxy_alloc(struct t7xx_
|
||||
port_prox->dev = dev;
|
||||
|
||||
for (i = 0; i < port_count; i++)
|
||||
- port_prox->ports[i].port_conf = &t7xx_md_port_conf[i];
|
||||
+ port_prox->ports[i].port_conf = &t7xx_port_conf[i];
|
||||
|
||||
port_prox->port_count = port_count;
|
||||
t7xx_proxy_init_all_ports(md);
|
||||
@@ -481,6 +492,7 @@ int t7xx_port_proxy_init(struct t7xx_mod
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ t7xx_cldma_set_recv_skb(md->md_ctrl[CLDMA_ID_AP], t7xx_port_proxy_recv_skb);
|
||||
t7xx_cldma_set_recv_skb(md->md_ctrl[CLDMA_ID_MD], t7xx_port_proxy_recv_skb);
|
||||
return 0;
|
||||
}
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_reg.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_reg.h
|
||||
@@ -56,7 +56,7 @@
|
||||
#define D2H_INT_RESUME_ACK BIT(12)
|
||||
#define D2H_INT_SUSPEND_ACK_AP BIT(13)
|
||||
#define D2H_INT_RESUME_ACK_AP BIT(14)
|
||||
-#define D2H_INT_ASYNC_SAP_HK BIT(15)
|
||||
+#define D2H_INT_ASYNC_AP_HK BIT(15)
|
||||
#define D2H_INT_ASYNC_MD_HK BIT(16)
|
||||
|
||||
/* Register base */
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_state_monitor.c
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_state_monitor.c
|
||||
@@ -285,8 +285,9 @@ static int fsm_routine_starting(struct t
|
||||
t7xx_fsm_broadcast_state(ctl, MD_STATE_WAITING_FOR_HS1);
|
||||
t7xx_md_event_notify(md, FSM_START);
|
||||
|
||||
- wait_event_interruptible_timeout(ctl->async_hk_wq, md->core_md.ready || ctl->exp_flg,
|
||||
- HZ * 60);
|
||||
+ wait_event_interruptible_timeout(ctl->async_hk_wq,
|
||||
+ (md->core_md.ready && md->core_ap.ready) ||
|
||||
+ ctl->exp_flg, HZ * 60);
|
||||
dev = &md->t7xx_dev->pdev->dev;
|
||||
|
||||
if (ctl->exp_flg)
|
||||
@@ -299,6 +300,13 @@ static int fsm_routine_starting(struct t
|
||||
|
||||
fsm_routine_exception(ctl, NULL, EXCEPTION_HS_TIMEOUT);
|
||||
return -ETIMEDOUT;
|
||||
+ } else if (!md->core_ap.ready) {
|
||||
+ dev_err(dev, "AP handshake timeout\n");
|
||||
+ if (md->core_ap.handshake_ongoing)
|
||||
+ t7xx_fsm_append_event(ctl, FSM_EVENT_AP_HS2_EXIT, NULL, 0);
|
||||
+
|
||||
+ fsm_routine_exception(ctl, NULL, EXCEPTION_HS_TIMEOUT);
|
||||
+ return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
t7xx_pci_pm_init_late(md->t7xx_dev);
|
||||
@@ -335,6 +343,7 @@ static void fsm_routine_start(struct t7x
|
||||
return;
|
||||
}
|
||||
|
||||
+ t7xx_cldma_hif_hw_init(md->md_ctrl[CLDMA_ID_AP]);
|
||||
t7xx_cldma_hif_hw_init(md->md_ctrl[CLDMA_ID_MD]);
|
||||
fsm_finish_command(ctl, cmd, fsm_routine_starting(ctl));
|
||||
}
|
||||
--- a/drivers/net/wwan/t7xx/t7xx_state_monitor.h
|
||||
+++ b/drivers/net/wwan/t7xx/t7xx_state_monitor.h
|
||||
@@ -38,10 +38,12 @@ enum t7xx_fsm_state {
|
||||
enum t7xx_fsm_event_state {
|
||||
FSM_EVENT_INVALID,
|
||||
FSM_EVENT_MD_HS2,
|
||||
+ FSM_EVENT_AP_HS2,
|
||||
FSM_EVENT_MD_EX,
|
||||
FSM_EVENT_MD_EX_REC_OK,
|
||||
FSM_EVENT_MD_EX_PASS,
|
||||
FSM_EVENT_MD_HS2_EXIT,
|
||||
+ FSM_EVENT_AP_HS2_EXIT,
|
||||
FSM_EVENT_MAX
|
||||
};
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
From d387e34fec407f881fdf165b5d7ec128ebff362f Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 19 Sep 2023 14:47:20 +0200
|
||||
Subject: [PATCH] net: sfp: add quirk for Fiberstone GPON-ONU-34-20BI
|
||||
|
||||
Fiberstone GPON-ONU-34-20B can operate at 2500base-X, but report 1.2GBd
|
||||
NRZ in their EEPROM.
|
||||
|
||||
The module also require the ignore tx fault fixup similar to Huawei MA5671A
|
||||
as it gets disabled on error messages with serial redirection enabled.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Link: https://lore.kernel.org/r/20230919124720.8210-1-ansuelsmth@gmail.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/phy/sfp.c | 5 +++++
|
||||
1 file changed, 5 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/sfp.c
|
||||
+++ b/drivers/net/phy/sfp.c
|
||||
@@ -393,6 +393,11 @@ static const struct sfp_quirk sfp_quirks
|
||||
SFP_QUIRK("ALCATELLUCENT", "3FE46541AA", sfp_quirk_2500basex,
|
||||
sfp_fixup_long_startup),
|
||||
|
||||
+ // Fiberstore GPON-ONU-34-20BI can operate at 2500base-X, but report 1.2GBd
|
||||
+ // NRZ in their EEPROM
|
||||
+ SFP_QUIRK("FS", "GPON-ONU-34-20BI", sfp_quirk_2500basex,
|
||||
+ sfp_fixup_ignore_tx_fault),
|
||||
+
|
||||
SFP_QUIRK_F("HALNy", "HL-GSFP", sfp_fixup_halny_gsfp),
|
||||
|
||||
// HG MXPD-483II-F 2.5G supports 2500Base-X, but incorrectly reports
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,183 +0,0 @@
|
||||
From e1fbfa4a995d42e02e22b0dff2f8b4fdee1504b3 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 14 Nov 2023 15:08:42 +0100
|
||||
Subject: [PATCH 2/3] net: phy: aquantia: move MMD_VEND define to header
|
||||
|
||||
Move MMD_VEND define to header to clean things up and in preparation for
|
||||
firmware loading support that require some define placed in
|
||||
aquantia_main.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/aquantia/aquantia.h | 69 +++++++++++++++++++++++
|
||||
drivers/net/phy/aquantia/aquantia_hwmon.c | 14 -----
|
||||
drivers/net/phy/aquantia/aquantia_main.c | 55 ------------------
|
||||
3 files changed, 69 insertions(+), 69 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/aquantia/aquantia.h
|
||||
+++ b/drivers/net/phy/aquantia/aquantia.h
|
||||
@@ -9,6 +9,75 @@
|
||||
#include <linux/device.h>
|
||||
#include <linux/phy.h>
|
||||
|
||||
+/* Vendor specific 1, MDIO_MMD_VEND1 */
|
||||
+#define VEND1_GLOBAL_FW_ID 0x0020
|
||||
+#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
|
||||
+#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
|
||||
+
|
||||
+/* The following registers all have similar layouts; first the registers... */
|
||||
+#define VEND1_GLOBAL_CFG_10M 0x0310
|
||||
+#define VEND1_GLOBAL_CFG_100M 0x031b
|
||||
+#define VEND1_GLOBAL_CFG_1G 0x031c
|
||||
+#define VEND1_GLOBAL_CFG_2_5G 0x031d
|
||||
+#define VEND1_GLOBAL_CFG_5G 0x031e
|
||||
+#define VEND1_GLOBAL_CFG_10G 0x031f
|
||||
+/* ...and now the fields */
|
||||
+#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
|
||||
+#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
|
||||
+#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
|
||||
+#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
|
||||
+
|
||||
+/* Vendor specific 1, MDIO_MMD_VEND2 */
|
||||
+#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
|
||||
+#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
|
||||
+#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
|
||||
+#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
|
||||
+#define VEND1_THERMAL_STAT1 0xc820
|
||||
+#define VEND1_THERMAL_STAT2 0xc821
|
||||
+#define VEND1_THERMAL_STAT2_VALID BIT(0)
|
||||
+#define VEND1_GENERAL_STAT1 0xc830
|
||||
+#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14)
|
||||
+#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13)
|
||||
+#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12)
|
||||
+#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11)
|
||||
+
|
||||
+#define VEND1_GLOBAL_GEN_STAT2 0xc831
|
||||
+#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15)
|
||||
+
|
||||
+#define VEND1_GLOBAL_RSVD_STAT1 0xc885
|
||||
+#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
|
||||
+#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
|
||||
+
|
||||
+#define VEND1_GLOBAL_RSVD_STAT9 0xc88d
|
||||
+#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
|
||||
+#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
|
||||
+
|
||||
+#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
|
||||
+#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
|
||||
+
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK 0xff00
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
|
||||
+#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
|
||||
+
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK 0xff01
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
|
||||
+#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
|
||||
+
|
||||
#if IS_REACHABLE(CONFIG_HWMON)
|
||||
int aqr_hwmon_probe(struct phy_device *phydev);
|
||||
#else
|
||||
--- a/drivers/net/phy/aquantia/aquantia_hwmon.c
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_hwmon.c
|
||||
@@ -13,20 +13,6 @@
|
||||
|
||||
#include "aquantia.h"
|
||||
|
||||
-/* Vendor specific 1, MDIO_MMD_VEND2 */
|
||||
-#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
|
||||
-#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
|
||||
-#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
|
||||
-#define VEND1_THERMAL_PROV_LOW_TEMP_WARN 0xc424
|
||||
-#define VEND1_THERMAL_STAT1 0xc820
|
||||
-#define VEND1_THERMAL_STAT2 0xc821
|
||||
-#define VEND1_THERMAL_STAT2_VALID BIT(0)
|
||||
-#define VEND1_GENERAL_STAT1 0xc830
|
||||
-#define VEND1_GENERAL_STAT1_HIGH_TEMP_FAIL BIT(14)
|
||||
-#define VEND1_GENERAL_STAT1_LOW_TEMP_FAIL BIT(13)
|
||||
-#define VEND1_GENERAL_STAT1_HIGH_TEMP_WARN BIT(12)
|
||||
-#define VEND1_GENERAL_STAT1_LOW_TEMP_WARN BIT(11)
|
||||
-
|
||||
#if IS_REACHABLE(CONFIG_HWMON)
|
||||
|
||||
static umode_t aqr_hwmon_is_visible(const void *data,
|
||||
--- a/drivers/net/phy/aquantia/aquantia_main.c
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_main.c
|
||||
@@ -89,61 +89,6 @@
|
||||
#define MDIO_C22EXT_STAT_SGMII_TX_FRAME_ALIGN_ERR 0xd31a
|
||||
#define MDIO_C22EXT_STAT_SGMII_TX_RUNT_FRAMES 0xd31b
|
||||
|
||||
-/* Vendor specific 1, MDIO_MMD_VEND1 */
|
||||
-#define VEND1_GLOBAL_FW_ID 0x0020
|
||||
-#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
|
||||
-#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
|
||||
-
|
||||
-#define VEND1_GLOBAL_GEN_STAT2 0xc831
|
||||
-#define VEND1_GLOBAL_GEN_STAT2_OP_IN_PROG BIT(15)
|
||||
-
|
||||
-/* The following registers all have similar layouts; first the registers... */
|
||||
-#define VEND1_GLOBAL_CFG_10M 0x0310
|
||||
-#define VEND1_GLOBAL_CFG_100M 0x031b
|
||||
-#define VEND1_GLOBAL_CFG_1G 0x031c
|
||||
-#define VEND1_GLOBAL_CFG_2_5G 0x031d
|
||||
-#define VEND1_GLOBAL_CFG_5G 0x031e
|
||||
-#define VEND1_GLOBAL_CFG_10G 0x031f
|
||||
-/* ...and now the fields */
|
||||
-#define VEND1_GLOBAL_CFG_RATE_ADAPT GENMASK(8, 7)
|
||||
-#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
|
||||
-#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
|
||||
-#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
|
||||
-
|
||||
-#define VEND1_GLOBAL_RSVD_STAT1 0xc885
|
||||
-#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
|
||||
-#define VEND1_GLOBAL_RSVD_STAT1_PROV_ID GENMASK(3, 0)
|
||||
-
|
||||
-#define VEND1_GLOBAL_RSVD_STAT9 0xc88d
|
||||
-#define VEND1_GLOBAL_RSVD_STAT9_MODE GENMASK(7, 0)
|
||||
-#define VEND1_GLOBAL_RSVD_STAT9_1000BT2 0x23
|
||||
-
|
||||
-#define VEND1_GLOBAL_INT_STD_STATUS 0xfc00
|
||||
-#define VEND1_GLOBAL_INT_VEND_STATUS 0xfc01
|
||||
-
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK 0xff00
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PMA1 BIT(15)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PMA2 BIT(14)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PCS1 BIT(13)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PCS2 BIT(12)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PCS3 BIT(11)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS1 BIT(10)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_PHY_XS2 BIT(9)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_AN1 BIT(8)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_AN2 BIT(7)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_GBE BIT(6)
|
||||
-#define VEND1_GLOBAL_INT_STD_MASK_ALL BIT(0)
|
||||
-
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK 0xff01
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_PMA BIT(15)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_PCS BIT(14)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_PHY_XS BIT(13)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_AN BIT(12)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_GBE BIT(11)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL1 BIT(2)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL2 BIT(1)
|
||||
-#define VEND1_GLOBAL_INT_VEND_MASK_GLOBAL3 BIT(0)
|
||||
-
|
||||
/* Sleep and timeout for checking if the Processor-Intensive
|
||||
* MDIO operation is finished
|
||||
*/
|
||||
@@ -1,504 +0,0 @@
|
||||
From e93984ebc1c82bd34f7a1b3391efaceee0a8ae96 Mon Sep 17 00:00:00 2001
|
||||
From: Robert Marko <robimarko@gmail.com>
|
||||
Date: Tue, 14 Nov 2023 15:08:43 +0100
|
||||
Subject: [PATCH 3/3] net: phy: aquantia: add firmware load support
|
||||
|
||||
Aquantia PHY-s require firmware to be loaded before they start operating.
|
||||
It can be automatically loaded in case when there is a SPI-NOR connected
|
||||
to Aquantia PHY-s or can be loaded from the host via MDIO.
|
||||
|
||||
This patch adds support for loading the firmware via MDIO as in most cases
|
||||
there is no SPI-NOR being used to save on cost.
|
||||
Firmware loading code itself is ported from mainline U-boot with cleanups.
|
||||
|
||||
The firmware has mixed values both in big and little endian.
|
||||
PHY core itself is big-endian but it expects values to be in little-endian.
|
||||
The firmware is little-endian but CRC-16 value for it is stored at the end
|
||||
of firmware in big-endian.
|
||||
|
||||
It seems the PHY does the conversion internally from firmware that is
|
||||
little-endian to the PHY that is big-endian on using the mailbox
|
||||
but mailbox returns a big-endian CRC-16 to verify the written data
|
||||
integrity.
|
||||
|
||||
Co-developed-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: Robert Marko <robimarko@gmail.com>
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/aquantia/Kconfig | 1 +
|
||||
drivers/net/phy/aquantia/Makefile | 2 +-
|
||||
drivers/net/phy/aquantia/aquantia.h | 32 ++
|
||||
drivers/net/phy/aquantia/aquantia_firmware.c | 370 +++++++++++++++++++
|
||||
drivers/net/phy/aquantia/aquantia_main.c | 6 +
|
||||
5 files changed, 410 insertions(+), 1 deletion(-)
|
||||
create mode 100644 drivers/net/phy/aquantia/aquantia_firmware.c
|
||||
|
||||
--- a/drivers/net/phy/aquantia/Kconfig
|
||||
+++ b/drivers/net/phy/aquantia/Kconfig
|
||||
@@ -1,5 +1,6 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config AQUANTIA_PHY
|
||||
tristate "Aquantia PHYs"
|
||||
+ select CRC_CCITT
|
||||
help
|
||||
Currently supports the Aquantia AQ1202, AQ2104, AQR105, AQR405
|
||||
--- a/drivers/net/phy/aquantia/Makefile
|
||||
+++ b/drivers/net/phy/aquantia/Makefile
|
||||
@@ -1,5 +1,5 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
-aquantia-objs += aquantia_main.o
|
||||
+aquantia-objs += aquantia_main.o aquantia_firmware.o
|
||||
ifdef CONFIG_HWMON
|
||||
aquantia-objs += aquantia_hwmon.o
|
||||
endif
|
||||
--- a/drivers/net/phy/aquantia/aquantia.h
|
||||
+++ b/drivers/net/phy/aquantia/aquantia.h
|
||||
@@ -10,10 +10,35 @@
|
||||
#include <linux/phy.h>
|
||||
|
||||
/* Vendor specific 1, MDIO_MMD_VEND1 */
|
||||
+#define VEND1_GLOBAL_SC 0x0
|
||||
+#define VEND1_GLOBAL_SC_SOFT_RESET BIT(15)
|
||||
+#define VEND1_GLOBAL_SC_LOW_POWER BIT(11)
|
||||
+
|
||||
#define VEND1_GLOBAL_FW_ID 0x0020
|
||||
#define VEND1_GLOBAL_FW_ID_MAJOR GENMASK(15, 8)
|
||||
#define VEND1_GLOBAL_FW_ID_MINOR GENMASK(7, 0)
|
||||
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1 0x0200
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE BIT(15)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE BIT(14)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET BIT(12)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE1_BUSY BIT(8)
|
||||
+
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE2 0x0201
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE3 0x0202
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK GENMASK(15, 0)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR_MASK, (u16)((x) >> 16))
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE4 0x0203
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK GENMASK(15, 2)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR_MASK, (u16)(x))
|
||||
+
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE5 0x0204
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK GENMASK(15, 0)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA_MASK, (u16)((x) >> 16))
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE6 0x0205
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK GENMASK(15, 0)
|
||||
+#define VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(x) FIELD_PREP(VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA_MASK, (u16)(x))
|
||||
+
|
||||
/* The following registers all have similar layouts; first the registers... */
|
||||
#define VEND1_GLOBAL_CFG_10M 0x0310
|
||||
#define VEND1_GLOBAL_CFG_100M 0x031b
|
||||
@@ -28,6 +53,11 @@
|
||||
#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
|
||||
|
||||
/* Vendor specific 1, MDIO_MMD_VEND2 */
|
||||
+#define VEND1_GLOBAL_CONTROL2 0xc001
|
||||
+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST BIT(15)
|
||||
+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD BIT(6)
|
||||
+#define VEND1_GLOBAL_CONTROL2_UP_RUN_STALL BIT(0)
|
||||
+
|
||||
#define VEND1_THERMAL_PROV_HIGH_TEMP_FAIL 0xc421
|
||||
#define VEND1_THERMAL_PROV_LOW_TEMP_FAIL 0xc422
|
||||
#define VEND1_THERMAL_PROV_HIGH_TEMP_WARN 0xc423
|
||||
@@ -83,3 +113,5 @@ int aqr_hwmon_probe(struct phy_device *p
|
||||
#else
|
||||
static inline int aqr_hwmon_probe(struct phy_device *phydev) { return 0; }
|
||||
#endif
|
||||
+
|
||||
+int aqr_firmware_load(struct phy_device *phydev);
|
||||
--- /dev/null
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_firmware.c
|
||||
@@ -0,0 +1,370 @@
|
||||
+// SPDX-License-Identifier: GPL-2.0
|
||||
+
|
||||
+#include <linux/bitfield.h>
|
||||
+#include <linux/of.h>
|
||||
+#include <linux/firmware.h>
|
||||
+#include <linux/crc-ccitt.h>
|
||||
+#include <linux/nvmem-consumer.h>
|
||||
+
|
||||
+#include <asm/unaligned.h>
|
||||
+
|
||||
+#include "aquantia.h"
|
||||
+
|
||||
+#define UP_RESET_SLEEP 100
|
||||
+
|
||||
+/* addresses of memory segments in the phy */
|
||||
+#define DRAM_BASE_ADDR 0x3FFE0000
|
||||
+#define IRAM_BASE_ADDR 0x40000000
|
||||
+
|
||||
+/* firmware image format constants */
|
||||
+#define VERSION_STRING_SIZE 0x40
|
||||
+#define VERSION_STRING_OFFSET 0x0200
|
||||
+/* primary offset is written at an offset from the start of the fw blob */
|
||||
+#define PRIMARY_OFFSET_OFFSET 0x8
|
||||
+/* primary offset needs to be then added to a base offset */
|
||||
+#define PRIMARY_OFFSET_SHIFT 12
|
||||
+#define PRIMARY_OFFSET(x) ((x) << PRIMARY_OFFSET_SHIFT)
|
||||
+#define HEADER_OFFSET 0x300
|
||||
+
|
||||
+struct aqr_fw_header {
|
||||
+ u32 padding;
|
||||
+ u8 iram_offset[3];
|
||||
+ u8 iram_size[3];
|
||||
+ u8 dram_offset[3];
|
||||
+ u8 dram_size[3];
|
||||
+} __packed;
|
||||
+
|
||||
+enum aqr_fw_src {
|
||||
+ AQR_FW_SRC_NVMEM = 0,
|
||||
+ AQR_FW_SRC_FS,
|
||||
+};
|
||||
+
|
||||
+static const char * const aqr_fw_src_string[] = {
|
||||
+ [AQR_FW_SRC_NVMEM] = "NVMEM",
|
||||
+ [AQR_FW_SRC_FS] = "FS",
|
||||
+};
|
||||
+
|
||||
+/* AQR firmware doesn't have fixed offsets for iram and dram section
|
||||
+ * but instead provide an header with the offset to use on reading
|
||||
+ * and parsing the firmware.
|
||||
+ *
|
||||
+ * AQR firmware can't be trusted and each offset is validated to be
|
||||
+ * not negative and be in the size of the firmware itself.
|
||||
+ */
|
||||
+static bool aqr_fw_validate_get(size_t size, size_t offset, size_t get_size)
|
||||
+{
|
||||
+ return offset + get_size <= size;
|
||||
+}
|
||||
+
|
||||
+static int aqr_fw_get_be16(const u8 *data, size_t offset, size_t size, u16 *value)
|
||||
+{
|
||||
+ if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *value = get_unaligned_be16(data + offset);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int aqr_fw_get_le16(const u8 *data, size_t offset, size_t size, u16 *value)
|
||||
+{
|
||||
+ if (!aqr_fw_validate_get(size, offset, sizeof(u16)))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *value = get_unaligned_le16(data + offset);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int aqr_fw_get_le24(const u8 *data, size_t offset, size_t size, u32 *value)
|
||||
+{
|
||||
+ if (!aqr_fw_validate_get(size, offset, sizeof(u8) * 3))
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ *value = get_unaligned_le24(data + offset);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* load data into the phy's memory */
|
||||
+static int aqr_fw_load_memory(struct phy_device *phydev, u32 addr,
|
||||
+ const u8 *data, size_t len)
|
||||
+{
|
||||
+ u16 crc = 0, up_crc;
|
||||
+ size_t pos;
|
||||
+
|
||||
+ /* PHY expect addr in LE */
|
||||
+ addr = (__force u32)cpu_to_le32(addr);
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE1_CRC_RESET);
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE3,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE3_MSW_ADDR(addr));
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE4,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE4_LSW_ADDR(addr));
|
||||
+
|
||||
+ /* We assume and enforce the size to be word aligned.
|
||||
+ * If a firmware that is not word aligned is found, please report upstream.
|
||||
+ */
|
||||
+ for (pos = 0; pos < len; pos += sizeof(u32)) {
|
||||
+ u32 word;
|
||||
+
|
||||
+ /* FW data is always stored in little-endian */
|
||||
+ word = get_unaligned((const u32 *)(data + pos));
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE5,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE5_MSW_DATA(word));
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE6,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE6_LSW_DATA(word));
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE1,
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE1_EXECUTE |
|
||||
+ VEND1_GLOBAL_MAILBOX_INTERFACE1_WRITE);
|
||||
+
|
||||
+ /* calculate CRC as we load data to the mailbox.
|
||||
+ * We convert word to big-endian as PHY is BE and mailbox will
|
||||
+ * return a BE CRC.
|
||||
+ */
|
||||
+ word = (__force u32)cpu_to_be32(word);
|
||||
+ crc = crc_ccitt_false(crc, (u8 *)&word, sizeof(word));
|
||||
+ }
|
||||
+
|
||||
+ up_crc = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_MAILBOX_INTERFACE2);
|
||||
+ if (crc != up_crc) {
|
||||
+ phydev_err(phydev, "CRC mismatch: calculated 0x%04x PHY 0x%04x\n",
|
||||
+ crc, up_crc);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int aqr_fw_boot(struct phy_device *phydev, const u8 *data, size_t size,
|
||||
+ enum aqr_fw_src fw_src)
|
||||
+{
|
||||
+ u16 calculated_crc, read_crc, read_primary_offset;
|
||||
+ u32 iram_offset = 0, iram_size = 0;
|
||||
+ u32 dram_offset = 0, dram_size = 0;
|
||||
+ char version[VERSION_STRING_SIZE];
|
||||
+ u32 primary_offset = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* extract saved CRC at the end of the fw
|
||||
+ * CRC is saved in big-endian as PHY is BE
|
||||
+ */
|
||||
+ ret = aqr_fw_get_be16(data, size - sizeof(u16), size, &read_crc);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "bad firmware CRC in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ calculated_crc = crc_ccitt_false(0, data, size - sizeof(u16));
|
||||
+ if (read_crc != calculated_crc) {
|
||||
+ phydev_err(phydev, "bad firmware CRC: file 0x%04x calculated 0x%04x\n",
|
||||
+ read_crc, calculated_crc);
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* Get the primary offset to extract DRAM and IRAM sections. */
|
||||
+ ret = aqr_fw_get_le16(data, PRIMARY_OFFSET_OFFSET, size, &read_primary_offset);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "bad primary offset in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ primary_offset = PRIMARY_OFFSET(read_primary_offset);
|
||||
+
|
||||
+ /* Find the DRAM and IRAM sections within the firmware file.
|
||||
+ * Make sure the fw_header is correctly in the firmware.
|
||||
+ */
|
||||
+ if (!aqr_fw_validate_get(size, primary_offset + HEADER_OFFSET,
|
||||
+ sizeof(struct aqr_fw_header))) {
|
||||
+ phydev_err(phydev, "bad fw_header in firmware\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ /* offset are in LE and values needs to be converted to cpu endian */
|
||||
+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
|
||||
+ offsetof(struct aqr_fw_header, iram_offset),
|
||||
+ size, &iram_offset);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "bad iram offset in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
|
||||
+ offsetof(struct aqr_fw_header, iram_size),
|
||||
+ size, &iram_size);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "invalid iram size in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
|
||||
+ offsetof(struct aqr_fw_header, dram_offset),
|
||||
+ size, &dram_offset);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "bad dram offset in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+ ret = aqr_fw_get_le24(data, primary_offset + HEADER_OFFSET +
|
||||
+ offsetof(struct aqr_fw_header, dram_size),
|
||||
+ size, &dram_size);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "invalid dram size in firmware\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Increment the offset with the primary offset.
|
||||
+ * Validate iram/dram offset and size.
|
||||
+ */
|
||||
+ iram_offset += primary_offset;
|
||||
+ if (iram_size % sizeof(u32)) {
|
||||
+ phydev_err(phydev, "iram size if not aligned to word size. Please report this upstream!\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ if (!aqr_fw_validate_get(size, iram_offset, iram_size)) {
|
||||
+ phydev_err(phydev, "invalid iram offset for iram size\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ dram_offset += primary_offset;
|
||||
+ if (dram_size % sizeof(u32)) {
|
||||
+ phydev_err(phydev, "dram size if not aligned to word size. Please report this upstream!\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ if (!aqr_fw_validate_get(size, dram_offset, dram_size)) {
|
||||
+ phydev_err(phydev, "invalid iram offset for iram size\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ phydev_dbg(phydev, "primary %d IRAM offset=%d size=%d DRAM offset=%d size=%d\n",
|
||||
+ primary_offset, iram_offset, iram_size, dram_offset, dram_size);
|
||||
+
|
||||
+ if (!aqr_fw_validate_get(size, dram_offset + VERSION_STRING_OFFSET,
|
||||
+ VERSION_STRING_SIZE)) {
|
||||
+ phydev_err(phydev, "invalid version in firmware\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ strscpy(version, (char *)data + dram_offset + VERSION_STRING_OFFSET,
|
||||
+ VERSION_STRING_SIZE);
|
||||
+ if (version[0] == '\0') {
|
||||
+ phydev_err(phydev, "invalid version in firmware\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+ phydev_info(phydev, "loading firmware version '%s' from '%s'\n", version,
|
||||
+ aqr_fw_src_string[fw_src]);
|
||||
+
|
||||
+ /* stall the microcprocessor */
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL | VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
|
||||
+
|
||||
+ phydev_dbg(phydev, "loading DRAM 0x%08x from offset=%d size=%d\n",
|
||||
+ DRAM_BASE_ADDR, dram_offset, dram_size);
|
||||
+ ret = aqr_fw_load_memory(phydev, DRAM_BASE_ADDR, data + dram_offset,
|
||||
+ dram_size);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ phydev_dbg(phydev, "loading IRAM 0x%08x from offset=%d size=%d\n",
|
||||
+ IRAM_BASE_ADDR, iram_offset, iram_size);
|
||||
+ ret = aqr_fw_load_memory(phydev, IRAM_BASE_ADDR, data + iram_offset,
|
||||
+ iram_size);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* make sure soft reset and low power mode are clear */
|
||||
+ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_SC,
|
||||
+ VEND1_GLOBAL_SC_SOFT_RESET | VEND1_GLOBAL_SC_LOW_POWER);
|
||||
+
|
||||
+ /* Release the microprocessor. UP_RESET must be held for 100 usec. */
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL |
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD |
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_RST);
|
||||
+ usleep_range(UP_RESET_SLEEP, UP_RESET_SLEEP * 2);
|
||||
+
|
||||
+ phy_write_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_CONTROL2,
|
||||
+ VEND1_GLOBAL_CONTROL2_UP_RUN_STALL_OVD);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int aqr_firmware_load_nvmem(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct nvmem_cell *cell;
|
||||
+ size_t size;
|
||||
+ u8 *buf;
|
||||
+ int ret;
|
||||
+
|
||||
+ cell = nvmem_cell_get(&phydev->mdio.dev, "firmware");
|
||||
+ if (IS_ERR(cell))
|
||||
+ return PTR_ERR(cell);
|
||||
+
|
||||
+ buf = nvmem_cell_read(cell, &size);
|
||||
+ if (IS_ERR(buf)) {
|
||||
+ ret = PTR_ERR(buf);
|
||||
+ goto exit;
|
||||
+ }
|
||||
+
|
||||
+ ret = aqr_fw_boot(phydev, buf, size, AQR_FW_SRC_NVMEM);
|
||||
+ if (ret)
|
||||
+ phydev_err(phydev, "firmware loading failed: %d\n", ret);
|
||||
+
|
||||
+ kfree(buf);
|
||||
+exit:
|
||||
+ nvmem_cell_put(cell);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+static int aqr_firmware_load_fs(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ const struct firmware *fw;
|
||||
+ const char *fw_name;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = of_property_read_string(dev->of_node, "firmware-name",
|
||||
+ &fw_name);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = request_firmware(&fw, fw_name, dev);
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "failed to find FW file %s (%d)\n",
|
||||
+ fw_name, ret);
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ ret = aqr_fw_boot(phydev, fw->data, fw->size, AQR_FW_SRC_FS);
|
||||
+ if (ret)
|
||||
+ phydev_err(phydev, "firmware loading failed: %d\n", ret);
|
||||
+
|
||||
+ release_firmware(fw);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
+int aqr_firmware_load(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Check if the firmware is not already loaded by pooling
|
||||
+ * the current version returned by the PHY. If 0 is returned,
|
||||
+ * no firmware is loaded.
|
||||
+ */
|
||||
+ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VEND1_GLOBAL_FW_ID);
|
||||
+ if (ret > 0)
|
||||
+ goto exit;
|
||||
+
|
||||
+ ret = aqr_firmware_load_nvmem(phydev);
|
||||
+ if (!ret)
|
||||
+ goto exit;
|
||||
+
|
||||
+ ret = aqr_firmware_load_fs(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+exit:
|
||||
+ return 0;
|
||||
+}
|
||||
--- a/drivers/net/phy/aquantia/aquantia_main.c
|
||||
+++ b/drivers/net/phy/aquantia/aquantia_main.c
|
||||
@@ -656,11 +656,17 @@ static int aqr107_resume(struct phy_devi
|
||||
|
||||
static int aqr107_probe(struct phy_device *phydev)
|
||||
{
|
||||
+ int ret;
|
||||
+
|
||||
phydev->priv = devm_kzalloc(&phydev->mdio.dev,
|
||||
sizeof(struct aqr107_priv), GFP_KERNEL);
|
||||
if (!phydev->priv)
|
||||
return -ENOMEM;
|
||||
|
||||
+ ret = aqr_firmware_load(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
return aqr_hwmon_probe(phydev);
|
||||
}
|
||||
|
||||
@@ -1,326 +0,0 @@
|
||||
From 4e4aafcddbbfcdd6eed5780e190fcbfac8b4685a Mon Sep 17 00:00:00 2001
|
||||
From: Andrew Lunn <andrew@lunn.ch>
|
||||
Date: Mon, 9 Jan 2023 16:30:41 +0100
|
||||
Subject: [PATCH] net: mdio: Add dedicated C45 API to MDIO bus drivers
|
||||
|
||||
Currently C22 and C45 transactions are mixed over a combined API calls
|
||||
which make use of a special bit in the reg address to indicate if a
|
||||
C45 transaction should be performed. This makes it impossible to know
|
||||
if the bus driver actually supports C45. Additionally, many C22 only
|
||||
drivers don't return -EOPNOTSUPP when asked to perform a C45
|
||||
transaction, they mistaking perform a C22 transaction.
|
||||
|
||||
This is the first step to cleanly separate C22 from C45. To maintain
|
||||
backwards compatibility until all drivers which are capable of
|
||||
performing C45 are converted to this new API, the helper functions
|
||||
will fall back to the older API if the new API is not
|
||||
supported. Eventually this fallback will be removed.
|
||||
|
||||
Signed-off-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: Michael Walle <michael@walle.cc>
|
||||
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
|
||||
---
|
||||
drivers/net/phy/mdio_bus.c | 189 +++++++++++++++++++++++++++++++++++++
|
||||
include/linux/mdio.h | 39 ++++----
|
||||
include/linux/phy.h | 5 +
|
||||
3 files changed, 214 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/mdio_bus.c
|
||||
+++ b/drivers/net/phy/mdio_bus.c
|
||||
@@ -832,6 +832,100 @@ int __mdiobus_modify_changed(struct mii_
|
||||
EXPORT_SYMBOL_GPL(__mdiobus_modify_changed);
|
||||
|
||||
/**
|
||||
+ * __mdiobus_c45_read - Unlocked version of the mdiobus_c45_read function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to read
|
||||
+ *
|
||||
+ * Read a MDIO bus register. Caller must hold the mdio bus lock.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
|
||||
+{
|
||||
+ int retval;
|
||||
+
|
||||
+ lockdep_assert_held_once(&bus->mdio_lock);
|
||||
+
|
||||
+ if (bus->read_c45)
|
||||
+ retval = bus->read_c45(bus, addr, devad, regnum);
|
||||
+ else
|
||||
+ retval = bus->read(bus, addr, mdiobus_c45_addr(devad, regnum));
|
||||
+
|
||||
+ trace_mdio_access(bus, 1, addr, regnum, retval, retval);
|
||||
+ mdiobus_stats_acct(&bus->stats[addr], true, retval);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(__mdiobus_c45_read);
|
||||
+
|
||||
+/**
|
||||
+ * __mdiobus_c45_write - Unlocked version of the mdiobus_write function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * Write a MDIO bus register. Caller must hold the mdio bus lock.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 val)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ lockdep_assert_held_once(&bus->mdio_lock);
|
||||
+
|
||||
+ if (bus->write_c45)
|
||||
+ err = bus->write_c45(bus, addr, devad, regnum, val);
|
||||
+ else
|
||||
+ err = bus->write(bus, addr, mdiobus_c45_addr(devad, regnum),
|
||||
+ val);
|
||||
+
|
||||
+ trace_mdio_access(bus, 0, addr, regnum, val, err);
|
||||
+ mdiobus_stats_acct(&bus->stats[addr], false, err);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL(__mdiobus_c45_write);
|
||||
+
|
||||
+/**
|
||||
+ * __mdiobus_c45_modify_changed - Unlocked version of the mdiobus_modify function
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to modify
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ *
|
||||
+ * Read, modify, and if any change, write the register value back to the
|
||||
+ * device. Any error returns a negative number.
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context.
|
||||
+ */
|
||||
+static int __mdiobus_c45_modify_changed(struct mii_bus *bus, int addr,
|
||||
+ int devad, u32 regnum, u16 mask,
|
||||
+ u16 set)
|
||||
+{
|
||||
+ int new, ret;
|
||||
+
|
||||
+ ret = __mdiobus_c45_read(bus, addr, devad, regnum);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ new = (ret & ~mask) | set;
|
||||
+ if (new == ret)
|
||||
+ return 0;
|
||||
+
|
||||
+ ret = __mdiobus_c45_write(bus, addr, devad, regnum, new);
|
||||
+
|
||||
+ return ret < 0 ? ret : 1;
|
||||
+}
|
||||
+
|
||||
+/**
|
||||
* mdiobus_read_nested - Nested version of the mdiobus_read function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -879,6 +973,29 @@ int mdiobus_read(struct mii_bus *bus, in
|
||||
EXPORT_SYMBOL(mdiobus_read);
|
||||
|
||||
/**
|
||||
+ * mdiobus_c45_read - Convenience function for reading a given MII mgmt register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to read
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum)
|
||||
+{
|
||||
+ int retval;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ retval = __mdiobus_c45_read(bus, addr, devad, regnum);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return retval;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_c45_read);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_write_nested - Nested version of the mdiobus_write function
|
||||
* @bus: the mii_bus struct
|
||||
* @addr: the phy address
|
||||
@@ -928,6 +1045,31 @@ int mdiobus_write(struct mii_bus *bus, i
|
||||
EXPORT_SYMBOL(mdiobus_write);
|
||||
|
||||
/**
|
||||
+ * mdiobus_c45_write - Convenience function for writing a given MII mgmt register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to write
|
||||
+ * @val: value to write to @regnum
|
||||
+ *
|
||||
+ * NOTE: MUST NOT be called from interrupt context,
|
||||
+ * because the bus read/write functions may wait for an interrupt
|
||||
+ * to conclude the operation.
|
||||
+ */
|
||||
+int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 val)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_c45_write(bus, addr, devad, regnum, val);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL(mdiobus_c45_write);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_modify - Convenience function for modifying a given mdio device
|
||||
* register
|
||||
* @bus: the mii_bus struct
|
||||
@@ -949,6 +1091,30 @@ int mdiobus_modify(struct mii_bus *bus,
|
||||
EXPORT_SYMBOL_GPL(mdiobus_modify);
|
||||
|
||||
/**
|
||||
+ * mdiobus_c45_modify - Convenience function for modifying a given mdio device
|
||||
+ * register
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to write
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ */
|
||||
+int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 mask, u16 set)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum,
|
||||
+ mask, set);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err < 0 ? err : 0;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(mdiobus_c45_modify);
|
||||
+
|
||||
+/**
|
||||
* mdiobus_modify_changed - Convenience function for modifying a given mdio
|
||||
* device register and returning if it changed
|
||||
* @bus: the mii_bus struct
|
||||
@@ -971,6 +1137,29 @@ int mdiobus_modify_changed(struct mii_bu
|
||||
EXPORT_SYMBOL_GPL(mdiobus_modify_changed);
|
||||
|
||||
/**
|
||||
+ * mdiobus_c45_modify_changed - Convenience function for modifying a given mdio
|
||||
+ * device register and returning if it changed
|
||||
+ * @bus: the mii_bus struct
|
||||
+ * @addr: the phy address
|
||||
+ * @devad: device address to read
|
||||
+ * @regnum: register number to write
|
||||
+ * @mask: bit mask of bits to clear
|
||||
+ * @set: bit mask of bits to set
|
||||
+ */
|
||||
+int mdiobus_c45_modify_changed(struct mii_bus *bus, int devad, int addr,
|
||||
+ u32 regnum, u16 mask, u16 set)
|
||||
+{
|
||||
+ int err;
|
||||
+
|
||||
+ mutex_lock(&bus->mdio_lock);
|
||||
+ err = __mdiobus_c45_modify_changed(bus, addr, devad, regnum, mask, set);
|
||||
+ mutex_unlock(&bus->mdio_lock);
|
||||
+
|
||||
+ return err;
|
||||
+}
|
||||
+EXPORT_SYMBOL_GPL(mdiobus_c45_modify_changed);
|
||||
+
|
||||
+/**
|
||||
* mdio_bus_match - determine if given MDIO driver supports the given
|
||||
* MDIO device
|
||||
* @dev: target MDIO device
|
||||
--- a/include/linux/mdio.h
|
||||
+++ b/include/linux/mdio.h
|
||||
@@ -423,6 +423,17 @@ int mdiobus_modify(struct mii_bus *bus,
|
||||
u16 set);
|
||||
int mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
|
||||
u16 mask, u16 set);
|
||||
+int __mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
|
||||
+int mdiobus_c45_read(struct mii_bus *bus, int addr, int devad, u32 regnum);
|
||||
+int __mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 val);
|
||||
+int mdiobus_c45_write(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 val);
|
||||
+int mdiobus_c45_modify(struct mii_bus *bus, int addr, int devad, u32 regnum,
|
||||
+ u16 mask, u16 set);
|
||||
+
|
||||
+int mdiobus_c45_modify_changed(struct mii_bus *bus, int addr, int devad,
|
||||
+ u32 regnum, u16 mask, u16 set);
|
||||
|
||||
static inline int mdiodev_read(struct mdio_device *mdiodev, u32 regnum)
|
||||
{
|
||||
@@ -463,29 +474,19 @@ static inline u16 mdiobus_c45_devad(u32
|
||||
return FIELD_GET(MII_DEVADDR_C45_MASK, regnum);
|
||||
}
|
||||
|
||||
-static inline int __mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
|
||||
- u16 regnum)
|
||||
-{
|
||||
- return __mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
|
||||
-}
|
||||
-
|
||||
-static inline int __mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
|
||||
- u16 regnum, u16 val)
|
||||
-{
|
||||
- return __mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum),
|
||||
- val);
|
||||
-}
|
||||
-
|
||||
-static inline int mdiobus_c45_read(struct mii_bus *bus, int prtad, int devad,
|
||||
- u16 regnum)
|
||||
+static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad,
|
||||
+ u32 regnum, u16 mask, u16 set)
|
||||
{
|
||||
- return mdiobus_read(bus, prtad, mdiobus_c45_addr(devad, regnum));
|
||||
+ return mdiobus_c45_modify(mdiodev->bus, mdiodev->addr, devad, regnum,
|
||||
+ mask, set);
|
||||
}
|
||||
|
||||
-static inline int mdiobus_c45_write(struct mii_bus *bus, int prtad, int devad,
|
||||
- u16 regnum, u16 val)
|
||||
+static inline int mdiodev_c45_modify_changed(struct mdio_device *mdiodev,
|
||||
+ int devad, u32 regnum, u16 mask,
|
||||
+ u16 set)
|
||||
{
|
||||
- return mdiobus_write(bus, prtad, mdiobus_c45_addr(devad, regnum), val);
|
||||
+ return mdiobus_c45_modify_changed(mdiodev->bus, mdiodev->addr, devad,
|
||||
+ regnum, mask, set);
|
||||
}
|
||||
|
||||
int mdiobus_register_device(struct mdio_device *mdiodev);
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -364,6 +364,11 @@ struct mii_bus {
|
||||
int (*read)(struct mii_bus *bus, int addr, int regnum);
|
||||
/** @write: Perform a write transfer on the bus */
|
||||
int (*write)(struct mii_bus *bus, int addr, int regnum, u16 val);
|
||||
+ /** @read_c45: Perform a C45 read transfer on the bus */
|
||||
+ int (*read_c45)(struct mii_bus *bus, int addr, int devnum, int regnum);
|
||||
+ /** @write_c45: Perform a C45 write transfer on the bus */
|
||||
+ int (*write_c45)(struct mii_bus *bus, int addr, int devnum,
|
||||
+ int regnum, u16 val);
|
||||
/** @reset: Perform a reset of the bus */
|
||||
int (*reset)(struct mii_bus *bus);
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
From 9a0e95e34e9c0a713ddfd48c3a88a20d2bdfd514 Mon Sep 17 00:00:00 2001
|
||||
From: Gabor Juhos <j4g8y7@gmail.com>
|
||||
Date: Fri, 11 Aug 2023 13:10:07 +0200
|
||||
Subject: [PATCH] net: phy: Introduce PSGMII PHY interface mode
|
||||
|
||||
The PSGMII interface is similar to QSGMII. The main difference
|
||||
is that the PSGMII interface combines five SGMII lines into a
|
||||
single link while in QSGMII only four lines are combined.
|
||||
|
||||
Similarly to the QSGMII, this interface mode might also needs
|
||||
special handling within the MAC driver.
|
||||
|
||||
It is commonly used by Qualcomm with their QCA807x PHY series and
|
||||
modern WiSoC-s.
|
||||
|
||||
Add definitions for the PHY layer to allow to express this type
|
||||
of connection between the MAC and PHY.
|
||||
|
||||
Signed-off-by: Gabor Juhos <j4g8y7@gmail.com>
|
||||
Signed-off-by: Robert Marko <robert.marko@sartura.hr>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
Documentation/networking/phy.rst | 4 ++++
|
||||
drivers/net/phy/phy-core.c | 2 ++
|
||||
drivers/net/phy/phylink.c | 3 +++
|
||||
include/linux/phy.h | 4 ++++
|
||||
4 files changed, 13 insertions(+)
|
||||
|
||||
--- a/Documentation/networking/phy.rst
|
||||
+++ b/Documentation/networking/phy.rst
|
||||
@@ -323,6 +323,10 @@ Some of the interface modes are describe
|
||||
contrast with the 1000BASE-X phy mode used for Clause 38 and 39 PMDs, this
|
||||
interface mode has different autonegotiation and only supports full duplex.
|
||||
|
||||
+``PHY_INTERFACE_MODE_PSGMII``
|
||||
+ This is the Penta SGMII mode, it is similar to QSGMII but it combines 5
|
||||
+ SGMII lines into a single link compared to 4 on QSGMII.
|
||||
+
|
||||
Pause frames / flow control
|
||||
===========================
|
||||
|
||||
--- a/drivers/net/phy/phy-core.c
|
||||
+++ b/drivers/net/phy/phy-core.c
|
||||
@@ -140,6 +140,8 @@ int phy_interface_num_ports(phy_interfac
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
return 4;
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
+ return 5;
|
||||
case PHY_INTERFACE_MODE_MAX:
|
||||
WARN_ONCE(1, "PHY_INTERFACE_MODE_MAX isn't a valid interface mode");
|
||||
return 0;
|
||||
--- a/drivers/net/phy/phylink.c
|
||||
+++ b/drivers/net/phy/phylink.c
|
||||
@@ -187,6 +187,7 @@ static int phylink_interface_max_speed(p
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
@@ -448,6 +449,7 @@ unsigned long phylink_get_capabilities(p
|
||||
case PHY_INTERFACE_MODE_RGMII_RXID:
|
||||
case PHY_INTERFACE_MODE_RGMII_ID:
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
@@ -814,6 +816,7 @@ static int phylink_parse_mode(struct phy
|
||||
|
||||
switch (pl->link_config.interface) {
|
||||
case PHY_INTERFACE_MODE_SGMII:
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
case PHY_INTERFACE_MODE_QUSGMII:
|
||||
case PHY_INTERFACE_MODE_RGMII:
|
||||
--- a/include/linux/phy.h
|
||||
+++ b/include/linux/phy.h
|
||||
@@ -103,6 +103,7 @@ extern const int phy_10gbit_features_arr
|
||||
* @PHY_INTERFACE_MODE_XGMII: 10 gigabit media-independent interface
|
||||
* @PHY_INTERFACE_MODE_XLGMII:40 gigabit media-independent interface
|
||||
* @PHY_INTERFACE_MODE_MOCA: Multimedia over Coax
|
||||
+ * @PHY_INTERFACE_MODE_PSGMII: Penta SGMII
|
||||
* @PHY_INTERFACE_MODE_QSGMII: Quad SGMII
|
||||
* @PHY_INTERFACE_MODE_TRGMII: Turbo RGMII
|
||||
* @PHY_INTERFACE_MODE_100BASEX: 100 BaseX
|
||||
@@ -140,6 +141,7 @@ typedef enum {
|
||||
PHY_INTERFACE_MODE_XGMII,
|
||||
PHY_INTERFACE_MODE_XLGMII,
|
||||
PHY_INTERFACE_MODE_MOCA,
|
||||
+ PHY_INTERFACE_MODE_PSGMII,
|
||||
PHY_INTERFACE_MODE_QSGMII,
|
||||
PHY_INTERFACE_MODE_TRGMII,
|
||||
PHY_INTERFACE_MODE_100BASEX,
|
||||
@@ -247,6 +249,8 @@ static inline const char *phy_modes(phy_
|
||||
return "xlgmii";
|
||||
case PHY_INTERFACE_MODE_MOCA:
|
||||
return "moca";
|
||||
+ case PHY_INTERFACE_MODE_PSGMII:
|
||||
+ return "psgmii";
|
||||
case PHY_INTERFACE_MODE_QSGMII:
|
||||
return "qsgmii";
|
||||
case PHY_INTERFACE_MODE_TRGMII:
|
||||
@@ -1,32 +0,0 @@
|
||||
From a593a2fcfdfb92cfd0ffc54bc81b07e6bfaaaf46 Mon Sep 17 00:00:00 2001
|
||||
From: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
Date: Thu, 16 Mar 2023 14:08:26 +0200
|
||||
Subject: [PATCH] net: phy: at803x: Replace of_gpio.h with what indeed is used
|
||||
|
||||
of_gpio.h in this driver is solely used as a proxy to other headers.
|
||||
This is incorrect usage of the of_gpio.h. Replace it .h with what
|
||||
indeed is used in the code.
|
||||
|
||||
Signed-off-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 3 +--
|
||||
1 file changed, 1 insertion(+), 2 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -13,12 +13,11 @@
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/etherdevice.h>
|
||||
#include <linux/ethtool_netlink.h>
|
||||
-#include <linux/of_gpio.h>
|
||||
#include <linux/bitfield.h>
|
||||
-#include <linux/gpio/consumer.h>
|
||||
#include <linux/regulator/of_regulator.h>
|
||||
#include <linux/regulator/driver.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
+#include <linux/of.h>
|
||||
#include <linux/phylink.h>
|
||||
#include <linux/sfp.h>
|
||||
#include <dt-bindings/net/qca-ar803x.h>
|
||||
@@ -1,70 +0,0 @@
|
||||
From 8b8bc13d89a7d23d14b0e041c73f037c9db997b1 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:19 +0800
|
||||
Subject: [PATCH 1/6] net: phy: at803x: support qca8081
|
||||
genphy_c45_pma_read_abilities
|
||||
|
||||
qca8081 PHY supports to use genphy_c45_pma_read_abilities for
|
||||
getting the PHY features supported except for the autoneg ability
|
||||
|
||||
but autoneg ability exists in MDIO_STAT1 instead of MMD7.1, add it
|
||||
manually after calling genphy_c45_pma_read_abilities.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 28 ++++++++++++++++++----------
|
||||
1 file changed, 18 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -902,15 +902,6 @@ static int at803x_get_features(struct ph
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID) {
|
||||
- err = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_NG_EXTABLE);
|
||||
- if (err < 0)
|
||||
- return err;
|
||||
-
|
||||
- linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported,
|
||||
- err & MDIO_PMA_NG_EXTABLE_2_5GBT);
|
||||
- }
|
||||
-
|
||||
if (phydev->drv->phy_id != ATH8031_PHY_ID)
|
||||
return 0;
|
||||
|
||||
@@ -1996,6 +1987,23 @@ static int qca808x_cable_test_get_status
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca808x_get_features(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = genphy_c45_pma_read_abilities(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* The autoneg ability is not existed in bit3 of MMD7.1,
|
||||
+ * but it is supported by qca808x PHY, so we add it here
|
||||
+ * manually.
|
||||
+ */
|
||||
+ linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static struct phy_driver at803x_driver[] = {
|
||||
{
|
||||
/* Qualcomm Atheros AR8035 */
|
||||
@@ -2163,7 +2171,7 @@ static struct phy_driver at803x_driver[]
|
||||
.set_tunable = at803x_set_tunable,
|
||||
.set_wol = at803x_set_wol,
|
||||
.get_wol = at803x_get_wol,
|
||||
- .get_features = at803x_get_features,
|
||||
+ .get_features = qca808x_get_features,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.suspend = genphy_suspend,
|
||||
.resume = genphy_resume,
|
||||
@@ -1,73 +0,0 @@
|
||||
From f3db55ae860a82e1224a909072783ef850e5d228 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:20 +0800
|
||||
Subject: [PATCH 2/6] net: phy: at803x: merge qca8081 slave seed function
|
||||
|
||||
merge the seed enablement and seed value configuration into
|
||||
one function, since the random seed value is needed to be
|
||||
configured when the seed is enabled.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 29 +++++++++--------------------
|
||||
1 file changed, 9 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1730,24 +1730,19 @@ static int qca808x_phy_fast_retrain_conf
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int qca808x_phy_ms_random_seed_set(struct phy_device *phydev)
|
||||
-{
|
||||
- u16 seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
|
||||
-
|
||||
- return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
|
||||
- QCA808X_MASTER_SLAVE_SEED_CFG,
|
||||
- FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value));
|
||||
-}
|
||||
-
|
||||
static int qca808x_phy_ms_seed_enable(struct phy_device *phydev, bool enable)
|
||||
{
|
||||
- u16 seed_enable = 0;
|
||||
+ u16 seed_value;
|
||||
|
||||
- if (enable)
|
||||
- seed_enable = QCA808X_MASTER_SLAVE_SEED_ENABLE;
|
||||
+ if (!enable)
|
||||
+ return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
|
||||
+ QCA808X_MASTER_SLAVE_SEED_ENABLE, 0);
|
||||
|
||||
+ seed_value = prandom_u32_max(QCA808X_MASTER_SLAVE_SEED_RANGE);
|
||||
return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_LOCAL_SEED,
|
||||
- QCA808X_MASTER_SLAVE_SEED_ENABLE, seed_enable);
|
||||
+ QCA808X_MASTER_SLAVE_SEED_CFG | QCA808X_MASTER_SLAVE_SEED_ENABLE,
|
||||
+ FIELD_PREP(QCA808X_MASTER_SLAVE_SEED_CFG, seed_value) |
|
||||
+ QCA808X_MASTER_SLAVE_SEED_ENABLE);
|
||||
}
|
||||
|
||||
static int qca808x_config_init(struct phy_device *phydev)
|
||||
@@ -1771,12 +1766,7 @@ static int qca808x_config_init(struct ph
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- /* Configure lower ramdom seed to make phy linked as slave mode */
|
||||
- ret = qca808x_phy_ms_random_seed_set(phydev);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- /* Enable seed */
|
||||
+ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
|
||||
ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
if (ret)
|
||||
return ret;
|
||||
@@ -1821,7 +1811,6 @@ static int qca808x_read_status(struct ph
|
||||
if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
|
||||
qca808x_phy_ms_seed_enable(phydev, false);
|
||||
} else {
|
||||
- qca808x_phy_ms_random_seed_set(phydev);
|
||||
qca808x_phy_ms_seed_enable(phydev, true);
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
From 7cc3209558002d95c0d45a1276ba4f5f741eec42 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:21 +0800
|
||||
Subject: [PATCH 3/6] net: phy: at803x: enable qca8081 slave seed conditionally
|
||||
|
||||
qca8081 is the single port PHY, the slave prefer mode is used
|
||||
by default.
|
||||
|
||||
if the phy master perfer mode is configured, the slave seed
|
||||
configuration should not be enabled, since the slave seed
|
||||
enablement is for making PHY linked as slave mode easily.
|
||||
|
||||
disable slave seed if the master mode is preferred.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 25 ++++++++++++++++++++-----
|
||||
1 file changed, 20 insertions(+), 5 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1745,6 +1745,12 @@ static int qca808x_phy_ms_seed_enable(st
|
||||
QCA808X_MASTER_SLAVE_SEED_ENABLE);
|
||||
}
|
||||
|
||||
+static bool qca808x_is_prefer_master(struct phy_device *phydev)
|
||||
+{
|
||||
+ return (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_FORCE) ||
|
||||
+ (phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
|
||||
+}
|
||||
+
|
||||
static int qca808x_config_init(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -1766,11 +1772,17 @@ static int qca808x_config_init(struct ph
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
|
||||
- ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
- if (ret)
|
||||
+ ret = genphy_read_master_slave(phydev);
|
||||
+ if (ret < 0)
|
||||
return ret;
|
||||
|
||||
+ if (!qca808x_is_prefer_master(phydev)) {
|
||||
+ /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
|
||||
+ ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
/* Configure adc threshold as 100mv for the link 10M */
|
||||
return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
|
||||
QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
|
||||
@@ -1802,13 +1814,16 @@ static int qca808x_read_status(struct ph
|
||||
phydev->interface = PHY_INTERFACE_MODE_SGMII;
|
||||
} else {
|
||||
/* generate seed as a lower random value to make PHY linked as SLAVE easily,
|
||||
- * except for master/slave configuration fault detected.
|
||||
+ * except for master/slave configuration fault detected or the master mode
|
||||
+ * preferred.
|
||||
+ *
|
||||
* the reason for not putting this code into the function link_change_notify is
|
||||
* the corner case where the link partner is also the qca8081 PHY and the seed
|
||||
* value is configured as the same value, the link can't be up and no link change
|
||||
* occurs.
|
||||
*/
|
||||
- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR) {
|
||||
+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
|
||||
+ qca808x_is_prefer_master(phydev)) {
|
||||
qca808x_phy_ms_seed_enable(phydev, false);
|
||||
} else {
|
||||
qca808x_phy_ms_seed_enable(phydev, true);
|
||||
@@ -1,48 +0,0 @@
|
||||
From fea7cfb83d1a2782e39cd101dd44ed2548539de5 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:22 +0800
|
||||
Subject: [PATCH 4/6] net: phy: at803x: support qca8081 1G chip type
|
||||
|
||||
The qca8081 1G chip version does not support 2.5 capability, which
|
||||
is distinguished from qca8081 2.5G chip according to the bit0 of
|
||||
register mmd7.0x901d, the 1G version chip also has the same PHY ID
|
||||
as the normal qca8081 2.5G chip.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 15 +++++++++++++++
|
||||
1 file changed, 15 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -272,6 +272,10 @@
|
||||
#define QCA808X_CDT_STATUS_STAT_OPEN 2
|
||||
#define QCA808X_CDT_STATUS_STAT_SHORT 3
|
||||
|
||||
+/* QCA808X 1G chip type */
|
||||
+#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
|
||||
+#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
|
||||
+
|
||||
MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
|
||||
MODULE_AUTHOR("Matus Ujhelyi");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -2005,6 +2009,17 @@ static int qca808x_get_features(struct p
|
||||
*/
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, phydev->supported);
|
||||
|
||||
+ /* As for the qca8081 1G version chip, the 2500baseT ability is also
|
||||
+ * existed in the bit0 of MMD1.21, we need to remove it manually if
|
||||
+ * it is the qca8081 1G chip according to the bit0 of MMD7.0x901d.
|
||||
+ */
|
||||
+ ret = phy_read_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_CHIP_TYPE);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (QCA808X_PHY_CHIP_TYPE_1G & ret)
|
||||
+ linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
From df9401ff3e6eeaa42bfb06761967f1b71f5afce7 Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:23 +0800
|
||||
Subject: [PATCH 5/6] net: phy: at803x: remove qca8081 1G fast retrain and
|
||||
slave seed config
|
||||
|
||||
The fast retrain and slave seed configs are only applicable when the 2.5G
|
||||
ability is supported.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 50 +++++++++++++++++++++++++---------------
|
||||
1 file changed, 32 insertions(+), 18 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1755,6 +1755,11 @@ static bool qca808x_is_prefer_master(str
|
||||
(phydev->master_slave_get == MASTER_SLAVE_CFG_MASTER_PREFERRED);
|
||||
}
|
||||
|
||||
+static bool qca808x_has_fast_retrain_or_slave_seed(struct phy_device *phydev)
|
||||
+{
|
||||
+ return linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->supported);
|
||||
+}
|
||||
+
|
||||
static int qca808x_config_init(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -1771,20 +1776,24 @@ static int qca808x_config_init(struct ph
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- /* Config the fast retrain for the link 2500M */
|
||||
- ret = qca808x_phy_fast_retrain_config(phydev);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = genphy_read_master_slave(phydev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- if (!qca808x_is_prefer_master(phydev)) {
|
||||
- /* Enable seed and configure lower ramdom seed to make phy linked as slave mode */
|
||||
- ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
|
||||
+ /* Config the fast retrain for the link 2500M */
|
||||
+ ret = qca808x_phy_fast_retrain_config(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
+
|
||||
+ ret = genphy_read_master_slave(phydev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (!qca808x_is_prefer_master(phydev)) {
|
||||
+ /* Enable seed and configure lower ramdom seed to make phy
|
||||
+ * linked as slave mode.
|
||||
+ */
|
||||
+ ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+ }
|
||||
}
|
||||
|
||||
/* Configure adc threshold as 100mv for the link 10M */
|
||||
@@ -1826,11 +1835,13 @@ static int qca808x_read_status(struct ph
|
||||
* value is configured as the same value, the link can't be up and no link change
|
||||
* occurs.
|
||||
*/
|
||||
- if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
|
||||
- qca808x_is_prefer_master(phydev)) {
|
||||
- qca808x_phy_ms_seed_enable(phydev, false);
|
||||
- } else {
|
||||
- qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
|
||||
+ if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
|
||||
+ qca808x_is_prefer_master(phydev)) {
|
||||
+ qca808x_phy_ms_seed_enable(phydev, false);
|
||||
+ } else {
|
||||
+ qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1845,7 +1856,10 @@ static int qca808x_soft_reset(struct phy
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
- return qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+ if (qca808x_has_fast_retrain_or_slave_seed(phydev))
|
||||
+ ret = qca808x_phy_ms_seed_enable(phydev, true);
|
||||
+
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static bool qca808x_cdt_fault_length_valid(int cdt_code)
|
||||
@@ -1,55 +0,0 @@
|
||||
From 723970affdd8766fa0d91cd34bf2ffc861538b5f Mon Sep 17 00:00:00 2001
|
||||
From: Luo Jie <quic_luoj@quicinc.com>
|
||||
Date: Sun, 16 Jul 2023 16:49:24 +0800
|
||||
Subject: [PATCH 6/6] net: phy: at803x: add qca8081 fifo reset on the link
|
||||
changed
|
||||
|
||||
The qca8081 sgmii fifo needs to be reset on link down and
|
||||
released on the link up in case of any abnormal issue
|
||||
such as the packet blocked on the PHY.
|
||||
|
||||
Signed-off-by: Luo Jie <quic_luoj@quicinc.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Reviewed-by: Russell King (Oracle) <rmk+kernel@armlinux.org.uk>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 14 ++++++++++++++
|
||||
1 file changed, 14 insertions(+)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -276,6 +276,9 @@
|
||||
#define QCA808X_PHY_MMD7_CHIP_TYPE 0x901d
|
||||
#define QCA808X_PHY_CHIP_TYPE_1G BIT(0)
|
||||
|
||||
+#define QCA8081_PHY_SERDES_MMD1_FIFO_CTRL 0x9072
|
||||
+#define QCA8081_PHY_FIFO_RSTN BIT(11)
|
||||
+
|
||||
MODULE_DESCRIPTION("Qualcomm Atheros AR803x and QCA808X PHY driver");
|
||||
MODULE_AUTHOR("Matus Ujhelyi");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -2037,6 +2040,16 @@ static int qca808x_get_features(struct p
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static void qca808x_link_change_notify(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* Assert interface sgmii fifo on link down, deassert it on link up,
|
||||
+ * the interface device address is always phy address added by 1.
|
||||
+ */
|
||||
+ mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
|
||||
+ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
|
||||
+ QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
|
||||
+}
|
||||
+
|
||||
static struct phy_driver at803x_driver[] = {
|
||||
{
|
||||
/* Qualcomm Atheros AR8035 */
|
||||
@@ -2213,6 +2226,7 @@ static struct phy_driver at803x_driver[]
|
||||
.soft_reset = qca808x_soft_reset,
|
||||
.cable_test_start = qca808x_cable_test_start,
|
||||
.cable_test_get_status = qca808x_cable_test_get_status,
|
||||
+ .link_change_notify = qca808x_link_change_notify,
|
||||
}, };
|
||||
|
||||
module_phy_driver(at803x_driver);
|
||||
@@ -1,69 +0,0 @@
|
||||
From 6a3b8c573b5a152a6aa7a0b54c5e18b84c6ba6f5 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:49 +0100
|
||||
Subject: [PATCH 02/13] net: phy: at803x: move disable WOL to specific at8031
|
||||
probe
|
||||
|
||||
Move the WOL disable call to specific at8031 probe to make at803x_probe
|
||||
more generic and drop extra check for PHY ID.
|
||||
|
||||
Keep the same previous behaviour by first calling at803x_probe and then
|
||||
disabling WOL.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 27 +++++++++++++++++----------
|
||||
1 file changed, 17 insertions(+), 10 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -886,15 +886,6 @@ static int at803x_probe(struct phy_devic
|
||||
priv->is_fiber = true;
|
||||
break;
|
||||
}
|
||||
-
|
||||
- /* Disable WoL in 1588 register which is enabled
|
||||
- * by default
|
||||
- */
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
- AT803X_PHY_MMD3_WOL_CTRL,
|
||||
- AT803X_WOL_EN, 0);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -1591,6 +1582,22 @@ static int at803x_cable_test_start(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int at8031_probe(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = at803x_probe(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* Disable WoL in 1588 register which is enabled
|
||||
+ * by default
|
||||
+ */
|
||||
+ return phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
+ AT803X_PHY_MMD3_WOL_CTRL,
|
||||
+ AT803X_WOL_EN, 0);
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2092,7 +2099,7 @@ static struct phy_driver at803x_driver[]
|
||||
PHY_ID_MATCH_EXACT(ATH8031_PHY_ID),
|
||||
.name = "Qualcomm Atheros AR8031/AR8033",
|
||||
.flags = PHY_POLL_CABLE_TEST,
|
||||
- .probe = at803x_probe,
|
||||
+ .probe = at8031_probe,
|
||||
.config_init = at803x_config_init,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
@@ -1,129 +0,0 @@
|
||||
From 07b1ad83b9ed6db1735ba10baf67b7a565ac0cef Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:50 +0100
|
||||
Subject: [PATCH 03/13] net: phy: at803x: raname hw_stats functions to qca83xx
|
||||
specific name
|
||||
|
||||
The function and the struct related to hw_stats were specific to qca83xx
|
||||
PHY but were called following the convention in the driver of calling
|
||||
everything with at803x prefix.
|
||||
|
||||
To better organize the code, rename these function a more specific name
|
||||
to better describe that they are specific to 83xx PHY family.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 44 ++++++++++++++++++++--------------------
|
||||
1 file changed, 22 insertions(+), 22 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -295,7 +295,7 @@ struct at803x_hw_stat {
|
||||
enum stat_access_type access_type;
|
||||
};
|
||||
|
||||
-static struct at803x_hw_stat at803x_hw_stats[] = {
|
||||
+static struct at803x_hw_stat qca83xx_hw_stats[] = {
|
||||
{ "phy_idle_errors", 0xa, GENMASK(7, 0), PHY},
|
||||
{ "phy_receive_errors", 0x15, GENMASK(15, 0), PHY},
|
||||
{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
|
||||
@@ -311,7 +311,7 @@ struct at803x_priv {
|
||||
bool is_1000basex;
|
||||
struct regulator_dev *vddio_rdev;
|
||||
struct regulator_dev *vddh_rdev;
|
||||
- u64 stats[ARRAY_SIZE(at803x_hw_stats)];
|
||||
+ u64 stats[ARRAY_SIZE(qca83xx_hw_stats)];
|
||||
};
|
||||
|
||||
struct at803x_context {
|
||||
@@ -529,24 +529,24 @@ static void at803x_get_wol(struct phy_de
|
||||
wol->wolopts |= WAKE_MAGIC;
|
||||
}
|
||||
|
||||
-static int at803x_get_sset_count(struct phy_device *phydev)
|
||||
+static int qca83xx_get_sset_count(struct phy_device *phydev)
|
||||
{
|
||||
- return ARRAY_SIZE(at803x_hw_stats);
|
||||
+ return ARRAY_SIZE(qca83xx_hw_stats);
|
||||
}
|
||||
|
||||
-static void at803x_get_strings(struct phy_device *phydev, u8 *data)
|
||||
+static void qca83xx_get_strings(struct phy_device *phydev, u8 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++) {
|
||||
+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++) {
|
||||
strscpy(data + i * ETH_GSTRING_LEN,
|
||||
- at803x_hw_stats[i].string, ETH_GSTRING_LEN);
|
||||
+ qca83xx_hw_stats[i].string, ETH_GSTRING_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
-static u64 at803x_get_stat(struct phy_device *phydev, int i)
|
||||
+static u64 qca83xx_get_stat(struct phy_device *phydev, int i)
|
||||
{
|
||||
- struct at803x_hw_stat stat = at803x_hw_stats[i];
|
||||
+ struct at803x_hw_stat stat = qca83xx_hw_stats[i];
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
int val;
|
||||
u64 ret;
|
||||
@@ -567,13 +567,13 @@ static u64 at803x_get_stat(struct phy_de
|
||||
return ret;
|
||||
}
|
||||
|
||||
-static void at803x_get_stats(struct phy_device *phydev,
|
||||
- struct ethtool_stats *stats, u64 *data)
|
||||
+static void qca83xx_get_stats(struct phy_device *phydev,
|
||||
+ struct ethtool_stats *stats, u64 *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
- for (i = 0; i < ARRAY_SIZE(at803x_hw_stats); i++)
|
||||
- data[i] = at803x_get_stat(phydev, i);
|
||||
+ for (i = 0; i < ARRAY_SIZE(qca83xx_hw_stats); i++)
|
||||
+ data[i] = qca83xx_get_stat(phydev, i);
|
||||
}
|
||||
|
||||
static int at803x_suspend(struct phy_device *phydev)
|
||||
@@ -2175,9 +2175,9 @@ static struct phy_driver at803x_driver[]
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
.config_init = qca83xx_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = at803x_get_sset_count,
|
||||
- .get_strings = at803x_get_strings,
|
||||
- .get_stats = at803x_get_stats,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
.suspend = qca83xx_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
@@ -2191,9 +2191,9 @@ static struct phy_driver at803x_driver[]
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
.config_init = qca83xx_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = at803x_get_sset_count,
|
||||
- .get_strings = at803x_get_strings,
|
||||
- .get_stats = at803x_get_stats,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
.suspend = qca83xx_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
@@ -2207,9 +2207,9 @@ static struct phy_driver at803x_driver[]
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
.config_init = qca83xx_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
- .get_sset_count = at803x_get_sset_count,
|
||||
- .get_strings = at803x_get_strings,
|
||||
- .get_stats = at803x_get_stats,
|
||||
+ .get_sset_count = qca83xx_get_sset_count,
|
||||
+ .get_strings = qca83xx_get_strings,
|
||||
+ .get_stats = qca83xx_get_stats,
|
||||
.suspend = qca83xx_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
@@ -1,155 +0,0 @@
|
||||
From d43cff3f82336c0bd965ea552232d9f4ddac71a6 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:51 +0100
|
||||
Subject: [PATCH 04/13] net: phy: at803x: move qca83xx specific check in
|
||||
dedicated functions
|
||||
|
||||
Rework qca83xx specific check to dedicated function to tidy things up
|
||||
and drop useless phy_id check.
|
||||
|
||||
Also drop an useless link_change_notify for QCA8337 as it did nothing an
|
||||
returned early.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 68 ++++++++++++++++++++++------------------
|
||||
1 file changed, 37 insertions(+), 31 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1623,27 +1623,26 @@ static int qca83xx_config_init(struct ph
|
||||
break;
|
||||
}
|
||||
|
||||
+ /* Following original QCA sourcecode set port to prefer master */
|
||||
+ phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int qca8327_config_init(struct phy_device *phydev)
|
||||
+{
|
||||
/* QCA8327 require DAC amplitude adjustment for 100m set to +6%.
|
||||
* Disable on init and enable only with 100m speed following
|
||||
* qca original source code.
|
||||
*/
|
||||
- if (phydev->drv->phy_id == QCA8327_A_PHY_ID ||
|
||||
- phydev->drv->phy_id == QCA8327_B_PHY_ID)
|
||||
- at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
- QCA8327_DEBUG_MANU_CTRL_EN, 0);
|
||||
+ at803x_debug_reg_mask(phydev, AT803X_DEBUG_ANALOG_TEST_CTRL,
|
||||
+ QCA8327_DEBUG_MANU_CTRL_EN, 0);
|
||||
|
||||
- /* Following original QCA sourcecode set port to prefer master */
|
||||
- phy_set_bits(phydev, MII_CTRL1000, CTL1000_PREFER_MASTER);
|
||||
-
|
||||
- return 0;
|
||||
+ return qca83xx_config_init(phydev);
|
||||
}
|
||||
|
||||
static void qca83xx_link_change_notify(struct phy_device *phydev)
|
||||
{
|
||||
- /* QCA8337 doesn't require DAC Amplitude adjustement */
|
||||
- if (phydev->drv->phy_id == QCA8337_PHY_ID)
|
||||
- return;
|
||||
-
|
||||
/* Set DAC Amplitude adjustment to +6% for 100m on link running */
|
||||
if (phydev->state == PHY_RUNNING) {
|
||||
if (phydev->speed == SPEED_100)
|
||||
@@ -1686,19 +1685,6 @@ static int qca83xx_resume(struct phy_dev
|
||||
|
||||
static int qca83xx_suspend(struct phy_device *phydev)
|
||||
{
|
||||
- u16 mask = 0;
|
||||
-
|
||||
- /* Only QCA8337 support actual suspend.
|
||||
- * QCA8327 cause port unreliability when phy suspend
|
||||
- * is set.
|
||||
- */
|
||||
- if (phydev->drv->phy_id == QCA8337_PHY_ID) {
|
||||
- genphy_suspend(phydev);
|
||||
- } else {
|
||||
- mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
|
||||
- phy_modify(phydev, MII_BMCR, mask, 0);
|
||||
- }
|
||||
-
|
||||
at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_GREEN,
|
||||
AT803X_DEBUG_GATE_CLK_IN1000, 0);
|
||||
|
||||
@@ -1709,6 +1695,27 @@ static int qca83xx_suspend(struct phy_de
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca8337_suspend(struct phy_device *phydev)
|
||||
+{
|
||||
+ /* Only QCA8337 support actual suspend. */
|
||||
+ genphy_suspend(phydev);
|
||||
+
|
||||
+ return qca83xx_suspend(phydev);
|
||||
+}
|
||||
+
|
||||
+static int qca8327_suspend(struct phy_device *phydev)
|
||||
+{
|
||||
+ u16 mask = 0;
|
||||
+
|
||||
+ /* QCA8327 cause port unreliability when phy suspend
|
||||
+ * is set.
|
||||
+ */
|
||||
+ mask |= ~(BMCR_SPEED1000 | BMCR_FULLDPLX);
|
||||
+ phy_modify(phydev, MII_BMCR, mask, 0);
|
||||
+
|
||||
+ return qca83xx_suspend(phydev);
|
||||
+}
|
||||
+
|
||||
static int qca808x_phy_fast_retrain_config(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -2170,7 +2177,6 @@ static struct phy_driver at803x_driver[]
|
||||
.phy_id_mask = QCA8K_PHY_ID_MASK,
|
||||
.name = "Qualcomm Atheros 8337 internal PHY",
|
||||
/* PHY_GBIT_FEATURES */
|
||||
- .link_change_notify = qca83xx_link_change_notify,
|
||||
.probe = at803x_probe,
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
.config_init = qca83xx_config_init,
|
||||
@@ -2178,7 +2184,7 @@ static struct phy_driver at803x_driver[]
|
||||
.get_sset_count = qca83xx_get_sset_count,
|
||||
.get_strings = qca83xx_get_strings,
|
||||
.get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca83xx_suspend,
|
||||
+ .suspend = qca8337_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
/* QCA8327-A from switch QCA8327-AL1A */
|
||||
@@ -2189,12 +2195,12 @@ static struct phy_driver at803x_driver[]
|
||||
.link_change_notify = qca83xx_link_change_notify,
|
||||
.probe = at803x_probe,
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
- .config_init = qca83xx_config_init,
|
||||
+ .config_init = qca8327_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.get_sset_count = qca83xx_get_sset_count,
|
||||
.get_strings = qca83xx_get_strings,
|
||||
.get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca83xx_suspend,
|
||||
+ .suspend = qca8327_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
/* QCA8327-B from switch QCA8327-BL1A */
|
||||
@@ -2205,12 +2211,12 @@ static struct phy_driver at803x_driver[]
|
||||
.link_change_notify = qca83xx_link_change_notify,
|
||||
.probe = at803x_probe,
|
||||
.flags = PHY_IS_INTERNAL,
|
||||
- .config_init = qca83xx_config_init,
|
||||
+ .config_init = qca8327_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.get_sset_count = qca83xx_get_sset_count,
|
||||
.get_strings = qca83xx_get_strings,
|
||||
.get_stats = qca83xx_get_stats,
|
||||
- .suspend = qca83xx_suspend,
|
||||
+ .suspend = qca8327_suspend,
|
||||
.resume = qca83xx_resume,
|
||||
}, {
|
||||
/* Qualcomm QCA8081 */
|
||||
@@ -1,94 +0,0 @@
|
||||
From 900eef75cc5018e149c52fe305c9c3fe424c52a7 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:52 +0100
|
||||
Subject: [PATCH 05/13] net: phy: at803x: move specific DT option for at8031 to
|
||||
specific probe
|
||||
|
||||
Move specific DT options for at8031 to specific probe to tidy things up
|
||||
and make at803x_parse_dt more generic.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 55 ++++++++++++++++++++++------------------
|
||||
1 file changed, 31 insertions(+), 24 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -825,30 +825,6 @@ static int at803x_parse_dt(struct phy_de
|
||||
}
|
||||
}
|
||||
|
||||
- /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
|
||||
- * options.
|
||||
- */
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- if (of_property_read_bool(node, "qca,keep-pll-enabled"))
|
||||
- priv->flags |= AT803X_KEEP_PLL_ENABLED;
|
||||
-
|
||||
- ret = at8031_register_regulators(phydev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
-
|
||||
- ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
|
||||
- "vddio");
|
||||
- if (ret) {
|
||||
- phydev_err(phydev, "failed to get VDDIO regulator\n");
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
- /* Only AR8031/8033 support 1000Base-X for SFP modules */
|
||||
- ret = phy_sfp_probe(phydev, &at803x_sfp_ops);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1582,6 +1558,30 @@ static int at803x_cable_test_start(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int at8031_parse_dt(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct device_node *node = phydev->mdio.dev.of_node;
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int ret;
|
||||
+
|
||||
+ if (of_property_read_bool(node, "qca,keep-pll-enabled"))
|
||||
+ priv->flags |= AT803X_KEEP_PLL_ENABLED;
|
||||
+
|
||||
+ ret = at8031_register_regulators(phydev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = devm_regulator_get_enable_optional(&phydev->mdio.dev,
|
||||
+ "vddio");
|
||||
+ if (ret) {
|
||||
+ phydev_err(phydev, "failed to get VDDIO regulator\n");
|
||||
+ return ret;
|
||||
+ }
|
||||
+
|
||||
+ /* Only AR8031/8033 support 1000Base-X for SFP modules */
|
||||
+ return phy_sfp_probe(phydev, &at803x_sfp_ops);
|
||||
+}
|
||||
+
|
||||
static int at8031_probe(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
@@ -1590,6 +1590,13 @@ static int at8031_probe(struct phy_devic
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ /* Only supported on AR8031/AR8033, the AR8030/AR8035 use strapping
|
||||
+ * options.
|
||||
+ */
|
||||
+ ret = at8031_parse_dt(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
/* Disable WoL in 1588 register which is enabled
|
||||
* by default
|
||||
*/
|
||||
@@ -1,78 +0,0 @@
|
||||
From 25d2ba94005fac18fe68878cddff59a67e115554 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:53 +0100
|
||||
Subject: [PATCH 06/13] net: phy: at803x: move specific at8031 probe mode check
|
||||
to dedicated probe
|
||||
|
||||
Move specific at8031 probe mode check to dedicated probe to make
|
||||
at803x_probe more generic and keep code tidy.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 39 +++++++++++++++++++--------------------
|
||||
1 file changed, 19 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -844,26 +844,6 @@ static int at803x_probe(struct phy_devic
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- int ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
|
||||
- int mode_cfg;
|
||||
-
|
||||
- if (ccr < 0)
|
||||
- return ccr;
|
||||
- mode_cfg = ccr & AT803X_MODE_CFG_MASK;
|
||||
-
|
||||
- switch (mode_cfg) {
|
||||
- case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
|
||||
- case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
|
||||
- priv->is_1000basex = true;
|
||||
- fallthrough;
|
||||
- case AT803X_MODE_CFG_FX100_RGMII_50OHM:
|
||||
- case AT803X_MODE_CFG_FX100_RGMII_75OHM:
|
||||
- priv->is_fiber = true;
|
||||
- break;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1584,6 +1564,9 @@ static int at8031_parse_dt(struct phy_de
|
||||
|
||||
static int at8031_probe(struct phy_device *phydev)
|
||||
{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int mode_cfg;
|
||||
+ int ccr;
|
||||
int ret;
|
||||
|
||||
ret = at803x_probe(phydev);
|
||||
@@ -1597,6 +1580,22 @@ static int at8031_probe(struct phy_devic
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
+ ccr = phy_read(phydev, AT803X_REG_CHIP_CONFIG);
|
||||
+ if (ccr < 0)
|
||||
+ return ccr;
|
||||
+ mode_cfg = ccr & AT803X_MODE_CFG_MASK;
|
||||
+
|
||||
+ switch (mode_cfg) {
|
||||
+ case AT803X_MODE_CFG_BX1000_RGMII_50OHM:
|
||||
+ case AT803X_MODE_CFG_BX1000_RGMII_75OHM:
|
||||
+ priv->is_1000basex = true;
|
||||
+ fallthrough;
|
||||
+ case AT803X_MODE_CFG_FX100_RGMII_50OHM:
|
||||
+ case AT803X_MODE_CFG_FX100_RGMII_75OHM:
|
||||
+ priv->is_fiber = true;
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
/* Disable WoL in 1588 register which is enabled
|
||||
* by default
|
||||
*/
|
||||
@@ -1,86 +0,0 @@
|
||||
From 3ae3bc426eaf57ca8f53d75777d9a5ef779bc7b7 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:54 +0100
|
||||
Subject: [PATCH 07/13] net: phy: at803x: move specific at8031 config_init to
|
||||
dedicated function
|
||||
|
||||
Move specific at8031 config_init to dedicated function to make
|
||||
at803x_config_init more generic and tidy things up.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 45 ++++++++++++++++++++++------------------
|
||||
1 file changed, 25 insertions(+), 20 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -951,27 +951,8 @@ static int at803x_hibernation_mode_confi
|
||||
|
||||
static int at803x_config_init(struct phy_device *phydev)
|
||||
{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- /* Some bootloaders leave the fiber page selected.
|
||||
- * Switch to the appropriate page (fiber or copper), as otherwise we
|
||||
- * read the PHY capabilities from the wrong page.
|
||||
- */
|
||||
- phy_lock_mdio_bus(phydev);
|
||||
- ret = at803x_write_page(phydev,
|
||||
- priv->is_fiber ? AT803X_PAGE_FIBER :
|
||||
- AT803X_PAGE_COPPER);
|
||||
- phy_unlock_mdio_bus(phydev);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
-
|
||||
- ret = at8031_pll_config(phydev);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
- }
|
||||
-
|
||||
/* The RX and TX delay default is:
|
||||
* after HW reset: RX delay enabled and TX delay disabled
|
||||
* after SW reset: RX delay enabled, while TX delay retains the
|
||||
@@ -1604,6 +1585,30 @@ static int at8031_probe(struct phy_devic
|
||||
AT803X_WOL_EN, 0);
|
||||
}
|
||||
|
||||
+static int at8031_config_init(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int ret;
|
||||
+
|
||||
+ /* Some bootloaders leave the fiber page selected.
|
||||
+ * Switch to the appropriate page (fiber or copper), as otherwise we
|
||||
+ * read the PHY capabilities from the wrong page.
|
||||
+ */
|
||||
+ phy_lock_mdio_bus(phydev);
|
||||
+ ret = at803x_write_page(phydev,
|
||||
+ priv->is_fiber ? AT803X_PAGE_FIBER :
|
||||
+ AT803X_PAGE_COPPER);
|
||||
+ phy_unlock_mdio_bus(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ ret = at8031_pll_config(phydev);
|
||||
+ if (ret < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return at803x_config_init(phydev);
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2113,7 +2118,7 @@ static struct phy_driver at803x_driver[]
|
||||
.name = "Qualcomm Atheros AR8031/AR8033",
|
||||
.flags = PHY_POLL_CABLE_TEST,
|
||||
.probe = at8031_probe,
|
||||
- .config_init = at803x_config_init,
|
||||
+ .config_init = at8031_config_init,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.set_wol = at803x_set_wol,
|
||||
@@ -1,92 +0,0 @@
|
||||
From 27b89c9dc1b0393090d68d651b82f30ad2696baa Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:55 +0100
|
||||
Subject: [PATCH 08/13] net: phy: at803x: move specific at8031 WOL bits to
|
||||
dedicated function
|
||||
|
||||
Move specific at8031 WOL enable/disable to dedicated function to make
|
||||
at803x_set_wol more generic.
|
||||
|
||||
This is needed in preparation for PHY driver split as qca8081 share the
|
||||
same function to toggle WOL settings.
|
||||
|
||||
In this new implementation WOL module in at8031 is enabled after the
|
||||
generic interrupt is setup. This should not cause any problem as the
|
||||
WOL_INT has a separate implementation and only relay on MAC bits.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 42 ++++++++++++++++++++++++----------------
|
||||
1 file changed, 25 insertions(+), 17 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -466,27 +466,11 @@ static int at803x_set_wol(struct phy_dev
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, offsets[i],
|
||||
mac[(i * 2) + 1] | (mac[(i * 2)] << 8));
|
||||
|
||||
- /* Enable WOL function for 1588 */
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
- AT803X_PHY_MMD3_WOL_CTRL,
|
||||
- 0, AT803X_WOL_EN);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- }
|
||||
/* Enable WOL interrupt */
|
||||
ret = phy_modify(phydev, AT803X_INTR_ENABLE, 0, AT803X_INTR_ENABLE_WOL);
|
||||
if (ret)
|
||||
return ret;
|
||||
} else {
|
||||
- /* Disable WoL function for 1588 */
|
||||
- if (phydev->drv->phy_id == ATH8031_PHY_ID) {
|
||||
- ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
- AT803X_PHY_MMD3_WOL_CTRL,
|
||||
- AT803X_WOL_EN, 0);
|
||||
- if (ret)
|
||||
- return ret;
|
||||
- }
|
||||
/* Disable WOL interrupt */
|
||||
ret = phy_modify(phydev, AT803X_INTR_ENABLE, AT803X_INTR_ENABLE_WOL, 0);
|
||||
if (ret)
|
||||
@@ -1609,6 +1593,30 @@ static int at8031_config_init(struct phy
|
||||
return at803x_config_init(phydev);
|
||||
}
|
||||
|
||||
+static int at8031_set_wol(struct phy_device *phydev,
|
||||
+ struct ethtool_wolinfo *wol)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ /* First setup MAC address and enable WOL interrupt */
|
||||
+ ret = at803x_set_wol(phydev, wol);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ if (wol->wolopts & WAKE_MAGIC)
|
||||
+ /* Enable WOL function for 1588 */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
+ AT803X_PHY_MMD3_WOL_CTRL,
|
||||
+ 0, AT803X_WOL_EN);
|
||||
+ else
|
||||
+ /* Disable WoL function for 1588 */
|
||||
+ ret = phy_modify_mmd(phydev, MDIO_MMD_PCS,
|
||||
+ AT803X_PHY_MMD3_WOL_CTRL,
|
||||
+ AT803X_WOL_EN, 0);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2121,7 +2129,7 @@ static struct phy_driver at803x_driver[]
|
||||
.config_init = at8031_config_init,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
- .set_wol = at803x_set_wol,
|
||||
+ .set_wol = at8031_set_wol,
|
||||
.get_wol = at803x_get_wol,
|
||||
.suspend = at803x_suspend,
|
||||
.resume = at803x_resume,
|
||||
@@ -1,78 +0,0 @@
|
||||
From 30dd62191d3dd97c08f7f9dc9ce77ffab457e4fb Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:56 +0100
|
||||
Subject: [PATCH 09/13] net: phy: at803x: move specific at8031 config_intr to
|
||||
dedicated function
|
||||
|
||||
Move specific at8031 config_intr bits to dedicated function to make
|
||||
at803x_config_initr more generic.
|
||||
|
||||
This is needed in preparation for PHY driver split as qca8081 share the
|
||||
same function to setup interrupts.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 30 ++++++++++++++++++++++++------
|
||||
1 file changed, 24 insertions(+), 6 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -990,7 +990,6 @@ static int at803x_ack_interrupt(struct p
|
||||
|
||||
static int at803x_config_intr(struct phy_device *phydev)
|
||||
{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
int err;
|
||||
int value;
|
||||
|
||||
@@ -1007,10 +1006,6 @@ static int at803x_config_intr(struct phy
|
||||
value |= AT803X_INTR_ENABLE_DUPLEX_CHANGED;
|
||||
value |= AT803X_INTR_ENABLE_LINK_FAIL;
|
||||
value |= AT803X_INTR_ENABLE_LINK_SUCCESS;
|
||||
- if (priv->is_fiber) {
|
||||
- value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
|
||||
- value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
|
||||
- }
|
||||
|
||||
err = phy_write(phydev, AT803X_INTR_ENABLE, value);
|
||||
} else {
|
||||
@@ -1617,6 +1612,29 @@ static int at8031_set_wol(struct phy_dev
|
||||
return ret;
|
||||
}
|
||||
|
||||
+static int at8031_config_intr(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int err, value = 0;
|
||||
+
|
||||
+ if (phydev->interrupts == PHY_INTERRUPT_ENABLED &&
|
||||
+ priv->is_fiber) {
|
||||
+ /* Clear any pending interrupts */
|
||||
+ err = at803x_ack_interrupt(phydev);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+
|
||||
+ value |= AT803X_INTR_ENABLE_LINK_FAIL_BX;
|
||||
+ value |= AT803X_INTR_ENABLE_LINK_SUCCESS_BX;
|
||||
+
|
||||
+ err = phy_set_bits(phydev, AT803X_INTR_ENABLE, value);
|
||||
+ if (err)
|
||||
+ return err;
|
||||
+ }
|
||||
+
|
||||
+ return at803x_config_intr(phydev);
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2137,7 +2155,7 @@ static struct phy_driver at803x_driver[]
|
||||
.write_page = at803x_write_page,
|
||||
.get_features = at803x_get_features,
|
||||
.read_status = at803x_read_status,
|
||||
- .config_intr = at803x_config_intr,
|
||||
+ .config_intr = at8031_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.get_tunable = at803x_get_tunable,
|
||||
.set_tunable = at803x_set_tunable,
|
||||
@@ -1,78 +0,0 @@
|
||||
From a5ab9d8e7ae0da8328ac1637a9755311508dc8ab Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:57 +0100
|
||||
Subject: [PATCH 10/13] net: phy: at803x: make at8031 related DT functions name
|
||||
more specific
|
||||
|
||||
Rename at8031 related DT function name to a more specific name
|
||||
referencing they are only related to at8031 and not to the generic
|
||||
at803x PHY family.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 16 ++++++++--------
|
||||
1 file changed, 8 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -583,7 +583,7 @@ static int at803x_resume(struct phy_devi
|
||||
return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
|
||||
}
|
||||
|
||||
-static int at803x_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
+static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
unsigned int selector)
|
||||
{
|
||||
struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
@@ -596,7 +596,7 @@ static int at803x_rgmii_reg_set_voltage_
|
||||
AT803X_DEBUG_RGMII_1V8, 0);
|
||||
}
|
||||
|
||||
-static int at803x_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
+static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
{
|
||||
struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
int val;
|
||||
@@ -610,8 +610,8 @@ static int at803x_rgmii_reg_get_voltage_
|
||||
|
||||
static const struct regulator_ops vddio_regulator_ops = {
|
||||
.list_voltage = regulator_list_voltage_table,
|
||||
- .set_voltage_sel = at803x_rgmii_reg_set_voltage_sel,
|
||||
- .get_voltage_sel = at803x_rgmii_reg_get_voltage_sel,
|
||||
+ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
|
||||
+ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
|
||||
};
|
||||
|
||||
static const unsigned int vddio_voltage_table[] = {
|
||||
@@ -666,7 +666,7 @@ static int at8031_register_regulators(st
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int at803x_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
+static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
{
|
||||
struct phy_device *phydev = upstream;
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
|
||||
@@ -710,10 +710,10 @@ static int at803x_sfp_insert(void *upstr
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static const struct sfp_upstream_ops at803x_sfp_ops = {
|
||||
+static const struct sfp_upstream_ops at8031_sfp_ops = {
|
||||
.attach = phy_sfp_attach,
|
||||
.detach = phy_sfp_detach,
|
||||
- .module_insert = at803x_sfp_insert,
|
||||
+ .module_insert = at8031_sfp_insert,
|
||||
};
|
||||
|
||||
static int at803x_parse_dt(struct phy_device *phydev)
|
||||
@@ -1519,7 +1519,7 @@ static int at8031_parse_dt(struct phy_de
|
||||
}
|
||||
|
||||
/* Only AR8031/8033 support 1000Base-X for SFP modules */
|
||||
- return phy_sfp_probe(phydev, &at803x_sfp_ops);
|
||||
+ return phy_sfp_probe(phydev, &at8031_sfp_ops);
|
||||
}
|
||||
|
||||
static int at8031_probe(struct phy_device *phydev)
|
||||
@@ -1,297 +0,0 @@
|
||||
From f932a6dc8bae0dae9645b5b1b4c65aed8a8acb2a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:58 +0100
|
||||
Subject: [PATCH 11/13] net: phy: at803x: move at8031 functions in dedicated
|
||||
section
|
||||
|
||||
Move at8031 functions in dedicated section with dedicated at8031
|
||||
parse_dt and probe.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 266 +++++++++++++++++++--------------------
|
||||
1 file changed, 133 insertions(+), 133 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -583,139 +583,6 @@ static int at803x_resume(struct phy_devi
|
||||
return phy_modify(phydev, MII_BMCR, BMCR_PDOWN | BMCR_ISOLATE, 0);
|
||||
}
|
||||
|
||||
-static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
- unsigned int selector)
|
||||
-{
|
||||
- struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
-
|
||||
- if (selector)
|
||||
- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
|
||||
- 0, AT803X_DEBUG_RGMII_1V8);
|
||||
- else
|
||||
- return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
|
||||
- AT803X_DEBUG_RGMII_1V8, 0);
|
||||
-}
|
||||
-
|
||||
-static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
-{
|
||||
- struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
- int val;
|
||||
-
|
||||
- val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
|
||||
- if (val < 0)
|
||||
- return val;
|
||||
-
|
||||
- return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
|
||||
-}
|
||||
-
|
||||
-static const struct regulator_ops vddio_regulator_ops = {
|
||||
- .list_voltage = regulator_list_voltage_table,
|
||||
- .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
|
||||
- .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
|
||||
-};
|
||||
-
|
||||
-static const unsigned int vddio_voltage_table[] = {
|
||||
- 1500000,
|
||||
- 1800000,
|
||||
-};
|
||||
-
|
||||
-static const struct regulator_desc vddio_desc = {
|
||||
- .name = "vddio",
|
||||
- .of_match = of_match_ptr("vddio-regulator"),
|
||||
- .n_voltages = ARRAY_SIZE(vddio_voltage_table),
|
||||
- .volt_table = vddio_voltage_table,
|
||||
- .ops = &vddio_regulator_ops,
|
||||
- .type = REGULATOR_VOLTAGE,
|
||||
- .owner = THIS_MODULE,
|
||||
-};
|
||||
-
|
||||
-static const struct regulator_ops vddh_regulator_ops = {
|
||||
-};
|
||||
-
|
||||
-static const struct regulator_desc vddh_desc = {
|
||||
- .name = "vddh",
|
||||
- .of_match = of_match_ptr("vddh-regulator"),
|
||||
- .n_voltages = 1,
|
||||
- .fixed_uV = 2500000,
|
||||
- .ops = &vddh_regulator_ops,
|
||||
- .type = REGULATOR_VOLTAGE,
|
||||
- .owner = THIS_MODULE,
|
||||
-};
|
||||
-
|
||||
-static int at8031_register_regulators(struct phy_device *phydev)
|
||||
-{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
- struct device *dev = &phydev->mdio.dev;
|
||||
- struct regulator_config config = { };
|
||||
-
|
||||
- config.dev = dev;
|
||||
- config.driver_data = phydev;
|
||||
-
|
||||
- priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
|
||||
- if (IS_ERR(priv->vddio_rdev)) {
|
||||
- phydev_err(phydev, "failed to register VDDIO regulator\n");
|
||||
- return PTR_ERR(priv->vddio_rdev);
|
||||
- }
|
||||
-
|
||||
- priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
|
||||
- if (IS_ERR(priv->vddh_rdev)) {
|
||||
- phydev_err(phydev, "failed to register VDDH regulator\n");
|
||||
- return PTR_ERR(priv->vddh_rdev);
|
||||
- }
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
-{
|
||||
- struct phy_device *phydev = upstream;
|
||||
- __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
|
||||
- __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
|
||||
- DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
- phy_interface_t iface;
|
||||
-
|
||||
- linkmode_zero(phy_support);
|
||||
- phylink_set(phy_support, 1000baseX_Full);
|
||||
- phylink_set(phy_support, 1000baseT_Full);
|
||||
- phylink_set(phy_support, Autoneg);
|
||||
- phylink_set(phy_support, Pause);
|
||||
- phylink_set(phy_support, Asym_Pause);
|
||||
-
|
||||
- linkmode_zero(sfp_support);
|
||||
- sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
|
||||
- /* Some modules support 10G modes as well as others we support.
|
||||
- * Mask out non-supported modes so the correct interface is picked.
|
||||
- */
|
||||
- linkmode_and(sfp_support, phy_support, sfp_support);
|
||||
-
|
||||
- if (linkmode_empty(sfp_support)) {
|
||||
- dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
|
||||
- return -EINVAL;
|
||||
- }
|
||||
-
|
||||
- iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
|
||||
-
|
||||
- /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
|
||||
- * interface for use with SFP modules.
|
||||
- * However, some copper modules detected as having a preferred SGMII
|
||||
- * interface do default to and function in 1000Base-X mode, so just
|
||||
- * print a warning and allow such modules, as they may have some chance
|
||||
- * of working.
|
||||
- */
|
||||
- if (iface == PHY_INTERFACE_MODE_SGMII)
|
||||
- dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
|
||||
- else if (iface != PHY_INTERFACE_MODE_1000BASEX)
|
||||
- return -EINVAL;
|
||||
-
|
||||
- return 0;
|
||||
-}
|
||||
-
|
||||
-static const struct sfp_upstream_ops at8031_sfp_ops = {
|
||||
- .attach = phy_sfp_attach,
|
||||
- .detach = phy_sfp_detach,
|
||||
- .module_insert = at8031_sfp_insert,
|
||||
-};
|
||||
-
|
||||
static int at803x_parse_dt(struct phy_device *phydev)
|
||||
{
|
||||
struct device_node *node = phydev->mdio.dev.of_node;
|
||||
@@ -1498,6 +1365,139 @@ static int at803x_cable_test_start(struc
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int at8031_rgmii_reg_set_voltage_sel(struct regulator_dev *rdev,
|
||||
+ unsigned int selector)
|
||||
+{
|
||||
+ struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
+
|
||||
+ if (selector)
|
||||
+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
|
||||
+ 0, AT803X_DEBUG_RGMII_1V8);
|
||||
+ else
|
||||
+ return at803x_debug_reg_mask(phydev, AT803X_DEBUG_REG_1F,
|
||||
+ AT803X_DEBUG_RGMII_1V8, 0);
|
||||
+}
|
||||
+
|
||||
+static int at8031_rgmii_reg_get_voltage_sel(struct regulator_dev *rdev)
|
||||
+{
|
||||
+ struct phy_device *phydev = rdev_get_drvdata(rdev);
|
||||
+ int val;
|
||||
+
|
||||
+ val = at803x_debug_reg_read(phydev, AT803X_DEBUG_REG_1F);
|
||||
+ if (val < 0)
|
||||
+ return val;
|
||||
+
|
||||
+ return (val & AT803X_DEBUG_RGMII_1V8) ? 1 : 0;
|
||||
+}
|
||||
+
|
||||
+static const struct regulator_ops vddio_regulator_ops = {
|
||||
+ .list_voltage = regulator_list_voltage_table,
|
||||
+ .set_voltage_sel = at8031_rgmii_reg_set_voltage_sel,
|
||||
+ .get_voltage_sel = at8031_rgmii_reg_get_voltage_sel,
|
||||
+};
|
||||
+
|
||||
+static const unsigned int vddio_voltage_table[] = {
|
||||
+ 1500000,
|
||||
+ 1800000,
|
||||
+};
|
||||
+
|
||||
+static const struct regulator_desc vddio_desc = {
|
||||
+ .name = "vddio",
|
||||
+ .of_match = of_match_ptr("vddio-regulator"),
|
||||
+ .n_voltages = ARRAY_SIZE(vddio_voltage_table),
|
||||
+ .volt_table = vddio_voltage_table,
|
||||
+ .ops = &vddio_regulator_ops,
|
||||
+ .type = REGULATOR_VOLTAGE,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static const struct regulator_ops vddh_regulator_ops = {
|
||||
+};
|
||||
+
|
||||
+static const struct regulator_desc vddh_desc = {
|
||||
+ .name = "vddh",
|
||||
+ .of_match = of_match_ptr("vddh-regulator"),
|
||||
+ .n_voltages = 1,
|
||||
+ .fixed_uV = 2500000,
|
||||
+ .ops = &vddh_regulator_ops,
|
||||
+ .type = REGULATOR_VOLTAGE,
|
||||
+ .owner = THIS_MODULE,
|
||||
+};
|
||||
+
|
||||
+static int at8031_register_regulators(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ struct device *dev = &phydev->mdio.dev;
|
||||
+ struct regulator_config config = { };
|
||||
+
|
||||
+ config.dev = dev;
|
||||
+ config.driver_data = phydev;
|
||||
+
|
||||
+ priv->vddio_rdev = devm_regulator_register(dev, &vddio_desc, &config);
|
||||
+ if (IS_ERR(priv->vddio_rdev)) {
|
||||
+ phydev_err(phydev, "failed to register VDDIO regulator\n");
|
||||
+ return PTR_ERR(priv->vddio_rdev);
|
||||
+ }
|
||||
+
|
||||
+ priv->vddh_rdev = devm_regulator_register(dev, &vddh_desc, &config);
|
||||
+ if (IS_ERR(priv->vddh_rdev)) {
|
||||
+ phydev_err(phydev, "failed to register VDDH regulator\n");
|
||||
+ return PTR_ERR(priv->vddh_rdev);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static int at8031_sfp_insert(void *upstream, const struct sfp_eeprom_id *id)
|
||||
+{
|
||||
+ struct phy_device *phydev = upstream;
|
||||
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_support);
|
||||
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(sfp_support);
|
||||
+ DECLARE_PHY_INTERFACE_MASK(interfaces);
|
||||
+ phy_interface_t iface;
|
||||
+
|
||||
+ linkmode_zero(phy_support);
|
||||
+ phylink_set(phy_support, 1000baseX_Full);
|
||||
+ phylink_set(phy_support, 1000baseT_Full);
|
||||
+ phylink_set(phy_support, Autoneg);
|
||||
+ phylink_set(phy_support, Pause);
|
||||
+ phylink_set(phy_support, Asym_Pause);
|
||||
+
|
||||
+ linkmode_zero(sfp_support);
|
||||
+ sfp_parse_support(phydev->sfp_bus, id, sfp_support, interfaces);
|
||||
+ /* Some modules support 10G modes as well as others we support.
|
||||
+ * Mask out non-supported modes so the correct interface is picked.
|
||||
+ */
|
||||
+ linkmode_and(sfp_support, phy_support, sfp_support);
|
||||
+
|
||||
+ if (linkmode_empty(sfp_support)) {
|
||||
+ dev_err(&phydev->mdio.dev, "incompatible SFP module inserted\n");
|
||||
+ return -EINVAL;
|
||||
+ }
|
||||
+
|
||||
+ iface = sfp_select_interface(phydev->sfp_bus, sfp_support);
|
||||
+
|
||||
+ /* Only 1000Base-X is supported by AR8031/8033 as the downstream SerDes
|
||||
+ * interface for use with SFP modules.
|
||||
+ * However, some copper modules detected as having a preferred SGMII
|
||||
+ * interface do default to and function in 1000Base-X mode, so just
|
||||
+ * print a warning and allow such modules, as they may have some chance
|
||||
+ * of working.
|
||||
+ */
|
||||
+ if (iface == PHY_INTERFACE_MODE_SGMII)
|
||||
+ dev_warn(&phydev->mdio.dev, "module may not function if 1000Base-X not supported\n");
|
||||
+ else if (iface != PHY_INTERFACE_MODE_1000BASEX)
|
||||
+ return -EINVAL;
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+static const struct sfp_upstream_ops at8031_sfp_ops = {
|
||||
+ .attach = phy_sfp_attach,
|
||||
+ .detach = phy_sfp_detach,
|
||||
+ .module_insert = at8031_sfp_insert,
|
||||
+};
|
||||
+
|
||||
static int at8031_parse_dt(struct phy_device *phydev)
|
||||
{
|
||||
struct device_node *node = phydev->mdio.dev.of_node;
|
||||
@@ -1,114 +0,0 @@
|
||||
From 21a2802a8365cfa82cc02187c1f95136d85592ad Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:51:59 +0100
|
||||
Subject: [PATCH 12/13] net: phy: at803x: move at8035 specific DT parse to
|
||||
dedicated probe
|
||||
|
||||
Move at8035 specific DT parse for clock out frequency to dedicated probe
|
||||
to make at803x probe function more generic.
|
||||
|
||||
This is to tidy code and no behaviour change are intended.
|
||||
|
||||
Detection logic is changed, we check if the clk 25m mask is set and if
|
||||
it's not zero, we assume the qca,clk-out-frequency property is set.
|
||||
|
||||
The property is checked in the generic at803x_parse_dt called by
|
||||
at803x_probe.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 60 +++++++++++++++++++++++++++-------------
|
||||
1 file changed, 41 insertions(+), 19 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -638,23 +638,6 @@ static int at803x_parse_dt(struct phy_de
|
||||
|
||||
priv->clk_25m_reg |= FIELD_PREP(AT803X_CLK_OUT_MASK, sel);
|
||||
priv->clk_25m_mask |= AT803X_CLK_OUT_MASK;
|
||||
-
|
||||
- /* Fixup for the AR8030/AR8035. This chip has another mask and
|
||||
- * doesn't support the DSP reference. Eg. the lowest bit of the
|
||||
- * mask. The upper two bits select the same frequencies. Mask
|
||||
- * the lowest bit here.
|
||||
- *
|
||||
- * Warning:
|
||||
- * There was no datasheet for the AR8030 available so this is
|
||||
- * just a guess. But the AR8035 is listed as pin compatible
|
||||
- * to the AR8030 so there might be a good chance it works on
|
||||
- * the AR8030 too.
|
||||
- */
|
||||
- if (phydev->drv->phy_id == ATH8030_PHY_ID ||
|
||||
- phydev->drv->phy_id == ATH8035_PHY_ID) {
|
||||
- priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
|
||||
- priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
|
||||
- }
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(node, "qca,clk-out-strength", &strength);
|
||||
@@ -1635,6 +1618,45 @@ static int at8031_config_intr(struct phy
|
||||
return at803x_config_intr(phydev);
|
||||
}
|
||||
|
||||
+static int at8035_parse_dt(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+
|
||||
+ /* Mask is set by the generic at803x_parse_dt
|
||||
+ * if property is set. Assume property is set
|
||||
+ * with the mask not zero.
|
||||
+ */
|
||||
+ if (priv->clk_25m_mask) {
|
||||
+ /* Fixup for the AR8030/AR8035. This chip has another mask and
|
||||
+ * doesn't support the DSP reference. Eg. the lowest bit of the
|
||||
+ * mask. The upper two bits select the same frequencies. Mask
|
||||
+ * the lowest bit here.
|
||||
+ *
|
||||
+ * Warning:
|
||||
+ * There was no datasheet for the AR8030 available so this is
|
||||
+ * just a guess. But the AR8035 is listed as pin compatible
|
||||
+ * to the AR8030 so there might be a good chance it works on
|
||||
+ * the AR8030 too.
|
||||
+ */
|
||||
+ priv->clk_25m_reg &= AT8035_CLK_OUT_MASK;
|
||||
+ priv->clk_25m_mask &= AT8035_CLK_OUT_MASK;
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* AR8030 and AR8035 shared the same special mask for clk_25m */
|
||||
+static int at8035_probe(struct phy_device *phydev)
|
||||
+{
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = at803x_probe(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ return at8035_parse_dt(phydev);
|
||||
+}
|
||||
+
|
||||
static int qca83xx_config_init(struct phy_device *phydev)
|
||||
{
|
||||
u8 switch_revision;
|
||||
@@ -2107,7 +2129,7 @@ static struct phy_driver at803x_driver[]
|
||||
PHY_ID_MATCH_EXACT(ATH8035_PHY_ID),
|
||||
.name = "Qualcomm Atheros AR8035",
|
||||
.flags = PHY_POLL_CABLE_TEST,
|
||||
- .probe = at803x_probe,
|
||||
+ .probe = at8035_probe,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
.config_init = at803x_config_init,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
@@ -2128,7 +2150,7 @@ static struct phy_driver at803x_driver[]
|
||||
.phy_id = ATH8030_PHY_ID,
|
||||
.name = "Qualcomm Atheros AR8030",
|
||||
.phy_id_mask = AT8030_PHY_ID_MASK,
|
||||
- .probe = at803x_probe,
|
||||
+ .probe = at8035_probe,
|
||||
.config_init = at803x_config_init,
|
||||
.link_change_notify = at803x_link_change_notify,
|
||||
.set_wol = at803x_set_wol,
|
||||
@@ -1,219 +0,0 @@
|
||||
From ef9df47b449e32e06501a11272809be49019bdb6 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Fri, 8 Dec 2023 15:52:00 +0100
|
||||
Subject: [PATCH 13/13] net: phy: at803x: drop specific PHY ID check from cable
|
||||
test functions
|
||||
|
||||
Drop specific PHY ID check for cable test functions for at803x. This is
|
||||
done to make functions more generic. While at it better describe what
|
||||
the functions does by using more symbolic function names.
|
||||
|
||||
PHYs that requires to set additional reg are moved to specific function
|
||||
calling the more generic one.
|
||||
|
||||
cdt_start and cdt_wait_for_completion are changed to take an additional
|
||||
arg to pass specific values specific to the PHY.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 95 +++++++++++++++++++++-------------------
|
||||
1 file changed, 50 insertions(+), 45 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1222,31 +1222,16 @@ static int at803x_cdt_fault_length(u16 s
|
||||
return (dt * 824) / 10;
|
||||
}
|
||||
|
||||
-static int at803x_cdt_start(struct phy_device *phydev, int pair)
|
||||
+static int at803x_cdt_start(struct phy_device *phydev,
|
||||
+ u32 cdt_start)
|
||||
{
|
||||
- u16 cdt;
|
||||
-
|
||||
- /* qca8081 takes the different bit 15 to enable CDT test */
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID)
|
||||
- cdt = QCA808X_CDT_ENABLE_TEST |
|
||||
- QCA808X_CDT_LENGTH_UNIT |
|
||||
- QCA808X_CDT_INTER_CHECK_DIS;
|
||||
- else
|
||||
- cdt = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
|
||||
- AT803X_CDT_ENABLE_TEST;
|
||||
-
|
||||
- return phy_write(phydev, AT803X_CDT, cdt);
|
||||
+ return phy_write(phydev, AT803X_CDT, cdt_start);
|
||||
}
|
||||
|
||||
-static int at803x_cdt_wait_for_completion(struct phy_device *phydev)
|
||||
+static int at803x_cdt_wait_for_completion(struct phy_device *phydev,
|
||||
+ u32 cdt_en)
|
||||
{
|
||||
int val, ret;
|
||||
- u16 cdt_en;
|
||||
-
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID)
|
||||
- cdt_en = QCA808X_CDT_ENABLE_TEST;
|
||||
- else
|
||||
- cdt_en = AT803X_CDT_ENABLE_TEST;
|
||||
|
||||
/* One test run takes about 25ms */
|
||||
ret = phy_read_poll_timeout(phydev, AT803X_CDT, val,
|
||||
@@ -1266,11 +1251,13 @@ static int at803x_cable_test_one_pair(st
|
||||
};
|
||||
int ret, val;
|
||||
|
||||
- ret = at803x_cdt_start(phydev, pair);
|
||||
+ val = FIELD_PREP(AT803X_CDT_MDI_PAIR_MASK, pair) |
|
||||
+ AT803X_CDT_ENABLE_TEST;
|
||||
+ ret = at803x_cdt_start(phydev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = at803x_cdt_wait_for_completion(phydev);
|
||||
+ ret = at803x_cdt_wait_for_completion(phydev, AT803X_CDT_ENABLE_TEST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1292,19 +1279,11 @@ static int at803x_cable_test_one_pair(st
|
||||
}
|
||||
|
||||
static int at803x_cable_test_get_status(struct phy_device *phydev,
|
||||
- bool *finished)
|
||||
+ bool *finished, unsigned long pair_mask)
|
||||
{
|
||||
- unsigned long pair_mask;
|
||||
int retries = 20;
|
||||
int pair, ret;
|
||||
|
||||
- if (phydev->phy_id == ATH9331_PHY_ID ||
|
||||
- phydev->phy_id == ATH8032_PHY_ID ||
|
||||
- phydev->phy_id == QCA9561_PHY_ID)
|
||||
- pair_mask = 0x3;
|
||||
- else
|
||||
- pair_mask = 0xf;
|
||||
-
|
||||
*finished = false;
|
||||
|
||||
/* According to the datasheet the CDT can be performed when
|
||||
@@ -1331,7 +1310,7 @@ static int at803x_cable_test_get_status(
|
||||
return 0;
|
||||
}
|
||||
|
||||
-static int at803x_cable_test_start(struct phy_device *phydev)
|
||||
+static void at803x_cable_test_autoneg(struct phy_device *phydev)
|
||||
{
|
||||
/* Enable auto-negotiation, but advertise no capabilities, no link
|
||||
* will be established. A restart of the auto-negotiation is not
|
||||
@@ -1339,11 +1318,11 @@ static int at803x_cable_test_start(struc
|
||||
*/
|
||||
phy_write(phydev, MII_BMCR, BMCR_ANENABLE);
|
||||
phy_write(phydev, MII_ADVERTISE, ADVERTISE_CSMA);
|
||||
- if (phydev->phy_id != ATH9331_PHY_ID &&
|
||||
- phydev->phy_id != ATH8032_PHY_ID &&
|
||||
- phydev->phy_id != QCA9561_PHY_ID)
|
||||
- phy_write(phydev, MII_CTRL1000, 0);
|
||||
+}
|
||||
|
||||
+static int at803x_cable_test_start(struct phy_device *phydev)
|
||||
+{
|
||||
+ at803x_cable_test_autoneg(phydev);
|
||||
/* we do all the (time consuming) work later */
|
||||
return 0;
|
||||
}
|
||||
@@ -1618,6 +1597,29 @@ static int at8031_config_intr(struct phy
|
||||
return at803x_config_intr(phydev);
|
||||
}
|
||||
|
||||
+/* AR8031 and AR8035 share the same cable test get status reg */
|
||||
+static int at8031_cable_test_get_status(struct phy_device *phydev,
|
||||
+ bool *finished)
|
||||
+{
|
||||
+ return at803x_cable_test_get_status(phydev, finished, 0xf);
|
||||
+}
|
||||
+
|
||||
+/* AR8031 and AR8035 share the same cable test start logic */
|
||||
+static int at8031_cable_test_start(struct phy_device *phydev)
|
||||
+{
|
||||
+ at803x_cable_test_autoneg(phydev);
|
||||
+ phy_write(phydev, MII_CTRL1000, 0);
|
||||
+ /* we do all the (time consuming) work later */
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+/* AR8032, AR9331 and QCA9561 share the same cable test get status reg */
|
||||
+static int at8032_cable_test_get_status(struct phy_device *phydev,
|
||||
+ bool *finished)
|
||||
+{
|
||||
+ return at803x_cable_test_get_status(phydev, finished, 0x3);
|
||||
+}
|
||||
+
|
||||
static int at8035_parse_dt(struct phy_device *phydev)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
@@ -2041,11 +2043,14 @@ static int qca808x_cable_test_get_status
|
||||
|
||||
*finished = false;
|
||||
|
||||
- ret = at803x_cdt_start(phydev, 0);
|
||||
+ val = QCA808X_CDT_ENABLE_TEST |
|
||||
+ QCA808X_CDT_LENGTH_UNIT |
|
||||
+ QCA808X_CDT_INTER_CHECK_DIS;
|
||||
+ ret = at803x_cdt_start(phydev, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = at803x_cdt_wait_for_completion(phydev);
|
||||
+ ret = at803x_cdt_wait_for_completion(phydev, QCA808X_CDT_ENABLE_TEST);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -2143,8 +2148,8 @@ static struct phy_driver at803x_driver[]
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.get_tunable = at803x_get_tunable,
|
||||
.set_tunable = at803x_set_tunable,
|
||||
- .cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_start = at8031_cable_test_start,
|
||||
+ .cable_test_get_status = at8031_cable_test_get_status,
|
||||
}, {
|
||||
/* Qualcomm Atheros AR8030 */
|
||||
.phy_id = ATH8030_PHY_ID,
|
||||
@@ -2181,8 +2186,8 @@ static struct phy_driver at803x_driver[]
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.get_tunable = at803x_get_tunable,
|
||||
.set_tunable = at803x_set_tunable,
|
||||
- .cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_start = at8031_cable_test_start,
|
||||
+ .cable_test_get_status = at8031_cable_test_get_status,
|
||||
}, {
|
||||
/* Qualcomm Atheros AR8032 */
|
||||
PHY_ID_MATCH_EXACT(ATH8032_PHY_ID),
|
||||
@@ -2197,7 +2202,7 @@ static struct phy_driver at803x_driver[]
|
||||
.config_intr = at803x_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_get_status = at8032_cable_test_get_status,
|
||||
}, {
|
||||
/* ATHEROS AR9331 */
|
||||
PHY_ID_MATCH_EXACT(ATH9331_PHY_ID),
|
||||
@@ -2210,7 +2215,7 @@ static struct phy_driver at803x_driver[]
|
||||
.config_intr = at803x_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_get_status = at8032_cable_test_get_status,
|
||||
.read_status = at803x_read_status,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
@@ -2226,7 +2231,7 @@ static struct phy_driver at803x_driver[]
|
||||
.config_intr = at803x_config_intr,
|
||||
.handle_interrupt = at803x_handle_interrupt,
|
||||
.cable_test_start = at803x_cable_test_start,
|
||||
- .cable_test_get_status = at803x_cable_test_get_status,
|
||||
+ .cable_test_get_status = at8032_cable_test_get_status,
|
||||
.read_status = at803x_read_status,
|
||||
.soft_reset = genphy_soft_reset,
|
||||
.config_aneg = at803x_config_aneg,
|
||||
@@ -1,116 +0,0 @@
|
||||
From 8e732f1c6f2dc5e18f766d0f1b11df9db2dd044a Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 14 Dec 2023 01:44:31 +0100
|
||||
Subject: [PATCH 1/2] net: phy: at803x: move specific qca808x config_aneg to
|
||||
dedicated function
|
||||
|
||||
Move specific qca808x config_aneg to dedicated function to permit easier
|
||||
split of qca808x portion from at803x driver.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 66 ++++++++++++++++++++++++----------------
|
||||
1 file changed, 40 insertions(+), 26 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1045,9 +1045,8 @@ static int at803x_config_mdix(struct phy
|
||||
FIELD_PREP(AT803X_SFC_MDI_CROSSOVER_MODE_M, val));
|
||||
}
|
||||
|
||||
-static int at803x_config_aneg(struct phy_device *phydev)
|
||||
+static int at803x_prepare_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
- struct at803x_priv *priv = phydev->priv;
|
||||
int ret;
|
||||
|
||||
ret = at803x_config_mdix(phydev, phydev->mdix_ctrl);
|
||||
@@ -1064,33 +1063,22 @@ static int at803x_config_aneg(struct phy
|
||||
return ret;
|
||||
}
|
||||
|
||||
- if (priv->is_1000basex)
|
||||
- return genphy_c37_config_aneg(phydev);
|
||||
-
|
||||
- /* Do not restart auto-negotiation by setting ret to 0 defautly,
|
||||
- * when calling __genphy_config_aneg later.
|
||||
- */
|
||||
- ret = 0;
|
||||
-
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID) {
|
||||
- int phy_ctrl = 0;
|
||||
+ return 0;
|
||||
+}
|
||||
|
||||
- /* The reg MII_BMCR also needs to be configured for force mode, the
|
||||
- * genphy_config_aneg is also needed.
|
||||
- */
|
||||
- if (phydev->autoneg == AUTONEG_DISABLE)
|
||||
- genphy_c45_pma_setup_forced(phydev);
|
||||
+static int at803x_config_aneg(struct phy_device *phydev)
|
||||
+{
|
||||
+ struct at803x_priv *priv = phydev->priv;
|
||||
+ int ret;
|
||||
|
||||
- if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising))
|
||||
- phy_ctrl = MDIO_AN_10GBT_CTRL_ADV2_5G;
|
||||
+ ret = at803x_prepare_config_aneg(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
|
||||
- ret = phy_modify_mmd_changed(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_CTRL,
|
||||
- MDIO_AN_10GBT_CTRL_ADV2_5G, phy_ctrl);
|
||||
- if (ret < 0)
|
||||
- return ret;
|
||||
- }
|
||||
+ if (priv->is_1000basex)
|
||||
+ return genphy_c37_config_aneg(phydev);
|
||||
|
||||
- return __genphy_config_aneg(phydev, ret);
|
||||
+ return genphy_config_aneg(phydev);
|
||||
}
|
||||
|
||||
static int at803x_get_downshift(struct phy_device *phydev, u8 *d)
|
||||
@@ -2118,6 +2106,32 @@ static int qca808x_get_features(struct p
|
||||
return 0;
|
||||
}
|
||||
|
||||
+static int qca808x_config_aneg(struct phy_device *phydev)
|
||||
+{
|
||||
+ int phy_ctrl = 0;
|
||||
+ int ret;
|
||||
+
|
||||
+ ret = at803x_prepare_config_aneg(phydev);
|
||||
+ if (ret)
|
||||
+ return ret;
|
||||
+
|
||||
+ /* The reg MII_BMCR also needs to be configured for force mode, the
|
||||
+ * genphy_config_aneg is also needed.
|
||||
+ */
|
||||
+ if (phydev->autoneg == AUTONEG_DISABLE)
|
||||
+ genphy_c45_pma_setup_forced(phydev);
|
||||
+
|
||||
+ 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 < 0)
|
||||
+ return ret;
|
||||
+
|
||||
+ return __genphy_config_aneg(phydev, ret);
|
||||
+}
|
||||
+
|
||||
static void qca808x_link_change_notify(struct phy_device *phydev)
|
||||
{
|
||||
/* Assert interface sgmii fifo on link down, deassert it on link up,
|
||||
@@ -2295,7 +2309,7 @@ static struct phy_driver at803x_driver[]
|
||||
.set_wol = at803x_set_wol,
|
||||
.get_wol = at803x_get_wol,
|
||||
.get_features = qca808x_get_features,
|
||||
- .config_aneg = at803x_config_aneg,
|
||||
+ .config_aneg = qca808x_config_aneg,
|
||||
.suspend = genphy_suspend,
|
||||
.resume = genphy_resume,
|
||||
.read_status = qca808x_read_status,
|
||||
@@ -1,97 +0,0 @@
|
||||
From 38eb804e8458ba181a03a0498ce4bf84eebd1931 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 14 Dec 2023 01:44:32 +0100
|
||||
Subject: [PATCH 2/2] net: phy: at803x: make read specific status function more
|
||||
generic
|
||||
|
||||
Rework read specific status function to be more generic. The function
|
||||
apply different speed mask based on the PHY ID. Make it more generic by
|
||||
adding an additional arg to pass the specific speed (ss) mask and use
|
||||
the provided mask to parse the speed value.
|
||||
|
||||
This is needed to permit an easier deatch of qca808x code from the
|
||||
at803x driver.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 26 ++++++++++++++++++--------
|
||||
1 file changed, 18 insertions(+), 8 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -301,6 +301,11 @@ static struct at803x_hw_stat qca83xx_hw_
|
||||
{ "eee_wake_errors", 0x16, GENMASK(15, 0), MMD},
|
||||
};
|
||||
|
||||
+struct at803x_ss_mask {
|
||||
+ u16 speed_mask;
|
||||
+ u8 speed_shift;
|
||||
+};
|
||||
+
|
||||
struct at803x_priv {
|
||||
int flags;
|
||||
u16 clk_25m_reg;
|
||||
@@ -921,7 +926,8 @@ static void at803x_link_change_notify(st
|
||||
}
|
||||
}
|
||||
|
||||
-static int at803x_read_specific_status(struct phy_device *phydev)
|
||||
+static int at803x_read_specific_status(struct phy_device *phydev,
|
||||
+ struct at803x_ss_mask ss_mask)
|
||||
{
|
||||
int ss;
|
||||
|
||||
@@ -940,11 +946,8 @@ static int at803x_read_specific_status(s
|
||||
if (sfc < 0)
|
||||
return sfc;
|
||||
|
||||
- /* qca8081 takes the different bits for speed value from at803x */
|
||||
- if (phydev->drv->phy_id == QCA8081_PHY_ID)
|
||||
- speed = FIELD_GET(QCA808X_SS_SPEED_MASK, ss);
|
||||
- else
|
||||
- speed = FIELD_GET(AT803X_SS_SPEED_MASK, ss);
|
||||
+ speed = ss & ss_mask.speed_mask;
|
||||
+ speed >>= ss_mask.speed_shift;
|
||||
|
||||
switch (speed) {
|
||||
case AT803X_SS_SPEED_10:
|
||||
@@ -989,6 +992,7 @@ static int at803x_read_specific_status(s
|
||||
static int at803x_read_status(struct phy_device *phydev)
|
||||
{
|
||||
struct at803x_priv *priv = phydev->priv;
|
||||
+ struct at803x_ss_mask ss_mask = { 0 };
|
||||
int err, old_link = phydev->link;
|
||||
|
||||
if (priv->is_1000basex)
|
||||
@@ -1012,7 +1016,9 @@ static int at803x_read_status(struct phy
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
- err = at803x_read_specific_status(phydev);
|
||||
+ ss_mask.speed_mask = AT803X_SS_SPEED_MASK;
|
||||
+ ss_mask.speed_shift = __bf_shf(AT803X_SS_SPEED_MASK);
|
||||
+ err = at803x_read_specific_status(phydev, ss_mask);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
@@ -1869,6 +1875,7 @@ static int qca808x_config_init(struct ph
|
||||
|
||||
static int qca808x_read_status(struct phy_device *phydev)
|
||||
{
|
||||
+ struct at803x_ss_mask ss_mask = { 0 };
|
||||
int ret;
|
||||
|
||||
ret = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
|
||||
@@ -1882,7 +1889,10 @@ static int qca808x_read_status(struct ph
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- ret = at803x_read_specific_status(phydev);
|
||||
+ /* qca8081 takes the different bits for speed value from at803x */
|
||||
+ ss_mask.speed_mask = QCA808X_SS_SPEED_MASK;
|
||||
+ ss_mask.speed_shift = __bf_shf(QCA808X_SS_SPEED_MASK);
|
||||
+ ret = at803x_read_specific_status(phydev, ss_mask);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
From fc9d7264ddc32eaa647d6bfcdc25cdf9f786fde0 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 18 Dec 2023 00:27:39 +0100
|
||||
Subject: [PATCH 1/2] net: phy: at803x: remove extra space after cast
|
||||
|
||||
Remove extra space after cast as reported by checkpatch to keep code
|
||||
clean.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20231217232739.27065-1-ansuelsmth@gmail.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -462,7 +462,7 @@ static int at803x_set_wol(struct phy_dev
|
||||
if (!ndev)
|
||||
return -ENODEV;
|
||||
|
||||
- mac = (const u8 *) ndev->dev_addr;
|
||||
+ mac = (const u8 *)ndev->dev_addr;
|
||||
|
||||
if (!is_valid_ether_addr(mac))
|
||||
return -EINVAL;
|
||||
@@ -1,38 +0,0 @@
|
||||
From 3ab5720881a924fb6405d9e6a3b09f1026467c47 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Mon, 18 Dec 2023 00:25:08 +0100
|
||||
Subject: [PATCH 2/2] net: phy: at803x: replace msleep(1) with usleep_range
|
||||
|
||||
Replace msleep(1) with usleep_range as suggested by timers-howto guide.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Link: https://lore.kernel.org/r/20231217232508.26470-1-ansuelsmth@gmail.com
|
||||
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -916,9 +916,9 @@ static void at803x_link_change_notify(st
|
||||
at803x_context_save(phydev, &context);
|
||||
|
||||
phy_device_reset(phydev, 1);
|
||||
- msleep(1);
|
||||
+ usleep_range(1000, 2000);
|
||||
phy_device_reset(phydev, 0);
|
||||
- msleep(1);
|
||||
+ usleep_range(1000, 2000);
|
||||
|
||||
at803x_context_restore(phydev, &context);
|
||||
|
||||
@@ -1733,7 +1733,7 @@ static int qca83xx_resume(struct phy_dev
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
- msleep(1);
|
||||
+ usleep_range(1000, 2000);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1,152 +0,0 @@
|
||||
From 7961ef1fa10ec35ad6923fb5751877116e4b035b Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Tue, 19 Dec 2023 21:21:24 +0100
|
||||
Subject: [PATCH] net: phy: at803x: better align function varibles to open
|
||||
parenthesis
|
||||
|
||||
Better align function variables to open parenthesis as suggested by
|
||||
checkpatch script for qca808x function to make code cleaner.
|
||||
|
||||
For cable_test_get_status function some additional rework was needed to
|
||||
handle too long functions.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 67 ++++++++++++++++++++++------------------
|
||||
1 file changed, 37 insertions(+), 30 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1781,27 +1781,27 @@ static int qca808x_phy_fast_retrain_conf
|
||||
return ret;
|
||||
|
||||
phy_write_mmd(phydev, MDIO_MMD_AN, QCA808X_PHY_MMD7_TOP_OPTION1,
|
||||
- QCA808X_TOP_OPTION1_DATA);
|
||||
+ QCA808X_TOP_OPTION1_DATA);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_20DB,
|
||||
- QCA808X_MSE_THRESHOLD_20DB_VALUE);
|
||||
+ QCA808X_MSE_THRESHOLD_20DB_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_17DB,
|
||||
- QCA808X_MSE_THRESHOLD_17DB_VALUE);
|
||||
+ QCA808X_MSE_THRESHOLD_17DB_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_27DB,
|
||||
- QCA808X_MSE_THRESHOLD_27DB_VALUE);
|
||||
+ QCA808X_MSE_THRESHOLD_27DB_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PMAPMD, QCA808X_PHY_MMD1_MSE_THRESHOLD_28DB,
|
||||
- QCA808X_MSE_THRESHOLD_28DB_VALUE);
|
||||
+ QCA808X_MSE_THRESHOLD_28DB_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_1,
|
||||
- QCA808X_MMD3_DEBUG_1_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_1_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_4,
|
||||
- QCA808X_MMD3_DEBUG_4_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_4_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_5,
|
||||
- QCA808X_MMD3_DEBUG_5_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_5_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_3,
|
||||
- QCA808X_MMD3_DEBUG_3_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_3_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_6,
|
||||
- QCA808X_MMD3_DEBUG_6_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_6_VALUE);
|
||||
phy_write_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_DEBUG_2,
|
||||
- QCA808X_MMD3_DEBUG_2_VALUE);
|
||||
+ QCA808X_MMD3_DEBUG_2_VALUE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1838,13 +1838,14 @@ static int qca808x_config_init(struct ph
|
||||
|
||||
/* Active adc&vga on 802.3az for the link 1000M and 100M */
|
||||
ret = phy_modify_mmd(phydev, MDIO_MMD_PCS, QCA808X_PHY_MMD3_ADDR_CLD_CTRL7,
|
||||
- QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
|
||||
+ QCA808X_8023AZ_AFE_CTRL_MASK, QCA808X_8023AZ_AFE_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Adjust the threshold on 802.3az for the link 1000M */
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_PCS,
|
||||
- QCA808X_PHY_MMD3_AZ_TRAINING_CTRL, QCA808X_MMD3_AZ_TRAINING_VAL);
|
||||
+ QCA808X_PHY_MMD3_AZ_TRAINING_CTRL,
|
||||
+ QCA808X_MMD3_AZ_TRAINING_VAL);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1870,7 +1871,8 @@ static int qca808x_config_init(struct ph
|
||||
|
||||
/* Configure adc threshold as 100mv for the link 10M */
|
||||
return at803x_debug_reg_mask(phydev, QCA808X_PHY_DEBUG_ADC_THRESHOLD,
|
||||
- QCA808X_ADC_THRESHOLD_MASK, QCA808X_ADC_THRESHOLD_100MV);
|
||||
+ QCA808X_ADC_THRESHOLD_MASK,
|
||||
+ QCA808X_ADC_THRESHOLD_100MV);
|
||||
}
|
||||
|
||||
static int qca808x_read_status(struct phy_device *phydev)
|
||||
@@ -1883,7 +1885,7 @@ static int qca808x_read_status(struct ph
|
||||
return ret;
|
||||
|
||||
linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising,
|
||||
- ret & MDIO_AN_10GBT_STAT_LP2_5G);
|
||||
+ ret & MDIO_AN_10GBT_STAT_LP2_5G);
|
||||
|
||||
ret = genphy_read_status(phydev);
|
||||
if (ret)
|
||||
@@ -1913,7 +1915,7 @@ static int qca808x_read_status(struct ph
|
||||
*/
|
||||
if (qca808x_has_fast_retrain_or_slave_seed(phydev)) {
|
||||
if (phydev->master_slave_state == MASTER_SLAVE_STATE_ERR ||
|
||||
- qca808x_is_prefer_master(phydev)) {
|
||||
+ qca808x_is_prefer_master(phydev)) {
|
||||
qca808x_phy_ms_seed_enable(phydev, false);
|
||||
} else {
|
||||
qca808x_phy_ms_seed_enable(phydev, true);
|
||||
@@ -2070,18 +2072,22 @@ static int qca808x_cable_test_get_status
|
||||
ethnl_cable_test_result(phydev, ETHTOOL_A_CABLE_PAIR_D,
|
||||
qca808x_cable_test_result_trans(pair_d));
|
||||
|
||||
- if (qca808x_cdt_fault_length_valid(pair_a))
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A,
|
||||
- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A));
|
||||
- if (qca808x_cdt_fault_length_valid(pair_b))
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B,
|
||||
- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B));
|
||||
- if (qca808x_cdt_fault_length_valid(pair_c))
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C,
|
||||
- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C));
|
||||
- if (qca808x_cdt_fault_length_valid(pair_d))
|
||||
- ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D,
|
||||
- qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D));
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_a)) {
|
||||
+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A);
|
||||
+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_A, val);
|
||||
+ }
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_b)) {
|
||||
+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B);
|
||||
+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_B, val);
|
||||
+ }
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_c)) {
|
||||
+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C);
|
||||
+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_C, val);
|
||||
+ }
|
||||
+ if (qca808x_cdt_fault_length_valid(pair_d)) {
|
||||
+ val = qca808x_cdt_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D);
|
||||
+ ethnl_cable_test_fault_length(phydev, ETHTOOL_A_CABLE_PAIR_D, val);
|
||||
+ }
|
||||
|
||||
*finished = true;
|
||||
|
||||
@@ -2148,8 +2154,9 @@ static void qca808x_link_change_notify(s
|
||||
* the interface device address is always phy address added by 1.
|
||||
*/
|
||||
mdiobus_c45_modify_changed(phydev->mdio.bus, phydev->mdio.addr + 1,
|
||||
- MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
|
||||
- QCA8081_PHY_FIFO_RSTN, phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
|
||||
+ MDIO_MMD_PMAPMD, QCA8081_PHY_SERDES_MMD1_FIFO_CTRL,
|
||||
+ QCA8081_PHY_FIFO_RSTN,
|
||||
+ phydev->link ? QCA8081_PHY_FIFO_RSTN : 0);
|
||||
}
|
||||
|
||||
static struct phy_driver at803x_driver[] = {
|
||||
@@ -1,62 +0,0 @@
|
||||
From 22eb276098da820d9440fad22901f9b74ed4d659 Mon Sep 17 00:00:00 2001
|
||||
From: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Date: Thu, 4 Jan 2024 22:30:38 +0100
|
||||
Subject: [PATCH 1/4] net: phy: at803x: generalize cdt fault length function
|
||||
|
||||
Generalize cable test fault length function since they all base on the
|
||||
same magic values (already reverse engineered to understand the meaning
|
||||
of it) to have consistenct values on every PHY.
|
||||
|
||||
Signed-off-by: Christian Marangi <ansuelsmth@gmail.com>
|
||||
Reviewed-by: Simon Horman <horms@kernel.org>
|
||||
Signed-off-by: David S. Miller <davem@davemloft.net>
|
||||
---
|
||||
drivers/net/phy/at803x.c | 13 ++++++-------
|
||||
1 file changed, 6 insertions(+), 7 deletions(-)
|
||||
|
||||
--- a/drivers/net/phy/at803x.c
|
||||
+++ b/drivers/net/phy/at803x.c
|
||||
@@ -1192,10 +1192,8 @@ static bool at803x_cdt_fault_length_vali
|
||||
return false;
|
||||
}
|
||||
|
||||
-static int at803x_cdt_fault_length(u16 status)
|
||||
+static int at803x_cdt_fault_length(int dt)
|
||||
{
|
||||
- int dt;
|
||||
-
|
||||
/* According to the datasheet the distance to the fault is
|
||||
* DELTA_TIME * 0.824 meters.
|
||||
*
|
||||
@@ -1211,8 +1209,6 @@ static int at803x_cdt_fault_length(u16 s
|
||||
* With a VF of 0.69 we get the factor 0.824 mentioned in the
|
||||
* datasheet.
|
||||
*/
|
||||
- dt = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, status);
|
||||
-
|
||||
return (dt * 824) / 10;
|
||||
}
|
||||
|
||||
@@ -1265,9 +1261,11 @@ static int at803x_cable_test_one_pair(st
|
||||
ethnl_cable_test_result(phydev, ethtool_pair[pair],
|
||||
at803x_cable_test_result_trans(val));
|
||||
|
||||
- if (at803x_cdt_fault_length_valid(val))
|
||||
+ if (at803x_cdt_fault_length_valid(val)) {
|
||||
+ val = FIELD_GET(AT803X_CDT_STATUS_DELTA_TIME_MASK, val);
|
||||
ethnl_cable_test_fault_length(phydev, ethtool_pair[pair],
|
||||
at803x_cdt_fault_length(val));
|
||||
+ }
|
||||
|
||||
return 1;
|
||||
}
|
||||
@@ -1992,7 +1990,8 @@ static int qca808x_cdt_fault_length(stru
|
||||
if (val < 0)
|
||||
return val;
|
||||
|
||||
- return (FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val) * 824) / 10;
|
||||
+ val = FIELD_GET(QCA808X_CDT_DIAG_LENGTH, val);
|
||||
+ return at803x_cdt_fault_length(val);
|
||||
}
|
||||
|
||||
static int qca808x_cable_test_start(struct phy_device *phydev)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user