diff --git a/include/autotools.mk b/include/autotools.mk index cba36ccd9fa..c378efe6f9d 100644 --- a/include/autotools.mk +++ b/include/autotools.mk @@ -124,13 +124,6 @@ ifneq ($(filter libtool-abiver,$(PKG_FIXUP)),) Hooks/Configure/Post += set_libtool_abiver endif -ifneq ($(filter libtool-ucxx,$(PKG_FIXUP)),) - PKG_BUILD_DEPENDS += libtool - ifeq ($(filter no-autoreconf,$(PKG_FIXUP)),) - Hooks/Configure/Pre += autoreconf_target - endif -endif - ifneq ($(filter autoreconf,$(PKG_FIXUP)),) ifeq ($(filter autoreconf,$(Hooks/Configure/Pre)),) Hooks/Configure/Pre += autoreconf_target @@ -166,12 +159,6 @@ ifneq ($(filter libtool,$(HOST_FIXUP)),) endif endif -ifneq ($(filter libtool-ucxx,$(HOST_FIXUP)),) - ifeq ($(filter no-autoreconf,$(HOST_FIXUP)),) - Hooks/HostConfigure/Pre += autoreconf_host - endif -endif - ifneq ($(filter autoreconf,$(HOST_FIXUP)),) ifeq ($(filter autoreconf,$(Hooks/HostConfigure/Pre)),) Hooks/HostConfigure/Pre += autoreconf_host diff --git a/include/kernel-5.10 b/include/kernel-5.10 index 7488f6c0eb3..61a182a2cd9 100644 --- a/include/kernel-5.10 +++ b/include/kernel-5.10 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.10 = .175 -LINUX_KERNEL_HASH-5.10.175 = e277562e28f234e36665ae12b7585f9557a83a86bc4a8de8840a305af6307bce +LINUX_VERSION-5.10 = .176 +LINUX_KERNEL_HASH-5.10.176 = ce072c60ba04173e05b2a1de3fefdeba5ac8b28b1958d92d21bdbf9b736ef793 diff --git a/include/kernel-5.15 b/include/kernel-5.15 index c50a5eb61fa..51d1c3c6fe7 100644 --- a/include/kernel-5.15 +++ b/include/kernel-5.15 @@ -1,2 +1,2 @@ -LINUX_VERSION-5.15 = .102 -LINUX_KERNEL_HASH-5.15.102 = 441cddfb970b97759eebdb9b142673662ce0770500e3ae8bcd4b90af369b01e6 +LINUX_VERSION-5.15 = .104 +LINUX_KERNEL_HASH-5.15.104 = 71c532ce09992e470f3259ffeb38d2b5bba990c243a559e4726a57412bd36b54 diff --git a/package/boot/uboot-envtools/files/ath79 b/package/boot/uboot-envtools/files/ath79 index b968fa8fdec..c7049f70292 100644 --- a/package/boot/uboot-envtools/files/ath79 +++ b/package/boot/uboot-envtools/files/ath79 @@ -88,6 +88,18 @@ ziking,cpe46b|\ zyxel,nbg6616) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x10000" "0x10000" ;; +aruba,ap-105|\ +aruba,ap-175|\ +dongwon,dw02-412h-64m|\ +dongwon,dw02-412h-128m|\ +glinet,gl-ar300m-lite|\ +glinet,gl-ar300m-nand|\ +glinet,gl-ar300m-nor|\ +glinet,gl-ar300m16) + idx="$(find_mtd_index u-boot-env)" + [ -n "$idx" ] && \ + ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x10000" + ;; buffalo,wzr-hp-ag300h) ubootenv_add_uci_config "/dev/mtd3" "0x0" "0x10000" "0x10000" ;; @@ -99,16 +111,6 @@ linksys,ea4500-v3) domywifi,dw33d) ubootenv_add_uci_config "/dev/mtd4" "0x0" "0x10000" "0x10000" ;; -dongwon,dw02-412h-64m|\ -dongwon,dw02-412h-128m|\ -glinet,gl-ar300m-lite|\ -glinet,gl-ar300m-nand|\ -glinet,gl-ar300m-nor|\ -glinet,gl-ar300m16) - idx="$(find_mtd_index u-boot-env)" - [ -n "$idx" ] && \ - ubootenv_add_uci_config "/dev/mtd$idx" "0x0" "0x10000" "0x10000" - ;; glinet,gl-ar150) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x8000" "0x10000" ;; diff --git a/package/boot/uboot-envtools/files/ramips b/package/boot/uboot-envtools/files/ramips index 6a0256c568d..bc562100365 100644 --- a/package/boot/uboot-envtools/files/ramips +++ b/package/boot/uboot-envtools/files/ramips @@ -19,7 +19,8 @@ alfa-network,r36m-e4g|\ alfa-network,tube-e4g|\ engenius,epg600|\ engenius,esr600h|\ -sitecom,wlr-4100-v1-002) +sitecom,wlr-4100-v1-002|\ +zyxel,keenetic-lite-iii-a) ubootenv_add_uci_config "/dev/mtd1" "0x0" "0x1000" "0x1000" ;; arcadyan,we420223-99) diff --git a/package/firmware/ipq-wifi/Makefile b/package/firmware/ipq-wifi/Makefile index f3bf1185f82..8eea981191e 100644 --- a/package/firmware/ipq-wifi/Makefile +++ b/package/firmware/ipq-wifi/Makefile @@ -6,9 +6,9 @@ PKG_RELEASE:=1 PKG_SOURCE_PROTO:=git PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware/qca-wireless.git -PKG_SOURCE_DATE:=2023-03-20 -PKG_SOURCE_VERSION:=f9cece02724b8ca2c1a166a46f0afa89e632d431 -PKG_MIRROR_HASH:=89c20798c7ec83114aa69467f2467fe32cbb74ebeca277c60a033af960ca6c04 +PKG_SOURCE_DATE:=2023-03-27 +PKG_SOURCE_VERSION:=ccd7e460cc798d90148a10539b6d94a5fd761004 +PKG_MIRROR_HASH:=e51d28c741aeb0867493a7bfc801b8b1977c942ed5d51d62c1aa8729c91cce32 PKG_FLAGS:=nonshared diff --git a/package/kernel/mac80211/patches/subsys/328-wifi-mac80211-fix-invalid-drv_sta_pre_rcu_remove-cal.patch b/package/kernel/mac80211/patches/subsys/328-wifi-mac80211-fix-invalid-drv_sta_pre_rcu_remove-cal.patch new file mode 100644 index 00000000000..289906c88c6 --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/328-wifi-mac80211-fix-invalid-drv_sta_pre_rcu_remove-cal.patch @@ -0,0 +1,25 @@ +From: Felix Fietkau +Date: Fri, 24 Mar 2023 13:04:17 +0100 +Subject: [PATCH] wifi: mac80211: fix invalid drv_sta_pre_rcu_remove calls for + non-uploaded sta + +Avoid potential data corruption issues caused by uninitialized driver +private data structures. + +Reported-by: Brian Coverstone +Fixes: 6a9d1b91f34d ("mac80211: add pre-RCU-sync sta removal driver operation") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/sta_info.c ++++ b/net/mac80211/sta_info.c +@@ -1241,7 +1241,8 @@ static int __must_check __sta_info_destr + list_del_rcu(&sta->list); + sta->removed = true; + +- drv_sta_pre_rcu_remove(local, sta->sdata, sta); ++ if (sta->uploaded) ++ drv_sta_pre_rcu_remove(local, sta->sdata, sta); + + if (sdata->vif.type == NL80211_IFTYPE_AP_VLAN && + rcu_access_pointer(sdata->u.vlan.sta) == sta) diff --git a/package/kernel/mac80211/patches/subsys/329-wifi-mac80211-fix-receiving-mesh-packets-in-forwardi.patch b/package/kernel/mac80211/patches/subsys/329-wifi-mac80211-fix-receiving-mesh-packets-in-forwardi.patch new file mode 100644 index 00000000000..6882694da8e --- /dev/null +++ b/package/kernel/mac80211/patches/subsys/329-wifi-mac80211-fix-receiving-mesh-packets-in-forwardi.patch @@ -0,0 +1,50 @@ +From: Felix Fietkau +Date: Sun, 26 Mar 2023 17:11:34 +0200 +Subject: [PATCH] wifi: mac80211: fix receiving mesh packets in forwarding=0 + networks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When forwarding is set to 0, frames are typically sent with ttl=1. +Move the ttl decrement check below the check for local receive in order to +fix packet drops. + +Reported-by: Thomas Hühn +Reported-by: Nick Hainke +Fixes: 986e43b19ae9 ("wifi: mac80211: fix receiving A-MSDU frames on mesh interfaces") +Signed-off-by: Felix Fietkau +--- + +--- a/net/mac80211/rx.c ++++ b/net/mac80211/rx.c +@@ -2828,14 +2828,6 @@ ieee80211_rx_mesh_data(struct ieee80211_ + if (sdata->crypto_tx_tailroom_needed_cnt) + tailroom = IEEE80211_ENCRYPT_TAILROOM; + +- if (!--mesh_hdr->ttl) { +- if (multicast) +- goto rx_accept; +- +- IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); +- return RX_DROP_MONITOR; +- } +- + if (mesh_hdr->flags & MESH_FLAGS_AE) { + struct mesh_path *mppath; + char *proxied_addr; +@@ -2874,6 +2866,14 @@ ieee80211_rx_mesh_data(struct ieee80211_ + if (ether_addr_equal(sdata->vif.addr, eth->h_dest)) + goto rx_accept; + ++ if (!--mesh_hdr->ttl) { ++ if (multicast) ++ goto rx_accept; ++ ++ IEEE80211_IFSTA_MESH_CTR_INC(ifmsh, dropped_frames_ttl); ++ return RX_DROP_MONITOR; ++ } ++ + if (!ifmsh->mshcfg.dot11MeshForwarding) { + if (is_multicast_ether_addr(eth->h_dest)) + goto rx_accept; diff --git a/package/utils/util-linux/Makefile b/package/utils/util-linux/Makefile index 21764a2d270..1ae6bb7b2d8 100644 --- a/package/utils/util-linux/Makefile +++ b/package/utils/util-linux/Makefile @@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=util-linux PKG_VERSION:=2.38.1 -PKG_RELEASE:=1 +PKG_RELEASE:=2 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz PKG_SOURCE_URL:=@KERNEL/linux/utils/$(PKG_NAME)/v2.38 @@ -415,6 +415,17 @@ define Package/rename/description expression in their name by replacement endef +define Package/rev +$(call Package/util-linux/Default) + TITLE:=Reverse lines characterwise +endef + +define Package/rev/description + rev utility copies the specified files to the standard output, reversing the + order of characters in every line. If no files are specified, the standard + input is read. +endef + define Package/partx-utils $(call Package/util-linux/Default) TITLE:=inform kernel about the presence and numbering of on-disk partitions @@ -804,6 +815,11 @@ define Package/rename/install $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/rename $(1)/usr/bin/ endef +define Package/rev/install + $(INSTALL_DIR) $(1)/usr/bin + $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/rev $(1)/usr/bin/ +endef + define Package/partx-utils/install $(INSTALL_DIR) $(1)/usr/sbin $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/partx $(1)/usr/sbin/ @@ -904,6 +920,7 @@ $(eval $(call BuildPackage,namei)) $(eval $(call BuildPackage,nsenter)) $(eval $(call BuildPackage,prlimit)) $(eval $(call BuildPackage,rename)) +$(eval $(call BuildPackage,rev)) $(eval $(call BuildPackage,partx-utils)) $(eval $(call BuildPackage,script-utils)) $(eval $(call BuildPackage,setterm)) diff --git a/rules.mk b/rules.mk index a2f5bcca4b0..57d7995d4fa 100644 --- a/rules.mk +++ b/rules.mk @@ -252,6 +252,7 @@ TARGET_NM:=$(TARGET_CROSS)gcc-nm TARGET_CC:=$(TARGET_CROSS)gcc TARGET_CXX:=$(TARGET_CROSS)g++ KPATCH:=$(SCRIPT_DIR)/patch-kernel.sh +FILECMD:=$(STAGING_DIR_HOST)/bin/file SED:=$(STAGING_DIR_HOST)/bin/sed -i -e ESED:=$(STAGING_DIR_HOST)/bin/sed -E -i -e MKHASH:=$(STAGING_DIR_HOST)/bin/mkhash diff --git a/target/linux/apm821xx/image/nand.mk b/target/linux/apm821xx/image/nand.mk index db3c9c07228..1d28df98e51 100644 --- a/target/linux/apm821xx/image/nand.mk +++ b/target/linux/apm821xx/image/nand.mk @@ -51,7 +51,7 @@ define Device/meraki_mx60 IMAGES := sysupgrade.bin DTB_SIZE := 20480 IMAGE_SIZE := 1021m - KERNEL := kernel-bin | gzip | dtb | MuImage-initramfs gzip + KERNEL := kernel-bin | libdeflate-gzip | dtb | MuImage-initramfs gzip IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata UBINIZE_OPTS := -E 5 DEVICE_COMPAT_VERSION := 2.0 @@ -70,7 +70,7 @@ define Device/netgear_wndap6x0 IMAGE_SIZE := 27392k IMAGES := sysupgrade.bin factory.img KERNEL_SIZE := 6080k - KERNEL := dtb | kernel-bin | gzip | MuImage-initramfs gzip + KERNEL := dtb | kernel-bin | libdeflate-gzip | MuImage-initramfs gzip IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata IMAGE/factory.img := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-ubi UBINIZE_OPTS := -E 5 @@ -114,7 +114,7 @@ define Device/netgear_wndr4700 # CHECK_DNI_FIRMWARE_ROOTFS_INTEGRITY in do_chk_dniimg() KERNEL := kernel-bin | lzma -d16 | uImage lzma | pad-offset $$(BLOCKSIZE) 64 | \ append-uImage-fakehdr filesystem | dtb | create-uImage-dtb | prepend-dtb - KERNEL_INITRAMFS := kernel-bin | gzip | dtb | MuImage-initramfs gzip + KERNEL_INITRAMFS := kernel-bin | libdeflate-gzip | dtb | MuImage-initramfs gzip IMAGE/factory.img := append-kernel | pad-to $$$$(KERNEL_SIZE) | append-ubi | \ netgear-dni | check-size IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata diff --git a/target/linux/apm821xx/image/sata.mk b/target/linux/apm821xx/image/sata.mk index 89af488e097..8a9bd579881 100644 --- a/target/linux/apm821xx/image/sata.mk +++ b/target/linux/apm821xx/image/sata.mk @@ -12,14 +12,14 @@ define Device/wd_mybooklive SUPPORTED_DEVICES += mbl wd,mybooklive-duo BLOCKSIZE := 1k DTB_SIZE := 16384 - KERNEL := kernel-bin | dtb | gzip | uImage gzip - KERNEL_INITRAMFS := kernel-bin | gzip | dtb | MuImage-initramfs gzip + KERNEL := kernel-bin | dtb | libdeflate-gzip | uImage gzip + KERNEL_INITRAMFS := kernel-bin | libdeflate-gzip | dtb | MuImage-initramfs gzip IMAGES := factory.img.gz sysupgrade.img.gz ARTIFACTS := apollo3g.dtb DEVICE_DTB := apollo3g.dtb FILESYSTEMS := ext4 squashfs - IMAGE/factory.img.gz := boot-script | boot-img | hdd-img | gzip - IMAGE/sysupgrade.img.gz := boot-script | boot-img | hdd-img | gzip | append-metadata + IMAGE/factory.img.gz := boot-script | boot-img | hdd-img | libdeflate-gzip + IMAGE/sysupgrade.img.gz := boot-script | boot-img | hdd-img | libdeflate-gzip | append-metadata ARTIFACT/apollo3g.dtb := export-dtb endef diff --git a/target/linux/ath79/dts/ar7161_aruba_ap-175.dts b/target/linux/ath79/dts/ar7161_aruba_ap-175.dts new file mode 100644 index 00000000000..dd29a687ba0 --- /dev/null +++ b/target/linux/ath79/dts/ar7161_aruba_ap-175.dts @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "ar7100.dtsi" + +#include +#include + +/ { + compatible = "aruba,ap-175", "qca,ar7161"; + model = "Aruba AP-175"; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + aliases { + led-boot = &led_power_amber; + led-failsafe = &led_power_amber; + led-upgrade = &led_power_amber; + label-mac-device = ð0; + }; + + leds { + compatible = "gpio-leds"; + + /* These internal LEDs cannot be seen when case is closed */ + internal_2g_green { + label = "green:internal_2g"; + gpios = <&gpio 3 GPIO_ACTIVE_HIGH>; + }; + + internal_5g_green { + label = "green:internal_5g"; + gpios = <&gpio 4 GPIO_ACTIVE_HIGH>; + }; + + /* These external LEDs are visible from the bottom panel */ + + led_power_amber: power_amber { + label = "amber:power"; + gpios = <&gpio_ext 5 GPIO_ACTIVE_HIGH>; + panic-indicator; + }; + + r1_act_blue { + label = "blue:r1_act"; + gpios = <&gpio_ext 0 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy1tpt"; + }; + + r1_rssi1_blue { + label = "blue:r1_rssi1"; + gpios = <&gpio_ext 1 GPIO_ACTIVE_HIGH>; + }; + + r1_rssi2_blue { + label = "blue:r1_rssi2"; + gpios = <&gpio_ext 2 GPIO_ACTIVE_HIGH>; + }; + + r1_rssi3_blue { + label = "blue:r1_rssi3"; + gpios = <&gpio_ext 3 GPIO_ACTIVE_HIGH>; + }; + + r1_rssi4_blue { + label = "blue:r1_rssi4"; + gpios = <&gpio_ext 4 GPIO_ACTIVE_HIGH>; + }; + + r0_act_amber { + label = "amber:r0_act"; + gpios = <&gpio_ext 8 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy0tpt"; + }; + + r0_rssi1_amber { + label = "amber:r0_rssi1"; + gpios = <&gpio_ext 9 GPIO_ACTIVE_HIGH>; + }; + + r0_rssi2_amber { + label = "amber:r0_rssi2"; + gpios = <&gpio_ext 10 GPIO_ACTIVE_HIGH>; + }; + + r0_rssi3_amber { + label = "amber:r0_rssi3"; + gpios = <&gpio_ext 11 GPIO_ACTIVE_HIGH>; + }; + + r0_rssi4_amber { + label = "amber:r0_rssi4"; + gpios = <&gpio_ext 12 GPIO_ACTIVE_HIGH>; + }; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + linux,code = ; + gpios = <&gpio 6 GPIO_ACTIVE_LOW>; + }; + }; + + i2c0: i2c { + compatible = "i2c-gpio"; + i2c-gpio,delay-us = <10>; + i2c-gpio,timeout-ms = <1>; + sda-gpios = <&gpio 1 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + scl-gpios = <&gpio 2 (GPIO_ACTIVE_HIGH|GPIO_OPEN_DRAIN)>; + + #address-cells = <1>; + #size-cells = <0>; + }; +}; + +&pcie0 { + status = "okay"; + + ath9k0: wifi@0,11 { + compatible = "pci168c,0029"; + nvmem-cells = <&macaddr_hwinfo_1c>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <1>; + reg = <0x8800 0 0 0 0>; + #gpio-cells = <2>; + gpio-controller; + }; + + ath9k1: wifi@0,12 { + compatible = "pci168c,0029"; + nvmem-cells = <&macaddr_hwinfo_1c>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <2>; + reg = <0x9000 0 0 0 0>; + #gpio-cells = <2>; + gpio-controller; + }; +}; + +&mdio0 { + status = "okay"; + + phy1: ethernet-phy@1 { + reg = <0x1>; + }; +}; + +ð0 { + status = "okay"; + nvmem-cells = <&macaddr_hwinfo_1c>; + nvmem-cell-names = "mac-address"; + + phy-mode = "rgmii"; + phy-handle = <&phy1>; +}; + +&spi { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <25000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x000000 0x40000>; + read-only; + }; + + partition@40000 { + label = "firmware"; + reg = <0x40000 0xfa0000>; + compatible = "denx,uimage"; + }; + + hwinfo: partition@fe0000 { + label = "hwinfo"; + reg = <0xfe0000 0x10000>; + read-only; + }; + + partition@ff0000 { + label = "u-boot-env"; + reg = <0xff0000 0x10000>; + read-only; + }; + }; + }; +}; + +&hwinfo { + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_hwinfo_1c: macaddr@1c { + reg = <0x1c 0x6>; + }; +}; + +&i2c0 { + gpio_ext: gpio@21 { + status = "okay"; + + compatible = "ti,tca6416"; + reg = <0x21>; + + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + }; + + temp-sensor@4a { + compatible = "national,lm75"; + reg = <0x4a>; + }; + + eeprom@50 { /* 24lc2561 */ + compatible = "atmel,24c256","at24"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x50>; + size = <256>; + }; + + ds1374c: rtc@68 { + status = "okay"; + + compatible = "dallas,ds1374"; + reg = <0x68>; + }; +}; diff --git a/target/linux/ath79/dts/ar7161_dlink_dir-825-b1.dts b/target/linux/ath79/dts/ar7161_dlink_dir-825-b1.dts index 6934a8f952b..0e39be7d0bb 100644 --- a/target/linux/ath79/dts/ar7161_dlink_dir-825-b1.dts +++ b/target/linux/ath79/dts/ar7161_dlink_dir-825-b1.dts @@ -139,7 +139,8 @@ ath9k0: wifi@0,11 { compatible = "pci168c,0029"; reg = <0x8800 0 0 0 0>; - qca,no-eeprom; + nvmem-cells = <&macaddr_lan>, <&cal_art_1000>; + nvmem-cell-names = "mac-address-ascii", "calibration"; #gpio-cells = <2>; gpio-controller; }; @@ -147,7 +148,9 @@ ath9k1: wifi@0,12 { compatible = "pci168c,0029"; reg = <0x9000 0 0 0 0>; - qca,no-eeprom; + nvmem-cells = <&macaddr_wan>, <&cal_art_5000>; + nvmem-cell-names = "mac-address-ascii", "calibration"; + mac-address-increment = <1>; #gpio-cells = <2>; gpio-controller; }; @@ -184,9 +187,28 @@ }; partition@660000 { + compatible = "nvmem-cells"; label = "caldata"; reg = <0x660000 0x010000>; read-only; + #address-cells = <1>; + #size-cells = <1>; + + cal_art_1000: cal@1000 { + reg = <0x1000 0xeb8>; + }; + + cal_art_5000: cal@5000 { + reg = <0x5000 0xeb8>; + }; + + macaddr_lan: macaddr@ffa0 { + reg = <0xffa0 0x11>; + }; + + macaddr_wan: macaddr@ffb4 { + reg = <0xffb4 0x11>; + }; }; fwconcat1: partition@670000 { @@ -202,6 +224,9 @@ pll-data = <0x11110000 0x00001099 0x00991099>; + nvmem-cells = <&macaddr_lan>; + nvmem-cell-names = "mac-address-ascii"; + fixed-link { speed = <1000>; full-duplex; @@ -213,5 +238,9 @@ pll-data = <0x11110000 0x00001099 0x00991099>; + nvmem-cells = <&macaddr_wan>; + nvmem-cell-names = "mac-address-ascii"; + phy-handle = <&phy4>; }; + diff --git a/target/linux/ath79/generic/base-files/etc/board.d/02_network b/target/linux/ath79/generic/base-files/etc/board.d/02_network index 31ea891d572..c6284edafd0 100644 --- a/target/linux/ath79/generic/base-files/etc/board.d/02_network +++ b/target/linux/ath79/generic/base-files/etc/board.d/02_network @@ -19,6 +19,7 @@ ath79_setup_interfaces() araknis,an-700-ap-i-ac|\ arduino,yun|\ aruba,ap-105|\ + aruba,ap-175|\ asus,rp-ac51|\ asus,rp-ac66|\ avm,fritz1750e|\ @@ -634,7 +635,6 @@ ath79_setup_macs() dlink,dir-629-a1) wan_mac=$(mtd_get_mac_text "mfcdata" 0x6a) ;; - dlink,dir-825-b1|\ trendnet,tew-673gru) lan_mac=$(mtd_get_mac_text "caldata" 0xffa0) wan_mac=$(mtd_get_mac_text "caldata" 0xffb4) diff --git a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom index 9827508c04a..022f5e29477 100644 --- a/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom +++ b/target/linux/ath79/generic/base-files/etc/hotplug.d/firmware/10-ath9k-eeprom @@ -124,7 +124,6 @@ case "$FIRMWARE" in buffalo,wzr-hp-ag300h) caldata_extract "art" 0x1000 0xeb8 ;; - dlink,dir-825-b1|\ trendnet,tew-673gru) caldata_extract "caldata" 0x1000 0xeb8 ath9k_patch_mac_crc $(mtd_get_mac_text "caldata" 0xffa0) 0x20c @@ -143,7 +142,6 @@ case "$FIRMWARE" in buffalo,wzr-hp-ag300h) caldata_extract "art" 0x5000 0xeb8 ;; - dlink,dir-825-b1|\ trendnet,tew-673gru) caldata_extract "caldata" 0x5000 0xeb8 ath9k_patch_mac_crc $(macaddr_add $(mtd_get_mac_text "caldata" 0xffb4) 1) 0x20c diff --git a/target/linux/ath79/image/generic.mk b/target/linux/ath79/image/generic.mk index 608b51766fa..328861de037 100644 --- a/target/linux/ath79/image/generic.mk +++ b/target/linux/ath79/image/generic.mk @@ -395,6 +395,15 @@ define Device/aruba_ap-105 endef TARGET_DEVICES += aruba_ap-105 +define Device/aruba_ap-175 + SOC := ar7161 + DEVICE_VENDOR := Aruba + DEVICE_MODEL := AP-175 + IMAGE_SIZE := 16000k + DEVICE_PACKAGES := kmod-gpio-pca953x kmod-hwmon-lm75 kmod-i2c-gpio kmod-rtc-ds1374 +endef +TARGET_DEVICES += aruba_ap-175 + define Device/asus_pl-ac56 SOC := qca9563 DEVICE_VENDOR := ASUS diff --git a/target/linux/bcm27xx/patches-5.15/950-0055-lan78xx-Enable-LEDs-and-auto-negotiation.patch b/target/linux/bcm27xx/patches-5.15/950-0055-lan78xx-Enable-LEDs-and-auto-negotiation.patch index 47fbf510fad..79928b1ddef 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0055-lan78xx-Enable-LEDs-and-auto-negotiation.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0055-lan78xx-Enable-LEDs-and-auto-negotiation.patch @@ -14,7 +14,7 @@ Signed-off-by: Phil Elwell --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c -@@ -2716,6 +2716,11 @@ static int lan78xx_reset(struct lan78xx_ +@@ -2691,6 +2691,11 @@ static int lan78xx_reset(struct lan78xx_ int ret; u32 buf; u8 sig; @@ -26,7 +26,7 @@ Signed-off-by: Phil Elwell ret = lan78xx_read_reg(dev, HW_CFG, &buf); if (ret < 0) -@@ -2797,6 +2802,10 @@ static int lan78xx_reset(struct lan78xx_ +@@ -2772,6 +2777,10 @@ static int lan78xx_reset(struct lan78xx_ buf |= HW_CFG_MEF_; @@ -37,7 +37,7 @@ Signed-off-by: Phil Elwell ret = lan78xx_write_reg(dev, HW_CFG, buf); if (ret < 0) return ret; -@@ -2895,6 +2904,9 @@ static int lan78xx_reset(struct lan78xx_ +@@ -2870,6 +2879,9 @@ static int lan78xx_reset(struct lan78xx_ buf |= MAC_CR_AUTO_DUPLEX_ | MAC_CR_AUTO_SPEED_; } } diff --git a/target/linux/bcm27xx/patches-5.15/950-0098-lan78xx-Read-initial-EEE-status-from-DT.patch b/target/linux/bcm27xx/patches-5.15/950-0098-lan78xx-Read-initial-EEE-status-from-DT.patch index ea0001b745a..50ea9f4c5b9 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0098-lan78xx-Read-initial-EEE-status-from-DT.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0098-lan78xx-Read-initial-EEE-status-from-DT.patch @@ -15,7 +15,7 @@ Signed-off-by: Phil Elwell --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c -@@ -2960,6 +2960,22 @@ static int lan78xx_open(struct net_devic +@@ -2935,6 +2935,22 @@ static int lan78xx_open(struct net_devic netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); diff --git a/target/linux/bcm27xx/patches-5.15/950-0104-net-lan78xx-Disable-TCP-Segmentation-Offload-TSO.patch b/target/linux/bcm27xx/patches-5.15/950-0104-net-lan78xx-Disable-TCP-Segmentation-Offload-TSO.patch index e716abb71e2..1b6c1d1f2b4 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0104-net-lan78xx-Disable-TCP-Segmentation-Offload-TSO.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0104-net-lan78xx-Disable-TCP-Segmentation-Offload-TSO.patch @@ -37,7 +37,7 @@ Signed-off-by: Dave Stevenson static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data) { u32 *buf; -@@ -3288,8 +3297,14 @@ static int lan78xx_bind(struct lan78xx_n +@@ -3263,8 +3272,14 @@ static int lan78xx_bind(struct lan78xx_n if (DEFAULT_RX_CSUM_ENABLE) dev->net->features |= NETIF_F_RXCSUM; diff --git a/target/linux/bcm27xx/patches-5.15/950-0105-lan78xx-Move-enabling-of-EEE-into-PHY-init-code.patch b/target/linux/bcm27xx/patches-5.15/950-0105-lan78xx-Move-enabling-of-EEE-into-PHY-init-code.patch index 5f0221e9f8b..91d27b82d93 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0105-lan78xx-Move-enabling-of-EEE-into-PHY-init-code.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0105-lan78xx-Move-enabling-of-EEE-into-PHY-init-code.patch @@ -16,7 +16,7 @@ Signed-off-by: Phil Elwell --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c -@@ -2277,6 +2277,22 @@ static int lan78xx_phy_init(struct lan78 +@@ -2252,6 +2252,22 @@ static int lan78xx_phy_init(struct lan78 mii_adv_to_linkmode_adv_t(fc, mii_adv); linkmode_or(phydev->advertising, fc, phydev->advertising); @@ -39,7 +39,7 @@ Signed-off-by: Phil Elwell if (phydev->mdio.dev.of_node) { u32 reg; int len; -@@ -2969,22 +2985,6 @@ static int lan78xx_open(struct net_devic +@@ -2944,22 +2960,6 @@ static int lan78xx_open(struct net_devic netif_dbg(dev, ifup, dev->net, "phy initialised successfully"); diff --git a/target/linux/bcm27xx/patches-5.15/950-0114-lan78xx-Debounce-link-events-to-minimize-poll-storm.patch b/target/linux/bcm27xx/patches-5.15/950-0114-lan78xx-Debounce-link-events-to-minimize-poll-storm.patch index bfe1c991bc3..334d391c1b3 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0114-lan78xx-Debounce-link-events-to-minimize-poll-storm.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0114-lan78xx-Debounce-link-events-to-minimize-poll-storm.patch @@ -28,7 +28,7 @@ See: https://github.com/raspberrypi/linux/issues/2447 static int lan78xx_read_reg(struct lan78xx_net *dev, u32 index, u32 *data) { u32 *buf; -@@ -4148,7 +4153,13 @@ static int lan78xx_probe(struct usb_inte +@@ -4123,7 +4128,13 @@ static int lan78xx_probe(struct usb_inte netdev->max_mtu = MAX_SINGLE_PACKET_SIZE; netif_set_gso_max_size(netdev, MAX_SINGLE_PACKET_SIZE - MAX_HEADER); diff --git a/target/linux/bcm27xx/patches-5.15/950-0115-lan78xx-EEE-support-is-now-a-PHY-property.patch b/target/linux/bcm27xx/patches-5.15/950-0115-lan78xx-EEE-support-is-now-a-PHY-property.patch index d62b6df5fbd..4bbf9ec6cb3 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0115-lan78xx-EEE-support-is-now-a-PHY-property.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0115-lan78xx-EEE-support-is-now-a-PHY-property.patch @@ -15,7 +15,7 @@ Signed-off-by: Phil Elwell --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c -@@ -2282,7 +2282,7 @@ static int lan78xx_phy_init(struct lan78 +@@ -2257,7 +2257,7 @@ static int lan78xx_phy_init(struct lan78 mii_adv_to_linkmode_adv_t(fc, mii_adv); linkmode_or(phydev->advertising, fc, phydev->advertising); diff --git a/target/linux/bcm27xx/patches-5.15/950-0118-lan78xx-use-default-alignment-for-rx-buffers.patch b/target/linux/bcm27xx/patches-5.15/950-0118-lan78xx-use-default-alignment-for-rx-buffers.patch index a903f92d150..6bb311b5766 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0118-lan78xx-use-default-alignment-for-rx-buffers.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0118-lan78xx-use-default-alignment-for-rx-buffers.patch @@ -12,7 +12,7 @@ in both dwc_otg and in ipv6 processing. --- a/drivers/net/usb/lan78xx.c +++ b/drivers/net/usb/lan78xx.c -@@ -3527,7 +3527,7 @@ static int rx_submit(struct lan78xx_net +@@ -3502,7 +3502,7 @@ static int rx_submit(struct lan78xx_net size_t size = dev->rx_urb_size; int ret = 0; diff --git a/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch b/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch index 7690153d3db..e756fa2fff6 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0281-media-i2c-Add-driver-for-Sony-IMX477-sensor.patch @@ -25,7 +25,7 @@ Signed-off-by: Naushir Patuck --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -17530,6 +17530,14 @@ T: git git://linuxtv.org/media_tree.git +@@ -17538,6 +17538,14 @@ T: git git://linuxtv.org/media_tree.git F: Documentation/devicetree/bindings/media/i2c/sony,imx412.yaml F: drivers/media/i2c/imx412.c diff --git a/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch b/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch index d60c0f372cc..27fd0e57938 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0413-Documentation-devicetree-Add-documentation-for-imx37.patch @@ -132,7 +132,7 @@ Signed-off-by: David Plowman +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -17544,6 +17544,7 @@ M: Raspberry Pi Kernel Maintenance --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c -@@ -230,12 +230,12 @@ static int lan87xx_read_status(struct ph +@@ -223,12 +223,12 @@ static int lan87xx_read_status(struct ph if (rc < 0) return rc; diff --git a/target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch b/target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch index b1af6872fbf..25e6555fdb5 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0520-dt-bindings-media-i2c-Add-IMX519-CMOS-sensor-binding.patch @@ -132,7 +132,7 @@ Signed-off-by: Lee Jackson +... --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -17548,6 +17548,14 @@ F: Documentation/devicetree/bindings/med +@@ -17556,6 +17556,14 @@ F: Documentation/devicetree/bindings/med F: Documentation/devicetree/bindings/media/i2c/imx477.yaml F: drivers/media/i2c/imx477.c diff --git a/target/linux/bcm27xx/patches-5.15/950-0700-net-phy-lan87xx-Decrease-phy-polling-rate.patch b/target/linux/bcm27xx/patches-5.15/950-0700-net-phy-lan87xx-Decrease-phy-polling-rate.patch index 3abac8ec259..0341a0b7bf8 100644 --- a/target/linux/bcm27xx/patches-5.15/950-0700-net-phy-lan87xx-Decrease-phy-polling-rate.patch +++ b/target/linux/bcm27xx/patches-5.15/950-0700-net-phy-lan87xx-Decrease-phy-polling-rate.patch @@ -18,8 +18,8 @@ Signed-off-by: Phil Elwell --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c -@@ -220,6 +220,8 @@ static int lan87xx_read_status(struct ph - int err = genphy_read_status(phydev); +@@ -213,6 +213,8 @@ static int lan87xx_read_status(struct ph + return err; if (!phydev->link && priv->energy_enable && phydev->irq == PHY_POLL) { + int energy_detected; @@ -27,7 +27,7 @@ Signed-off-by: Phil Elwell /* Disable EDPD to wake up PHY */ int rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); if (rc < 0) -@@ -235,7 +237,7 @@ static int lan87xx_read_status(struct ph +@@ -228,7 +230,7 @@ static int lan87xx_read_status(struct ph */ read_poll_timeout(phy_read, rc, rc & MII_LAN83C185_ENERGYON || rc < 0, @@ -36,7 +36,7 @@ Signed-off-by: Phil Elwell MII_LAN83C185_CTRL_STATUS); if (rc < 0) return rc; -@@ -245,10 +247,16 @@ static int lan87xx_read_status(struct ph +@@ -238,10 +240,16 @@ static int lan87xx_read_status(struct ph if (rc < 0) return rc; diff --git a/target/linux/bcm47xx/config-5.10 b/target/linux/bcm47xx/config-5.10 index 31d6d1de390..bee300d9c02 100644 --- a/target/linux/bcm47xx/config-5.10 +++ b/target/linux/bcm47xx/config-5.10 @@ -1,4 +1,3 @@ -CONFIG_ADM6996_PHY=y CONFIG_ARCH_BINFMT_ELF_STATE=y CONFIG_ARCH_CLOCKSOURCE_DATA=y CONFIG_ARCH_DISCARD_MEMBLOCK=y diff --git a/target/linux/bcm47xx/config-5.15 b/target/linux/bcm47xx/config-5.15 index 0714c9a57c6..6f091c0e7fe 100644 --- a/target/linux/bcm47xx/config-5.15 +++ b/target/linux/bcm47xx/config-5.15 @@ -1,4 +1,3 @@ -CONFIG_ADM6996_PHY=y CONFIG_ARCH_32BIT_OFF_T=y CONFIG_ARCH_HIBERNATION_POSSIBLE=y CONFIG_ARCH_KEEP_MEMBLOCK=y diff --git a/target/linux/bcm47xx/legacy/config-default b/target/linux/bcm47xx/legacy/config-default index 8a52e475c7f..b80b03b5cb7 100644 --- a/target/linux/bcm47xx/legacy/config-default +++ b/target/linux/bcm47xx/legacy/config-default @@ -1,8 +1,8 @@ +CONFIG_ADM6996_PHY=y CONFIG_B44=y CONFIG_B44_PCI=y CONFIG_B44_PCICORE_AUTOSELECT=y CONFIG_B44_PCI_AUTOSELECT=y # CONFIG_BCM47XX_BCMA is not set # CONFIG_BCMA is not set -# CONFIG_MTD_NAND is not set # CONFIG_SSB_DRIVER_GIGE is not set diff --git a/target/linux/bcm47xx/mips74k/config-default b/target/linux/bcm47xx/mips74k/config-default index 2c3c40377ae..3ba49507d42 100644 --- a/target/linux/bcm47xx/mips74k/config-default +++ b/target/linux/bcm47xx/mips74k/config-default @@ -1,20 +1,16 @@ -# CONFIG_ADM6996_PHY is not set # CONFIG_BCM47XX_SSB is not set CONFIG_BGMAC=y CONFIG_BGMAC_BCMA=y CONFIG_BOUNCE=y +CONFIG_CPU_HAS_DIEI=y +CONFIG_CPU_HAS_RIXI=y # CONFIG_CPU_MIPS32_R1 is not set -# CONFIG_CPU_MIPSR1 is not set CONFIG_CPU_MIPS32_R2=y CONFIG_CPU_MIPSR2=y -# CONFIG_FIXED_PHY is not set +CONFIG_CPU_SUPPORTS_MSA=y # CONFIG_GPIO_WDT is not set CONFIG_HIGHMEM=y +CONFIG_KMAP_LOCAL=y +CONFIG_MIPS_SPRAM=y # CONFIG_SSB is not set -# CONFIG_SSB_DRIVER_EXTIF is not set -# CONFIG_SSB_DRIVER_GIGE is not set -# CONFIG_SSB_DRIVER_MIPS is not set -# CONFIG_SSB_EMBEDDED is not set -# CONFIG_SSB_PCICORE_HOSTMODE is not set -# CONFIG_SSB_SERIAL is not set -# CONFIG_SSB_SFLASH is not set +CONFIG_TARGET_ISA_REV=2 diff --git a/target/linux/bcm47xx/patches-5.15/170-bgmac-fix-initial-chip-reset-to-support-BCM5358.patch b/target/linux/bcm47xx/patches-5.15/170-bgmac-fix-initial-chip-reset-to-support-BCM5358.patch deleted file mode 100644 index f5f998e0f51..00000000000 --- a/target/linux/bcm47xx/patches-5.15/170-bgmac-fix-initial-chip-reset-to-support-BCM5358.patch +++ /dev/null @@ -1,76 +0,0 @@ -From 327dabbd0111910a7d174b0b812d608d6b67bead Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Rafa=C5=82=20Mi=C5=82ecki?= -Date: Mon, 8 Aug 2022 23:05:25 +0200 -Subject: [PATCH] bgmac: fix *initial* chip reset to support BCM5358 -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -While bringing hardware up we should perform a full reset including the -switch bit (BGMAC_BCMA_IOCTL_SW_RESET aka SICF_SWRST). It's what -specification says and what reference driver does. - -This seems to be critical for the BCM5358. Without this hardware doesn't -get initialized properly and doesn't seem to transmit or receive any -packets. - -Originally bgmac was calling bgmac_chip_reset() before setting -"has_robosw" property which resulted in expected behaviour. That has -changed as a side effect of adding platform device support which -regressed BCM5358 support. - -Fixes: f6a95a24957a ("net: ethernet: bgmac: Add platform device support") -Cc: Jon Mason -Signed-off-by: Rafał Miłecki ---- - drivers/net/ethernet/broadcom/bgmac.c | 8 ++++++-- - drivers/net/ethernet/broadcom/bgmac.h | 2 ++ - 2 files changed, 8 insertions(+), 2 deletions(-) - ---- a/drivers/net/ethernet/broadcom/bgmac.c -+++ b/drivers/net/ethernet/broadcom/bgmac.c -@@ -891,13 +891,13 @@ static void bgmac_chip_reset_idm_config( - - if (iost & BGMAC_BCMA_IOST_ATTACHED) { - flags = BGMAC_BCMA_IOCTL_SW_CLKEN; -- if (!bgmac->has_robosw) -+ if (bgmac->in_init || !bgmac->has_robosw) - flags |= BGMAC_BCMA_IOCTL_SW_RESET; - } - bgmac_clk_enable(bgmac, flags); - } - -- if (iost & BGMAC_BCMA_IOST_ATTACHED && !bgmac->has_robosw) -+ if (iost & BGMAC_BCMA_IOST_ATTACHED && (bgmac->in_init || !bgmac->has_robosw)) - bgmac_idm_write(bgmac, BCMA_IOCTL, - bgmac_idm_read(bgmac, BCMA_IOCTL) & - ~BGMAC_BCMA_IOCTL_SW_RESET); -@@ -1502,6 +1502,8 @@ int bgmac_enet_probe(struct bgmac *bgmac - struct net_device *net_dev = bgmac->net_dev; - int err; - -+ bgmac->in_init = true; -+ - bgmac_chip_intrs_off(bgmac); - - net_dev->irq = bgmac->irq; -@@ -1562,6 +1564,8 @@ int bgmac_enet_probe(struct bgmac *bgmac - bgmac->b53_device = &bgmac_b53_dev; - } - -+ bgmac->in_init = false; -+ - err = register_netdev(bgmac->net_dev); - if (err) { - dev_err(bgmac->dev, "Cannot register net device\n"); ---- a/drivers/net/ethernet/broadcom/bgmac.h -+++ b/drivers/net/ethernet/broadcom/bgmac.h -@@ -475,6 +475,8 @@ struct bgmac { - int irq; - u32 int_mask; - -+ bool in_init; -+ - /* Current MAC state */ - int mac_speed; - int mac_duplex; diff --git a/target/linux/bmips/nand/base-files/lib/upgrade/platform.sh b/target/linux/bmips/nand/base-files/lib/upgrade/platform.sh index 69901eb29c4..33217ed975b 100644 --- a/target/linux/bmips/nand/base-files/lib/upgrade/platform.sh +++ b/target/linux/bmips/nand/base-files/lib/upgrade/platform.sh @@ -47,9 +47,7 @@ platform_do_upgrade() { case "$(board_name)" in comtrend,vr-3032u|\ huawei,hg253s-v2|\ - netgear,dgnd3700-v2|\ - sercomm,h500-s-lowi|\ - sercomm,h500-s-vfes) + netgear,dgnd3700-v2) cfe_jffs2_nand_upgrade "$1" ;; *) diff --git a/target/linux/generic/backport-5.10/601-v5.12-net-implement-threaded-able-napi-poll-loop-support.patch b/target/linux/generic/backport-5.10/601-v5.12-net-implement-threaded-able-napi-poll-loop-support.patch index ca8d98b5736..9d5fb6e20ca 100644 --- a/target/linux/generic/backport-5.10/601-v5.12-net-implement-threaded-able-napi-poll-loop-support.patch +++ b/target/linux/generic/backport-5.10/601-v5.12-net-implement-threaded-able-napi-poll-loop-support.patch @@ -30,7 +30,7 @@ Signed-off-by: David S. Miller --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -354,6 +354,7 @@ struct napi_struct { +@@ -356,6 +356,7 @@ struct napi_struct { struct list_head dev_list; struct hlist_node napi_hash_node; unsigned int napi_id; @@ -38,7 +38,7 @@ Signed-off-by: David S. Miller }; enum { -@@ -364,6 +365,7 @@ enum { +@@ -366,6 +367,7 @@ enum { NAPI_STATE_LISTED, /* NAPI added to system lists */ NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */ NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */ @@ -46,7 +46,7 @@ Signed-off-by: David S. Miller }; enum { -@@ -374,6 +376,7 @@ enum { +@@ -376,6 +378,7 @@ enum { NAPIF_STATE_LISTED = BIT(NAPI_STATE_LISTED), NAPIF_STATE_NO_BUSY_POLL = BIT(NAPI_STATE_NO_BUSY_POLL), NAPIF_STATE_IN_BUSY_POLL = BIT(NAPI_STATE_IN_BUSY_POLL), @@ -54,7 +54,7 @@ Signed-off-by: David S. Miller }; enum gro_result { -@@ -504,20 +507,7 @@ static inline bool napi_complete(struct +@@ -506,20 +509,7 @@ static inline bool napi_complete(struct */ void napi_disable(struct napi_struct *n); @@ -76,7 +76,7 @@ Signed-off-by: David S. Miller /** * napi_synchronize - wait until NAPI is not running -@@ -1863,6 +1853,8 @@ enum netdev_ml_priv_type { +@@ -1865,6 +1855,8 @@ enum netdev_ml_priv_type { * * @wol_enabled: Wake-on-LAN is enabled * @@ -85,7 +85,7 @@ Signed-off-by: David S. Miller * @net_notifier_list: List of per-net netdev notifier block * that follow this device when it is moved * to another network namespace. -@@ -2182,6 +2174,7 @@ struct net_device { +@@ -2184,6 +2176,7 @@ struct net_device { struct lock_class_key *qdisc_running_key; bool proto_down; unsigned wol_enabled:1; diff --git a/target/linux/generic/backport-5.10/602-v5.12-net-add-sysfs-attribute-to-control-napi-threaded-mod.patch b/target/linux/generic/backport-5.10/602-v5.12-net-add-sysfs-attribute-to-control-napi-threaded-mod.patch index fdb06703ee8..05d5f59f807 100644 --- a/target/linux/generic/backport-5.10/602-v5.12-net-add-sysfs-attribute-to-control-napi-threaded-mod.patch +++ b/target/linux/generic/backport-5.10/602-v5.12-net-add-sysfs-attribute-to-control-napi-threaded-mod.patch @@ -46,7 +46,7 @@ Signed-off-by: David S. Miller + == ================================== --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -498,6 +498,8 @@ static inline bool napi_complete(struct +@@ -500,6 +500,8 @@ static inline bool napi_complete(struct return napi_complete_done(n, 0); } diff --git a/target/linux/generic/backport-5.10/603-v5.12-net-fix-race-between-napi-kthread-mode-and-busy-poll.patch b/target/linux/generic/backport-5.10/603-v5.12-net-fix-race-between-napi-kthread-mode-and-busy-poll.patch index 1afeb9ddc58..b83078d51c7 100644 --- a/target/linux/generic/backport-5.10/603-v5.12-net-fix-race-between-napi-kthread-mode-and-busy-poll.patch +++ b/target/linux/generic/backport-5.10/603-v5.12-net-fix-race-between-napi-kthread-mode-and-busy-poll.patch @@ -27,7 +27,7 @@ Cc: Hannes Frederic Sowa --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -366,6 +366,7 @@ enum { +@@ -368,6 +368,7 @@ enum { NAPI_STATE_NO_BUSY_POLL,/* Do not add in napi_hash, no busy polling */ NAPI_STATE_IN_BUSY_POLL,/* sk_busy_loop() owns this NAPI */ NAPI_STATE_THREADED, /* The poll is performed inside its own thread*/ @@ -35,7 +35,7 @@ Cc: Hannes Frederic Sowa }; enum { -@@ -377,6 +378,7 @@ enum { +@@ -379,6 +380,7 @@ enum { NAPIF_STATE_NO_BUSY_POLL = BIT(NAPI_STATE_NO_BUSY_POLL), NAPIF_STATE_IN_BUSY_POLL = BIT(NAPI_STATE_IN_BUSY_POLL), NAPIF_STATE_THREADED = BIT(NAPI_STATE_THREADED), diff --git a/target/linux/generic/backport-5.10/610-v5.13-11-net-resolve-forwarding-path-from-virtual-netdevice-a.patch b/target/linux/generic/backport-5.10/610-v5.13-11-net-resolve-forwarding-path-from-virtual-netdevice-a.patch index 30a183a4a57..b3c0c2e9271 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-11-net-resolve-forwarding-path-from-virtual-netdevice-a.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-11-net-resolve-forwarding-path-from-virtual-netdevice-a.patch @@ -58,7 +58,7 @@ Signed-off-by: Pablo Neira Ayuso --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -848,6 +848,27 @@ typedef u16 (*select_queue_fallback_t)(s +@@ -850,6 +850,27 @@ typedef u16 (*select_queue_fallback_t)(s struct sk_buff *skb, struct net_device *sb_dev); @@ -86,7 +86,7 @@ Signed-off-by: Pablo Neira Ayuso enum tc_setup_type { TC_SETUP_QDISC_MQPRIO, TC_SETUP_CLSU32, -@@ -1294,6 +1315,8 @@ struct netdev_net_notifier { +@@ -1296,6 +1317,8 @@ struct netdev_net_notifier { * struct net_device *(*ndo_get_peer_dev)(struct net_device *dev); * If a device is paired with a peer device, return the peer instance. * The caller must be under RCU read context. @@ -95,7 +95,7 @@ Signed-off-by: Pablo Neira Ayuso */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); -@@ -1502,6 +1525,8 @@ struct net_device_ops { +@@ -1504,6 +1527,8 @@ struct net_device_ops { int (*ndo_tunnel_ctl)(struct net_device *dev, struct ip_tunnel_parm *p, int cmd); struct net_device * (*ndo_get_peer_dev)(struct net_device *dev); @@ -104,7 +104,7 @@ Signed-off-by: Pablo Neira Ayuso }; /** -@@ -2849,6 +2874,8 @@ void dev_remove_offload(struct packet_of +@@ -2851,6 +2876,8 @@ void dev_remove_offload(struct packet_of int dev_get_iflink(const struct net_device *dev); int dev_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb); diff --git a/target/linux/generic/backport-5.10/610-v5.13-12-net-8021q-resolve-forwarding-path-for-vlan-devices.patch b/target/linux/generic/backport-5.10/610-v5.13-12-net-8021q-resolve-forwarding-path-for-vlan-devices.patch index 4da3e388e78..a906dc06ce0 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-12-net-8021q-resolve-forwarding-path-for-vlan-devices.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-12-net-8021q-resolve-forwarding-path-for-vlan-devices.patch @@ -28,7 +28,7 @@ Signed-off-by: Pablo Neira Ayuso --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -850,11 +850,18 @@ typedef u16 (*select_queue_fallback_t)(s +@@ -852,11 +852,18 @@ typedef u16 (*select_queue_fallback_t)(s enum net_device_path_type { DEV_PATH_ETHERNET = 0, diff --git a/target/linux/generic/backport-5.10/610-v5.13-13-net-bridge-resolve-forwarding-path-for-bridge-device.patch b/target/linux/generic/backport-5.10/610-v5.13-13-net-bridge-resolve-forwarding-path-for-bridge-device.patch index d6bbc77abee..f5a6dd6ebc1 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-13-net-bridge-resolve-forwarding-path-for-bridge-device.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-13-net-bridge-resolve-forwarding-path-for-bridge-device.patch @@ -9,7 +9,7 @@ Signed-off-by: Pablo Neira Ayuso --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -851,6 +851,7 @@ typedef u16 (*select_queue_fallback_t)(s +@@ -853,6 +853,7 @@ typedef u16 (*select_queue_fallback_t)(s enum net_device_path_type { DEV_PATH_ETHERNET = 0, DEV_PATH_VLAN, diff --git a/target/linux/generic/backport-5.10/610-v5.13-14-net-bridge-resolve-forwarding-path-for-VLAN-tag-acti.patch b/target/linux/generic/backport-5.10/610-v5.13-14-net-bridge-resolve-forwarding-path-for-VLAN-tag-acti.patch index cf110cd066b..9e62546a6cc 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-14-net-bridge-resolve-forwarding-path-for-VLAN-tag-acti.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-14-net-bridge-resolve-forwarding-path-for-VLAN-tag-acti.patch @@ -15,7 +15,7 @@ Signed-off-by: Pablo Neira Ayuso --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -862,10 +862,20 @@ struct net_device_path { +@@ -864,10 +864,20 @@ struct net_device_path { u16 id; __be16 proto; } encap; @@ -36,7 +36,7 @@ Signed-off-by: Pablo Neira Ayuso struct net_device_path_stack { int num_paths; -@@ -875,6 +885,12 @@ struct net_device_path_stack { +@@ -877,6 +887,12 @@ struct net_device_path_stack { struct net_device_path_ctx { const struct net_device *dev; const u8 *daddr; diff --git a/target/linux/generic/backport-5.10/610-v5.13-15-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch b/target/linux/generic/backport-5.10/610-v5.13-15-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch index 8e76c1fa523..c714ff05841 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-15-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-15-net-ppp-resolve-forwarding-path-for-bridge-pppoe-dev.patch @@ -83,7 +83,7 @@ Signed-off-by: Pablo Neira Ayuso static int pppoe_recvmsg(struct socket *sock, struct msghdr *m, --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -852,6 +852,7 @@ enum net_device_path_type { +@@ -854,6 +854,7 @@ enum net_device_path_type { DEV_PATH_ETHERNET = 0, DEV_PATH_VLAN, DEV_PATH_BRIDGE, @@ -91,7 +91,7 @@ Signed-off-by: Pablo Neira Ayuso }; struct net_device_path { -@@ -861,6 +862,7 @@ struct net_device_path { +@@ -863,6 +864,7 @@ struct net_device_path { struct { u16 id; __be16 proto; diff --git a/target/linux/generic/backport-5.10/610-v5.13-16-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch b/target/linux/generic/backport-5.10/610-v5.13-16-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch index 5c1a0bdf136..a277f0ccf04 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-16-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-16-net-dsa-resolve-forwarding-path-for-dsa-slave-ports.patch @@ -10,7 +10,7 @@ Signed-off-by: Pablo Neira Ayuso --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -853,6 +853,7 @@ enum net_device_path_type { +@@ -855,6 +855,7 @@ enum net_device_path_type { DEV_PATH_VLAN, DEV_PATH_BRIDGE, DEV_PATH_PPPOE, @@ -18,7 +18,7 @@ Signed-off-by: Pablo Neira Ayuso }; struct net_device_path { -@@ -873,6 +874,10 @@ struct net_device_path { +@@ -875,6 +876,10 @@ struct net_device_path { u16 vlan_id; __be16 vlan_proto; } bridge; diff --git a/target/linux/generic/backport-5.10/610-v5.13-27-netfilter-flowtable-bridge-vlan-hardware-offload-and.patch b/target/linux/generic/backport-5.10/610-v5.13-27-netfilter-flowtable-bridge-vlan-hardware-offload-and.patch index 37f9033b112..08c92d731a3 100644 --- a/target/linux/generic/backport-5.10/610-v5.13-27-netfilter-flowtable-bridge-vlan-hardware-offload-and.patch +++ b/target/linux/generic/backport-5.10/610-v5.13-27-netfilter-flowtable-bridge-vlan-hardware-offload-and.patch @@ -12,7 +12,7 @@ Signed-off-by: Pablo Neira Ayuso --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -870,6 +870,7 @@ struct net_device_path { +@@ -872,6 +872,7 @@ struct net_device_path { DEV_PATH_BR_VLAN_KEEP, DEV_PATH_BR_VLAN_TAG, DEV_PATH_BR_VLAN_UNTAG, diff --git a/target/linux/generic/backport-5.10/615-v5.14-ip-Treat-IPv4-segment-s-lowest-address-as-unicast.patch b/target/linux/generic/backport-5.10/615-v5.14-ip-Treat-IPv4-segment-s-lowest-address-as-unicast.patch index 8aabbe28599..76e50d15eb2 100644 --- a/target/linux/generic/backport-5.10/615-v5.14-ip-Treat-IPv4-segment-s-lowest-address-as-unicast.patch +++ b/target/linux/generic/backport-5.10/615-v5.14-ip-Treat-IPv4-segment-s-lowest-address-as-unicast.patch @@ -18,7 +18,7 @@ Link: https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git/commit --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c -@@ -1129,10 +1129,8 @@ void fib_add_ifaddr(struct in_ifaddr *if +@@ -1132,10 +1132,8 @@ void fib_add_ifaddr(struct in_ifaddr *if prefix, ifa->ifa_prefixlen, prim, ifa->ifa_rt_priority); diff --git a/target/linux/generic/backport-5.15/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch b/target/linux/generic/backport-5.15/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch index 2a4207c3b54..df854ffd3d8 100644 --- a/target/linux/generic/backport-5.15/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch +++ b/target/linux/generic/backport-5.15/020-v6.1-01-mm-x86-arm64-add-arch_has_hw_pte_young.patch @@ -1,104 +1,360 @@ -From a8e6015d9534f39abc08e6804566af059e498a60 Mon Sep 17 00:00:00 2001 +From a4103262b01a1b8704b37c01c7c813df91b7b119 Mon Sep 17 00:00:00 2001 From: Yu Zhao -Date: Wed, 4 Aug 2021 01:31:34 -0600 -Subject: [PATCH 01/10] mm: x86, arm64: add arch_has_hw_pte_young() +Date: Sun, 18 Sep 2022 01:59:58 -0600 +Subject: [PATCH 01/29] mm: x86, arm64: add arch_has_hw_pte_young() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit -Some architectures automatically set the accessed bit in PTEs, e.g., -x86 and arm64 v8.2. On architectures that do not have this capability, -clearing the accessed bit in a PTE triggers a page fault following the -TLB miss of this PTE. +Patch series "Multi-Gen LRU Framework", v14. -Being aware of this capability can help make better decisions, i.e., -whether to limit the size of each batch of PTEs and the burst of -batches when clearing the accessed bit. +What's new +========== +1. OpenWrt, in addition to Android, Arch Linux Zen, Armbian, ChromeOS, + Liquorix, post-factum and XanMod, is now shipping MGLRU on 5.15. +2. Fixed long-tailed direct reclaim latency seen on high-memory (TBs) + machines. The old direct reclaim backoff, which tries to enforce a + minimum fairness among all eligible memcgs, over-swapped by about + (total_mem>>DEF_PRIORITY)-nr_to_reclaim. The new backoff, which + pulls the plug on swapping once the target is met, trades some + fairness for curtailed latency: + https://lore.kernel.org/r/20220918080010.2920238-10-yuzhao@google.com/ +3. Fixed minior build warnings and conflicts. More comments and nits. +TLDR +==== +The current page reclaim is too expensive in terms of CPU usage and it +often makes poor choices about what to evict. This patchset offers an +alternative solution that is performant, versatile and +straightforward. + +Patchset overview +================= +The design and implementation overview is in patch 14: +https://lore.kernel.org/r/20220918080010.2920238-15-yuzhao@google.com/ + +01. mm: x86, arm64: add arch_has_hw_pte_young() +02. mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG +Take advantage of hardware features when trying to clear the accessed +bit in many PTEs. + +03. mm/vmscan.c: refactor shrink_node() +04. Revert "include/linux/mm_inline.h: fold __update_lru_size() into + its sole caller" +Minor refactors to improve readability for the following patches. + +05. mm: multi-gen LRU: groundwork +Adds the basic data structure and the functions that insert pages to +and remove pages from the multi-gen LRU (MGLRU) lists. + +06. mm: multi-gen LRU: minimal implementation +A minimal implementation without optimizations. + +07. mm: multi-gen LRU: exploit locality in rmap +Exploits spatial locality to improve efficiency when using the rmap. + +08. mm: multi-gen LRU: support page table walks +Further exploits spatial locality by optionally scanning page tables. + +09. mm: multi-gen LRU: optimize multiple memcgs +Optimizes the overall performance for multiple memcgs running mixed +types of workloads. + +10. mm: multi-gen LRU: kill switch +Adds a kill switch to enable or disable MGLRU at runtime. + +11. mm: multi-gen LRU: thrashing prevention +12. mm: multi-gen LRU: debugfs interface +Provide userspace with features like thrashing prevention, working set +estimation and proactive reclaim. + +13. mm: multi-gen LRU: admin guide +14. mm: multi-gen LRU: design doc +Add an admin guide and a design doc. + +Benchmark results +================= +Independent lab results +----------------------- +Based on the popularity of searches [01] and the memory usage in +Google's public cloud, the most popular open-source memory-hungry +applications, in alphabetical order, are: + Apache Cassandra Memcached + Apache Hadoop MongoDB + Apache Spark PostgreSQL + MariaDB (MySQL) Redis + +An independent lab evaluated MGLRU with the most widely used benchmark +suites for the above applications. They posted 960 data points along +with kernel metrics and perf profiles collected over more than 500 +hours of total benchmark time. Their final reports show that, with 95% +confidence intervals (CIs), the above applications all performed +significantly better for at least part of their benchmark matrices. + +On 5.14: +1. Apache Spark [02] took 95% CIs [9.28, 11.19]% and [12.20, 14.93]% + less wall time to sort three billion random integers, respectively, + under the medium- and the high-concurrency conditions, when + overcommitting memory. There were no statistically significant + changes in wall time for the rest of the benchmark matrix. +2. MariaDB [03] achieved 95% CIs [5.24, 10.71]% and [20.22, 25.97]% + more transactions per minute (TPM), respectively, under the medium- + and the high-concurrency conditions, when overcommitting memory. + There were no statistically significant changes in TPM for the rest + of the benchmark matrix. +3. Memcached [04] achieved 95% CIs [23.54, 32.25]%, [20.76, 41.61]% + and [21.59, 30.02]% more operations per second (OPS), respectively, + for sequential access, random access and Gaussian (distribution) + access, when THP=always; 95% CIs [13.85, 15.97]% and + [23.94, 29.92]% more OPS, respectively, for random access and + Gaussian access, when THP=never. There were no statistically + significant changes in OPS for the rest of the benchmark matrix. +4. MongoDB [05] achieved 95% CIs [2.23, 3.44]%, [6.97, 9.73]% and + [2.16, 3.55]% more operations per second (OPS), respectively, for + exponential (distribution) access, random access and Zipfian + (distribution) access, when underutilizing memory; 95% CIs + [8.83, 10.03]%, [21.12, 23.14]% and [5.53, 6.46]% more OPS, + respectively, for exponential access, random access and Zipfian + access, when overcommitting memory. + +On 5.15: +5. Apache Cassandra [06] achieved 95% CIs [1.06, 4.10]%, [1.94, 5.43]% + and [4.11, 7.50]% more operations per second (OPS), respectively, + for exponential (distribution) access, random access and Zipfian + (distribution) access, when swap was off; 95% CIs [0.50, 2.60]%, + [6.51, 8.77]% and [3.29, 6.75]% more OPS, respectively, for + exponential access, random access and Zipfian access, when swap was + on. +6. Apache Hadoop [07] took 95% CIs [5.31, 9.69]% and [2.02, 7.86]% + less average wall time to finish twelve parallel TeraSort jobs, + respectively, under the medium- and the high-concurrency + conditions, when swap was on. There were no statistically + significant changes in average wall time for the rest of the + benchmark matrix. +7. PostgreSQL [08] achieved 95% CI [1.75, 6.42]% more transactions per + minute (TPM) under the high-concurrency condition, when swap was + off; 95% CIs [12.82, 18.69]% and [22.70, 46.86]% more TPM, + respectively, under the medium- and the high-concurrency + conditions, when swap was on. There were no statistically + significant changes in TPM for the rest of the benchmark matrix. +8. Redis [09] achieved 95% CIs [0.58, 5.94]%, [6.55, 14.58]% and + [11.47, 19.36]% more total operations per second (OPS), + respectively, for sequential access, random access and Gaussian + (distribution) access, when THP=always; 95% CIs [1.27, 3.54]%, + [10.11, 14.81]% and [8.75, 13.64]% more total OPS, respectively, + for sequential access, random access and Gaussian access, when + THP=never. + +Our lab results +--------------- +To supplement the above results, we ran the following benchmark suites +on 5.16-rc7 and found no regressions [10]. + fs_fio_bench_hdd_mq pft + fs_lmbench pgsql-hammerdb + fs_parallelio redis + fs_postmark stream + hackbench sysbenchthread + kernbench tpcc_spark + memcached unixbench + multichase vm-scalability + mutilate will-it-scale + nginx + +[01] https://trends.google.com +[02] https://lore.kernel.org/r/20211102002002.92051-1-bot@edi.works/ +[03] https://lore.kernel.org/r/20211009054315.47073-1-bot@edi.works/ +[04] https://lore.kernel.org/r/20211021194103.65648-1-bot@edi.works/ +[05] https://lore.kernel.org/r/20211109021346.50266-1-bot@edi.works/ +[06] https://lore.kernel.org/r/20211202062806.80365-1-bot@edi.works/ +[07] https://lore.kernel.org/r/20211209072416.33606-1-bot@edi.works/ +[08] https://lore.kernel.org/r/20211218071041.24077-1-bot@edi.works/ +[09] https://lore.kernel.org/r/20211122053248.57311-1-bot@edi.works/ +[10] https://lore.kernel.org/r/20220104202247.2903702-1-yuzhao@google.com/ + +Read-world applications +======================= +Third-party testimonials +------------------------ +Konstantin reported [11]: + I have Archlinux with 8G RAM + zswap + swap. While developing, I + have lots of apps opened such as multiple LSP-servers for different + langs, chats, two browsers, etc... Usually, my system gets quickly + to a point of SWAP-storms, where I have to kill LSP-servers, + restart browsers to free memory, etc, otherwise the system lags + heavily and is barely usable. + + 1.5 day ago I migrated from 5.11.15 kernel to 5.12 + the LRU + patchset, and I started up by opening lots of apps to create memory + pressure, and worked for a day like this. Till now I had not a + single SWAP-storm, and mind you I got 3.4G in SWAP. I was never + getting to the point of 3G in SWAP before without a single + SWAP-storm. + +Vaibhav from IBM reported [12]: + In a synthetic MongoDB Benchmark, seeing an average of ~19% + throughput improvement on POWER10(Radix MMU + 64K Page Size) with + MGLRU patches on top of 5.16 kernel for MongoDB + YCSB across + three different request distributions, namely, Exponential, Uniform + and Zipfan. + +Shuang from U of Rochester reported [13]: + With the MGLRU, fio achieved 95% CIs [38.95, 40.26]%, [4.12, 6.64]% + and [9.26, 10.36]% higher throughput, respectively, for random + access, Zipfian (distribution) access and Gaussian (distribution) + access, when the average number of jobs per CPU is 1; 95% CIs + [42.32, 49.15]%, [9.44, 9.89]% and [20.99, 22.86]% higher + throughput, respectively, for random access, Zipfian access and + Gaussian access, when the average number of jobs per CPU is 2. + +Daniel from Michigan Tech reported [14]: + With Memcached allocating ~100GB of byte-addressable Optante, + performance improvement in terms of throughput (measured as queries + per second) was about 10% for a series of workloads. + +Large-scale deployments +----------------------- +We've rolled out MGLRU to tens of millions of ChromeOS users and +about a million Android users. Google's fleetwide profiling [15] shows +an overall 40% decrease in kswapd CPU usage, in addition to +improvements in other UX metrics, e.g., an 85% decrease in the number +of low-memory kills at the 75th percentile and an 18% decrease in +app launch time at the 50th percentile. + +The downstream kernels that have been using MGLRU include: +1. Android [16] +2. Arch Linux Zen [17] +3. Armbian [18] +4. ChromeOS [19] +5. Liquorix [20] +6. OpenWrt [21] +7. post-factum [22] +8. XanMod [23] + +[11] https://lore.kernel.org/r/140226722f2032c86301fbd326d91baefe3d7d23.camel@yandex.ru/ +[12] https://lore.kernel.org/r/87czj3mux0.fsf@vajain21.in.ibm.com/ +[13] https://lore.kernel.org/r/20220105024423.26409-1-szhai2@cs.rochester.edu/ +[14] https://lore.kernel.org/r/CA+4-3vksGvKd18FgRinxhqHetBS1hQekJE2gwco8Ja-bJWKtFw@mail.gmail.com/ +[15] https://dl.acm.org/doi/10.1145/2749469.2750392 +[16] https://android.com +[17] https://archlinux.org +[18] https://armbian.com +[19] https://chromium.org +[20] https://liquorix.net +[21] https://openwrt.org +[22] https://codeberg.org/pf-kernel +[23] https://xanmod.org + +Summary +======= +The facts are: +1. The independent lab results and the real-world applications + indicate substantial improvements; there are no known regressions. +2. Thrashing prevention, working set estimation and proactive reclaim + work out of the box; there are no equivalent solutions. +3. There is a lot of new code; no smaller changes have been + demonstrated similar effects. + +Our options, accordingly, are: +1. Given the amount of evidence, the reported improvements will likely + materialize for a wide range of workloads. +2. Gauging the interest from the past discussions, the new features + will likely be put to use for both personal computers and data + centers. +3. Based on Google's track record, the new code will likely be well + maintained in the long term. It'd be more difficult if not + impossible to achieve similar effects with other approaches. + +This patch (of 14): + +Some architectures automatically set the accessed bit in PTEs, e.g., x86 +and arm64 v8.2. On architectures that do not have this capability, +clearing the accessed bit in a PTE usually triggers a page fault following +the TLB miss of this PTE (to emulate the accessed bit). + +Being aware of this capability can help make better decisions, e.g., +whether to spread the work out over a period of time to reduce bursty page +faults when trying to clear the accessed bit in many PTEs. + +Note that theoretically this capability can be unreliable, e.g., +hotplugged CPUs might be different from builtin ones. Therefore it should +not be used in architecture-independent code that involves correctness, +e.g., to determine whether TLB flushes are required (in combination with +the accessed bit). + +Link: https://lkml.kernel.org/r/20220918080010.2920238-1-yuzhao@google.com +Link: https://lkml.kernel.org/r/20220918080010.2920238-2-yuzhao@google.com Signed-off-by: Yu Zhao -Change-Id: Ib49b44fb56df3333a2ff1fcc496fb1980b976e7a +Reviewed-by: Barry Song +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Acked-by: Will Deacon +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: linux-arm-kernel@lists.infradead.org +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Miaohe Lin +Cc: Mike Rapoport +Cc: Qi Zheng +Signed-off-by: Andrew Morton --- - arch/arm64/include/asm/cpufeature.h | 5 +++++ - arch/arm64/include/asm/pgtable.h | 13 ++++++++----- - arch/arm64/kernel/cpufeature.c | 10 ++++++++++ - arch/arm64/tools/cpucaps | 1 + - arch/x86/include/asm/pgtable.h | 6 +++--- - include/linux/pgtable.h | 13 +++++++++++++ - mm/memory.c | 14 +------------- - 7 files changed, 41 insertions(+), 21 deletions(-) + arch/arm64/include/asm/pgtable.h | 14 ++------------ + arch/x86/include/asm/pgtable.h | 6 +++--- + include/linux/pgtable.h | 13 +++++++++++++ + mm/memory.c | 14 +------------- + 4 files changed, 19 insertions(+), 28 deletions(-) ---- a/arch/arm64/include/asm/cpufeature.h -+++ b/arch/arm64/include/asm/cpufeature.h -@@ -808,6 +808,11 @@ static inline bool system_supports_tlb_r - cpus_have_const_cap(ARM64_HAS_TLB_RANGE); - } - -+static inline bool system_has_hw_af(void) -+{ -+ return IS_ENABLED(CONFIG_ARM64_HW_AFDBM) && cpus_have_const_cap(ARM64_HW_AF); -+} -+ - extern int do_emulate_mrs(struct pt_regs *regs, u32 sys_reg, u32 rt); - - static inline u32 id_aa64mmfr0_parange_to_phys_shift(int parange) --- a/arch/arm64/include/asm/pgtable.h +++ b/arch/arm64/include/asm/pgtable.h -@@ -999,13 +999,16 @@ static inline void update_mmu_cache(stru +@@ -999,23 +999,13 @@ static inline void update_mmu_cache(stru * page after fork() + CoW for pfn mappings. We don't always have a * hardware-managed access flag on arm64. */ -static inline bool arch_faults_on_old_pte(void) -+static inline bool arch_has_hw_pte_young(bool local) - { +-{ - WARN_ON(preemptible()); -+ if (local) { -+ WARN_ON(preemptible()); -+ return cpu_has_hw_af(); -+ } - +- - return !cpu_has_hw_af(); -+ return system_has_hw_af(); - } +-} -#define arch_faults_on_old_pte arch_faults_on_old_pte -+#define arch_has_hw_pte_young arch_has_hw_pte_young ++#define arch_has_hw_pte_young cpu_has_hw_af /* * Experimentally, it's cheap to set the access flag in hardware and we -@@ -1013,7 +1016,7 @@ static inline bool arch_faults_on_old_pt + * benefit from prefaulting mappings as 'old' to start with. */ - static inline bool arch_wants_old_prefaulted_pte(void) - { +-static inline bool arch_wants_old_prefaulted_pte(void) +-{ - return !arch_faults_on_old_pte(); -+ return arch_has_hw_pte_young(true); - } - #define arch_wants_old_prefaulted_pte arch_wants_old_prefaulted_pte +-} +-#define arch_wants_old_prefaulted_pte arch_wants_old_prefaulted_pte ++#define arch_wants_old_prefaulted_pte cpu_has_hw_af + + #endif /* !__ASSEMBLY__ */ ---- a/arch/arm64/kernel/cpufeature.c -+++ b/arch/arm64/kernel/cpufeature.c -@@ -2197,6 +2197,16 @@ static const struct arm64_cpu_capabiliti - .matches = has_hw_dbm, - .cpu_enable = cpu_enable_hw_dbm, - }, -+ { -+ .desc = "Hardware update of the Access flag", -+ .type = ARM64_CPUCAP_SYSTEM_FEATURE, -+ .capability = ARM64_HW_AF, -+ .sys_reg = SYS_ID_AA64MMFR1_EL1, -+ .sign = FTR_UNSIGNED, -+ .field_pos = ID_AA64MMFR1_HADBS_SHIFT, -+ .min_field_value = 1, -+ .matches = has_cpuid_feature, -+ }, - #endif - { - .desc = "CRC32 instructions", ---- a/arch/arm64/tools/cpucaps -+++ b/arch/arm64/tools/cpucaps -@@ -35,6 +35,7 @@ HAS_STAGE2_FWB - HAS_SYSREG_GIC_CPUIF - HAS_TLB_RANGE - HAS_VIRT_HOST_EXTN -+HW_AF - HW_DBM - KVM_PROTECTED_MODE - MISMATCHED_CACHE_TYPE --- a/arch/x86/include/asm/pgtable.h +++ b/arch/x86/include/asm/pgtable.h @@ -1397,10 +1397,10 @@ static inline bool arch_has_pfn_modify_c @@ -108,7 +364,7 @@ Change-Id: Ib49b44fb56df3333a2ff1fcc496fb1980b976e7a -#define arch_faults_on_old_pte arch_faults_on_old_pte -static inline bool arch_faults_on_old_pte(void) +#define arch_has_hw_pte_young arch_has_hw_pte_young -+static inline bool arch_has_hw_pte_young(bool local) ++static inline bool arch_has_hw_pte_young(void) { - return false; + return true; @@ -123,12 +379,12 @@ Change-Id: Ib49b44fb56df3333a2ff1fcc496fb1980b976e7a +#ifndef arch_has_hw_pte_young +/* -+ * Return whether the accessed bit is supported by the local CPU or all CPUs. ++ * Return whether the accessed bit is supported on the local CPU. + * -+ * Those arches which have hw access flag feature need to implement their own -+ * helper. By default, "false" means pagefault will be hit on old pte. ++ * This stub assumes accessing through an old PTE triggers a page fault. ++ * Architectures that automatically set the access bit should overwrite it. + */ -+static inline bool arch_has_hw_pte_young(bool local) ++static inline bool arch_has_hw_pte_young(void) +{ + return false; +} @@ -163,7 +419,7 @@ Change-Id: Ib49b44fb56df3333a2ff1fcc496fb1980b976e7a * take a double page fault, so mark it accessed here. */ - if (arch_faults_on_old_pte() && !pte_young(vmf->orig_pte)) { -+ if (!arch_has_hw_pte_young(true) && !pte_young(vmf->orig_pte)) { ++ if (!arch_has_hw_pte_young() && !pte_young(vmf->orig_pte)) { pte_t entry; vmf->pte = pte_offset_map_lock(mm, vmf->pmd, addr, &vmf->ptl); diff --git a/target/linux/generic/backport-5.15/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch b/target/linux/generic/backport-5.15/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch index 785af275f50..9e0430ea2aa 100644 --- a/target/linux/generic/backport-5.15/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch +++ b/target/linux/generic/backport-5.15/020-v6.1-02-mm-x86-add-CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG.patch @@ -1,47 +1,89 @@ -From f8b663bbfa30af5515e222fd74df20ea4e8393a2 Mon Sep 17 00:00:00 2001 +From 493de1c4b0f2cd909169401da8c445f6c8a7e29d Mon Sep 17 00:00:00 2001 From: Yu Zhao -Date: Sat, 26 Sep 2020 21:17:18 -0600 -Subject: [PATCH 02/10] mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG +Date: Sun, 18 Sep 2022 01:59:59 -0600 +Subject: [PATCH 02/29] mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit -Some architectures support the accessed bit on non-leaf PMD entries, -e.g., x86_64 sets the accessed bit on a non-leaf PMD entry when using -it as part of linear address translation [1]. As an optimization, page -table walkers who are interested in the accessed bit can skip the PTEs -under a non-leaf PMD entry if the accessed bit is cleared on this PMD -entry. +Some architectures support the accessed bit in non-leaf PMD entries, e.g., +x86 sets the accessed bit in a non-leaf PMD entry when using it as part of +linear address translation [1]. Page table walkers that clear the +accessed bit may use this capability to reduce their search space. -Although an inline function may be preferable, this capability is -added as a configuration option to look consistent when used with the -existing macros. +Note that: +1. Although an inline function is preferable, this capability is added + as a configuration option for consistency with the existing macros. +2. Due to the little interest in other varieties, this capability was + only tested on Intel and AMD CPUs. + +Thanks to the following developers for their efforts [2][3]. + Randy Dunlap + Stephen Rothwell [1]: Intel 64 and IA-32 Architectures Software Developer's Manual Volume 3 (June 2021), section 4.8 +[2] https://lore.kernel.org/r/bfdcc7c8-922f-61a9-aa15-7e7250f04af7@infradead.org/ +[3] https://lore.kernel.org/r/20220413151513.5a0d7a7e@canb.auug.org.au/ +Link: https://lkml.kernel.org/r/20220918080010.2920238-3-yuzhao@google.com Signed-off-by: Yu Zhao +Reviewed-by: Barry Song +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte Tested-by: Konstantin Kharlamov -Change-Id: I1a17be3ae926f721f7b17ea1539e5c39e8c4f9a8 +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton --- - arch/Kconfig | 9 +++++++++ + arch/Kconfig | 8 ++++++++ arch/x86/Kconfig | 1 + arch/x86/include/asm/pgtable.h | 3 ++- arch/x86/mm/pgtable.c | 5 ++++- include/linux/pgtable.h | 4 ++-- - 5 files changed, 18 insertions(+), 4 deletions(-) + 5 files changed, 17 insertions(+), 4 deletions(-) --- a/arch/Kconfig +++ b/arch/Kconfig -@@ -1295,6 +1295,15 @@ config ARCH_HAS_ELFCORE_COMPAT +@@ -1295,6 +1295,14 @@ config ARCH_HAS_ELFCORE_COMPAT config ARCH_HAS_PARANOID_L1D_FLUSH bool +config ARCH_HAS_NONLEAF_PMD_YOUNG + bool -+ depends on PGTABLE_LEVELS > 2 + help -+ Architectures that select this are able to set the accessed bit on -+ non-leaf PMD entries in addition to leaf PTE entries where pages are -+ mapped. For them, page table walkers that clear the accessed bit may -+ stop at non-leaf PMD entries if they do not see the accessed bit. ++ Architectures that select this option are capable of setting the ++ accessed bit in non-leaf PMD entries when using them as part of linear ++ address translations. Page table walkers that clear the accessed bit ++ may use this capability to reduce their search space. + source "kernel/gcov/Kconfig" @@ -52,7 +94,7 @@ Change-Id: I1a17be3ae926f721f7b17ea1539e5c39e8c4f9a8 select ARCH_HAS_PMEM_API if X86_64 select ARCH_HAS_PTE_DEVMAP if X86_64 select ARCH_HAS_PTE_SPECIAL -+ select ARCH_HAS_NONLEAF_PMD_YOUNG if X86_64 ++ select ARCH_HAS_NONLEAF_PMD_YOUNG if PGTABLE_LEVELS > 2 select ARCH_HAS_UACCESS_FLUSHCACHE if X86_64 select ARCH_HAS_COPY_MC if X86_64 select ARCH_HAS_SET_MEMORY diff --git a/target/linux/generic/backport-5.15/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch b/target/linux/generic/backport-5.15/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch index 31161e2a0f5..b8d2917d26c 100644 --- a/target/linux/generic/backport-5.15/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch +++ b/target/linux/generic/backport-5.15/020-v6.1-03-mm-vmscan.c-refactor-shrink_node.patch @@ -1,21 +1,58 @@ -From a810f8e2f1bdd0707eaf05c8b4ba84a3ff2801bd Mon Sep 17 00:00:00 2001 +From 9e17efd11450d3d2069adaa3c58db9ac8ebd1c66 Mon Sep 17 00:00:00 2001 From: Yu Zhao -Date: Sun, 27 Sep 2020 20:49:08 -0600 -Subject: [PATCH 03/10] mm/vmscan.c: refactor shrink_node() +Date: Sun, 18 Sep 2022 02:00:00 -0600 +Subject: [PATCH 03/29] mm/vmscan.c: refactor shrink_node() +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit -This patch refactors shrink_node(). This will make the upcoming -changes to mm/vmscan.c more readable. +This patch refactors shrink_node() to improve readability for the upcoming +changes to mm/vmscan.c. +Link: https://lkml.kernel.org/r/20220918080010.2920238-4-yuzhao@google.com Signed-off-by: Yu Zhao +Reviewed-by: Barry Song +Reviewed-by: Miaohe Lin +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte Tested-by: Konstantin Kharlamov -Change-Id: Iae734b5b4030205b7db6e8c841f747b6f6ae1a04 +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton --- - mm/vmscan.c | 186 +++++++++++++++++++++++++++------------------------- - 1 file changed, 98 insertions(+), 88 deletions(-) + mm/vmscan.c | 198 +++++++++++++++++++++++++++------------------------- + 1 file changed, 104 insertions(+), 94 deletions(-) --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -2497,6 +2497,103 @@ enum scan_balance { +@@ -2497,6 +2497,109 @@ enum scan_balance { SCAN_FILE, }; @@ -27,6 +64,12 @@ Change-Id: Iae734b5b4030205b7db6e8c841f747b6f6ae1a04 + target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat); + + /* ++ * Flush the memory cgroup stats, so that we read accurate per-memcg ++ * lruvec stats for heuristics. ++ */ ++ mem_cgroup_flush_stats(); ++ ++ /* + * Determine the scan balance between anon and file LRUs. + */ + spin_lock_irq(&target_lruvec->lru_lock); @@ -119,7 +162,7 @@ Change-Id: Iae734b5b4030205b7db6e8c841f747b6f6ae1a04 /* * Determine how aggressively the anon and file LRU lists should be * scanned. The relative value of each set of LRU lists is determined -@@ -2965,7 +3062,6 @@ static void shrink_node(pg_data_t *pgdat +@@ -2965,109 +3068,16 @@ static void shrink_node(pg_data_t *pgdat unsigned long nr_reclaimed, nr_scanned; struct lruvec *target_lruvec; bool reclaimable = false; @@ -127,7 +170,15 @@ Change-Id: Iae734b5b4030205b7db6e8c841f747b6f6ae1a04 target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat); -@@ -2981,93 +3077,7 @@ again: + again: +- /* +- * Flush the memory cgroup stats, so that we read accurate per-memcg +- * lruvec stats for heuristics. +- */ +- mem_cgroup_flush_stats(); +- + memset(&sc->nr, 0, sizeof(sc->nr)); + nr_reclaimed = sc->nr_reclaimed; nr_scanned = sc->nr_scanned; diff --git a/target/linux/generic/backport-5.15/020-v6.1-04-Revert-include-linux-mm_inline.h-fold-__update_lru_s.patch b/target/linux/generic/backport-5.15/020-v6.1-04-Revert-include-linux-mm_inline.h-fold-__update_lru_s.patch new file mode 100644 index 00000000000..2f277a56e1c --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-04-Revert-include-linux-mm_inline.h-fold-__update_lru_s.patch @@ -0,0 +1,82 @@ +From 03705be42114db7cc5bd6eb7bf7e8703c94d4880 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:01 -0600 +Subject: [PATCH 04/29] Revert "include/linux/mm_inline.h: fold + __update_lru_size() into its sole caller" +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +This patch undoes the following refactor: commit 289ccba18af4 +("include/linux/mm_inline.h: fold __update_lru_size() into its sole +caller") + +The upcoming changes to include/linux/mm_inline.h will reuse +__update_lru_size(). + +Link: https://lkml.kernel.org/r/20220918080010.2920238-5-yuzhao@google.com +Signed-off-by: Yu Zhao +Reviewed-by: Miaohe Lin +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Barry Song +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + include/linux/mm_inline.h | 9 ++++++++- + 1 file changed, 8 insertions(+), 1 deletion(-) + +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -24,7 +24,7 @@ static inline int page_is_file_lru(struc + return !PageSwapBacked(page); + } + +-static __always_inline void update_lru_size(struct lruvec *lruvec, ++static __always_inline void __update_lru_size(struct lruvec *lruvec, + enum lru_list lru, enum zone_type zid, + int nr_pages) + { +@@ -33,6 +33,13 @@ static __always_inline void update_lru_s + __mod_lruvec_state(lruvec, NR_LRU_BASE + lru, nr_pages); + __mod_zone_page_state(&pgdat->node_zones[zid], + NR_ZONE_LRU_BASE + lru, nr_pages); ++} ++ ++static __always_inline void update_lru_size(struct lruvec *lruvec, ++ enum lru_list lru, enum zone_type zid, ++ long nr_pages) ++{ ++ __update_lru_size(lruvec, lru, zid, nr_pages); + #ifdef CONFIG_MEMCG + mem_cgroup_update_lru_size(lruvec, lru, zid, nr_pages); + #endif diff --git a/target/linux/generic/backport-5.15/020-v6.1-04-mm-multigenerational-lru-groundwork.patch b/target/linux/generic/backport-5.15/020-v6.1-04-mm-multigenerational-lru-groundwork.patch deleted file mode 100644 index 1cb82d1b8f9..00000000000 --- a/target/linux/generic/backport-5.15/020-v6.1-04-mm-multigenerational-lru-groundwork.patch +++ /dev/null @@ -1,996 +0,0 @@ -From 05f366c941ae2bb8ba21c79fafcb747a5a6b967b Mon Sep 17 00:00:00 2001 -From: Yu Zhao -Date: Mon, 25 Jan 2021 21:12:33 -0700 -Subject: [PATCH 04/10] mm: multigenerational lru: groundwork - -For each lruvec, evictable pages are divided into multiple -generations. The youngest generation number is stored in -lrugen->max_seq for both anon and file types as they are aged on an -equal footing. The oldest generation numbers are stored in -lrugen->min_seq[] separately for anon and file types as clean file -pages can be evicted regardless of swap constraints. These three -variables are monotonically increasing. Generation numbers are -truncated into order_base_2(MAX_NR_GENS+1) bits in order to fit into -page->flags. The sliding window technique is used to prevent truncated -generation numbers from overlapping. Each truncated generation number -is an index to -lrugen->lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]. - -The framework comprises two conceptually independent components: the -aging, which produces young generations, and the eviction, which -consumes old generations. Both can be invoked independently from user -space for the purpose of working set estimation and proactive reclaim. - -The protection of hot pages and the selection of cold pages are based -on page access types and patterns. There are two access types: one via -page tables and the other via file descriptors. The protection of the -former type is by design stronger because: - 1) The uncertainty in determining the access patterns of the former - type is higher due to the coalesced nature of the accessed bit. - 2) The cost of evicting the former type is higher due to the TLB - flushes required and the likelihood of involving I/O. - 3) The penalty of under-protecting the former type is higher because - applications usually do not prepare themselves for major faults like - they do for blocked I/O. For example, client applications commonly - dedicate blocked I/O to separate threads to avoid UI janks that - negatively affect user experience. - -There are also two access patterns: one with temporal locality and the -other without. The latter pattern, e.g., random and sequential, needs -to be explicitly excluded to avoid weakening the protection of the -former pattern. Generally the former type follows the former pattern -unless MADV_SEQUENTIAL is specified and the latter type follows the -latter pattern unless outlying refaults have been observed. - -Upon faulting, a page is added to the youngest generation, which -provides the strongest protection as the eviction will not consider -this page before the aging has scanned it at least twice. The first -scan clears the accessed bit set during the initial fault. And the -second scan makes sure this page has not been used since the first -scan. A page from any other generations is brought back to the -youngest generation whenever the aging finds the accessed bit set on -any of the PTEs mapping this page. - -Unmapped pages are initially added to the oldest generation and then -conditionally protected by tiers. This is done later [PATCH 07/10]. - -Signed-off-by: Yu Zhao -Tested-by: Konstantin Kharlamov -Change-Id: I71de7cd15b8dfa6f9fdd838023474693c4fee0a7 ---- - fs/fuse/dev.c | 3 +- - include/linux/cgroup.h | 15 +- - include/linux/mm.h | 36 ++++ - include/linux/mm_inline.h | 182 ++++++++++++++++++++ - include/linux/mmzone.h | 70 ++++++++ - include/linux/page-flags-layout.h | 19 ++- - include/linux/page-flags.h | 4 +- - include/linux/sched.h | 3 + - kernel/bounds.c | 3 + - kernel/cgroup/cgroup-internal.h | 1 - - mm/huge_memory.c | 3 +- - mm/memcontrol.c | 1 + - mm/memory.c | 7 + - mm/mm_init.c | 6 +- - mm/page_alloc.c | 1 + - mm/swap.c | 9 +- - mm/swapfile.c | 2 + - mm/vmscan.c | 268 ++++++++++++++++++++++++++++++ - 18 files changed, 618 insertions(+), 15 deletions(-) - ---- a/fs/fuse/dev.c -+++ b/fs/fuse/dev.c -@@ -785,7 +785,8 @@ static int fuse_check_page(struct page * - 1 << PG_active | - 1 << PG_workingset | - 1 << PG_reclaim | -- 1 << PG_waiters))) { -+ 1 << PG_waiters | -+ LRU_GEN_MASK | LRU_REFS_MASK))) { - dump_page(page, "fuse: trying to steal weird page"); - return 1; - } ---- a/include/linux/cgroup.h -+++ b/include/linux/cgroup.h -@@ -433,6 +433,18 @@ static inline void cgroup_put(struct cgr - css_put(&cgrp->self); - } - -+extern struct mutex cgroup_mutex; -+ -+static inline void cgroup_lock(void) -+{ -+ mutex_lock(&cgroup_mutex); -+} -+ -+static inline void cgroup_unlock(void) -+{ -+ mutex_unlock(&cgroup_mutex); -+} -+ - /** - * task_css_set_check - obtain a task's css_set with extra access conditions - * @task: the task to obtain css_set for -@@ -447,7 +459,6 @@ static inline void cgroup_put(struct cgr - * as locks used during the cgroup_subsys::attach() methods. - */ - #ifdef CONFIG_PROVE_RCU --extern struct mutex cgroup_mutex; - extern spinlock_t css_set_lock; - #define task_css_set_check(task, __c) \ - rcu_dereference_check((task)->cgroups, \ -@@ -708,6 +719,8 @@ struct cgroup; - static inline u64 cgroup_id(const struct cgroup *cgrp) { return 1; } - static inline void css_get(struct cgroup_subsys_state *css) {} - static inline void css_put(struct cgroup_subsys_state *css) {} -+static inline void cgroup_lock(void) {} -+static inline void cgroup_unlock(void) {} - static inline int cgroup_attach_task_all(struct task_struct *from, - struct task_struct *t) { return 0; } - static inline int cgroupstats_build(struct cgroupstats *stats, ---- a/include/linux/mm.h -+++ b/include/linux/mm.h -@@ -1093,6 +1093,8 @@ vm_fault_t finish_mkwrite_fault(struct v - #define ZONES_PGOFF (NODES_PGOFF - ZONES_WIDTH) - #define LAST_CPUPID_PGOFF (ZONES_PGOFF - LAST_CPUPID_WIDTH) - #define KASAN_TAG_PGOFF (LAST_CPUPID_PGOFF - KASAN_TAG_WIDTH) -+#define LRU_GEN_PGOFF (KASAN_TAG_PGOFF - LRU_GEN_WIDTH) -+#define LRU_REFS_PGOFF (LRU_GEN_PGOFF - LRU_REFS_WIDTH) - - /* - * Define the bit shifts to access each section. For non-existent -@@ -1807,6 +1809,40 @@ static inline void unmap_mapping_range(s - loff_t const holebegin, loff_t const holelen, int even_cows) { } - #endif - -+#ifdef CONFIG_LRU_GEN -+static inline void task_enter_nonseq_fault(void) -+{ -+ WARN_ON(current->in_nonseq_fault); -+ -+ current->in_nonseq_fault = 1; -+} -+ -+static inline void task_exit_nonseq_fault(void) -+{ -+ WARN_ON(!current->in_nonseq_fault); -+ -+ current->in_nonseq_fault = 0; -+} -+ -+static inline bool task_in_nonseq_fault(void) -+{ -+ return current->in_nonseq_fault; -+} -+#else -+static inline void task_enter_nonseq_fault(void) -+{ -+} -+ -+static inline void task_exit_nonseq_fault(void) -+{ -+} -+ -+static inline bool task_in_nonseq_fault(void) -+{ -+ return false; -+} -+#endif /* CONFIG_LRU_GEN */ -+ - static inline void unmap_shared_mapping_range(struct address_space *mapping, - loff_t const holebegin, loff_t const holelen) - { ---- a/include/linux/mm_inline.h -+++ b/include/linux/mm_inline.h -@@ -79,11 +79,187 @@ static __always_inline enum lru_list pag - return lru; - } - -+#ifdef CONFIG_LRU_GEN -+ -+static inline bool lru_gen_enabled(void) -+{ -+#ifdef CONFIG_LRU_GEN_ENABLED -+ DECLARE_STATIC_KEY_TRUE(lru_gen_static_key); -+ -+ return static_branch_likely(&lru_gen_static_key); -+#else -+ DECLARE_STATIC_KEY_FALSE(lru_gen_static_key); -+ -+ return static_branch_unlikely(&lru_gen_static_key); -+#endif -+} -+ -+/* Return an index within the sliding window that tracks MAX_NR_GENS generations. */ -+static inline int lru_gen_from_seq(unsigned long seq) -+{ -+ return seq % MAX_NR_GENS; -+} -+ -+/* The youngest and the second youngest generations are counted as active. */ -+static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen) -+{ -+ unsigned long max_seq = lruvec->evictable.max_seq; -+ -+ VM_BUG_ON(gen >= MAX_NR_GENS); -+ -+ return gen == lru_gen_from_seq(max_seq) || gen == lru_gen_from_seq(max_seq - 1); -+} -+ -+/* Update the sizes of the multigenerational lru lists. */ -+static inline void lru_gen_update_size(struct page *page, struct lruvec *lruvec, -+ int old_gen, int new_gen) -+{ -+ int type = page_is_file_lru(page); -+ int zone = page_zonenum(page); -+ int delta = thp_nr_pages(page); -+ enum lru_list lru = type * LRU_FILE; -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ lockdep_assert_held(&lruvec->lru_lock); -+ VM_BUG_ON(old_gen != -1 && old_gen >= MAX_NR_GENS); -+ VM_BUG_ON(new_gen != -1 && new_gen >= MAX_NR_GENS); -+ VM_BUG_ON(old_gen == -1 && new_gen == -1); -+ -+ if (old_gen >= 0) -+ WRITE_ONCE(lrugen->sizes[old_gen][type][zone], -+ lrugen->sizes[old_gen][type][zone] - delta); -+ if (new_gen >= 0) -+ WRITE_ONCE(lrugen->sizes[new_gen][type][zone], -+ lrugen->sizes[new_gen][type][zone] + delta); -+ -+ if (old_gen < 0) { -+ if (lru_gen_is_active(lruvec, new_gen)) -+ lru += LRU_ACTIVE; -+ update_lru_size(lruvec, lru, zone, delta); -+ return; -+ } -+ -+ if (new_gen < 0) { -+ if (lru_gen_is_active(lruvec, old_gen)) -+ lru += LRU_ACTIVE; -+ update_lru_size(lruvec, lru, zone, -delta); -+ return; -+ } -+ -+ if (!lru_gen_is_active(lruvec, old_gen) && lru_gen_is_active(lruvec, new_gen)) { -+ update_lru_size(lruvec, lru, zone, -delta); -+ update_lru_size(lruvec, lru + LRU_ACTIVE, zone, delta); -+ } -+ -+ VM_BUG_ON(lru_gen_is_active(lruvec, old_gen) && !lru_gen_is_active(lruvec, new_gen)); -+} -+ -+/* Add a page to one of the multigenerational lru lists. Return true on success. */ -+static inline bool lru_gen_add_page(struct page *page, struct lruvec *lruvec, bool reclaiming) -+{ -+ int gen; -+ unsigned long old_flags, new_flags; -+ int type = page_is_file_lru(page); -+ int zone = page_zonenum(page); -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ if (PageUnevictable(page) || !lrugen->enabled[type]) -+ return false; -+ /* -+ * If a page shouldn't be considered for eviction, i.e., a page mapped -+ * upon fault during which the accessed bit is set, add it to the -+ * youngest generation. -+ * -+ * If a page can't be evicted immediately, i.e., an anon page not in -+ * swap cache or a dirty page pending writeback, add it to the second -+ * oldest generation. -+ * -+ * If a page could be evicted immediately, e.g., a clean page, add it to -+ * the oldest generation. -+ */ -+ if (PageActive(page)) -+ gen = lru_gen_from_seq(lrugen->max_seq); -+ else if ((!type && !PageSwapCache(page)) || -+ (PageReclaim(page) && (PageDirty(page) || PageWriteback(page)))) -+ gen = lru_gen_from_seq(lrugen->min_seq[type] + 1); -+ else -+ gen = lru_gen_from_seq(lrugen->min_seq[type]); -+ -+ do { -+ new_flags = old_flags = READ_ONCE(page->flags); -+ VM_BUG_ON_PAGE(new_flags & LRU_GEN_MASK, page); -+ -+ new_flags &= ~(LRU_GEN_MASK | BIT(PG_active)); -+ new_flags |= (gen + 1UL) << LRU_GEN_PGOFF; -+ } while (cmpxchg(&page->flags, old_flags, new_flags) != old_flags); -+ -+ lru_gen_update_size(page, lruvec, -1, gen); -+ /* for rotate_reclaimable_page() */ -+ if (reclaiming) -+ list_add_tail(&page->lru, &lrugen->lists[gen][type][zone]); -+ else -+ list_add(&page->lru, &lrugen->lists[gen][type][zone]); -+ -+ return true; -+} -+ -+/* Delete a page from one of the multigenerational lru lists. Return true on success. */ -+static inline bool lru_gen_del_page(struct page *page, struct lruvec *lruvec, bool reclaiming) -+{ -+ int gen; -+ unsigned long old_flags, new_flags; -+ -+ do { -+ new_flags = old_flags = READ_ONCE(page->flags); -+ if (!(new_flags & LRU_GEN_MASK)) -+ return false; -+ -+ VM_BUG_ON_PAGE(PageActive(page), page); -+ VM_BUG_ON_PAGE(PageUnevictable(page), page); -+ -+ gen = ((new_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; -+ -+ new_flags &= ~LRU_GEN_MASK; -+ /* for shrink_page_list() */ -+ if (reclaiming) -+ new_flags &= ~(BIT(PG_referenced) | BIT(PG_reclaim)); -+ else if (lru_gen_is_active(lruvec, gen)) -+ new_flags |= BIT(PG_active); -+ } while (cmpxchg(&page->flags, old_flags, new_flags) != old_flags); -+ -+ lru_gen_update_size(page, lruvec, gen, -1); -+ list_del(&page->lru); -+ -+ return true; -+} -+ -+#else -+ -+static inline bool lru_gen_enabled(void) -+{ -+ return false; -+} -+ -+static inline bool lru_gen_add_page(struct page *page, struct lruvec *lruvec, bool reclaiming) -+{ -+ return false; -+} -+ -+static inline bool lru_gen_del_page(struct page *page, struct lruvec *lruvec, bool reclaiming) -+{ -+ return false; -+} -+ -+#endif /* CONFIG_LRU_GEN */ -+ - static __always_inline void add_page_to_lru_list(struct page *page, - struct lruvec *lruvec) - { - enum lru_list lru = page_lru(page); - -+ if (lru_gen_add_page(page, lruvec, false)) -+ return; -+ - update_lru_size(lruvec, lru, page_zonenum(page), thp_nr_pages(page)); - list_add(&page->lru, &lruvec->lists[lru]); - } -@@ -93,6 +269,9 @@ static __always_inline void add_page_to_ - { - enum lru_list lru = page_lru(page); - -+ if (lru_gen_add_page(page, lruvec, true)) -+ return; -+ - update_lru_size(lruvec, lru, page_zonenum(page), thp_nr_pages(page)); - list_add_tail(&page->lru, &lruvec->lists[lru]); - } -@@ -100,6 +279,9 @@ static __always_inline void add_page_to_ - static __always_inline void del_page_from_lru_list(struct page *page, - struct lruvec *lruvec) - { -+ if (lru_gen_del_page(page, lruvec, false)) -+ return; -+ - list_del(&page->lru); - update_lru_size(lruvec, page_lru(page), page_zonenum(page), - -thp_nr_pages(page)); ---- a/include/linux/mmzone.h -+++ b/include/linux/mmzone.h -@@ -294,6 +294,72 @@ enum lruvec_flags { - */ - }; - -+struct lruvec; -+ -+#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF) -+#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF) -+ -+#ifdef CONFIG_LRU_GEN -+ -+/* -+ * For each lruvec, evictable pages are divided into multiple generations. The -+ * youngest and the oldest generation numbers, AKA max_seq and min_seq, are -+ * monotonically increasing. The sliding window technique is used to track at -+ * least MIN_NR_GENS and at most MAX_NR_GENS generations. An offset within the -+ * window, AKA gen, indexes an array of per-type and per-zone lists for the -+ * corresponding generation. The counter in page->flags stores gen+1 while a -+ * page is on one of the multigenerational lru lists. Otherwise, it stores 0. -+ * -+ * After a page is faulted in, the aging must check the accessed bit at least -+ * twice before the eviction would consider it. The first check clears the -+ * accessed bit set during the initial fault. The second check makes sure this -+ * page hasn't been used since then. -+ */ -+#define MIN_NR_GENS 2 -+#define MAX_NR_GENS ((unsigned int)CONFIG_NR_LRU_GENS) -+ -+struct lrugen { -+ /* the aging increments the max generation number */ -+ unsigned long max_seq; -+ /* the eviction increments the min generation numbers */ -+ unsigned long min_seq[ANON_AND_FILE]; -+ /* the birth time of each generation in jiffies */ -+ unsigned long timestamps[MAX_NR_GENS]; -+ /* the multigenerational lru lists */ -+ struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; -+ /* the sizes of the multigenerational lru lists in pages */ -+ unsigned long sizes[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; -+ /* whether the multigenerational lru is enabled */ -+ bool enabled[ANON_AND_FILE]; -+}; -+ -+#define MAX_BATCH_SIZE 8192 -+ -+void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec); -+void lru_gen_change_state(bool enable, bool main, bool swap); -+ -+#ifdef CONFIG_MEMCG -+void lru_gen_init_memcg(struct mem_cgroup *memcg); -+#endif -+ -+#else /* !CONFIG_LRU_GEN */ -+ -+static inline void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec) -+{ -+} -+ -+static inline void lru_gen_change_state(bool enable, bool main, bool swap) -+{ -+} -+ -+#ifdef CONFIG_MEMCG -+static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) -+{ -+} -+#endif -+ -+#endif /* CONFIG_LRU_GEN */ -+ - struct lruvec { - struct list_head lists[NR_LRU_LISTS]; - /* per lruvec lru_lock for memcg */ -@@ -311,6 +377,10 @@ struct lruvec { - unsigned long refaults[ANON_AND_FILE]; - /* Various lruvec state flags (enum lruvec_flags) */ - unsigned long flags; -+#ifdef CONFIG_LRU_GEN -+ /* unevictable pages are on LRU_UNEVICTABLE */ -+ struct lrugen evictable; -+#endif - #ifdef CONFIG_MEMCG - struct pglist_data *pgdat; - #endif ---- a/include/linux/page-flags-layout.h -+++ b/include/linux/page-flags-layout.h -@@ -26,6 +26,14 @@ - - #define ZONES_WIDTH ZONES_SHIFT - -+#ifdef CONFIG_LRU_GEN -+/* LRU_GEN_WIDTH is generated from order_base_2(CONFIG_NR_LRU_GENS + 1). */ -+#define LRU_REFS_WIDTH (CONFIG_TIERS_PER_GEN - 2) -+#else -+#define LRU_GEN_WIDTH 0 -+#define LRU_REFS_WIDTH 0 -+#endif /* CONFIG_LRU_GEN */ -+ - #ifdef CONFIG_SPARSEMEM - #include - #define SECTIONS_SHIFT (MAX_PHYSMEM_BITS - SECTION_SIZE_BITS) -@@ -55,7 +63,8 @@ - #define SECTIONS_WIDTH 0 - #endif - --#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS -+#if ZONES_WIDTH + LRU_GEN_WIDTH + LRU_REFS_WIDTH + SECTIONS_WIDTH + NODES_SHIFT \ -+ <= BITS_PER_LONG - NR_PAGEFLAGS - #define NODES_WIDTH NODES_SHIFT - #elif defined(CONFIG_SPARSEMEM_VMEMMAP) - #error "Vmemmap: No space for nodes field in page flags" -@@ -89,8 +98,8 @@ - #define LAST_CPUPID_SHIFT 0 - #endif - --#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_SHIFT \ -- <= BITS_PER_LONG - NR_PAGEFLAGS -+#if ZONES_WIDTH + LRU_GEN_WIDTH + LRU_REFS_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \ -+ KASAN_TAG_WIDTH + LAST_CPUPID_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS - #define LAST_CPUPID_WIDTH LAST_CPUPID_SHIFT - #else - #define LAST_CPUPID_WIDTH 0 -@@ -100,8 +109,8 @@ - #define LAST_CPUPID_NOT_IN_PAGE_FLAGS - #endif - --#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH \ -- > BITS_PER_LONG - NR_PAGEFLAGS -+#if ZONES_WIDTH + LRU_GEN_WIDTH + LRU_REFS_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \ -+ KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH > BITS_PER_LONG - NR_PAGEFLAGS - #error "Not enough bits in page flags" - #endif - ---- a/include/linux/page-flags.h -+++ b/include/linux/page-flags.h -@@ -845,7 +845,7 @@ static inline void ClearPageSlabPfmemall - 1UL << PG_private | 1UL << PG_private_2 | \ - 1UL << PG_writeback | 1UL << PG_reserved | \ - 1UL << PG_slab | 1UL << PG_active | \ -- 1UL << PG_unevictable | __PG_MLOCKED) -+ 1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK) - - /* - * Flags checked when a page is prepped for return by the page allocator. -@@ -856,7 +856,7 @@ static inline void ClearPageSlabPfmemall - * alloc-free cycle to prevent from reusing the page. - */ - #define PAGE_FLAGS_CHECK_AT_PREP \ -- (PAGEFLAGS_MASK & ~__PG_HWPOISON) -+ ((PAGEFLAGS_MASK & ~__PG_HWPOISON) | LRU_GEN_MASK | LRU_REFS_MASK) - - #define PAGE_FLAGS_PRIVATE \ - (1UL << PG_private | 1UL << PG_private_2) ---- a/include/linux/sched.h -+++ b/include/linux/sched.h -@@ -911,6 +911,9 @@ struct task_struct { - #ifdef CONFIG_MEMCG - unsigned in_user_fault:1; - #endif -+#ifdef CONFIG_LRU_GEN -+ unsigned in_nonseq_fault:1; -+#endif - #ifdef CONFIG_COMPAT_BRK - unsigned brk_randomized:1; - #endif ---- a/kernel/bounds.c -+++ b/kernel/bounds.c -@@ -22,6 +22,9 @@ int main(void) - DEFINE(NR_CPUS_BITS, ilog2(CONFIG_NR_CPUS)); - #endif - DEFINE(SPINLOCK_SIZE, sizeof(spinlock_t)); -+#ifdef CONFIG_LRU_GEN -+ DEFINE(LRU_GEN_WIDTH, order_base_2(CONFIG_NR_LRU_GENS + 1)); -+#endif - /* End of constants */ - - return 0; ---- a/kernel/cgroup/cgroup-internal.h -+++ b/kernel/cgroup/cgroup-internal.h -@@ -165,7 +165,6 @@ struct cgroup_mgctx { - #define DEFINE_CGROUP_MGCTX(name) \ - struct cgroup_mgctx name = CGROUP_MGCTX_INIT(name) - --extern struct mutex cgroup_mutex; - extern spinlock_t css_set_lock; - extern struct cgroup_subsys *cgroup_subsys[]; - extern struct list_head cgroup_roots; ---- a/mm/huge_memory.c -+++ b/mm/huge_memory.c -@@ -2364,7 +2364,8 @@ static void __split_huge_page_tail(struc - #ifdef CONFIG_64BIT - (1L << PG_arch_2) | - #endif -- (1L << PG_dirty))); -+ (1L << PG_dirty) | -+ LRU_GEN_MASK | LRU_REFS_MASK)); - - /* ->mapping in first tail page is compound_mapcount */ - VM_BUG_ON_PAGE(tail > 2 && page_tail->mapping != TAIL_MAPPING, ---- a/mm/memcontrol.c -+++ b/mm/memcontrol.c -@@ -5241,6 +5241,7 @@ static struct mem_cgroup *mem_cgroup_all - memcg->deferred_split_queue.split_queue_len = 0; - #endif - idr_replace(&mem_cgroup_idr, memcg, memcg->id.id); -+ lru_gen_init_memcg(memcg); - return memcg; - fail: - mem_cgroup_id_remove(memcg); ---- a/mm/memory.c -+++ b/mm/memory.c -@@ -4788,6 +4788,7 @@ vm_fault_t handle_mm_fault(struct vm_are - unsigned int flags, struct pt_regs *regs) - { - vm_fault_t ret; -+ bool nonseq_fault = !(vma->vm_flags & VM_SEQ_READ); - - __set_current_state(TASK_RUNNING); - -@@ -4809,11 +4810,17 @@ vm_fault_t handle_mm_fault(struct vm_are - if (flags & FAULT_FLAG_USER) - mem_cgroup_enter_user_fault(); - -+ if (nonseq_fault) -+ task_enter_nonseq_fault(); -+ - if (unlikely(is_vm_hugetlb_page(vma))) - ret = hugetlb_fault(vma->vm_mm, vma, address, flags); - else - ret = __handle_mm_fault(vma, address, flags); - -+ if (nonseq_fault) -+ task_exit_nonseq_fault(); -+ - if (flags & FAULT_FLAG_USER) { - mem_cgroup_exit_user_fault(); - /* ---- a/mm/mm_init.c -+++ b/mm/mm_init.c -@@ -65,14 +65,16 @@ void __init mminit_verify_pageflags_layo - - shift = 8 * sizeof(unsigned long); - width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH -- - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH; -+ - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH - LRU_GEN_WIDTH - LRU_REFS_WIDTH; - mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths", -- "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Flags %d\n", -+ "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Gen %d Tier %d Flags %d\n", - SECTIONS_WIDTH, - NODES_WIDTH, - ZONES_WIDTH, - LAST_CPUPID_WIDTH, - KASAN_TAG_WIDTH, -+ LRU_GEN_WIDTH, -+ LRU_REFS_WIDTH, - NR_PAGEFLAGS); - mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts", - "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d\n", ---- a/mm/page_alloc.c -+++ b/mm/page_alloc.c -@@ -7459,6 +7459,7 @@ static void __meminit pgdat_init_interna - - pgdat_page_ext_init(pgdat); - lruvec_init(&pgdat->__lruvec); -+ lru_gen_init_state(NULL, &pgdat->__lruvec); - } - - static void __meminit zone_init_internals(struct zone *zone, enum zone_type idx, int nid, ---- a/mm/swap.c -+++ b/mm/swap.c -@@ -446,6 +446,11 @@ void lru_cache_add(struct page *page) - VM_BUG_ON_PAGE(PageActive(page) && PageUnevictable(page), page); - VM_BUG_ON_PAGE(PageLRU(page), page); - -+ /* see the comment in lru_gen_add_page() */ -+ if (lru_gen_enabled() && !PageUnevictable(page) && -+ task_in_nonseq_fault() && !(current->flags & PF_MEMALLOC)) -+ SetPageActive(page); -+ - get_page(page); - local_lock(&lru_pvecs.lock); - pvec = this_cpu_ptr(&lru_pvecs.lru_add); -@@ -547,7 +552,7 @@ static void lru_deactivate_file_fn(struc - - static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec) - { -- if (PageActive(page) && !PageUnevictable(page)) { -+ if (!PageUnevictable(page) && (PageActive(page) || lru_gen_enabled())) { - int nr_pages = thp_nr_pages(page); - - del_page_from_lru_list(page, lruvec); -@@ -661,7 +666,7 @@ void deactivate_file_page(struct page *p - */ - void deactivate_page(struct page *page) - { -- if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) { -+ if (PageLRU(page) && !PageUnevictable(page) && (PageActive(page) || lru_gen_enabled())) { - struct pagevec *pvec; - - local_lock(&lru_pvecs.lock); ---- a/mm/swapfile.c -+++ b/mm/swapfile.c -@@ -2689,6 +2689,7 @@ SYSCALL_DEFINE1(swapoff, const char __us - err = 0; - atomic_inc(&proc_poll_event); - wake_up_interruptible(&proc_poll_wait); -+ lru_gen_change_state(false, false, true); - - out_dput: - filp_close(victim, NULL); -@@ -3350,6 +3351,7 @@ SYSCALL_DEFINE2(swapon, const char __use - mutex_unlock(&swapon_mutex); - atomic_inc(&proc_poll_event); - wake_up_interruptible(&proc_poll_wait); -+ lru_gen_change_state(true, false, true); - - error = 0; - goto out; ---- a/mm/vmscan.c -+++ b/mm/vmscan.c -@@ -50,6 +50,7 @@ - #include - #include - #include -+#include - - #include - #include -@@ -2815,6 +2816,273 @@ static bool can_age_anon_pages(struct pg - return can_demote(pgdat->node_id, sc); - } - -+#ifdef CONFIG_LRU_GEN -+ -+/****************************************************************************** -+ * shorthand helpers -+ ******************************************************************************/ -+ -+#define for_each_gen_type_zone(gen, type, zone) \ -+ for ((gen) = 0; (gen) < MAX_NR_GENS; (gen)++) \ -+ for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \ -+ for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++) -+ -+static int page_lru_gen(struct page *page) -+{ -+ unsigned long flags = READ_ONCE(page->flags); -+ -+ return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; -+} -+ -+static struct lruvec *get_lruvec(int nid, struct mem_cgroup *memcg) -+{ -+ struct pglist_data *pgdat = NODE_DATA(nid); -+ -+#ifdef CONFIG_MEMCG -+ if (memcg) { -+ struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec; -+ -+ if (lruvec->pgdat != pgdat) -+ lruvec->pgdat = pgdat; -+ -+ return lruvec; -+ } -+#endif -+ return pgdat ? &pgdat->__lruvec : NULL; -+} -+ -+static int get_nr_gens(struct lruvec *lruvec, int type) -+{ -+ return lruvec->evictable.max_seq - lruvec->evictable.min_seq[type] + 1; -+} -+ -+static bool __maybe_unused seq_is_valid(struct lruvec *lruvec) -+{ -+ return get_nr_gens(lruvec, 1) >= MIN_NR_GENS && -+ get_nr_gens(lruvec, 1) <= get_nr_gens(lruvec, 0) && -+ get_nr_gens(lruvec, 0) <= MAX_NR_GENS; -+} -+ -+/****************************************************************************** -+ * state change -+ ******************************************************************************/ -+ -+#ifdef CONFIG_LRU_GEN_ENABLED -+DEFINE_STATIC_KEY_TRUE(lru_gen_static_key); -+#else -+DEFINE_STATIC_KEY_FALSE(lru_gen_static_key); -+#endif -+ -+static int lru_gen_nr_swapfiles; -+ -+static bool __maybe_unused state_is_valid(struct lruvec *lruvec) -+{ -+ int gen, type, zone; -+ enum lru_list lru; -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ for_each_evictable_lru(lru) { -+ type = is_file_lru(lru); -+ -+ if (lrugen->enabled[type] && !list_empty(&lruvec->lists[lru])) -+ return false; -+ } -+ -+ for_each_gen_type_zone(gen, type, zone) { -+ if (!lrugen->enabled[type] && !list_empty(&lrugen->lists[gen][type][zone])) -+ return false; -+ -+ /* unlikely but not a bug when reset_batch_size() is pending */ -+ VM_WARN_ON(!lrugen->enabled[type] && lrugen->sizes[gen][type][zone]); -+ } -+ -+ return true; -+} -+ -+static bool fill_lists(struct lruvec *lruvec) -+{ -+ enum lru_list lru; -+ int remaining = MAX_BATCH_SIZE; -+ -+ for_each_evictable_lru(lru) { -+ int type = is_file_lru(lru); -+ bool active = is_active_lru(lru); -+ struct list_head *head = &lruvec->lists[lru]; -+ -+ if (!lruvec->evictable.enabled[type]) -+ continue; -+ -+ while (!list_empty(head)) { -+ bool success; -+ struct page *page = lru_to_page(head); -+ -+ VM_BUG_ON_PAGE(PageTail(page), page); -+ VM_BUG_ON_PAGE(PageUnevictable(page), page); -+ VM_BUG_ON_PAGE(PageActive(page) != active, page); -+ VM_BUG_ON_PAGE(page_is_file_lru(page) != type, page); -+ VM_BUG_ON_PAGE(page_lru_gen(page) < MAX_NR_GENS, page); -+ -+ prefetchw_prev_lru_page(page, head, flags); -+ -+ del_page_from_lru_list(page, lruvec); -+ success = lru_gen_add_page(page, lruvec, false); -+ VM_BUG_ON(!success); -+ -+ if (!--remaining) -+ return false; -+ } -+ } -+ -+ return true; -+} -+ -+static bool drain_lists(struct lruvec *lruvec) -+{ -+ int gen, type, zone; -+ int remaining = MAX_BATCH_SIZE; -+ -+ for_each_gen_type_zone(gen, type, zone) { -+ struct list_head *head = &lruvec->evictable.lists[gen][type][zone]; -+ -+ if (lruvec->evictable.enabled[type]) -+ continue; -+ -+ while (!list_empty(head)) { -+ bool success; -+ struct page *page = lru_to_page(head); -+ -+ VM_BUG_ON_PAGE(PageTail(page), page); -+ VM_BUG_ON_PAGE(PageUnevictable(page), page); -+ VM_BUG_ON_PAGE(PageActive(page), page); -+ VM_BUG_ON_PAGE(page_is_file_lru(page) != type, page); -+ VM_BUG_ON_PAGE(page_zonenum(page) != zone, page); -+ -+ prefetchw_prev_lru_page(page, head, flags); -+ -+ success = lru_gen_del_page(page, lruvec, false); -+ VM_BUG_ON(!success); -+ add_page_to_lru_list(page, lruvec); -+ -+ if (!--remaining) -+ return false; -+ } -+ } -+ -+ return true; -+} -+ -+/* -+ * For file page tracking, we enable/disable it according to the main switch. -+ * For anon page tracking, we only enabled it when the main switch is on and -+ * there is at least one swapfile; we disable it when there are no swapfiles -+ * regardless of the value of the main switch. Otherwise, we will eventually -+ * reach the max size of the sliding window and have to call inc_min_seq(). -+ */ -+void lru_gen_change_state(bool enable, bool main, bool swap) -+{ -+ static DEFINE_MUTEX(state_mutex); -+ -+ struct mem_cgroup *memcg; -+ -+ mem_hotplug_begin(); -+ cgroup_lock(); -+ mutex_lock(&state_mutex); -+ -+ if (swap) { -+ if (enable) -+ swap = !lru_gen_nr_swapfiles++; -+ else -+ swap = !--lru_gen_nr_swapfiles; -+ } -+ -+ if (main && enable != lru_gen_enabled()) { -+ if (enable) -+ static_branch_enable(&lru_gen_static_key); -+ else -+ static_branch_disable(&lru_gen_static_key); -+ } else if (!swap || !lru_gen_enabled()) -+ goto unlock; -+ -+ memcg = mem_cgroup_iter(NULL, NULL, NULL); -+ do { -+ int nid; -+ -+ for_each_node(nid) { -+ struct lruvec *lruvec = get_lruvec(nid, memcg); -+ -+ if (!lruvec) -+ continue; -+ -+ spin_lock_irq(&lruvec->lru_lock); -+ -+ VM_BUG_ON(!seq_is_valid(lruvec)); -+ VM_BUG_ON(!state_is_valid(lruvec)); -+ -+ lruvec->evictable.enabled[0] = lru_gen_enabled() && lru_gen_nr_swapfiles; -+ lruvec->evictable.enabled[1] = lru_gen_enabled(); -+ -+ while (!(enable ? fill_lists(lruvec) : drain_lists(lruvec))) { -+ spin_unlock_irq(&lruvec->lru_lock); -+ cond_resched(); -+ spin_lock_irq(&lruvec->lru_lock); -+ } -+ -+ spin_unlock_irq(&lruvec->lru_lock); -+ } -+ -+ cond_resched(); -+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); -+unlock: -+ mutex_unlock(&state_mutex); -+ cgroup_unlock(); -+ mem_hotplug_done(); -+} -+ -+/****************************************************************************** -+ * initialization -+ ******************************************************************************/ -+ -+void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec) -+{ -+ int i; -+ int gen, type, zone; -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ lrugen->max_seq = MIN_NR_GENS + 1; -+ lrugen->enabled[0] = lru_gen_enabled() && lru_gen_nr_swapfiles; -+ lrugen->enabled[1] = lru_gen_enabled(); -+ -+ for (i = 0; i <= MIN_NR_GENS + 1; i++) -+ lrugen->timestamps[i] = jiffies; -+ -+ for_each_gen_type_zone(gen, type, zone) -+ INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); -+} -+ -+#ifdef CONFIG_MEMCG -+void lru_gen_init_memcg(struct mem_cgroup *memcg) -+{ -+ int nid; -+ -+ for_each_node(nid) { -+ struct lruvec *lruvec = get_lruvec(nid, memcg); -+ -+ lru_gen_init_state(memcg, lruvec); -+ } -+} -+#endif -+ -+static int __init init_lru_gen(void) -+{ -+ BUILD_BUG_ON(MIN_NR_GENS + 1 >= MAX_NR_GENS); -+ BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS); -+ -+ return 0; -+}; -+late_initcall(init_lru_gen); -+ -+#endif /* CONFIG_LRU_GEN */ -+ - static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) - { - unsigned long nr[NR_LRU_LISTS]; diff --git a/target/linux/generic/backport-5.15/020-v6.1-05-mm-multi-gen-LRU-groundwork.patch b/target/linux/generic/backport-5.15/020-v6.1-05-mm-multi-gen-LRU-groundwork.patch new file mode 100644 index 00000000000..577c2817daa --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-05-mm-multi-gen-LRU-groundwork.patch @@ -0,0 +1,807 @@ +From a9b328add8422921a0dbbef162730800e16e8cfd Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:02 -0600 +Subject: [PATCH 05/29] mm: multi-gen LRU: groundwork +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Evictable pages are divided into multiple generations for each lruvec. +The youngest generation number is stored in lrugen->max_seq for both +anon and file types as they are aged on an equal footing. The oldest +generation numbers are stored in lrugen->min_seq[] separately for anon +and file types as clean file pages can be evicted regardless of swap +constraints. These three variables are monotonically increasing. + +Generation numbers are truncated into order_base_2(MAX_NR_GENS+1) bits +in order to fit into the gen counter in page->flags. Each truncated +generation number is an index to lrugen->lists[]. The sliding window +technique is used to track at least MIN_NR_GENS and at most +MAX_NR_GENS generations. The gen counter stores a value within [1, +MAX_NR_GENS] while a page is on one of lrugen->lists[]. Otherwise it +stores 0. + +There are two conceptually independent procedures: "the aging", which +produces young generations, and "the eviction", which consumes old +generations. They form a closed-loop system, i.e., "the page reclaim". +Both procedures can be invoked from userspace for the purposes of working +set estimation and proactive reclaim. These techniques are commonly used +to optimize job scheduling (bin packing) in data centers [1][2]. + +To avoid confusion, the terms "hot" and "cold" will be applied to the +multi-gen LRU, as a new convention; the terms "active" and "inactive" will +be applied to the active/inactive LRU, as usual. + +The protection of hot pages and the selection of cold pages are based +on page access channels and patterns. There are two access channels: +one through page tables and the other through file descriptors. The +protection of the former channel is by design stronger because: +1. The uncertainty in determining the access patterns of the former + channel is higher due to the approximation of the accessed bit. +2. The cost of evicting the former channel is higher due to the TLB + flushes required and the likelihood of encountering the dirty bit. +3. The penalty of underprotecting the former channel is higher because + applications usually do not prepare themselves for major page + faults like they do for blocked I/O. E.g., GUI applications + commonly use dedicated I/O threads to avoid blocking rendering + threads. + +There are also two access patterns: one with temporal locality and the +other without. For the reasons listed above, the former channel is +assumed to follow the former pattern unless VM_SEQ_READ or VM_RAND_READ is +present; the latter channel is assumed to follow the latter pattern unless +outlying refaults have been observed [3][4]. + +The next patch will address the "outlying refaults". Three macros, i.e., +LRU_REFS_WIDTH, LRU_REFS_PGOFF and LRU_REFS_MASK, used later are added in +this patch to make the entire patchset less diffy. + +A page is added to the youngest generation on faulting. The aging needs +to check the accessed bit at least twice before handing this page over to +the eviction. The first check takes care of the accessed bit set on the +initial fault; the second check makes sure this page has not been used +since then. This protocol, AKA second chance, requires a minimum of two +generations, hence MIN_NR_GENS. + +[1] https://dl.acm.org/doi/10.1145/3297858.3304053 +[2] https://dl.acm.org/doi/10.1145/3503222.3507731 +[3] https://lwn.net/Articles/495543/ +[4] https://lwn.net/Articles/815342/ + +Link: https://lkml.kernel.org/r/20220918080010.2920238-6-yuzhao@google.com +Signed-off-by: Yu Zhao +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Barry Song +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + fs/fuse/dev.c | 3 +- + include/linux/mm.h | 2 + + include/linux/mm_inline.h | 177 +++++++++++++++++++++++++++++- + include/linux/mmzone.h | 100 +++++++++++++++++ + include/linux/page-flags-layout.h | 13 ++- + include/linux/page-flags.h | 4 +- + include/linux/sched.h | 4 + + kernel/bounds.c | 5 + + mm/Kconfig | 8 ++ + mm/huge_memory.c | 3 +- + mm/memcontrol.c | 2 + + mm/memory.c | 25 +++++ + mm/mm_init.c | 6 +- + mm/mmzone.c | 2 + + mm/swap.c | 10 +- + mm/vmscan.c | 75 +++++++++++++ + 16 files changed, 425 insertions(+), 14 deletions(-) + +--- a/fs/fuse/dev.c ++++ b/fs/fuse/dev.c +@@ -785,7 +785,8 @@ static int fuse_check_page(struct page * + 1 << PG_active | + 1 << PG_workingset | + 1 << PG_reclaim | +- 1 << PG_waiters))) { ++ 1 << PG_waiters | ++ LRU_GEN_MASK | LRU_REFS_MASK))) { + dump_page(page, "fuse: trying to steal weird page"); + return 1; + } +--- a/include/linux/mm.h ++++ b/include/linux/mm.h +@@ -1093,6 +1093,8 @@ vm_fault_t finish_mkwrite_fault(struct v + #define ZONES_PGOFF (NODES_PGOFF - ZONES_WIDTH) + #define LAST_CPUPID_PGOFF (ZONES_PGOFF - LAST_CPUPID_WIDTH) + #define KASAN_TAG_PGOFF (LAST_CPUPID_PGOFF - KASAN_TAG_WIDTH) ++#define LRU_GEN_PGOFF (KASAN_TAG_PGOFF - LRU_GEN_WIDTH) ++#define LRU_REFS_PGOFF (LRU_GEN_PGOFF - LRU_REFS_WIDTH) + + /* + * Define the bit shifts to access each section. For non-existent +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -26,10 +26,13 @@ static inline int page_is_file_lru(struc + + static __always_inline void __update_lru_size(struct lruvec *lruvec, + enum lru_list lru, enum zone_type zid, +- int nr_pages) ++ long nr_pages) + { + struct pglist_data *pgdat = lruvec_pgdat(lruvec); + ++ lockdep_assert_held(&lruvec->lru_lock); ++ WARN_ON_ONCE(nr_pages != (int)nr_pages); ++ + __mod_lruvec_state(lruvec, NR_LRU_BASE + lru, nr_pages); + __mod_zone_page_state(&pgdat->node_zones[zid], + NR_ZONE_LRU_BASE + lru, nr_pages); +@@ -86,11 +89,177 @@ static __always_inline enum lru_list pag + return lru; + } + ++#ifdef CONFIG_LRU_GEN ++ ++static inline bool lru_gen_enabled(void) ++{ ++ return true; ++} ++ ++static inline bool lru_gen_in_fault(void) ++{ ++ return current->in_lru_fault; ++} ++ ++static inline int lru_gen_from_seq(unsigned long seq) ++{ ++ return seq % MAX_NR_GENS; ++} ++ ++static inline int page_lru_gen(struct page *page) ++{ ++ unsigned long flags = READ_ONCE(page->flags); ++ ++ return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; ++} ++ ++static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen) ++{ ++ unsigned long max_seq = lruvec->lrugen.max_seq; ++ ++ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS); ++ ++ /* see the comment on MIN_NR_GENS */ ++ return gen == lru_gen_from_seq(max_seq) || gen == lru_gen_from_seq(max_seq - 1); ++} ++ ++static inline void lru_gen_update_size(struct lruvec *lruvec, struct page *page, ++ int old_gen, int new_gen) ++{ ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ int delta = thp_nr_pages(page); ++ enum lru_list lru = type * LRU_INACTIVE_FILE; ++ struct lru_gen_struct *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); ++ VM_WARN_ON_ONCE(old_gen == -1 && new_gen == -1); ++ ++ if (old_gen >= 0) ++ WRITE_ONCE(lrugen->nr_pages[old_gen][type][zone], ++ lrugen->nr_pages[old_gen][type][zone] - delta); ++ if (new_gen >= 0) ++ WRITE_ONCE(lrugen->nr_pages[new_gen][type][zone], ++ lrugen->nr_pages[new_gen][type][zone] + delta); ++ ++ /* addition */ ++ if (old_gen < 0) { ++ if (lru_gen_is_active(lruvec, new_gen)) ++ lru += LRU_ACTIVE; ++ __update_lru_size(lruvec, lru, zone, delta); ++ return; ++ } ++ ++ /* deletion */ ++ if (new_gen < 0) { ++ if (lru_gen_is_active(lruvec, old_gen)) ++ lru += LRU_ACTIVE; ++ __update_lru_size(lruvec, lru, zone, -delta); ++ return; ++ } ++} ++ ++static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming) ++{ ++ unsigned long seq; ++ unsigned long flags; ++ int gen = page_lru_gen(page); ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ ++ VM_WARN_ON_ONCE_PAGE(gen != -1, page); ++ ++ if (PageUnevictable(page)) ++ return false; ++ /* ++ * There are three common cases for this page: ++ * 1. If it's hot, e.g., freshly faulted in or previously hot and ++ * migrated, add it to the youngest generation. ++ * 2. If it's cold but can't be evicted immediately, i.e., an anon page ++ * not in swapcache or a dirty page pending writeback, add it to the ++ * second oldest generation. ++ * 3. Everything else (clean, cold) is added to the oldest generation. ++ */ ++ if (PageActive(page)) ++ seq = lrugen->max_seq; ++ else if ((type == LRU_GEN_ANON && !PageSwapCache(page)) || ++ (PageReclaim(page) && ++ (PageDirty(page) || PageWriteback(page)))) ++ seq = lrugen->min_seq[type] + 1; ++ else ++ seq = lrugen->min_seq[type]; ++ ++ gen = lru_gen_from_seq(seq); ++ flags = (gen + 1UL) << LRU_GEN_PGOFF; ++ /* see the comment on MIN_NR_GENS about PG_active */ ++ set_mask_bits(&page->flags, LRU_GEN_MASK | BIT(PG_active), flags); ++ ++ lru_gen_update_size(lruvec, page, -1, gen); ++ /* for rotate_reclaimable_page() */ ++ if (reclaiming) ++ list_add_tail(&page->lru, &lrugen->lists[gen][type][zone]); ++ else ++ list_add(&page->lru, &lrugen->lists[gen][type][zone]); ++ ++ return true; ++} ++ ++static inline bool lru_gen_del_page(struct lruvec *lruvec, struct page *page, bool reclaiming) ++{ ++ unsigned long flags; ++ int gen = page_lru_gen(page); ++ ++ if (gen < 0) ++ return false; ++ ++ VM_WARN_ON_ONCE_PAGE(PageActive(page), page); ++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page); ++ ++ /* for migrate_page_states() */ ++ flags = !reclaiming && lru_gen_is_active(lruvec, gen) ? BIT(PG_active) : 0; ++ flags = set_mask_bits(&page->flags, LRU_GEN_MASK, flags); ++ gen = ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; ++ ++ lru_gen_update_size(lruvec, page, gen, -1); ++ list_del(&page->lru); ++ ++ return true; ++} ++ ++#else /* !CONFIG_LRU_GEN */ ++ ++static inline bool lru_gen_enabled(void) ++{ ++ return false; ++} ++ ++static inline bool lru_gen_in_fault(void) ++{ ++ return false; ++} ++ ++static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming) ++{ ++ return false; ++} ++ ++static inline bool lru_gen_del_page(struct lruvec *lruvec, struct page *page, bool reclaiming) ++{ ++ return false; ++} ++ ++#endif /* CONFIG_LRU_GEN */ ++ + static __always_inline void add_page_to_lru_list(struct page *page, + struct lruvec *lruvec) + { + enum lru_list lru = page_lru(page); + ++ if (lru_gen_add_page(lruvec, page, false)) ++ return; ++ + update_lru_size(lruvec, lru, page_zonenum(page), thp_nr_pages(page)); + list_add(&page->lru, &lruvec->lists[lru]); + } +@@ -100,6 +269,9 @@ static __always_inline void add_page_to_ + { + enum lru_list lru = page_lru(page); + ++ if (lru_gen_add_page(lruvec, page, true)) ++ return; ++ + update_lru_size(lruvec, lru, page_zonenum(page), thp_nr_pages(page)); + list_add_tail(&page->lru, &lruvec->lists[lru]); + } +@@ -107,6 +279,9 @@ static __always_inline void add_page_to_ + static __always_inline void del_page_from_lru_list(struct page *page, + struct lruvec *lruvec) + { ++ if (lru_gen_del_page(lruvec, page, false)) ++ return; ++ + list_del(&page->lru); + update_lru_size(lruvec, page_lru(page), page_zonenum(page), + -thp_nr_pages(page)); +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -294,6 +294,102 @@ enum lruvec_flags { + */ + }; + ++#endif /* !__GENERATING_BOUNDS_H */ ++ ++/* ++ * Evictable pages are divided into multiple generations. The youngest and the ++ * oldest generation numbers, max_seq and min_seq, are monotonically increasing. ++ * They form a sliding window of a variable size [MIN_NR_GENS, MAX_NR_GENS]. An ++ * offset within MAX_NR_GENS, i.e., gen, indexes the LRU list of the ++ * corresponding generation. The gen counter in page->flags stores gen+1 while ++ * a page is on one of lrugen->lists[]. Otherwise it stores 0. ++ * ++ * A page is added to the youngest generation on faulting. The aging needs to ++ * check the accessed bit at least twice before handing this page over to the ++ * eviction. The first check takes care of the accessed bit set on the initial ++ * fault; the second check makes sure this page hasn't been used since then. ++ * This process, AKA second chance, requires a minimum of two generations, ++ * hence MIN_NR_GENS. And to maintain ABI compatibility with the active/inactive ++ * LRU, e.g., /proc/vmstat, these two generations are considered active; the ++ * rest of generations, if they exist, are considered inactive. See ++ * lru_gen_is_active(). ++ * ++ * PG_active is always cleared while a page is on one of lrugen->lists[] so that ++ * the aging needs not to worry about it. And it's set again when a page ++ * considered active is isolated for non-reclaiming purposes, e.g., migration. ++ * See lru_gen_add_page() and lru_gen_del_page(). ++ * ++ * MAX_NR_GENS is set to 4 so that the multi-gen LRU can support twice the ++ * number of categories of the active/inactive LRU when keeping track of ++ * accesses through page tables. This requires order_base_2(MAX_NR_GENS+1) bits ++ * in page->flags. ++ */ ++#define MIN_NR_GENS 2U ++#define MAX_NR_GENS 4U ++ ++#ifndef __GENERATING_BOUNDS_H ++ ++struct lruvec; ++ ++#define LRU_GEN_MASK ((BIT(LRU_GEN_WIDTH) - 1) << LRU_GEN_PGOFF) ++#define LRU_REFS_MASK ((BIT(LRU_REFS_WIDTH) - 1) << LRU_REFS_PGOFF) ++ ++#ifdef CONFIG_LRU_GEN ++ ++enum { ++ LRU_GEN_ANON, ++ LRU_GEN_FILE, ++}; ++ ++/* ++ * The youngest generation number is stored in max_seq for both anon and file ++ * types as they are aged on an equal footing. The oldest generation numbers are ++ * stored in min_seq[] separately for anon and file types as clean file pages ++ * can be evicted regardless of swap constraints. ++ * ++ * Normally anon and file min_seq are in sync. But if swapping is constrained, ++ * e.g., out of swap space, file min_seq is allowed to advance and leave anon ++ * min_seq behind. ++ * ++ * The number of pages in each generation is eventually consistent and therefore ++ * can be transiently negative. ++ */ ++struct lru_gen_struct { ++ /* the aging increments the youngest generation number */ ++ unsigned long max_seq; ++ /* the eviction increments the oldest generation numbers */ ++ unsigned long min_seq[ANON_AND_FILE]; ++ /* the multi-gen LRU lists, lazily sorted on eviction */ ++ struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++ /* the multi-gen LRU sizes, eventually consistent */ ++ long nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++}; ++ ++void lru_gen_init_lruvec(struct lruvec *lruvec); ++ ++#ifdef CONFIG_MEMCG ++void lru_gen_init_memcg(struct mem_cgroup *memcg); ++void lru_gen_exit_memcg(struct mem_cgroup *memcg); ++#endif ++ ++#else /* !CONFIG_LRU_GEN */ ++ ++static inline void lru_gen_init_lruvec(struct lruvec *lruvec) ++{ ++} ++ ++#ifdef CONFIG_MEMCG ++static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) ++{ ++} ++ ++static inline void lru_gen_exit_memcg(struct mem_cgroup *memcg) ++{ ++} ++#endif ++ ++#endif /* CONFIG_LRU_GEN */ ++ + struct lruvec { + struct list_head lists[NR_LRU_LISTS]; + /* per lruvec lru_lock for memcg */ +@@ -311,6 +407,10 @@ struct lruvec { + unsigned long refaults[ANON_AND_FILE]; + /* Various lruvec state flags (enum lruvec_flags) */ + unsigned long flags; ++#ifdef CONFIG_LRU_GEN ++ /* evictable pages divided into generations */ ++ struct lru_gen_struct lrugen; ++#endif + #ifdef CONFIG_MEMCG + struct pglist_data *pgdat; + #endif +--- a/include/linux/page-flags-layout.h ++++ b/include/linux/page-flags-layout.h +@@ -55,7 +55,8 @@ + #define SECTIONS_WIDTH 0 + #endif + +-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS ++#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_SHIFT \ ++ <= BITS_PER_LONG - NR_PAGEFLAGS + #define NODES_WIDTH NODES_SHIFT + #elif defined(CONFIG_SPARSEMEM_VMEMMAP) + #error "Vmemmap: No space for nodes field in page flags" +@@ -89,8 +90,8 @@ + #define LAST_CPUPID_SHIFT 0 + #endif + +-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_SHIFT \ +- <= BITS_PER_LONG - NR_PAGEFLAGS ++#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \ ++ KASAN_TAG_WIDTH + LAST_CPUPID_SHIFT <= BITS_PER_LONG - NR_PAGEFLAGS + #define LAST_CPUPID_WIDTH LAST_CPUPID_SHIFT + #else + #define LAST_CPUPID_WIDTH 0 +@@ -100,10 +101,12 @@ + #define LAST_CPUPID_NOT_IN_PAGE_FLAGS + #endif + +-#if ZONES_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH \ +- > BITS_PER_LONG - NR_PAGEFLAGS ++#if ZONES_WIDTH + LRU_GEN_WIDTH + SECTIONS_WIDTH + NODES_WIDTH + \ ++ KASAN_TAG_WIDTH + LAST_CPUPID_WIDTH > BITS_PER_LONG - NR_PAGEFLAGS + #error "Not enough bits in page flags" + #endif + ++#define LRU_REFS_WIDTH 0 ++ + #endif + #endif /* _LINUX_PAGE_FLAGS_LAYOUT */ +--- a/include/linux/page-flags.h ++++ b/include/linux/page-flags.h +@@ -845,7 +845,7 @@ static inline void ClearPageSlabPfmemall + 1UL << PG_private | 1UL << PG_private_2 | \ + 1UL << PG_writeback | 1UL << PG_reserved | \ + 1UL << PG_slab | 1UL << PG_active | \ +- 1UL << PG_unevictable | __PG_MLOCKED) ++ 1UL << PG_unevictable | __PG_MLOCKED | LRU_GEN_MASK) + + /* + * Flags checked when a page is prepped for return by the page allocator. +@@ -856,7 +856,7 @@ static inline void ClearPageSlabPfmemall + * alloc-free cycle to prevent from reusing the page. + */ + #define PAGE_FLAGS_CHECK_AT_PREP \ +- (PAGEFLAGS_MASK & ~__PG_HWPOISON) ++ ((PAGEFLAGS_MASK & ~__PG_HWPOISON) | LRU_GEN_MASK | LRU_REFS_MASK) + + #define PAGE_FLAGS_PRIVATE \ + (1UL << PG_private | 1UL << PG_private_2) +--- a/include/linux/sched.h ++++ b/include/linux/sched.h +@@ -911,6 +911,10 @@ struct task_struct { + #ifdef CONFIG_MEMCG + unsigned in_user_fault:1; + #endif ++#ifdef CONFIG_LRU_GEN ++ /* whether the LRU algorithm may apply to this access */ ++ unsigned in_lru_fault:1; ++#endif + #ifdef CONFIG_COMPAT_BRK + unsigned brk_randomized:1; + #endif +--- a/kernel/bounds.c ++++ b/kernel/bounds.c +@@ -22,6 +22,11 @@ int main(void) + DEFINE(NR_CPUS_BITS, ilog2(CONFIG_NR_CPUS)); + #endif + DEFINE(SPINLOCK_SIZE, sizeof(spinlock_t)); ++#ifdef CONFIG_LRU_GEN ++ DEFINE(LRU_GEN_WIDTH, order_base_2(MAX_NR_GENS + 1)); ++#else ++ DEFINE(LRU_GEN_WIDTH, 0); ++#endif + /* End of constants */ + + return 0; +--- a/mm/Kconfig ++++ b/mm/Kconfig +@@ -897,6 +897,14 @@ config IO_MAPPING + config SECRETMEM + def_bool ARCH_HAS_SET_DIRECT_MAP && !EMBEDDED + ++config LRU_GEN ++ bool "Multi-Gen LRU" ++ depends on MMU ++ # make sure page->flags has enough spare bits ++ depends on 64BIT || !SPARSEMEM || SPARSEMEM_VMEMMAP ++ help ++ A high performance LRU implementation to overcommit memory. ++ + source "mm/damon/Kconfig" + + endmenu +--- a/mm/huge_memory.c ++++ b/mm/huge_memory.c +@@ -2366,7 +2366,8 @@ static void __split_huge_page_tail(struc + #ifdef CONFIG_64BIT + (1L << PG_arch_2) | + #endif +- (1L << PG_dirty))); ++ (1L << PG_dirty) | ++ LRU_GEN_MASK | LRU_REFS_MASK)); + + /* ->mapping in first tail page is compound_mapcount */ + VM_BUG_ON_PAGE(tail > 2 && page_tail->mapping != TAIL_MAPPING, +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -5178,6 +5178,7 @@ static void __mem_cgroup_free(struct mem + + static void mem_cgroup_free(struct mem_cgroup *memcg) + { ++ lru_gen_exit_memcg(memcg); + memcg_wb_domain_exit(memcg); + __mem_cgroup_free(memcg); + } +@@ -5241,6 +5242,7 @@ static struct mem_cgroup *mem_cgroup_all + memcg->deferred_split_queue.split_queue_len = 0; + #endif + idr_replace(&mem_cgroup_idr, memcg, memcg->id.id); ++ lru_gen_init_memcg(memcg); + return memcg; + fail: + mem_cgroup_id_remove(memcg); +--- a/mm/memory.c ++++ b/mm/memory.c +@@ -4778,6 +4778,27 @@ static inline void mm_account_fault(stru + perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs, address); + } + ++#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)); ++} ++ ++static void lru_gen_exit_fault(void) ++{ ++ current->in_lru_fault = false; ++} ++#else ++static void lru_gen_enter_fault(struct vm_area_struct *vma) ++{ ++} ++ ++static void lru_gen_exit_fault(void) ++{ ++} ++#endif /* CONFIG_LRU_GEN */ ++ + /* + * By the time we get here, we already hold the mm semaphore + * +@@ -4809,11 +4830,15 @@ vm_fault_t handle_mm_fault(struct vm_are + if (flags & FAULT_FLAG_USER) + mem_cgroup_enter_user_fault(); + ++ lru_gen_enter_fault(vma); ++ + if (unlikely(is_vm_hugetlb_page(vma))) + ret = hugetlb_fault(vma->vm_mm, vma, address, flags); + else + ret = __handle_mm_fault(vma, address, flags); + ++ lru_gen_exit_fault(); ++ + if (flags & FAULT_FLAG_USER) { + mem_cgroup_exit_user_fault(); + /* +--- a/mm/mm_init.c ++++ b/mm/mm_init.c +@@ -65,14 +65,16 @@ void __init mminit_verify_pageflags_layo + + shift = 8 * sizeof(unsigned long); + width = shift - SECTIONS_WIDTH - NODES_WIDTH - ZONES_WIDTH +- - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH; ++ - LAST_CPUPID_SHIFT - KASAN_TAG_WIDTH - LRU_GEN_WIDTH - LRU_REFS_WIDTH; + mminit_dprintk(MMINIT_TRACE, "pageflags_layout_widths", +- "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Flags %d\n", ++ "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d Gen %d Tier %d Flags %d\n", + SECTIONS_WIDTH, + NODES_WIDTH, + ZONES_WIDTH, + LAST_CPUPID_WIDTH, + KASAN_TAG_WIDTH, ++ LRU_GEN_WIDTH, ++ LRU_REFS_WIDTH, + NR_PAGEFLAGS); + mminit_dprintk(MMINIT_TRACE, "pageflags_layout_shifts", + "Section %d Node %d Zone %d Lastcpupid %d Kasantag %d\n", +--- a/mm/mmzone.c ++++ b/mm/mmzone.c +@@ -81,6 +81,8 @@ void lruvec_init(struct lruvec *lruvec) + + for_each_lru(lru) + INIT_LIST_HEAD(&lruvec->lists[lru]); ++ ++ lru_gen_init_lruvec(lruvec); + } + + #if defined(CONFIG_NUMA_BALANCING) && !defined(LAST_CPUPID_NOT_IN_PAGE_FLAGS) +--- a/mm/swap.c ++++ b/mm/swap.c +@@ -446,6 +446,11 @@ void lru_cache_add(struct page *page) + VM_BUG_ON_PAGE(PageActive(page) && PageUnevictable(page), page); + VM_BUG_ON_PAGE(PageLRU(page), page); + ++ /* see the comment in lru_gen_add_page() */ ++ if (lru_gen_enabled() && !PageUnevictable(page) && ++ lru_gen_in_fault() && !(current->flags & PF_MEMALLOC)) ++ SetPageActive(page); ++ + get_page(page); + local_lock(&lru_pvecs.lock); + pvec = this_cpu_ptr(&lru_pvecs.lru_add); +@@ -547,7 +552,7 @@ static void lru_deactivate_file_fn(struc + + static void lru_deactivate_fn(struct page *page, struct lruvec *lruvec) + { +- if (PageActive(page) && !PageUnevictable(page)) { ++ if (!PageUnevictable(page) && (PageActive(page) || lru_gen_enabled())) { + int nr_pages = thp_nr_pages(page); + + del_page_from_lru_list(page, lruvec); +@@ -661,7 +666,8 @@ void deactivate_file_page(struct page *p + */ + void deactivate_page(struct page *page) + { +- if (PageLRU(page) && PageActive(page) && !PageUnevictable(page)) { ++ if (PageLRU(page) && !PageUnevictable(page) && ++ (PageActive(page) || lru_gen_enabled())) { + struct pagevec *pvec; + + local_lock(&lru_pvecs.lock); +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -2821,6 +2821,81 @@ static bool can_age_anon_pages(struct pg + return can_demote(pgdat->node_id, sc); + } + ++#ifdef CONFIG_LRU_GEN ++ ++/****************************************************************************** ++ * shorthand helpers ++ ******************************************************************************/ ++ ++#define for_each_gen_type_zone(gen, type, zone) \ ++ for ((gen) = 0; (gen) < MAX_NR_GENS; (gen)++) \ ++ for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \ ++ for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++) ++ ++static struct lruvec __maybe_unused *get_lruvec(struct mem_cgroup *memcg, int nid) ++{ ++ struct pglist_data *pgdat = NODE_DATA(nid); ++ ++#ifdef CONFIG_MEMCG ++ if (memcg) { ++ struct lruvec *lruvec = &memcg->nodeinfo[nid]->lruvec; ++ ++ /* for hotadd_new_pgdat() */ ++ if (!lruvec->pgdat) ++ lruvec->pgdat = pgdat; ++ ++ return lruvec; ++ } ++#endif ++ VM_WARN_ON_ONCE(!mem_cgroup_disabled()); ++ ++ return pgdat ? &pgdat->__lruvec : NULL; ++} ++ ++/****************************************************************************** ++ * initialization ++ ******************************************************************************/ ++ ++void lru_gen_init_lruvec(struct lruvec *lruvec) ++{ ++ int gen, type, zone; ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ ++ lrugen->max_seq = MIN_NR_GENS + 1; ++ ++ for_each_gen_type_zone(gen, type, zone) ++ INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); ++} ++ ++#ifdef CONFIG_MEMCG ++void lru_gen_init_memcg(struct mem_cgroup *memcg) ++{ ++} ++ ++void lru_gen_exit_memcg(struct mem_cgroup *memcg) ++{ ++ int nid; ++ ++ for_each_node(nid) { ++ struct lruvec *lruvec = get_lruvec(memcg, nid); ++ ++ VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0, ++ sizeof(lruvec->lrugen.nr_pages))); ++ } ++} ++#endif ++ ++static int __init init_lru_gen(void) ++{ ++ BUILD_BUG_ON(MIN_NR_GENS + 1 >= MAX_NR_GENS); ++ BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS); ++ ++ return 0; ++}; ++late_initcall(init_lru_gen); ++ ++#endif /* CONFIG_LRU_GEN */ ++ + static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) + { + unsigned long nr[NR_LRU_LISTS]; diff --git a/target/linux/generic/backport-5.15/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch b/target/linux/generic/backport-5.15/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch deleted file mode 100644 index a1c6e0f75d9..00000000000 --- a/target/linux/generic/backport-5.15/020-v6.1-05-mm-multigenerational-lru-mm_struct-list.patch +++ /dev/null @@ -1,760 +0,0 @@ -From 534bcc4a0bb5b24600891ce793f0295a142e9dae Mon Sep 17 00:00:00 2001 -From: Yu Zhao -Date: Mon, 5 Apr 2021 04:17:41 -0600 -Subject: [PATCH 05/10] mm: multigenerational lru: mm_struct list - -To scan PTEs for accessed pages, a mm_struct list is maintained for -each memcg. When multiple threads traverse the same memcg->mm_list, -each of them gets a unique mm_struct and therefore they can run -walk_page_range() concurrently to reach page tables of all processes -of this memcg. - -This infrastructure also provides the following optimizations: - 1) it allows walkers to skip processes that have been sleeping since - the last walk by tracking the usage of mm_struct between context - switches. - 2) it allows walkers to add interesting items they find during a - walk to a Bloom filter so that they can skip uninteresting items - during the next walk by testing whether an item is in this Bloom - filter. - -Signed-off-by: Yu Zhao -Tested-by: Konstantin Kharlamov -Change-Id: I25d9eda8c6bdc7c3653b9f210a159d6c247c81e8 ---- - fs/exec.c | 2 + - include/linux/memcontrol.h | 4 + - include/linux/mm_inline.h | 6 + - include/linux/mm_types.h | 75 +++++++++ - include/linux/mmzone.h | 63 +++++++ - kernel/exit.c | 1 + - kernel/fork.c | 9 + - kernel/sched/core.c | 1 + - mm/memcontrol.c | 25 +++ - mm/vmscan.c | 331 +++++++++++++++++++++++++++++++++++++ - 10 files changed, 517 insertions(+) - ---- a/fs/exec.c -+++ b/fs/exec.c -@@ -1013,6 +1013,7 @@ static int exec_mmap(struct mm_struct *m - active_mm = tsk->active_mm; - tsk->active_mm = mm; - tsk->mm = mm; -+ lru_gen_add_mm(mm); - /* - * This prevents preemption while active_mm is being loaded and - * it and mm are being updated, which could cause problems for -@@ -1023,6 +1024,7 @@ static int exec_mmap(struct mm_struct *m - if (!IS_ENABLED(CONFIG_ARCH_WANT_IRQS_OFF_ACTIVATE_MM)) - local_irq_enable(); - activate_mm(active_mm, mm); -+ lru_gen_activate_mm(mm); - if (IS_ENABLED(CONFIG_ARCH_WANT_IRQS_OFF_ACTIVATE_MM)) - local_irq_enable(); - tsk->mm->vmacache_seqnum = 0; ---- a/include/linux/memcontrol.h -+++ b/include/linux/memcontrol.h -@@ -348,6 +348,10 @@ struct mem_cgroup { - struct deferred_split deferred_split_queue; - #endif - -+#ifdef CONFIG_LRU_GEN -+ struct lru_gen_mm_list mm_list; -+#endif -+ - struct mem_cgroup_per_node *nodeinfo[]; - }; - ---- a/include/linux/mm_inline.h -+++ b/include/linux/mm_inline.h -@@ -100,6 +100,12 @@ static inline int lru_gen_from_seq(unsig - return seq % MAX_NR_GENS; - } - -+/* Return a proper index regardless whether we keep stats for historical generations. */ -+static inline int lru_hist_from_seq(unsigned long seq) -+{ -+ return seq % NR_HIST_GENS; -+} -+ - /* The youngest and the second youngest generations are counted as active. */ - static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen) - { ---- a/include/linux/mm_types.h -+++ b/include/linux/mm_types.h -@@ -3,6 +3,7 @@ - #define _LINUX_MM_TYPES_H - - #include -+#include - - #include - #include -@@ -15,6 +16,8 @@ - #include - #include - #include -+#include -+#include - - #include - -@@ -580,6 +583,18 @@ struct mm_struct { - #ifdef CONFIG_IOMMU_SUPPORT - u32 pasid; - #endif -+#ifdef CONFIG_LRU_GEN -+ struct { -+ /* the node of a global or per-memcg mm_struct list */ -+ struct list_head list; -+#ifdef CONFIG_MEMCG -+ /* points to the memcg of the owner task above */ -+ struct mem_cgroup *memcg; -+#endif -+ /* whether this mm_struct has been used since the last walk */ -+ nodemask_t nodes; -+ } lrugen; -+#endif /* CONFIG_LRU_GEN */ - } __randomize_layout; - - /* -@@ -606,6 +621,66 @@ static inline cpumask_t *mm_cpumask(stru - return (struct cpumask *)&mm->cpu_bitmap; - } - -+#ifdef CONFIG_LRU_GEN -+ -+struct lru_gen_mm_list { -+ /* a global or per-memcg mm_struct list */ -+ struct list_head fifo; -+ /* protects the list above */ -+ spinlock_t lock; -+}; -+ -+void lru_gen_add_mm(struct mm_struct *mm); -+void lru_gen_del_mm(struct mm_struct *mm); -+#ifdef CONFIG_MEMCG -+void lru_gen_migrate_mm(struct mm_struct *mm); -+#endif -+ -+static inline void lru_gen_init_mm(struct mm_struct *mm) -+{ -+ INIT_LIST_HEAD(&mm->lrugen.list); -+#ifdef CONFIG_MEMCG -+ mm->lrugen.memcg = NULL; -+#endif -+ nodes_clear(mm->lrugen.nodes); -+} -+ -+/* Track the usage of each mm_struct so that we can skip inactive ones. */ -+static inline void lru_gen_activate_mm(struct mm_struct *mm) -+{ -+ /* unlikely but not a bug when racing with lru_gen_migrate_mm() */ -+ VM_WARN_ON(list_empty(&mm->lrugen.list)); -+ -+ if (!(current->flags & PF_KTHREAD) && !nodes_full(mm->lrugen.nodes)) -+ nodes_setall(mm->lrugen.nodes); -+} -+ -+#else /* !CONFIG_LRU_GEN */ -+ -+static inline void lru_gen_add_mm(struct mm_struct *mm) -+{ -+} -+ -+static inline void lru_gen_del_mm(struct mm_struct *mm) -+{ -+} -+ -+#ifdef CONFIG_MEMCG -+static inline void lru_gen_migrate_mm(struct mm_struct *mm) -+{ -+} -+#endif -+ -+static inline void lru_gen_init_mm(struct mm_struct *mm) -+{ -+} -+ -+static inline void lru_gen_activate_mm(struct mm_struct *mm) -+{ -+} -+ -+#endif /* CONFIG_LRU_GEN */ -+ - struct mmu_gather; - extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm); - extern void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm); ---- a/include/linux/mmzone.h -+++ b/include/linux/mmzone.h -@@ -318,6 +318,13 @@ struct lruvec; - #define MIN_NR_GENS 2 - #define MAX_NR_GENS ((unsigned int)CONFIG_NR_LRU_GENS) - -+/* Whether to keep stats for historical generations. */ -+#ifdef CONFIG_LRU_GEN_STATS -+#define NR_HIST_GENS ((unsigned int)CONFIG_NR_LRU_GENS) -+#else -+#define NR_HIST_GENS 1U -+#endif -+ - struct lrugen { - /* the aging increments the max generation number */ - unsigned long max_seq; -@@ -333,13 +340,63 @@ struct lrugen { - bool enabled[ANON_AND_FILE]; - }; - -+enum { -+ MM_LEAF_TOTAL, /* total leaf entries */ -+ MM_LEAF_OLD, /* old leaf entries */ -+ MM_LEAF_YOUNG, /* young leaf entries */ -+ MM_NONLEAF_TOTAL, /* total non-leaf entries */ -+ MM_NONLEAF_PREV, /* previously worthy non-leaf entries */ -+ MM_NONLEAF_CUR, /* currently worthy non-leaf entries */ -+ NR_MM_STATS -+}; -+ -+/* mnemonic codes for the stats above */ -+#define MM_STAT_CODES "toydpc" -+ -+/* double buffering bloom filters */ -+#define NR_BLOOM_FILTERS 2 -+ -+struct lru_gen_mm_walk { -+ /* set to max_seq after each round of walk */ -+ unsigned long seq; -+ /* the next mm_struct on the list to walk */ -+ struct list_head *head; -+ /* the first mm_struct never walked before */ -+ struct list_head *tail; -+ /* to wait for the last walker to finish */ -+ struct wait_queue_head wait; -+ /* bloom filters flip after each round of walk */ -+ unsigned long *filters[NR_BLOOM_FILTERS]; -+ /* page table stats for debugging */ -+ unsigned long stats[NR_HIST_GENS][NR_MM_STATS]; -+ /* the number of concurrent walkers */ -+ int nr_walkers; -+}; -+ -+#define MIN_BATCH_SIZE 64 - #define MAX_BATCH_SIZE 8192 - -+struct mm_walk_args { -+ struct mem_cgroup *memcg; -+ unsigned long max_seq; -+ unsigned long start_pfn; -+ unsigned long end_pfn; -+ unsigned long next_addr; -+ unsigned long bitmap[BITS_TO_LONGS(MIN_BATCH_SIZE)]; -+ int node_id; -+ int swappiness; -+ int batch_size; -+ int nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; -+ int mm_stats[NR_MM_STATS]; -+ bool use_filter; -+}; -+ - void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec); - void lru_gen_change_state(bool enable, bool main, bool swap); - - #ifdef CONFIG_MEMCG - void lru_gen_init_memcg(struct mem_cgroup *memcg); -+void lru_gen_free_memcg(struct mem_cgroup *memcg); - #endif - - #else /* !CONFIG_LRU_GEN */ -@@ -356,6 +413,10 @@ static inline void lru_gen_change_state( - static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) - { - } -+ -+static inline void lru_gen_free_memcg(struct mem_cgroup *memcg) -+{ -+} - #endif - - #endif /* CONFIG_LRU_GEN */ -@@ -380,6 +441,8 @@ struct lruvec { - #ifdef CONFIG_LRU_GEN - /* unevictable pages are on LRU_UNEVICTABLE */ - struct lrugen evictable; -+ /* state for mm list and page table walks */ -+ struct lru_gen_mm_walk mm_walk; - #endif - #ifdef CONFIG_MEMCG - struct pglist_data *pgdat; ---- a/kernel/exit.c -+++ b/kernel/exit.c -@@ -469,6 +469,7 @@ assign_new_owner: - goto retry; - } - WRITE_ONCE(mm->owner, c); -+ lru_gen_migrate_mm(mm); - task_unlock(c); - put_task_struct(c); - } ---- a/kernel/fork.c -+++ b/kernel/fork.c -@@ -1083,6 +1083,7 @@ static struct mm_struct *mm_init(struct - goto fail_nocontext; - - mm->user_ns = get_user_ns(user_ns); -+ lru_gen_init_mm(mm); - return mm; - - fail_nocontext: -@@ -1125,6 +1126,7 @@ static inline void __mmput(struct mm_str - } - if (mm->binfmt) - module_put(mm->binfmt->module); -+ lru_gen_del_mm(mm); - mmdrop(mm); - } - -@@ -2622,6 +2624,13 @@ pid_t kernel_clone(struct kernel_clone_a - get_task_struct(p); - } - -+ if (IS_ENABLED(CONFIG_LRU_GEN) && !(clone_flags & CLONE_VM)) { -+ /* lock the task to synchronize with memcg migration */ -+ task_lock(p); -+ lru_gen_add_mm(p->mm); -+ task_unlock(p); -+ } -+ - wake_up_new_task(p); - - /* forking complete and child started to run, tell ptracer */ ---- a/kernel/sched/core.c -+++ b/kernel/sched/core.c -@@ -5007,6 +5007,7 @@ context_switch(struct rq *rq, struct tas - * finish_task_switch()'s mmdrop(). - */ - switch_mm_irqs_off(prev->active_mm, next->mm, next); -+ lru_gen_activate_mm(next->mm); - - if (!prev->mm) { // from kernel - /* will mmdrop() in finish_task_switch(). */ ---- a/mm/memcontrol.c -+++ b/mm/memcontrol.c -@@ -5178,6 +5178,7 @@ static void __mem_cgroup_free(struct mem - - static void mem_cgroup_free(struct mem_cgroup *memcg) - { -+ lru_gen_free_memcg(memcg); - memcg_wb_domain_exit(memcg); - __mem_cgroup_free(memcg); - } -@@ -6210,6 +6211,29 @@ static void mem_cgroup_move_task(void) - } - #endif - -+#ifdef CONFIG_LRU_GEN -+static void mem_cgroup_attach(struct cgroup_taskset *tset) -+{ -+ struct cgroup_subsys_state *css; -+ struct task_struct *task = NULL; -+ -+ cgroup_taskset_for_each_leader(task, css, tset) -+ break; -+ -+ if (!task) -+ return; -+ -+ task_lock(task); -+ if (task->mm && task->mm->owner == task) -+ lru_gen_migrate_mm(task->mm); -+ task_unlock(task); -+} -+#else -+static void mem_cgroup_attach(struct cgroup_taskset *tset) -+{ -+} -+#endif /* CONFIG_LRU_GEN */ -+ - static int seq_puts_memcg_tunable(struct seq_file *m, unsigned long value) - { - if (value == PAGE_COUNTER_MAX) -@@ -6553,6 +6577,7 @@ struct cgroup_subsys memory_cgrp_subsys - .css_reset = mem_cgroup_css_reset, - .css_rstat_flush = mem_cgroup_css_rstat_flush, - .can_attach = mem_cgroup_can_attach, -+ .attach = mem_cgroup_attach, - .cancel_attach = mem_cgroup_cancel_attach, - .post_attach = mem_cgroup_move_task, - .dfl_cftypes = memory_files, ---- a/mm/vmscan.c -+++ b/mm/vmscan.c -@@ -2864,6 +2864,306 @@ static bool __maybe_unused seq_is_valid( - } - - /****************************************************************************** -+ * mm_struct list -+ ******************************************************************************/ -+ -+static struct lru_gen_mm_list *get_mm_list(struct mem_cgroup *memcg) -+{ -+ static struct lru_gen_mm_list mm_list = { -+ .fifo = LIST_HEAD_INIT(mm_list.fifo), -+ .lock = __SPIN_LOCK_UNLOCKED(mm_list.lock), -+ }; -+ -+#ifdef CONFIG_MEMCG -+ if (memcg) -+ return &memcg->mm_list; -+#endif -+ return &mm_list; -+} -+ -+void lru_gen_add_mm(struct mm_struct *mm) -+{ -+ int nid; -+ struct mem_cgroup *memcg = get_mem_cgroup_from_mm(mm); -+ struct lru_gen_mm_list *mm_list = get_mm_list(memcg); -+ -+ VM_BUG_ON_MM(!list_empty(&mm->lrugen.list), mm); -+#ifdef CONFIG_MEMCG -+ VM_BUG_ON_MM(mm->lrugen.memcg, mm); -+ mm->lrugen.memcg = memcg; -+#endif -+ spin_lock(&mm_list->lock); -+ -+ list_add_tail(&mm->lrugen.list, &mm_list->fifo); -+ -+ for_each_node(nid) { -+ struct lruvec *lruvec = get_lruvec(nid, memcg); -+ -+ if (!lruvec) -+ continue; -+ -+ if (lruvec->mm_walk.tail == &mm_list->fifo) -+ lruvec->mm_walk.tail = lruvec->mm_walk.tail->prev; -+ } -+ -+ spin_unlock(&mm_list->lock); -+} -+ -+void lru_gen_del_mm(struct mm_struct *mm) -+{ -+ int nid; -+ struct lru_gen_mm_list *mm_list; -+ struct mem_cgroup *memcg = NULL; -+ -+ if (list_empty(&mm->lrugen.list)) -+ return; -+ -+#ifdef CONFIG_MEMCG -+ memcg = mm->lrugen.memcg; -+#endif -+ mm_list = get_mm_list(memcg); -+ -+ spin_lock(&mm_list->lock); -+ -+ for_each_node(nid) { -+ struct lruvec *lruvec = get_lruvec(nid, memcg); -+ -+ if (!lruvec) -+ continue; -+ -+ if (lruvec->mm_walk.tail == &mm->lrugen.list) -+ lruvec->mm_walk.tail = lruvec->mm_walk.tail->next; -+ -+ if (lruvec->mm_walk.head != &mm->lrugen.list) -+ continue; -+ -+ lruvec->mm_walk.head = lruvec->mm_walk.head->next; -+ if (lruvec->mm_walk.head == &mm_list->fifo) -+ WRITE_ONCE(lruvec->mm_walk.seq, lruvec->mm_walk.seq + 1); -+ } -+ -+ list_del_init(&mm->lrugen.list); -+ -+ spin_unlock(&mm_list->lock); -+ -+#ifdef CONFIG_MEMCG -+ mem_cgroup_put(mm->lrugen.memcg); -+ mm->lrugen.memcg = NULL; -+#endif -+} -+ -+#ifdef CONFIG_MEMCG -+void lru_gen_migrate_mm(struct mm_struct *mm) -+{ -+ struct mem_cgroup *memcg; -+ -+ lockdep_assert_held(&mm->owner->alloc_lock); -+ -+ if (mem_cgroup_disabled()) -+ return; -+ -+ rcu_read_lock(); -+ memcg = mem_cgroup_from_task(mm->owner); -+ rcu_read_unlock(); -+ if (memcg == mm->lrugen.memcg) -+ return; -+ -+ VM_BUG_ON_MM(!mm->lrugen.memcg, mm); -+ VM_BUG_ON_MM(list_empty(&mm->lrugen.list), mm); -+ -+ lru_gen_del_mm(mm); -+ lru_gen_add_mm(mm); -+} -+#endif -+ -+#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 clear_bloom_filter(struct lruvec *lruvec, unsigned long seq) -+{ -+ unsigned long *filter; -+ int gen = filter_gen_from_seq(seq); -+ -+ lockdep_assert_held(&get_mm_list(lruvec_memcg(lruvec))->lock); -+ -+ filter = lruvec->mm_walk.filters[gen]; -+ if (filter) { -+ bitmap_clear(filter, 0, BIT(BLOOM_FILTER_SHIFT)); -+ return; -+ } -+ -+ filter = bitmap_zalloc(BIT(BLOOM_FILTER_SHIFT), GFP_ATOMIC); -+ WRITE_ONCE(lruvec->mm_walk.filters[gen], filter); -+} -+ -+static void set_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_walk.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_walk.filters[gen]); -+ if (!filter) -+ return false; -+ -+ get_item_key(item, key); -+ -+ return test_bit(key[0], filter) && test_bit(key[1], filter); -+} -+ -+static void reset_mm_stats(struct lruvec *lruvec, bool last, struct mm_walk_args *args) -+{ -+ int i; -+ int hist = lru_hist_from_seq(args->max_seq); -+ -+ lockdep_assert_held(&get_mm_list(lruvec_memcg(lruvec))->lock); -+ -+ for (i = 0; i < NR_MM_STATS; i++) { -+ WRITE_ONCE(lruvec->mm_walk.stats[hist][i], -+ lruvec->mm_walk.stats[hist][i] + args->mm_stats[i]); -+ args->mm_stats[i] = 0; -+ } -+ -+ if (!last || NR_HIST_GENS == 1) -+ return; -+ -+ hist = lru_hist_from_seq(args->max_seq + 1); -+ for (i = 0; i < NR_MM_STATS; i++) -+ WRITE_ONCE(lruvec->mm_walk.stats[hist][i], 0); -+} -+ -+static bool should_skip_mm(struct mm_struct *mm, struct mm_walk_args *args) -+{ -+ int type; -+ unsigned long size = 0; -+ -+ if (cpumask_empty(mm_cpumask(mm)) && !node_isset(args->node_id, mm->lrugen.nodes)) -+ return true; -+ -+ if (mm_is_oom_victim(mm)) -+ return true; -+ -+ for (type = !args->swappiness; type < ANON_AND_FILE; type++) { -+ size += type ? get_mm_counter(mm, MM_FILEPAGES) : -+ get_mm_counter(mm, MM_ANONPAGES) + -+ get_mm_counter(mm, MM_SHMEMPAGES); -+ } -+ -+ if (size < MIN_BATCH_SIZE) -+ return true; -+ -+ if (!mmget_not_zero(mm)) -+ return true; -+ -+ node_clear(args->node_id, mm->lrugen.nodes); -+ -+ return false; -+} -+ -+/* To support multiple walkers that concurrently walk an mm_struct list. */ -+static bool get_next_mm(struct lruvec *lruvec, struct mm_walk_args *args, -+ struct mm_struct **iter) -+{ -+ bool first = false; -+ bool last = true; -+ struct mm_struct *mm = NULL; -+ struct lru_gen_mm_walk *mm_walk = &lruvec->mm_walk; -+ struct lru_gen_mm_list *mm_list = get_mm_list(args->memcg); -+ -+ if (*iter) -+ mmput_async(*iter); -+ else if (args->max_seq <= READ_ONCE(mm_walk->seq)) -+ return false; -+ -+ spin_lock(&mm_list->lock); -+ -+ VM_BUG_ON(args->max_seq > mm_walk->seq + 1); -+ VM_BUG_ON(*iter && args->max_seq < mm_walk->seq); -+ VM_BUG_ON(*iter && !mm_walk->nr_walkers); -+ -+ if (args->max_seq <= mm_walk->seq) { -+ if (!*iter) -+ last = false; -+ goto done; -+ } -+ -+ if (mm_walk->head == &mm_list->fifo) { -+ VM_BUG_ON(mm_walk->nr_walkers); -+ mm_walk->head = mm_walk->head->next; -+ first = true; -+ } -+ -+ while (!mm && mm_walk->head != &mm_list->fifo) { -+ mm = list_entry(mm_walk->head, struct mm_struct, lrugen.list); -+ -+ mm_walk->head = mm_walk->head->next; -+ -+ if (mm_walk->tail == &mm->lrugen.list) { -+ mm_walk->tail = mm_walk->tail->next; -+ args->use_filter = false; -+ } -+ -+ if (should_skip_mm(mm, args)) -+ mm = NULL; -+ } -+ -+ if (mm_walk->head == &mm_list->fifo) -+ WRITE_ONCE(mm_walk->seq, mm_walk->seq + 1); -+done: -+ if (*iter && !mm) -+ mm_walk->nr_walkers--; -+ if (!*iter && mm) -+ mm_walk->nr_walkers++; -+ -+ if (mm_walk->nr_walkers) -+ last = false; -+ -+ if (mm && first) -+ clear_bloom_filter(lruvec, args->max_seq + 1); -+ -+ if (*iter || last) -+ reset_mm_stats(lruvec, last, args); -+ -+ spin_unlock(&mm_list->lock); -+ -+ *iter = mm; -+ -+ return last; -+} -+ -+/****************************************************************************** - * state change - ******************************************************************************/ - -@@ -3047,6 +3347,7 @@ void lru_gen_init_state(struct mem_cgrou - int i; - int gen, type, zone; - struct lrugen *lrugen = &lruvec->evictable; -+ struct lru_gen_mm_list *mm_list = get_mm_list(memcg); - - lrugen->max_seq = MIN_NR_GENS + 1; - lrugen->enabled[0] = lru_gen_enabled() && lru_gen_nr_swapfiles; -@@ -3057,6 +3358,17 @@ void lru_gen_init_state(struct mem_cgrou - - for_each_gen_type_zone(gen, type, zone) - INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); -+ -+ if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG) && !memcg) -+ spin_lock(&mm_list->lock); -+ -+ lruvec->mm_walk.seq = MIN_NR_GENS; -+ lruvec->mm_walk.head = &mm_list->fifo; -+ lruvec->mm_walk.tail = &mm_list->fifo; -+ init_waitqueue_head(&lruvec->mm_walk.wait); -+ -+ if (IS_ENABLED(CONFIG_MEMORY_HOTPLUG) && !memcg) -+ spin_unlock(&mm_list->lock); - } - - #ifdef CONFIG_MEMCG -@@ -3064,18 +3376,37 @@ void lru_gen_init_memcg(struct mem_cgrou - { - int nid; - -+ INIT_LIST_HEAD(&memcg->mm_list.fifo); -+ spin_lock_init(&memcg->mm_list.lock); -+ - for_each_node(nid) { - struct lruvec *lruvec = get_lruvec(nid, memcg); - - lru_gen_init_state(memcg, lruvec); - } - } -+ -+void lru_gen_free_memcg(struct mem_cgroup *memcg) -+{ -+ int nid; -+ -+ for_each_node(nid) { -+ int i; -+ struct lruvec *lruvec = get_lruvec(nid, memcg); -+ -+ for (i = 0; i < NR_BLOOM_FILTERS; i++) { -+ bitmap_free(lruvec->mm_walk.filters[i]); -+ lruvec->mm_walk.filters[i] = NULL; -+ } -+ } -+} - #endif - - static int __init init_lru_gen(void) - { - BUILD_BUG_ON(MIN_NR_GENS + 1 >= MAX_NR_GENS); - BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS); -+ BUILD_BUG_ON(sizeof(MM_STAT_CODES) != NR_MM_STATS + 1); - - return 0; - }; diff --git a/target/linux/generic/backport-5.15/020-v6.1-06-mm-multi-gen-LRU-minimal-implementation.patch b/target/linux/generic/backport-5.15/020-v6.1-06-mm-multi-gen-LRU-minimal-implementation.patch new file mode 100644 index 00000000000..f8a7d9bd7f6 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-06-mm-multi-gen-LRU-minimal-implementation.patch @@ -0,0 +1,1447 @@ +From b564b9471cd60ef1ee3961a224898ce4a9620d84 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:03 -0600 +Subject: [PATCH 06/29] mm: multi-gen LRU: minimal implementation +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To avoid confusion, the terms "promotion" and "demotion" will be applied +to the multi-gen LRU, as a new convention; the terms "activation" and +"deactivation" will be applied to the active/inactive LRU, as usual. + +The aging produces young generations. Given an lruvec, it increments +max_seq when max_seq-min_seq+1 approaches MIN_NR_GENS. The aging promotes +hot pages to the youngest generation when it finds them accessed through +page tables; the demotion of cold pages happens consequently when it +increments max_seq. Promotion in the aging path does not involve any LRU +list operations, only the updates of the gen counter and +lrugen->nr_pages[]; demotion, unless as the result of the increment of +max_seq, requires LRU list operations, e.g., lru_deactivate_fn(). The +aging has the complexity O(nr_hot_pages), since it is only interested in +hot pages. + +The eviction consumes old generations. Given an lruvec, it increments +min_seq when lrugen->lists[] indexed by min_seq%MAX_NR_GENS becomes empty. +A feedback loop modeled after the PID controller monitors refaults over +anon and file types and decides which type to evict when both types are +available from the same generation. + +The protection of pages accessed multiple times through file descriptors +takes place in the eviction path. Each generation is divided into +multiple tiers. A page accessed N times through file descriptors is in +tier order_base_2(N). Tiers do not have dedicated lrugen->lists[], only +bits in page->flags. The aforementioned feedback loop also monitors +refaults over all tiers and decides when to protect pages in which tiers +(N>1), using the first tier (N=0,1) as a baseline. The first tier +contains single-use unmapped clean pages, which are most likely the best +choices. In contrast to promotion in the aging path, the protection of a +page in the eviction path is achieved by moving this page to the next +generation, i.e., min_seq+1, if the feedback loop decides so. This +approach has the following advantages: + +1. It removes the cost of activation in the buffered access path by + inferring whether pages accessed multiple times through file + descriptors are statistically hot and thus worth protecting in the + eviction path. +2. It takes pages accessed through page tables into account and avoids + overprotecting pages accessed multiple times through file + descriptors. (Pages accessed through page tables are in the first + tier, since N=0.) +3. More tiers provide better protection for pages accessed more than + twice through file descriptors, when under heavy buffered I/O + workloads. + +Server benchmark results: + Single workload: + fio (buffered I/O): +[30, 32]% + IOPS BW + 5.19-rc1: 2673k 10.2GiB/s + patch1-6: 3491k 13.3GiB/s + + Single workload: + memcached (anon): -[4, 6]% + Ops/sec KB/sec + 5.19-rc1: 1161501.04 45177.25 + patch1-6: 1106168.46 43025.04 + + Configurations: + CPU: two Xeon 6154 + Mem: total 256G + + Node 1 was only used as a ram disk to reduce the variance in the + results. + + patch drivers/block/brd.c < gfp_flags = GFP_NOIO | __GFP_ZERO | __GFP_HIGHMEM | __GFP_THISNODE; + > page = alloc_pages_node(1, gfp_flags, 0); + EOF + + cat >>/etc/systemd/system.conf <>/etc/memcached.conf </sys/fs/cgroup/user.slice/test/memory.max + echo $$ >/sys/fs/cgroup/user.slice/test/cgroup.procs + fio -name=mglru --numjobs=72 --directory=/mnt --size=1408m \ + --buffered=1 --ioengine=io_uring --iodepth=128 \ + --iodepth_batch_submit=32 --iodepth_batch_complete=32 \ + --rw=randread --random_distribution=random --norandommap \ + --time_based --ramp_time=10m --runtime=5m --group_reporting + + cat memcached.sh + modprobe brd rd_nr=1 rd_size=113246208 + swapoff -a + mkswap /dev/ram0 + swapon /dev/ram0 + + memtier_benchmark -S /var/run/memcached/memcached.sock \ + -P memcache_binary -n allkeys --key-minimum=1 \ + --key-maximum=65000000 --key-pattern=P:P -c 1 -t 36 \ + --ratio 1:0 --pipeline 8 -d 2000 + + memtier_benchmark -S /var/run/memcached/memcached.sock \ + -P memcache_binary -n allkeys --key-minimum=1 \ + --key-maximum=65000000 --key-pattern=R:R -c 1 -t 36 \ + --ratio 0:1 --pipeline 8 --randomize --distinct-client-seed + +Client benchmark results: + kswapd profiles: + 5.19-rc1 + 40.33% page_vma_mapped_walk (overhead) + 21.80% lzo1x_1_do_compress (real work) + 7.53% do_raw_spin_lock + 3.95% _raw_spin_unlock_irq + 2.52% vma_interval_tree_iter_next + 2.37% page_referenced_one + 2.28% vma_interval_tree_subtree_search + 1.97% anon_vma_interval_tree_iter_first + 1.60% ptep_clear_flush + 1.06% __zram_bvec_write + + patch1-6 + 39.03% lzo1x_1_do_compress (real work) + 18.47% page_vma_mapped_walk (overhead) + 6.74% _raw_spin_unlock_irq + 3.97% do_raw_spin_lock + 2.49% ptep_clear_flush + 2.48% anon_vma_interval_tree_iter_first + 1.92% page_referenced_one + 1.88% __zram_bvec_write + 1.48% memmove + 1.31% vma_interval_tree_iter_next + + Configurations: + CPU: single Snapdragon 7c + Mem: total 4G + + ChromeOS MemoryPressure [1] + +[1] https://chromium.googlesource.com/chromiumos/platform/tast-tests/ + +Link: https://lkml.kernel.org/r/20220918080010.2920238-7-yuzhao@google.com +Signed-off-by: Yu Zhao +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Barry Song +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + include/linux/mm_inline.h | 36 ++ + include/linux/mmzone.h | 41 ++ + include/linux/page-flags-layout.h | 5 +- + kernel/bounds.c | 2 + + mm/Kconfig | 11 + + mm/swap.c | 39 ++ + mm/vmscan.c | 792 +++++++++++++++++++++++++++++- + mm/workingset.c | 110 ++++- + 8 files changed, 1025 insertions(+), 11 deletions(-) + +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -106,6 +106,33 @@ static inline int lru_gen_from_seq(unsig + return seq % MAX_NR_GENS; + } + ++static inline int lru_hist_from_seq(unsigned long seq) ++{ ++ return seq % NR_HIST_GENS; ++} ++ ++static inline int lru_tier_from_refs(int refs) ++{ ++ VM_WARN_ON_ONCE(refs > BIT(LRU_REFS_WIDTH)); ++ ++ /* see the comment in page_lru_refs() */ ++ return order_base_2(refs + 1); ++} ++ ++static inline int page_lru_refs(struct page *page) ++{ ++ unsigned long flags = READ_ONCE(page->flags); ++ bool workingset = flags & BIT(PG_workingset); ++ ++ /* ++ * Return the number of accesses beyond PG_referenced, i.e., N-1 if the ++ * total number of accesses is N>1, since N=0,1 both map to the first ++ * tier. lru_tier_from_refs() will account for this off-by-one. Also see ++ * the comment on MAX_NR_TIERS. ++ */ ++ return ((flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF) + workingset; ++} ++ + static inline int page_lru_gen(struct page *page) + { + unsigned long flags = READ_ONCE(page->flags); +@@ -158,6 +185,15 @@ static inline void lru_gen_update_size(s + __update_lru_size(lruvec, lru, zone, -delta); + return; + } ++ ++ /* promotion */ ++ if (!lru_gen_is_active(lruvec, old_gen) && lru_gen_is_active(lruvec, new_gen)) { ++ __update_lru_size(lruvec, lru, zone, -delta); ++ __update_lru_size(lruvec, lru + LRU_ACTIVE, zone, delta); ++ } ++ ++ /* demotion requires isolation, e.g., lru_deactivate_fn() */ ++ VM_WARN_ON_ONCE(lru_gen_is_active(lruvec, old_gen) && !lru_gen_is_active(lruvec, new_gen)); + } + + static inline bool lru_gen_add_page(struct lruvec *lruvec, struct page *page, bool reclaiming) +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -327,6 +327,28 @@ enum lruvec_flags { + #define MIN_NR_GENS 2U + #define MAX_NR_GENS 4U + ++/* ++ * Each generation is divided into multiple tiers. A page accessed N times ++ * through file descriptors is in tier order_base_2(N). A page in the first tier ++ * (N=0,1) is marked by PG_referenced unless it was faulted in through page ++ * tables or read ahead. A page in any other tier (N>1) is marked by ++ * PG_referenced and PG_workingset. This implies a minimum of two tiers is ++ * supported without using additional bits in page->flags. ++ * ++ * In contrast to moving across generations which requires the LRU lock, moving ++ * across tiers only involves atomic operations on page->flags and therefore ++ * has a negligible cost in the buffered access path. In the eviction path, ++ * comparisons of refaulted/(evicted+protected) from the first tier and the ++ * rest infer whether pages accessed multiple times through file descriptors ++ * are statistically hot and thus worth protecting. ++ * ++ * MAX_NR_TIERS is set to 4 so that the multi-gen LRU can support twice the ++ * number of categories of the active/inactive LRU when keeping track of ++ * accesses through file descriptors. This uses MAX_NR_TIERS-2 spare bits in ++ * page->flags. ++ */ ++#define MAX_NR_TIERS 4U ++ + #ifndef __GENERATING_BOUNDS_H + + struct lruvec; +@@ -341,6 +363,16 @@ enum { + LRU_GEN_FILE, + }; + ++#define MIN_LRU_BATCH BITS_PER_LONG ++#define MAX_LRU_BATCH (MIN_LRU_BATCH * 64) ++ ++/* whether to keep historical stats from evicted generations */ ++#ifdef CONFIG_LRU_GEN_STATS ++#define NR_HIST_GENS MAX_NR_GENS ++#else ++#define NR_HIST_GENS 1U ++#endif ++ + /* + * The youngest generation number is stored in max_seq for both anon and file + * types as they are aged on an equal footing. The oldest generation numbers are +@@ -363,6 +395,15 @@ struct lru_gen_struct { + struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; + /* the multi-gen LRU sizes, eventually consistent */ + long nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++ /* the exponential moving average of refaulted */ ++ unsigned long avg_refaulted[ANON_AND_FILE][MAX_NR_TIERS]; ++ /* the exponential moving average of evicted+protected */ ++ unsigned long avg_total[ANON_AND_FILE][MAX_NR_TIERS]; ++ /* the first tier doesn't need protection, hence the minus one */ ++ unsigned long protected[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS - 1]; ++ /* can be modified without holding the LRU lock */ ++ atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; ++ atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; + }; + + void lru_gen_init_lruvec(struct lruvec *lruvec); +--- a/include/linux/page-flags-layout.h ++++ b/include/linux/page-flags-layout.h +@@ -106,7 +106,10 @@ + #error "Not enough bits in page flags" + #endif + +-#define LRU_REFS_WIDTH 0 ++/* see the comment on MAX_NR_TIERS */ ++#define LRU_REFS_WIDTH min(__LRU_REFS_WIDTH, BITS_PER_LONG - NR_PAGEFLAGS - \ ++ ZONES_WIDTH - LRU_GEN_WIDTH - SECTIONS_WIDTH - \ ++ NODES_WIDTH - KASAN_TAG_WIDTH - LAST_CPUPID_WIDTH) + + #endif + #endif /* _LINUX_PAGE_FLAGS_LAYOUT */ +--- a/kernel/bounds.c ++++ b/kernel/bounds.c +@@ -24,8 +24,10 @@ int main(void) + DEFINE(SPINLOCK_SIZE, sizeof(spinlock_t)); + #ifdef CONFIG_LRU_GEN + DEFINE(LRU_GEN_WIDTH, order_base_2(MAX_NR_GENS + 1)); ++ DEFINE(__LRU_REFS_WIDTH, MAX_NR_TIERS - 2); + #else + DEFINE(LRU_GEN_WIDTH, 0); ++ DEFINE(__LRU_REFS_WIDTH, 0); + #endif + /* End of constants */ + +--- a/mm/Kconfig ++++ b/mm/Kconfig +@@ -897,6 +897,7 @@ config IO_MAPPING + config SECRETMEM + def_bool ARCH_HAS_SET_DIRECT_MAP && !EMBEDDED + ++# multi-gen LRU { + config LRU_GEN + bool "Multi-Gen LRU" + depends on MMU +@@ -905,6 +906,16 @@ config LRU_GEN + help + A high performance LRU implementation to overcommit memory. + ++config LRU_GEN_STATS ++ bool "Full stats for debugging" ++ depends on LRU_GEN ++ help ++ Do not enable this option unless you plan to look at historical stats ++ from evicted generations for debugging purpose. ++ ++ This option has a per-memcg and per-node memory overhead. ++# } ++ + source "mm/damon/Kconfig" + + endmenu +--- a/mm/swap.c ++++ b/mm/swap.c +@@ -389,6 +389,40 @@ static void __lru_cache_activate_page(st + local_unlock(&lru_pvecs.lock); + } + ++#ifdef CONFIG_LRU_GEN ++static void page_inc_refs(struct page *page) ++{ ++ unsigned long new_flags, old_flags = READ_ONCE(page->flags); ++ ++ if (PageUnevictable(page)) ++ return; ++ ++ if (!PageReferenced(page)) { ++ SetPageReferenced(page); ++ return; ++ } ++ ++ if (!PageWorkingset(page)) { ++ SetPageWorkingset(page); ++ return; ++ } ++ ++ /* see the comment on MAX_NR_TIERS */ ++ do { ++ new_flags = old_flags & LRU_REFS_MASK; ++ if (new_flags == LRU_REFS_MASK) ++ break; ++ ++ new_flags += BIT(LRU_REFS_PGOFF); ++ new_flags |= old_flags & ~LRU_REFS_MASK; ++ } while (!try_cmpxchg(&page->flags, &old_flags, new_flags)); ++} ++#else ++static void page_inc_refs(struct page *page) ++{ ++} ++#endif /* CONFIG_LRU_GEN */ ++ + /* + * Mark a page as having seen activity. + * +@@ -403,6 +437,11 @@ void mark_page_accessed(struct page *pag + { + page = compound_head(page); + ++ if (lru_gen_enabled()) { ++ page_inc_refs(page); ++ return; ++ } ++ + if (!PageReferenced(page)) { + SetPageReferenced(page); + } else if (PageUnevictable(page)) { +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -1142,9 +1142,11 @@ static int __remove_mapping(struct addre + + if (PageSwapCache(page)) { + swp_entry_t swap = { .val = page_private(page) }; +- mem_cgroup_swapout(page, swap); ++ ++ /* get a shadow entry before mem_cgroup_swapout() clears page_memcg() */ + if (reclaimed && !mapping_exiting(mapping)) + shadow = workingset_eviction(page, target_memcg); ++ mem_cgroup_swapout(page, swap); + __delete_from_swap_cache(page, swap, shadow); + xa_unlock_irq(&mapping->i_pages); + put_swap_page(page, swap); +@@ -2502,6 +2504,9 @@ static void prepare_scan_count(pg_data_t + unsigned long file; + struct lruvec *target_lruvec; + ++ if (lru_gen_enabled()) ++ return; ++ + target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat); + + /* +@@ -2827,6 +2832,17 @@ static bool can_age_anon_pages(struct pg + * shorthand helpers + ******************************************************************************/ + ++#define LRU_REFS_FLAGS (BIT(PG_referenced) | BIT(PG_workingset)) ++ ++#define DEFINE_MAX_SEQ(lruvec) \ ++ unsigned long max_seq = READ_ONCE((lruvec)->lrugen.max_seq) ++ ++#define DEFINE_MIN_SEQ(lruvec) \ ++ unsigned long min_seq[ANON_AND_FILE] = { \ ++ READ_ONCE((lruvec)->lrugen.min_seq[LRU_GEN_ANON]), \ ++ READ_ONCE((lruvec)->lrugen.min_seq[LRU_GEN_FILE]), \ ++ } ++ + #define for_each_gen_type_zone(gen, type, zone) \ + for ((gen) = 0; (gen) < MAX_NR_GENS; (gen)++) \ + for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \ +@@ -2852,6 +2868,745 @@ static struct lruvec __maybe_unused *get + return pgdat ? &pgdat->__lruvec : NULL; + } + ++static int get_swappiness(struct lruvec *lruvec, struct scan_control *sc) ++{ ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ struct pglist_data *pgdat = lruvec_pgdat(lruvec); ++ ++ if (!can_demote(pgdat->node_id, sc) && ++ mem_cgroup_get_nr_swap_pages(memcg) < MIN_LRU_BATCH) ++ return 0; ++ ++ return mem_cgroup_swappiness(memcg); ++} ++ ++static int get_nr_gens(struct lruvec *lruvec, int type) ++{ ++ return lruvec->lrugen.max_seq - lruvec->lrugen.min_seq[type] + 1; ++} ++ ++static bool __maybe_unused seq_is_valid(struct lruvec *lruvec) ++{ ++ /* see the comment on lru_gen_struct */ ++ 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; ++} ++ ++/****************************************************************************** ++ * refault feedback loop ++ ******************************************************************************/ ++ ++/* ++ * A feedback loop based on Proportional-Integral-Derivative (PID) controller. ++ * ++ * The P term is refaulted/(evicted+protected) from a tier in the generation ++ * currently being evicted; the I term is the exponential moving average of the ++ * P term over the generations previously evicted, using the smoothing factor ++ * 1/2; the D term isn't supported. ++ * ++ * The setpoint (SP) is always the first tier of one type; the process variable ++ * (PV) is either any tier of the other type or any other tier of the same ++ * type. ++ * ++ * The error is the difference between the SP and the PV; the correction is to ++ * turn off protection when SP>PV or turn on protection when SPlrugen; ++ int hist = lru_hist_from_seq(lrugen->min_seq[type]); ++ ++ pos->refaulted = lrugen->avg_refaulted[type][tier] + ++ atomic_long_read(&lrugen->refaulted[hist][type][tier]); ++ pos->total = lrugen->avg_total[type][tier] + ++ atomic_long_read(&lrugen->evicted[hist][type][tier]); ++ if (tier) ++ pos->total += lrugen->protected[hist][type][tier - 1]; ++ pos->gain = gain; ++} ++ ++static void reset_ctrl_pos(struct lruvec *lruvec, int type, bool carryover) ++{ ++ int hist, tier; ++ struct lru_gen_struct *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; ++ ++ lockdep_assert_held(&lruvec->lru_lock); ++ ++ if (!carryover && !clear) ++ return; ++ ++ hist = lru_hist_from_seq(seq); ++ ++ for (tier = 0; tier < MAX_NR_TIERS; tier++) { ++ if (carryover) { ++ unsigned long sum; ++ ++ sum = lrugen->avg_refaulted[type][tier] + ++ atomic_long_read(&lrugen->refaulted[hist][type][tier]); ++ WRITE_ONCE(lrugen->avg_refaulted[type][tier], sum / 2); ++ ++ sum = lrugen->avg_total[type][tier] + ++ atomic_long_read(&lrugen->evicted[hist][type][tier]); ++ if (tier) ++ sum += lrugen->protected[hist][type][tier - 1]; ++ WRITE_ONCE(lrugen->avg_total[type][tier], sum / 2); ++ } ++ ++ if (clear) { ++ atomic_long_set(&lrugen->refaulted[hist][type][tier], 0); ++ atomic_long_set(&lrugen->evicted[hist][type][tier], 0); ++ if (tier) ++ WRITE_ONCE(lrugen->protected[hist][type][tier - 1], 0); ++ } ++ } ++} ++ ++static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv) ++{ ++ /* ++ * Return true if the PV has a limited number of refaults or a lower ++ * refaulted/total than the SP. ++ */ ++ return pv->refaulted < MIN_LRU_BATCH || ++ pv->refaulted * (sp->total + MIN_LRU_BATCH) * sp->gain <= ++ (sp->refaulted + 1) * pv->total * pv->gain; ++} ++ ++/****************************************************************************** ++ * the aging ++ ******************************************************************************/ ++ ++/* protect pages accessed multiple times through file descriptors */ ++static int page_inc_gen(struct lruvec *lruvec, struct page *page, bool reclaiming) ++{ ++ int type = page_is_file_lru(page); ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]); ++ unsigned long new_flags, old_flags = READ_ONCE(page->flags); ++ ++ VM_WARN_ON_ONCE_PAGE(!(old_flags & LRU_GEN_MASK), page); ++ ++ do { ++ new_gen = (old_gen + 1) % MAX_NR_GENS; ++ ++ new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_MASK | LRU_REFS_FLAGS); ++ new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF; ++ /* for end_page_writeback() */ ++ if (reclaiming) ++ new_flags |= BIT(PG_reclaim); ++ } while (!try_cmpxchg(&page->flags, &old_flags, new_flags)); ++ ++ lru_gen_update_size(lruvec, page, old_gen, new_gen); ++ ++ return new_gen; ++} ++ ++static void inc_min_seq(struct lruvec *lruvec, int type) ++{ ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ ++ reset_ctrl_pos(lruvec, type, true); ++ WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1); ++} ++ ++static bool try_to_inc_min_seq(struct lruvec *lruvec, bool can_swap) ++{ ++ int gen, type, zone; ++ bool success = false; ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ DEFINE_MIN_SEQ(lruvec); ++ ++ VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); ++ ++ /* find the oldest populated generation */ ++ for (type = !can_swap; type < ANON_AND_FILE; type++) { ++ while (min_seq[type] + MIN_NR_GENS <= lrugen->max_seq) { ++ gen = lru_gen_from_seq(min_seq[type]); ++ ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) { ++ if (!list_empty(&lrugen->lists[gen][type][zone])) ++ goto next; ++ } ++ ++ min_seq[type]++; ++ } ++next: ++ ; ++ } ++ ++ /* see the comment on lru_gen_struct */ ++ 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]); ++ } ++ ++ for (type = !can_swap; type < ANON_AND_FILE; type++) { ++ if (min_seq[type] == lrugen->min_seq[type]) ++ continue; ++ ++ reset_ctrl_pos(lruvec, type, true); ++ WRITE_ONCE(lrugen->min_seq[type], min_seq[type]); ++ success = true; ++ } ++ ++ return success; ++} ++ ++static void inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, bool can_swap) ++{ ++ int prev, next; ++ int type, zone; ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ ++ spin_lock_irq(&lruvec->lru_lock); ++ ++ VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); ++ ++ if (max_seq != lrugen->max_seq) ++ goto unlock; ++ ++ for (type = ANON_AND_FILE - 1; type >= 0; type--) { ++ if (get_nr_gens(lruvec, type) != MAX_NR_GENS) ++ continue; ++ ++ VM_WARN_ON_ONCE(type == LRU_GEN_FILE || can_swap); ++ ++ inc_min_seq(lruvec, type); ++ } ++ ++ /* ++ * Update the active/inactive LRU sizes for compatibility. Both sides of ++ * the current max_seq need to be covered, since max_seq+1 can overlap ++ * with min_seq[LRU_GEN_ANON] if swapping is constrained. And if they do ++ * overlap, cold/hot inversion happens. ++ */ ++ prev = lru_gen_from_seq(lrugen->max_seq - 1); ++ next = lru_gen_from_seq(lrugen->max_seq + 1); ++ ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) { ++ enum lru_list lru = type * LRU_INACTIVE_FILE; ++ long delta = lrugen->nr_pages[prev][type][zone] - ++ lrugen->nr_pages[next][type][zone]; ++ ++ if (!delta) ++ continue; ++ ++ __update_lru_size(lruvec, lru, zone, delta); ++ __update_lru_size(lruvec, lru + LRU_ACTIVE, zone, -delta); ++ } ++ } ++ ++ for (type = 0; type < ANON_AND_FILE; type++) ++ reset_ctrl_pos(lruvec, type, false); ++ ++ /* make sure preceding modifications appear */ ++ smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); ++unlock: ++ spin_unlock_irq(&lruvec->lru_lock); ++} ++ ++static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsigned long *min_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_struct *lrugen = &lruvec->lrugen; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ ++ 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 true; ++ 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 void age_lruvec(struct lruvec *lruvec, struct scan_control *sc) ++{ ++ bool need_aging; ++ unsigned long nr_to_scan; ++ int swappiness = get_swappiness(lruvec, sc); ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ DEFINE_MAX_SEQ(lruvec); ++ DEFINE_MIN_SEQ(lruvec); ++ ++ VM_WARN_ON_ONCE(sc->memcg_low_reclaim); ++ ++ mem_cgroup_calculate_protection(NULL, memcg); ++ ++ if (mem_cgroup_below_min(memcg)) ++ return; ++ ++ need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan); ++ if (need_aging) ++ inc_max_seq(lruvec, max_seq, swappiness); ++} ++ ++static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) ++{ ++ struct mem_cgroup *memcg; ++ ++ VM_WARN_ON_ONCE(!current_is_kswapd()); ++ ++ memcg = mem_cgroup_iter(NULL, NULL, NULL); ++ do { ++ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); ++ ++ age_lruvec(lruvec, sc); ++ ++ cond_resched(); ++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); ++} ++ ++/****************************************************************************** ++ * the eviction ++ ******************************************************************************/ ++ ++static bool sort_page(struct lruvec *lruvec, struct page *page, int tier_idx) ++{ ++ bool success; ++ int gen = page_lru_gen(page); ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ int delta = thp_nr_pages(page); ++ int refs = page_lru_refs(page); ++ int tier = lru_tier_from_refs(refs); ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ ++ VM_WARN_ON_ONCE_PAGE(gen >= MAX_NR_GENS, page); ++ ++ /* unevictable */ ++ if (!page_evictable(page)) { ++ success = lru_gen_del_page(lruvec, page, true); ++ VM_WARN_ON_ONCE_PAGE(!success, page); ++ SetPageUnevictable(page); ++ add_page_to_lru_list(page, lruvec); ++ __count_vm_events(UNEVICTABLE_PGCULLED, delta); ++ return true; ++ } ++ ++ /* dirty lazyfree */ ++ if (type == LRU_GEN_FILE && PageAnon(page) && PageDirty(page)) { ++ success = lru_gen_del_page(lruvec, page, true); ++ VM_WARN_ON_ONCE_PAGE(!success, page); ++ SetPageSwapBacked(page); ++ add_page_to_lru_list_tail(page, lruvec); ++ return true; ++ } ++ ++ /* protected */ ++ if (tier > tier_idx) { ++ int hist = lru_hist_from_seq(lrugen->min_seq[type]); ++ ++ gen = page_inc_gen(lruvec, page, false); ++ list_move_tail(&page->lru, &lrugen->lists[gen][type][zone]); ++ ++ WRITE_ONCE(lrugen->protected[hist][type][tier - 1], ++ lrugen->protected[hist][type][tier - 1] + delta); ++ __mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta); ++ return true; ++ } ++ ++ /* waiting for writeback */ ++ if (PageLocked(page) || PageWriteback(page) || ++ (type == LRU_GEN_FILE && PageDirty(page))) { ++ gen = page_inc_gen(lruvec, page, true); ++ list_move(&page->lru, &lrugen->lists[gen][type][zone]); ++ return true; ++ } ++ ++ return false; ++} ++ ++static bool isolate_page(struct lruvec *lruvec, struct page *page, struct scan_control *sc) ++{ ++ bool success; ++ ++ /* unmapping inhibited */ ++ if (!sc->may_unmap && page_mapped(page)) ++ return false; ++ ++ /* swapping inhibited */ ++ if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) && ++ (PageDirty(page) || ++ (PageAnon(page) && !PageSwapCache(page)))) ++ return false; ++ ++ /* raced with release_pages() */ ++ if (!get_page_unless_zero(page)) ++ return false; ++ ++ /* raced with another isolation */ ++ if (!TestClearPageLRU(page)) { ++ put_page(page); ++ return false; ++ } ++ ++ /* see the comment on MAX_NR_TIERS */ ++ if (!PageReferenced(page)) ++ set_mask_bits(&page->flags, LRU_REFS_MASK | LRU_REFS_FLAGS, 0); ++ ++ /* for shrink_page_list() */ ++ ClearPageReclaim(page); ++ ClearPageReferenced(page); ++ ++ success = lru_gen_del_page(lruvec, page, true); ++ VM_WARN_ON_ONCE_PAGE(!success, page); ++ ++ return true; ++} ++ ++static int scan_pages(struct lruvec *lruvec, struct scan_control *sc, ++ int type, int tier, struct list_head *list) ++{ ++ int gen, zone; ++ enum vm_event_item item; ++ int sorted = 0; ++ int scanned = 0; ++ int isolated = 0; ++ int remaining = MAX_LRU_BATCH; ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ ++ VM_WARN_ON_ONCE(!list_empty(list)); ++ ++ if (get_nr_gens(lruvec, type) == MIN_NR_GENS) ++ return 0; ++ ++ gen = lru_gen_from_seq(lrugen->min_seq[type]); ++ ++ for (zone = sc->reclaim_idx; zone >= 0; zone--) { ++ LIST_HEAD(moved); ++ int skipped = 0; ++ struct list_head *head = &lrugen->lists[gen][type][zone]; ++ ++ while (!list_empty(head)) { ++ struct page *page = lru_to_page(head); ++ int delta = thp_nr_pages(page); ++ ++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page); ++ VM_WARN_ON_ONCE_PAGE(PageActive(page), page); ++ VM_WARN_ON_ONCE_PAGE(page_is_file_lru(page) != type, page); ++ VM_WARN_ON_ONCE_PAGE(page_zonenum(page) != zone, page); ++ ++ scanned += delta; ++ ++ if (sort_page(lruvec, page, tier)) ++ sorted += delta; ++ else if (isolate_page(lruvec, page, sc)) { ++ list_add(&page->lru, list); ++ isolated += delta; ++ } else { ++ list_move(&page->lru, &moved); ++ skipped += delta; ++ } ++ ++ if (!--remaining || max(isolated, skipped) >= MIN_LRU_BATCH) ++ break; ++ } ++ ++ if (skipped) { ++ list_splice(&moved, head); ++ __count_zid_vm_events(PGSCAN_SKIP, zone, skipped); ++ } ++ ++ if (!remaining || isolated >= MIN_LRU_BATCH) ++ break; ++ } ++ ++ item = current_is_kswapd() ? PGSCAN_KSWAPD : PGSCAN_DIRECT; ++ if (!cgroup_reclaim(sc)) { ++ __count_vm_events(item, isolated); ++ __count_vm_events(PGREFILL, sorted); ++ } ++ __count_memcg_events(memcg, item, isolated); ++ __count_memcg_events(memcg, PGREFILL, sorted); ++ __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. ++ */ ++ return isolated || !remaining ? scanned : 0; ++} ++ ++static int get_tier_idx(struct lruvec *lruvec, int type) ++{ ++ int tier; ++ struct ctrl_pos sp, pv; ++ ++ /* ++ * To leave a margin for fluctuations, use a larger gain factor (1:2). ++ * This value is chosen because any other tier would have at least twice ++ * as many refaults as the first tier. ++ */ ++ read_ctrl_pos(lruvec, type, 0, 1, &sp); ++ for (tier = 1; tier < MAX_NR_TIERS; tier++) { ++ read_ctrl_pos(lruvec, type, tier, 2, &pv); ++ if (!positive_ctrl_err(&sp, &pv)) ++ break; ++ } ++ ++ return tier - 1; ++} ++ ++static int get_type_to_scan(struct lruvec *lruvec, int swappiness, int *tier_idx) ++{ ++ int type, tier; ++ struct ctrl_pos sp, pv; ++ int gain[ANON_AND_FILE] = { swappiness, 200 - swappiness }; ++ ++ /* ++ * Compare the first tier of anon with that of file to determine which ++ * type to scan. Also need to compare other tiers of the selected type ++ * with the first tier of the other type to determine the last tier (of ++ * the selected type) to evict. ++ */ ++ read_ctrl_pos(lruvec, LRU_GEN_ANON, 0, gain[LRU_GEN_ANON], &sp); ++ read_ctrl_pos(lruvec, LRU_GEN_FILE, 0, gain[LRU_GEN_FILE], &pv); ++ type = positive_ctrl_err(&sp, &pv); ++ ++ read_ctrl_pos(lruvec, !type, 0, gain[!type], &sp); ++ for (tier = 1; tier < MAX_NR_TIERS; tier++) { ++ read_ctrl_pos(lruvec, type, tier, gain[type], &pv); ++ if (!positive_ctrl_err(&sp, &pv)) ++ break; ++ } ++ ++ *tier_idx = tier - 1; ++ ++ return type; ++} ++ ++static int isolate_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness, ++ int *type_scanned, struct list_head *list) ++{ ++ int i; ++ int type; ++ int scanned; ++ int tier = -1; ++ DEFINE_MIN_SEQ(lruvec); ++ ++ /* ++ * Try to make the obvious choice first. When anon and file are both ++ * available from the same generation, interpret swappiness 1 as file ++ * first and 200 as anon first. ++ */ ++ if (!swappiness) ++ type = LRU_GEN_FILE; ++ else if (min_seq[LRU_GEN_ANON] < min_seq[LRU_GEN_FILE]) ++ type = LRU_GEN_ANON; ++ else if (swappiness == 1) ++ type = LRU_GEN_FILE; ++ else if (swappiness == 200) ++ type = LRU_GEN_ANON; ++ else ++ type = get_type_to_scan(lruvec, swappiness, &tier); ++ ++ for (i = !swappiness; i < ANON_AND_FILE; i++) { ++ if (tier < 0) ++ tier = get_tier_idx(lruvec, type); ++ ++ scanned = scan_pages(lruvec, sc, type, tier, list); ++ if (scanned) ++ break; ++ ++ type = !type; ++ tier = -1; ++ } ++ ++ *type_scanned = type; ++ ++ return scanned; ++} ++ ++static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness) ++{ ++ int type; ++ int scanned; ++ int reclaimed; ++ LIST_HEAD(list); ++ struct page *page; ++ enum vm_event_item item; ++ struct reclaim_stat stat; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ struct pglist_data *pgdat = lruvec_pgdat(lruvec); ++ ++ spin_lock_irq(&lruvec->lru_lock); ++ ++ scanned = isolate_pages(lruvec, sc, swappiness, &type, &list); ++ ++ scanned += try_to_inc_min_seq(lruvec, swappiness); ++ ++ if (get_nr_gens(lruvec, !swappiness) == MIN_NR_GENS) ++ scanned = 0; ++ ++ spin_unlock_irq(&lruvec->lru_lock); ++ ++ if (list_empty(&list)) ++ return scanned; ++ ++ reclaimed = shrink_page_list(&list, pgdat, sc, &stat, false); ++ ++ list_for_each_entry(page, &list, lru) { ++ /* restore LRU_REFS_FLAGS cleared by isolate_page() */ ++ if (PageWorkingset(page)) ++ SetPageReferenced(page); ++ ++ /* don't add rejected pages to the oldest generation */ ++ if (PageReclaim(page) && ++ (PageDirty(page) || PageWriteback(page))) ++ ClearPageActive(page); ++ else ++ SetPageActive(page); ++ } ++ ++ spin_lock_irq(&lruvec->lru_lock); ++ ++ move_pages_to_lru(lruvec, &list); ++ ++ item = current_is_kswapd() ? PGSTEAL_KSWAPD : PGSTEAL_DIRECT; ++ if (!cgroup_reclaim(sc)) ++ __count_vm_events(item, reclaimed); ++ __count_memcg_events(memcg, item, reclaimed); ++ __count_vm_events(PGSTEAL_ANON + type, reclaimed); ++ ++ spin_unlock_irq(&lruvec->lru_lock); ++ ++ mem_cgroup_uncharge_list(&list); ++ free_unref_page_list(&list); ++ ++ sc->nr_reclaimed += reclaimed; ++ ++ return scanned; ++} ++ ++static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, ++ bool can_swap) ++{ ++ bool need_aging; ++ 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) ++ return nr_to_scan; ++ ++ /* skip the aging path at the default priority */ ++ if (sc->priority == DEF_PRIORITY) ++ goto done; ++ ++ /* leave the work to lru_gen_age_node() */ ++ if (current_is_kswapd()) ++ return 0; ++ ++ inc_max_seq(lruvec, max_seq, can_swap); ++done: ++ return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0; ++} ++ ++static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) ++{ ++ struct blk_plug plug; ++ unsigned long scanned = 0; ++ ++ lru_add_drain(); ++ ++ blk_start_plug(&plug); ++ ++ while (true) { ++ int delta; ++ int swappiness; ++ unsigned long nr_to_scan; ++ ++ 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) ++ break; ++ ++ delta = evict_pages(lruvec, sc, swappiness); ++ if (!delta) ++ break; ++ ++ scanned += delta; ++ if (scanned >= nr_to_scan) ++ break; ++ ++ cond_resched(); ++ } ++ ++ blk_finish_plug(&plug); ++} ++ + /****************************************************************************** + * initialization + ******************************************************************************/ +@@ -2894,6 +3649,16 @@ static int __init init_lru_gen(void) + }; + late_initcall(init_lru_gen); + ++#else /* !CONFIG_LRU_GEN */ ++ ++static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) ++{ ++} ++ ++static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) ++{ ++} ++ + #endif /* CONFIG_LRU_GEN */ + + static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) +@@ -2907,6 +3672,11 @@ static void shrink_lruvec(struct lruvec + bool proportional_reclaim; + struct blk_plug plug; + ++ if (lru_gen_enabled()) { ++ lru_gen_shrink_lruvec(lruvec, sc); ++ return; ++ } ++ + get_scan_count(lruvec, sc, nr); + + /* Record the original scan target for proportional adjustments later */ +@@ -3372,6 +4142,9 @@ static void snapshot_refaults(struct mem + struct lruvec *target_lruvec; + unsigned long refaults; + ++ if (lru_gen_enabled()) ++ return; ++ + target_lruvec = mem_cgroup_lruvec(target_memcg, pgdat); + refaults = lruvec_page_state(target_lruvec, WORKINGSET_ACTIVATE_ANON); + target_lruvec->refaults[0] = refaults; +@@ -3736,12 +4509,16 @@ unsigned long try_to_free_mem_cgroup_pag + } + #endif + +-static void age_active_anon(struct pglist_data *pgdat, +- struct scan_control *sc) ++static void kswapd_age_node(struct pglist_data *pgdat, struct scan_control *sc) + { + struct mem_cgroup *memcg; + struct lruvec *lruvec; + ++ if (lru_gen_enabled()) { ++ lru_gen_age_node(pgdat, sc); ++ return; ++ } ++ + if (!can_age_anon_pages(pgdat, sc)) + return; + +@@ -4058,12 +4835,11 @@ restart: + sc.may_swap = !nr_boost_reclaim; + + /* +- * Do some background aging of the anon list, to give +- * pages a chance to be referenced before reclaiming. All +- * pages are rotated regardless of classzone as this is +- * about consistent aging. ++ * Do some background aging, to give pages a chance to be ++ * referenced before reclaiming. All pages are rotated ++ * regardless of classzone as this is about consistent aging. + */ +- age_active_anon(pgdat, &sc); ++ kswapd_age_node(pgdat, &sc); + + /* + * If we're getting trouble reclaiming, start doing writepage +--- a/mm/workingset.c ++++ b/mm/workingset.c +@@ -187,7 +187,6 @@ static unsigned int bucket_order __read_ + static void *pack_shadow(int memcgid, pg_data_t *pgdat, unsigned long eviction, + bool workingset) + { +- eviction >>= bucket_order; + eviction &= EVICTION_MASK; + eviction = (eviction << MEM_CGROUP_ID_SHIFT) | memcgid; + eviction = (eviction << NODES_SHIFT) | pgdat->node_id; +@@ -212,10 +211,107 @@ static void unpack_shadow(void *shadow, + + *memcgidp = memcgid; + *pgdat = NODE_DATA(nid); +- *evictionp = entry << bucket_order; ++ *evictionp = entry; + *workingsetp = workingset; + } + ++#ifdef CONFIG_LRU_GEN ++ ++static void *lru_gen_eviction(struct page *page) ++{ ++ int hist; ++ unsigned long token; ++ unsigned long min_seq; ++ struct lruvec *lruvec; ++ struct lru_gen_struct *lrugen; ++ int type = page_is_file_lru(page); ++ int delta = thp_nr_pages(page); ++ int refs = page_lru_refs(page); ++ int tier = lru_tier_from_refs(refs); ++ struct mem_cgroup *memcg = page_memcg(page); ++ struct pglist_data *pgdat = page_pgdat(page); ++ ++ BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH > BITS_PER_LONG - EVICTION_SHIFT); ++ ++ lruvec = mem_cgroup_lruvec(memcg, pgdat); ++ lrugen = &lruvec->lrugen; ++ min_seq = READ_ONCE(lrugen->min_seq[type]); ++ token = (min_seq << LRU_REFS_WIDTH) | max(refs - 1, 0); ++ ++ hist = lru_hist_from_seq(min_seq); ++ atomic_long_add(delta, &lrugen->evicted[hist][type][tier]); ++ ++ return pack_shadow(mem_cgroup_id(memcg), pgdat, token, refs); ++} ++ ++static void lru_gen_refault(struct page *page, void *shadow) ++{ ++ int hist, tier, refs; ++ int memcg_id; ++ bool workingset; ++ unsigned long token; ++ unsigned long min_seq; ++ struct lruvec *lruvec; ++ struct lru_gen_struct *lrugen; ++ struct mem_cgroup *memcg; ++ struct pglist_data *pgdat; ++ int type = page_is_file_lru(page); ++ int delta = thp_nr_pages(page); ++ ++ unpack_shadow(shadow, &memcg_id, &pgdat, &token, &workingset); ++ ++ if (pgdat != page_pgdat(page)) ++ return; ++ ++ rcu_read_lock(); ++ ++ memcg = page_memcg_rcu(page); ++ if (memcg_id != mem_cgroup_id(memcg)) ++ goto unlock; ++ ++ lruvec = mem_cgroup_lruvec(memcg, pgdat); ++ lrugen = &lruvec->lrugen; ++ ++ min_seq = READ_ONCE(lrugen->min_seq[type]); ++ if ((token >> LRU_REFS_WIDTH) != (min_seq & (EVICTION_MASK >> LRU_REFS_WIDTH))) ++ goto unlock; ++ ++ hist = lru_hist_from_seq(min_seq); ++ /* see the comment in page_lru_refs() */ ++ refs = (token & (BIT(LRU_REFS_WIDTH) - 1)) + workingset; ++ tier = lru_tier_from_refs(refs); ++ ++ atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]); ++ mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + type, delta); ++ ++ /* ++ * Count the following two cases as stalls: ++ * 1. For pages accessed through page tables, hotter pages pushed out ++ * hot pages which refaulted immediately. ++ * 2. For pages accessed multiple times through file descriptors, ++ * numbers of accesses might have been out of the range. ++ */ ++ if (lru_gen_in_fault() || refs == BIT(LRU_REFS_WIDTH)) { ++ SetPageWorkingset(page); ++ mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + type, delta); ++ } ++unlock: ++ rcu_read_unlock(); ++} ++ ++#else /* !CONFIG_LRU_GEN */ ++ ++static void *lru_gen_eviction(struct page *page) ++{ ++ return NULL; ++} ++ ++static void lru_gen_refault(struct page *page, void *shadow) ++{ ++} ++ ++#endif /* CONFIG_LRU_GEN */ ++ + /** + * workingset_age_nonresident - age non-resident entries as LRU ages + * @lruvec: the lruvec that was aged +@@ -264,10 +360,14 @@ void *workingset_eviction(struct page *p + VM_BUG_ON_PAGE(page_count(page), page); + VM_BUG_ON_PAGE(!PageLocked(page), page); + ++ if (lru_gen_enabled()) ++ return lru_gen_eviction(page); ++ + lruvec = mem_cgroup_lruvec(target_memcg, pgdat); + /* XXX: target_memcg can be NULL, go through lruvec */ + memcgid = mem_cgroup_id(lruvec_memcg(lruvec)); + eviction = atomic_long_read(&lruvec->nonresident_age); ++ eviction >>= bucket_order; + workingset_age_nonresident(lruvec, thp_nr_pages(page)); + return pack_shadow(memcgid, pgdat, eviction, PageWorkingset(page)); + } +@@ -296,7 +396,13 @@ void workingset_refault(struct page *pag + bool workingset; + int memcgid; + ++ if (lru_gen_enabled()) { ++ lru_gen_refault(page, shadow); ++ return; ++ } ++ + unpack_shadow(shadow, &memcgid, &pgdat, &eviction, &workingset); ++ eviction <<= bucket_order; + + rcu_read_lock(); + /* diff --git a/target/linux/generic/backport-5.15/020-v6.1-06-mm-multigenerational-lru-aging.patch b/target/linux/generic/backport-5.15/020-v6.1-06-mm-multigenerational-lru-aging.patch deleted file mode 100644 index 2ff49681c7b..00000000000 --- a/target/linux/generic/backport-5.15/020-v6.1-06-mm-multigenerational-lru-aging.patch +++ /dev/null @@ -1,1176 +0,0 @@ -From 8217cd2238c40cf77208aa27a7cc09879e685890 Mon Sep 17 00:00:00 2001 -From: Yu Zhao -Date: Mon, 5 Apr 2021 04:35:07 -0600 -Subject: [PATCH 06/10] mm: multigenerational lru: aging - -The aging produces young generations. Given an lruvec, the aging -traverses lruvec_memcg()->mm_list and calls walk_page_range() to scan -PTEs for accessed pages. Upon finding one, the aging updates its -generation number to max_seq (modulo MAX_NR_GENS). After each round of -traversal, the aging increments max_seq. The aging is due when -min_seq[] reaches max_seq-1. - -The aging uses the following optimizations when walking page tables: - 1) It skips non-leaf PMD entries that have the accessed bit cleared - when CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y. - 2) It does not zigzag between a PGD table and the same PMD or PTE - table spanning multiple VMAs. In other words, it finishes all the - VMAs within the range of the same PMD or PTE table before it returns - to this PGD table. This optimizes workloads that have large numbers - of tiny VMAs, especially when CONFIG_PGTABLE_LEVELS=5. - -Signed-off-by: Yu Zhao -Tested-by: Konstantin Kharlamov -Change-Id: I3ae8abc3100d023cecb3a699d86020ae6fc10a45 ---- - include/linux/memcontrol.h | 3 + - include/linux/mmzone.h | 9 + - include/linux/oom.h | 16 + - include/linux/swap.h | 3 + - mm/memcontrol.c | 5 + - mm/oom_kill.c | 4 +- - mm/rmap.c | 8 + - mm/vmscan.c | 948 +++++++++++++++++++++++++++++++++++++ - 8 files changed, 994 insertions(+), 2 deletions(-) - ---- a/include/linux/memcontrol.h -+++ b/include/linux/memcontrol.h -@@ -1367,10 +1367,13 @@ mem_cgroup_print_oom_meminfo(struct mem_ - - static inline void lock_page_memcg(struct page *page) - { -+ /* to match page_memcg_rcu() */ -+ rcu_read_lock(); - } - - static inline void unlock_page_memcg(struct page *page) - { -+ rcu_read_unlock(); - } - - static inline void mem_cgroup_handle_over_high(void) ---- a/include/linux/mmzone.h -+++ b/include/linux/mmzone.h -@@ -295,6 +295,7 @@ enum lruvec_flags { - }; - - struct lruvec; -+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) -@@ -393,6 +394,7 @@ struct mm_walk_args { - - void lru_gen_init_state(struct mem_cgroup *memcg, struct lruvec *lruvec); - void lru_gen_change_state(bool enable, bool main, bool swap); -+void lru_gen_look_around(struct page_vma_mapped_walk *pvmw); - - #ifdef CONFIG_MEMCG - void lru_gen_init_memcg(struct mem_cgroup *memcg); -@@ -409,6 +411,10 @@ static inline void lru_gen_change_state( - { - } - -+static inline void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) -+{ -+} -+ - #ifdef CONFIG_MEMCG - static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) - { -@@ -1028,6 +1034,9 @@ typedef struct pglist_data { - - unsigned long flags; - -+#ifdef CONFIG_LRU_GEN -+ struct mm_walk_args mm_walk_args; -+#endif - ZONE_PADDING(_pad2_) - - /* Per-node vmstats */ ---- a/include/linux/oom.h -+++ b/include/linux/oom.h -@@ -57,6 +57,22 @@ struct oom_control { - extern struct mutex oom_lock; - extern struct mutex oom_adj_mutex; - -+#ifdef CONFIG_MMU -+extern struct task_struct *oom_reaper_list; -+extern struct wait_queue_head oom_reaper_wait; -+ -+static inline bool oom_reaping_in_progress(void) -+{ -+ /* racy check to see if oom reaping could be in progress */ -+ return READ_ONCE(oom_reaper_list) || !waitqueue_active(&oom_reaper_wait); -+} -+#else -+static inline bool oom_reaping_in_progress(void) -+{ -+ return false; -+} -+#endif -+ - static inline void set_current_oom_origin(void) - { - current->signal->oom_flag_origin = true; ---- a/include/linux/swap.h -+++ b/include/linux/swap.h -@@ -137,6 +137,9 @@ union swap_header { - */ - struct reclaim_state { - unsigned long reclaimed_slab; -+#ifdef CONFIG_LRU_GEN -+ struct mm_walk_args *mm_walk_args; -+#endif - }; - - #ifdef __KERNEL__ ---- a/mm/memcontrol.c -+++ b/mm/memcontrol.c -@@ -1304,12 +1304,17 @@ void mem_cgroup_update_lru_size(struct l - *lru_size += nr_pages; - - size = *lru_size; -+#ifdef CONFIG_LRU_GEN -+ /* unlikely but not a bug when reset_batch_size() is pending */ -+ VM_WARN_ON(size + MAX_BATCH_SIZE < 0); -+#else - if (WARN_ONCE(size < 0, - "%s(%p, %d, %d): lru_size %ld\n", - __func__, lruvec, lru, nr_pages, size)) { - VM_BUG_ON(1); - *lru_size = 0; - } -+#endif - - if (nr_pages > 0) - *lru_size += nr_pages; ---- a/mm/oom_kill.c -+++ b/mm/oom_kill.c -@@ -508,8 +508,8 @@ bool process_shares_mm(struct task_struc - * victim (if that is possible) to help the OOM killer to move on. - */ - static struct task_struct *oom_reaper_th; --static DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait); --static struct task_struct *oom_reaper_list; -+DECLARE_WAIT_QUEUE_HEAD(oom_reaper_wait); -+struct task_struct *oom_reaper_list; - static DEFINE_SPINLOCK(oom_reaper_lock); - - bool __oom_reap_task_mm(struct mm_struct *mm) ---- a/mm/rmap.c -+++ b/mm/rmap.c -@@ -73,6 +73,7 @@ - #include - #include - #include -+#include - - #include - -@@ -793,6 +794,13 @@ static bool page_referenced_one(struct p - } - - if (pvmw.pte) { -+ /* the multigenerational lru exploits the spatial locality */ -+ if (lru_gen_enabled() && pte_young(*pvmw.pte) && -+ !(vma->vm_flags & VM_SEQ_READ)) { -+ lru_gen_look_around(&pvmw); -+ referenced++; -+ } -+ - if (ptep_clear_flush_young_notify(vma, address, - pvmw.pte)) { - /* ---- a/mm/vmscan.c -+++ b/mm/vmscan.c -@@ -51,6 +51,8 @@ - #include - #include - #include -+#include -+#include - - #include - #include -@@ -2822,6 +2824,15 @@ static bool can_age_anon_pages(struct pg - * shorthand helpers - ******************************************************************************/ - -+#define DEFINE_MAX_SEQ(lruvec) \ -+ unsigned long max_seq = READ_ONCE((lruvec)->evictable.max_seq) -+ -+#define DEFINE_MIN_SEQ(lruvec) \ -+ unsigned long min_seq[ANON_AND_FILE] = { \ -+ READ_ONCE((lruvec)->evictable.min_seq[0]), \ -+ READ_ONCE((lruvec)->evictable.min_seq[1]), \ -+ } -+ - #define for_each_gen_type_zone(gen, type, zone) \ - for ((gen) = 0; (gen) < MAX_NR_GENS; (gen)++) \ - for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \ -@@ -2834,6 +2845,12 @@ static int page_lru_gen(struct page *pag - return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; - } - -+static int get_swappiness(struct mem_cgroup *memcg) -+{ -+ return mem_cgroup_get_nr_swap_pages(memcg) >= MIN_BATCH_SIZE ? -+ mem_cgroup_swappiness(memcg) : 0; -+} -+ - static struct lruvec *get_lruvec(int nid, struct mem_cgroup *memcg) - { - struct pglist_data *pgdat = NODE_DATA(nid); -@@ -3164,6 +3181,926 @@ done: - } - - /****************************************************************************** -+ * the aging -+ ******************************************************************************/ -+ -+static int page_update_gen(struct page *page, int gen) -+{ -+ unsigned long old_flags, new_flags; -+ -+ VM_BUG_ON(gen >= MAX_NR_GENS); -+ -+ do { -+ new_flags = old_flags = READ_ONCE(page->flags); -+ -+ if (!(new_flags & LRU_GEN_MASK)) { -+ new_flags |= BIT(PG_referenced); -+ continue; -+ } -+ -+ new_flags &= ~LRU_GEN_MASK; -+ new_flags |= (gen + 1UL) << LRU_GEN_PGOFF; -+ } while (new_flags != old_flags && -+ cmpxchg(&page->flags, old_flags, new_flags) != old_flags); -+ -+ return ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; -+} -+ -+static void page_inc_gen(struct page *page, struct lruvec *lruvec, bool reclaiming) -+{ -+ int old_gen, new_gen; -+ unsigned long old_flags, new_flags; -+ int type = page_is_file_lru(page); -+ int zone = page_zonenum(page); -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ old_gen = lru_gen_from_seq(lrugen->min_seq[type]); -+ -+ do { -+ new_flags = old_flags = READ_ONCE(page->flags); -+ VM_BUG_ON_PAGE(!(new_flags & LRU_GEN_MASK), page); -+ -+ new_gen = ((new_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; -+ /* page_update_gen() has updated this page? */ -+ if (new_gen >= 0 && new_gen != old_gen) { -+ list_move(&page->lru, &lrugen->lists[new_gen][type][zone]); -+ return; -+ } -+ -+ new_gen = (old_gen + 1) % MAX_NR_GENS; -+ -+ new_flags &= ~LRU_GEN_MASK; -+ new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF; -+ /* for end_page_writeback() */ -+ if (reclaiming) -+ new_flags |= BIT(PG_reclaim); -+ } while (cmpxchg(&page->flags, old_flags, new_flags) != old_flags); -+ -+ lru_gen_update_size(page, lruvec, old_gen, new_gen); -+ if (reclaiming) -+ list_move(&page->lru, &lrugen->lists[new_gen][type][zone]); -+ else -+ list_move_tail(&page->lru, &lrugen->lists[new_gen][type][zone]); -+} -+ -+static void update_batch_size(struct page *page, int old_gen, int new_gen, -+ struct mm_walk_args *args) -+{ -+ int type = page_is_file_lru(page); -+ int zone = page_zonenum(page); -+ int delta = thp_nr_pages(page); -+ -+ VM_BUG_ON(old_gen >= MAX_NR_GENS); -+ VM_BUG_ON(new_gen >= MAX_NR_GENS); -+ -+ args->batch_size++; -+ -+ args->nr_pages[old_gen][type][zone] -= delta; -+ args->nr_pages[new_gen][type][zone] += delta; -+} -+ -+static void reset_batch_size(struct lruvec *lruvec, struct mm_walk_args *args) -+{ -+ int gen, type, zone; -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ args->batch_size = 0; -+ -+ for_each_gen_type_zone(gen, type, zone) { -+ enum lru_list lru = type * LRU_FILE; -+ int delta = args->nr_pages[gen][type][zone]; -+ -+ if (!delta) -+ continue; -+ -+ args->nr_pages[gen][type][zone] = 0; -+ WRITE_ONCE(lrugen->sizes[gen][type][zone], -+ lrugen->sizes[gen][type][zone] + delta); -+ -+ if (lru_gen_is_active(lruvec, gen)) -+ lru += LRU_ACTIVE; -+ update_lru_size(lruvec, lru, zone, delta); -+ } -+} -+ -+static int should_skip_vma(unsigned long start, unsigned long end, struct mm_walk *walk) -+{ -+ struct address_space *mapping; -+ struct vm_area_struct *vma = walk->vma; -+ struct mm_walk_args *args = walk->private; -+ -+ if (!vma_is_accessible(vma) || is_vm_hugetlb_page(vma) || -+ (vma->vm_flags & (VM_LOCKED | VM_SPECIAL | VM_SEQ_READ))) -+ return true; -+ -+ if (vma_is_anonymous(vma)) -+ return !args->swappiness; -+ -+ if (WARN_ON_ONCE(!vma->vm_file || !vma->vm_file->f_mapping)) -+ return true; -+ -+ mapping = vma->vm_file->f_mapping; -+ if (!mapping->a_ops->writepage) -+ return true; -+ -+ return (shmem_mapping(mapping) && !args->swappiness) || mapping_unevictable(mapping); -+} -+ -+/* -+ * Some userspace memory allocators create many single-page VMAs. So instead of -+ * returning back to the PGD table for each of such VMAs, we finish at least an -+ * entire PMD table and therefore avoid many zigzags. -+ */ -+static bool get_next_vma(struct mm_walk *walk, unsigned long mask, unsigned long size, -+ unsigned long *start, unsigned long *end) -+{ -+ unsigned long next = round_up(*end, size); -+ -+ VM_BUG_ON(mask & size); -+ VM_BUG_ON(*start >= *end); -+ VM_BUG_ON((next & mask) != (*start & mask)); -+ -+ while (walk->vma) { -+ if (next >= walk->vma->vm_end) { -+ walk->vma = walk->vma->vm_next; -+ continue; -+ } -+ -+ if ((next & mask) != (walk->vma->vm_start & mask)) -+ return false; -+ -+ if (should_skip_vma(walk->vma->vm_start, walk->vma->vm_end, walk)) { -+ walk->vma = walk->vma->vm_next; -+ continue; -+ } -+ -+ *start = max(next, walk->vma->vm_start); -+ next = (next | ~mask) + 1; -+ /* rounded-up boundaries can wrap to 0 */ -+ *end = next && next < walk->vma->vm_end ? next : walk->vma->vm_end; -+ -+ return true; -+ } -+ -+ return false; -+} -+ -+static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end, -+ struct mm_walk *walk) -+{ -+ int i; -+ pte_t *pte; -+ spinlock_t *ptl; -+ unsigned long addr; -+ int worth = 0; -+ struct mm_walk_args *args = walk->private; -+ int old_gen, new_gen = lru_gen_from_seq(args->max_seq); -+ -+ VM_BUG_ON(pmd_leaf(*pmd)); -+ -+ pte = pte_offset_map_lock(walk->mm, pmd, start & PMD_MASK, &ptl); -+ arch_enter_lazy_mmu_mode(); -+restart: -+ for (i = pte_index(start), addr = start; addr != end; i++, addr += PAGE_SIZE) { -+ struct page *page; -+ unsigned long pfn = pte_pfn(pte[i]); -+ -+ args->mm_stats[MM_LEAF_TOTAL]++; -+ -+ if (!pte_present(pte[i]) || is_zero_pfn(pfn)) -+ continue; -+ -+ if (WARN_ON_ONCE(pte_devmap(pte[i]) || pte_special(pte[i]))) -+ continue; -+ -+ if (!pte_young(pte[i])) { -+ args->mm_stats[MM_LEAF_OLD]++; -+ continue; -+ } -+ -+ VM_BUG_ON(!pfn_valid(pfn)); -+ if (pfn < args->start_pfn || pfn >= args->end_pfn) -+ continue; -+ -+ page = compound_head(pfn_to_page(pfn)); -+ if (page_to_nid(page) != args->node_id) -+ continue; -+ -+ if (page_memcg_rcu(page) != args->memcg) -+ continue; -+ -+ VM_BUG_ON(addr < walk->vma->vm_start || addr >= walk->vma->vm_end); -+ if (!ptep_test_and_clear_young(walk->vma, addr, pte + i)) -+ continue; -+ -+ args->mm_stats[MM_LEAF_YOUNG]++; -+ -+ if (pte_dirty(pte[i]) && !PageDirty(page) && -+ !(PageAnon(page) && PageSwapBacked(page) && !PageSwapCache(page))) -+ set_page_dirty(page); -+ -+ old_gen = page_update_gen(page, new_gen); -+ if (old_gen >= 0 && old_gen != new_gen) -+ update_batch_size(page, old_gen, new_gen, args); -+ -+ worth++; -+ } -+ -+ if (i < PTRS_PER_PTE && get_next_vma(walk, PMD_MASK, PAGE_SIZE, &start, &end)) -+ goto restart; -+ -+ arch_leave_lazy_mmu_mode(); -+ pte_unmap_unlock(pte, ptl); -+ -+ return worth >= MIN_BATCH_SIZE / 2; -+} -+ -+/* -+ * We scan PMD entries in two passes. The first pass reaches to PTE tables and -+ * doesn't take the PMD lock. The second pass clears the accessed bit on PMD -+ * entries and needs to take the PMD lock. -+ */ -+#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) -+static void walk_pmd_range_locked(pud_t *pud, unsigned long start, int offset, -+ struct vm_area_struct *vma, struct mm_walk *walk) -+{ -+ int i; -+ pmd_t *pmd; -+ spinlock_t *ptl; -+ struct mm_walk_args *args = walk->private; -+ int old_gen, new_gen = lru_gen_from_seq(args->max_seq); -+ -+ VM_BUG_ON(pud_leaf(*pud)); -+ -+ start = (start & PUD_MASK) + offset * PMD_SIZE; -+ pmd = pmd_offset(pud, start); -+ ptl = pmd_lock(walk->mm, pmd); -+ arch_enter_lazy_mmu_mode(); -+ -+ for_each_set_bit(i, args->bitmap, MIN_BATCH_SIZE) { -+ struct page *page; -+ unsigned long pfn = pmd_pfn(pmd[i]); -+ unsigned long addr = start + i * PMD_SIZE; -+ -+ if (!pmd_present(pmd[i]) || is_huge_zero_pmd(pmd[i])) -+ continue; -+ -+ if (WARN_ON_ONCE(pmd_devmap(pmd[i]))) -+ continue; -+ -+ if (!pmd_trans_huge(pmd[i])) { -+ if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)) -+ pmdp_test_and_clear_young(vma, addr, pmd + i); -+ continue; -+ } -+ -+ VM_BUG_ON(!pfn_valid(pfn)); -+ if (pfn < args->start_pfn || pfn >= args->end_pfn) -+ continue; -+ -+ page = pfn_to_page(pfn); -+ VM_BUG_ON_PAGE(PageTail(page), page); -+ if (page_to_nid(page) != args->node_id) -+ continue; -+ -+ if (page_memcg_rcu(page) != args->memcg) -+ continue; -+ -+ VM_BUG_ON(addr < vma->vm_start || addr >= vma->vm_end); -+ if (!pmdp_test_and_clear_young(vma, addr, pmd + i)) -+ continue; -+ -+ args->mm_stats[MM_LEAF_YOUNG]++; -+ -+ if (pmd_dirty(pmd[i]) && !PageDirty(page) && -+ !(PageAnon(page) && PageSwapBacked(page) && !PageSwapCache(page))) -+ set_page_dirty(page); -+ -+ old_gen = page_update_gen(page, new_gen); -+ if (old_gen >= 0 && old_gen != new_gen) -+ update_batch_size(page, old_gen, new_gen, args); -+ } -+ -+ arch_leave_lazy_mmu_mode(); -+ spin_unlock(ptl); -+ -+ bitmap_zero(args->bitmap, MIN_BATCH_SIZE); -+} -+#else -+static void walk_pmd_range_locked(pud_t *pud, unsigned long start, int offset, -+ struct vm_area_struct *vma, struct mm_walk *walk) -+{ -+} -+#endif -+ -+static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end, -+ struct mm_walk *walk) -+{ -+ int i; -+ pmd_t *pmd; -+ unsigned long next; -+ unsigned long addr; -+ struct vm_area_struct *vma; -+ int offset = -1; -+ bool reset = false; -+ struct mm_walk_args *args = walk->private; -+ struct lruvec *lruvec = get_lruvec(args->node_id, args->memcg); -+ -+ VM_BUG_ON(pud_leaf(*pud)); -+ -+ pmd = pmd_offset(pud, start & PUD_MASK); -+restart: -+ vma = walk->vma; -+ for (i = pmd_index(start), addr = start; addr != end; i++, addr = next) { -+ pmd_t val = pmd_read_atomic(pmd + i); -+ -+ /* for pmd_read_atomic() */ -+ barrier(); -+ -+ next = pmd_addr_end(addr, end); -+ -+ if (!pmd_present(val)) { -+ args->mm_stats[MM_LEAF_TOTAL]++; -+ continue; -+ } -+ -+#ifdef CONFIG_TRANSPARENT_HUGEPAGE -+ if (pmd_trans_huge(val)) { -+ unsigned long pfn = pmd_pfn(val); -+ -+ args->mm_stats[MM_LEAF_TOTAL]++; -+ -+ if (is_huge_zero_pmd(val)) -+ continue; -+ -+ if (!pmd_young(val)) { -+ args->mm_stats[MM_LEAF_OLD]++; -+ continue; -+ } -+ -+ if (pfn < args->start_pfn || pfn >= args->end_pfn) -+ continue; -+ -+ if (offset < 0) -+ offset = i; -+ else if (i - offset >= MIN_BATCH_SIZE) { -+ walk_pmd_range_locked(pud, start, offset, vma, walk); -+ offset = i; -+ } -+ __set_bit(i - offset, args->bitmap); -+ reset = true; -+ continue; -+ } -+#endif -+ args->mm_stats[MM_NONLEAF_TOTAL]++; -+ -+#ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG -+ if (!pmd_young(val)) -+ continue; -+ -+ if (offset < 0) -+ offset = i; -+ else if (i - offset >= MIN_BATCH_SIZE) { -+ walk_pmd_range_locked(pud, start, offset, vma, walk); -+ offset = i; -+ reset = false; -+ } -+ __set_bit(i - offset, args->bitmap); -+#endif -+ if (args->use_filter && !test_bloom_filter(lruvec, args->max_seq, pmd + i)) -+ continue; -+ -+ args->mm_stats[MM_NONLEAF_PREV]++; -+ -+ if (!walk_pte_range(&val, addr, next, walk)) -+ continue; -+ -+ args->mm_stats[MM_NONLEAF_CUR]++; -+ -+ set_bloom_filter(lruvec, args->max_seq + 1, pmd + i); -+ } -+ -+ if (reset) { -+ walk_pmd_range_locked(pud, start, offset, vma, walk); -+ offset = -1; -+ reset = false; -+ } -+ -+ if (i < PTRS_PER_PMD && get_next_vma(walk, PUD_MASK, PMD_SIZE, &start, &end)) -+ goto restart; -+ -+ if (offset >= 0) -+ walk_pmd_range_locked(pud, start, offset, vma, walk); -+} -+ -+static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end, -+ struct mm_walk *walk) -+{ -+ int i; -+ pud_t *pud; -+ unsigned long addr; -+ unsigned long next; -+ struct mm_walk_args *args = walk->private; -+ -+ VM_BUG_ON(p4d_leaf(*p4d)); -+ -+ pud = pud_offset(p4d, start & P4D_MASK); -+restart: -+ for (i = pud_index(start), addr = start; addr != end; i++, addr = next) { -+ pud_t val = READ_ONCE(pud[i]); -+ -+ next = pud_addr_end(addr, end); -+ -+ if (!pud_present(val) || WARN_ON_ONCE(pud_leaf(val))) -+ continue; -+ -+ walk_pmd_range(&val, addr, next, walk); -+ -+ if (args->batch_size >= MAX_BATCH_SIZE) { -+ end = (addr | ~PUD_MASK) + 1; -+ goto done; -+ } -+ } -+ -+ if (i < PTRS_PER_PUD && get_next_vma(walk, P4D_MASK, PUD_SIZE, &start, &end)) -+ goto restart; -+ -+ end = round_up(end, P4D_SIZE); -+done: -+ /* rounded-up boundaries can wrap to 0 */ -+ args->next_addr = end && walk->vma ? max(end, walk->vma->vm_start) : 0; -+ -+ return -EAGAIN; -+} -+ -+static void walk_mm(struct lruvec *lruvec, struct mm_struct *mm, struct mm_walk_args *args) -+{ -+ static const struct mm_walk_ops mm_walk_ops = { -+ .test_walk = should_skip_vma, -+ .p4d_entry = walk_pud_range, -+ }; -+ -+ int err; -+ -+ args->next_addr = FIRST_USER_ADDRESS; -+ -+ do { -+ unsigned long start = args->next_addr; -+ unsigned long end = mm->highest_vm_end; -+ -+ err = -EBUSY; -+ -+ rcu_read_lock(); -+#ifdef CONFIG_MEMCG -+ if (args->memcg && atomic_read(&args->memcg->moving_account)) -+ goto contended; -+#endif -+ if (!mmap_read_trylock(mm)) -+ goto contended; -+ -+ err = walk_page_range(mm, start, end, &mm_walk_ops, args); -+ -+ mmap_read_unlock(mm); -+ -+ if (args->batch_size) { -+ spin_lock_irq(&lruvec->lru_lock); -+ reset_batch_size(lruvec, args); -+ spin_unlock_irq(&lruvec->lru_lock); -+ } -+contended: -+ rcu_read_unlock(); -+ -+ cond_resched(); -+ } while (err == -EAGAIN && args->next_addr && !mm_is_oom_victim(mm)); -+} -+ -+static struct mm_walk_args *alloc_mm_walk_args(void) -+{ -+ if (!current->reclaim_state || !current->reclaim_state->mm_walk_args) -+ return kvzalloc(sizeof(struct mm_walk_args), GFP_KERNEL); -+ -+ return current->reclaim_state->mm_walk_args; -+} -+ -+static void free_mm_walk_args(struct mm_walk_args *args) -+{ -+ if (!current->reclaim_state || !current->reclaim_state->mm_walk_args) -+ kvfree(args); -+} -+ -+static bool inc_min_seq(struct lruvec *lruvec, int type) -+{ -+ int gen, zone; -+ int remaining = MAX_BATCH_SIZE; -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ VM_BUG_ON(!seq_is_valid(lruvec)); -+ -+ if (get_nr_gens(lruvec, type) != MAX_NR_GENS) -+ return true; -+ -+ gen = lru_gen_from_seq(lrugen->min_seq[type]); -+ -+ for (zone = 0; zone < MAX_NR_ZONES; zone++) { -+ struct list_head *head = &lrugen->lists[gen][type][zone]; -+ -+ while (!list_empty(head)) { -+ struct page *page = lru_to_page(head); -+ -+ VM_BUG_ON_PAGE(PageTail(page), page); -+ VM_BUG_ON_PAGE(PageUnevictable(page), page); -+ VM_BUG_ON_PAGE(PageActive(page), page); -+ VM_BUG_ON_PAGE(page_is_file_lru(page) != type, page); -+ VM_BUG_ON_PAGE(page_zonenum(page) != zone, page); -+ -+ prefetchw_prev_lru_page(page, head, flags); -+ -+ page_inc_gen(page, lruvec, false); -+ -+ if (!--remaining) -+ return false; -+ } -+ } -+ -+ WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1); -+ -+ return true; -+} -+ -+static bool try_to_inc_min_seq(struct lruvec *lruvec, int swappiness) -+{ -+ int gen, type, zone; -+ bool success = false; -+ struct lrugen *lrugen = &lruvec->evictable; -+ DEFINE_MIN_SEQ(lruvec); -+ -+ VM_BUG_ON(!seq_is_valid(lruvec)); -+ -+ for (type = 0; type < ANON_AND_FILE; type++) { -+ while (lrugen->max_seq - min_seq[type] >= MIN_NR_GENS) { -+ gen = lru_gen_from_seq(min_seq[type]); -+ -+ for (zone = 0; zone < MAX_NR_ZONES; zone++) { -+ if (!list_empty(&lrugen->lists[gen][type][zone])) -+ goto next; -+ } -+ -+ min_seq[type]++; -+ } -+next: -+ ; -+ } -+ -+ min_seq[0] = min(min_seq[0], min_seq[1]); -+ if (swappiness) -+ min_seq[1] = max(min_seq[0], lrugen->min_seq[1]); -+ -+ for (type = 0; type < ANON_AND_FILE; type++) { -+ if (min_seq[type] == lrugen->min_seq[type]) -+ continue; -+ -+ WRITE_ONCE(lrugen->min_seq[type], min_seq[type]); -+ success = true; -+ } -+ -+ return success; -+} -+ -+static void inc_max_seq(struct lruvec *lruvec, unsigned long max_seq) -+{ -+ int gen, type, zone; -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ spin_lock_irq(&lruvec->lru_lock); -+ -+ VM_BUG_ON(!seq_is_valid(lruvec)); -+ -+ if (max_seq != lrugen->max_seq) -+ goto unlock; -+ -+ if (!try_to_inc_min_seq(lruvec, true)) { -+ for (type = ANON_AND_FILE - 1; type >= 0; type--) { -+ while (!inc_min_seq(lruvec, type)) { -+ spin_unlock_irq(&lruvec->lru_lock); -+ cond_resched(); -+ spin_lock_irq(&lruvec->lru_lock); -+ } -+ } -+ } -+ -+ gen = lru_gen_from_seq(lrugen->max_seq - 1); -+ for (type = 0; type < ANON_AND_FILE; type++) { -+ for (zone = 0; zone < MAX_NR_ZONES; zone++) { -+ enum lru_list lru = type * LRU_FILE; -+ long delta = lrugen->sizes[gen][type][zone]; -+ -+ if (!delta) -+ continue; -+ -+ WARN_ON_ONCE(delta != (int)delta); -+ -+ update_lru_size(lruvec, lru, zone, delta); -+ update_lru_size(lruvec, lru + LRU_ACTIVE, zone, -delta); -+ } -+ } -+ -+ gen = lru_gen_from_seq(lrugen->max_seq + 1); -+ for (type = 0; type < ANON_AND_FILE; type++) { -+ for (zone = 0; zone < MAX_NR_ZONES; zone++) { -+ enum lru_list lru = type * LRU_FILE; -+ long delta = lrugen->sizes[gen][type][zone]; -+ -+ if (!delta) -+ continue; -+ -+ WARN_ON_ONCE(delta != (int)delta); -+ -+ update_lru_size(lruvec, lru, zone, -delta); -+ update_lru_size(lruvec, lru + LRU_ACTIVE, zone, delta); -+ } -+ } -+ -+ WRITE_ONCE(lrugen->timestamps[gen], jiffies); -+ /* make sure all preceding modifications appear first */ -+ smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); -+unlock: -+ spin_unlock_irq(&lruvec->lru_lock); -+} -+ -+/* Main function used by the foreground, the background and the user-triggered aging. */ -+static bool try_to_inc_max_seq(struct lruvec *lruvec, struct scan_control *sc, int swappiness, -+ unsigned long max_seq, bool use_filter) -+{ -+ bool last; -+ struct mm_walk_args *args; -+ struct mm_struct *mm = NULL; -+ struct lrugen *lrugen = &lruvec->evictable; -+ struct mem_cgroup *memcg = lruvec_memcg(lruvec); -+ struct pglist_data *pgdat = lruvec_pgdat(lruvec); -+ int nid = pgdat->node_id; -+ -+ VM_BUG_ON(max_seq > READ_ONCE(lrugen->max_seq)); -+ -+ /* -+ * If we are not from run_aging() and clearing the accessed bit may -+ * trigger page faults, then don't proceed to clearing all accessed -+ * PTEs. Instead, fallback to lru_gen_look_around(), which only clears a -+ * handful of accessed PTEs. This is less efficient but causes fewer -+ * page faults on CPUs that don't have the capability. -+ */ -+ if ((current->flags & PF_MEMALLOC) && !arch_has_hw_pte_young(false)) { -+ inc_max_seq(lruvec, max_seq); -+ return true; -+ } -+ -+ args = alloc_mm_walk_args(); -+ if (!args) -+ return false; -+ -+ args->memcg = memcg; -+ args->max_seq = max_seq; -+ args->start_pfn = pgdat->node_start_pfn; -+ args->end_pfn = pgdat_end_pfn(pgdat); -+ args->node_id = nid; -+ args->swappiness = swappiness; -+ args->use_filter = use_filter; -+ -+ do { -+ last = get_next_mm(lruvec, args, &mm); -+ if (mm) -+ walk_mm(lruvec, mm, args); -+ -+ cond_resched(); -+ } while (mm); -+ -+ free_mm_walk_args(args); -+ -+ if (!last) { -+ /* don't wait unless we may have trouble reclaiming */ -+ if (!current_is_kswapd() && sc->priority < DEF_PRIORITY - 2) -+ wait_event_killable(lruvec->mm_walk.wait, -+ max_seq < READ_ONCE(lrugen->max_seq)); -+ -+ return max_seq < READ_ONCE(lrugen->max_seq); -+ } -+ -+ VM_BUG_ON(max_seq != READ_ONCE(lrugen->max_seq)); -+ -+ inc_max_seq(lruvec, max_seq); -+ /* either we see any waiters or they will see updated max_seq */ -+ if (wq_has_sleeper(&lruvec->mm_walk.wait)) -+ wake_up_all(&lruvec->mm_walk.wait); -+ -+ wakeup_flusher_threads(WB_REASON_VMSCAN); -+ -+ return true; -+} -+ -+static long get_nr_evictable(struct lruvec *lruvec, struct scan_control *sc, int swappiness, -+ unsigned long max_seq, unsigned long *min_seq, bool *low) -+{ -+ int gen, type, zone; -+ long max = 0; -+ long min = 0; -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ for (type = !swappiness; type < ANON_AND_FILE; type++) { -+ unsigned long seq; -+ -+ for (seq = min_seq[type]; seq <= max_seq; seq++) { -+ long size = 0; -+ -+ gen = lru_gen_from_seq(seq); -+ -+ for (zone = 0; zone <= sc->reclaim_idx; zone++) -+ size += READ_ONCE(lrugen->sizes[gen][type][zone]); -+ -+ max += size; -+ if (type && max_seq - seq >= MIN_NR_GENS) -+ min += size; -+ } -+ } -+ -+ *low = max_seq - min_seq[1] <= MIN_NR_GENS && min < MIN_BATCH_SIZE; -+ -+ return max > 0 ? max : 0; -+} -+ -+static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, -+ unsigned long min_ttl) -+{ -+ bool low; -+ long nr_to_scan; -+ struct mem_cgroup *memcg = lruvec_memcg(lruvec); -+ int swappiness = get_swappiness(memcg); -+ DEFINE_MAX_SEQ(lruvec); -+ DEFINE_MIN_SEQ(lruvec); -+ -+ if (mem_cgroup_below_min(memcg)) -+ return false; -+ -+ if (min_ttl) { -+ int gen = lru_gen_from_seq(min_seq[1]); -+ unsigned long birth = READ_ONCE(lruvec->evictable.timestamps[gen]); -+ -+ if (time_is_after_jiffies(birth + min_ttl)) -+ return false; -+ } -+ -+ nr_to_scan = get_nr_evictable(lruvec, sc, swappiness, max_seq, min_seq, &low); -+ if (!nr_to_scan) -+ return false; -+ -+ nr_to_scan >>= sc->priority; -+ -+ if (!mem_cgroup_online(memcg)) -+ nr_to_scan++; -+ -+ if (nr_to_scan && low && (!mem_cgroup_below_low(memcg) || sc->memcg_low_reclaim)) -+ try_to_inc_max_seq(lruvec, sc, swappiness, max_seq, true); -+ -+ return true; -+} -+ -+/* Protect the working set accessed within the last N milliseconds. */ -+static unsigned long lru_gen_min_ttl __read_mostly; -+ -+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_BUG_ON(!current_is_kswapd()); -+ -+ if (!sc->force_deactivate) { -+ sc->force_deactivate = 1; -+ return; -+ } -+ -+ current->reclaim_state->mm_walk_args = &pgdat->mm_walk_args; -+ -+ 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; -+ -+ cond_resched(); -+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); -+ -+ if (!success && mutex_trylock(&oom_lock)) { -+ struct oom_control oc = { -+ .gfp_mask = sc->gfp_mask, -+ .order = sc->order, -+ }; -+ -+ /* to avoid overkilling */ -+ if (!oom_reaping_in_progress()) -+ out_of_memory(&oc); -+ -+ mutex_unlock(&oom_lock); -+ } -+ -+ current->reclaim_state->mm_walk_args = NULL; -+} -+ -+/* Scan the vicinity of an accessed PTE when shrink_page_list() uses the rmap. */ -+void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) -+{ -+ int i; -+ pte_t *pte; -+ struct page *page; -+ int old_gen, new_gen; -+ unsigned long start; -+ unsigned long end; -+ unsigned long addr; -+ struct mm_walk_args *args; -+ int worth = 0; -+ struct mem_cgroup *memcg = page_memcg(pvmw->page); -+ struct pglist_data *pgdat = page_pgdat(pvmw->page); -+ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); -+ DEFINE_MAX_SEQ(lruvec); -+ -+ lockdep_assert_held(pvmw->ptl); -+ VM_BUG_ON_PAGE(PageLRU(pvmw->page), pvmw->page); -+ -+ args = current->reclaim_state ? current->reclaim_state->mm_walk_args : NULL; -+ if (!args) -+ return; -+ -+ start = max(pvmw->address & PMD_MASK, pvmw->vma->vm_start); -+ end = min(pvmw->address | ~PMD_MASK, pvmw->vma->vm_end - 1) + 1; -+ -+ if (end - start > MIN_BATCH_SIZE * PAGE_SIZE) { -+ if (pvmw->address - start < MIN_BATCH_SIZE * PAGE_SIZE / 2) -+ end = start + MIN_BATCH_SIZE * PAGE_SIZE; -+ else if (end - pvmw->address < MIN_BATCH_SIZE * PAGE_SIZE / 2) -+ start = end - MIN_BATCH_SIZE * PAGE_SIZE; -+ else { -+ start = pvmw->address - MIN_BATCH_SIZE * PAGE_SIZE / 2; -+ end = pvmw->address + MIN_BATCH_SIZE * PAGE_SIZE / 2; -+ } -+ } -+ -+ pte = pvmw->pte - (pvmw->address - start) / PAGE_SIZE; -+ new_gen = lru_gen_from_seq(max_seq); -+ -+ lock_page_memcg(pvmw->page); -+ arch_enter_lazy_mmu_mode(); -+ -+ for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) { -+ unsigned long pfn = pte_pfn(pte[i]); -+ -+ if (!pte_present(pte[i]) || is_zero_pfn(pfn)) -+ continue; -+ -+ if (WARN_ON_ONCE(pte_devmap(pte[i]) || pte_special(pte[i]))) -+ continue; -+ -+ VM_BUG_ON(!pfn_valid(pfn)); -+ if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) -+ continue; -+ -+ worth++; -+ -+ if (!pte_young(pte[i])) -+ continue; -+ -+ page = compound_head(pfn_to_page(pfn)); -+ if (page_to_nid(page) != pgdat->node_id) -+ continue; -+ -+ if (page_memcg_rcu(page) != memcg) -+ continue; -+ -+ VM_BUG_ON(addr < pvmw->vma->vm_start || addr >= pvmw->vma->vm_end); -+ if (!ptep_test_and_clear_young(pvmw->vma, addr, pte + i)) -+ continue; -+ -+ if (pte_dirty(pte[i]) && !PageDirty(page) && -+ !(PageAnon(page) && PageSwapBacked(page) && !PageSwapCache(page))) -+ __set_bit(i, args->bitmap); -+ -+ old_gen = page_update_gen(page, new_gen); -+ if (old_gen >= 0 && old_gen != new_gen) -+ update_batch_size(page, old_gen, new_gen, args); -+ } -+ -+ arch_leave_lazy_mmu_mode(); -+ unlock_page_memcg(pvmw->page); -+ -+ if (worth >= MIN_BATCH_SIZE / 2) -+ set_bloom_filter(lruvec, max_seq, pvmw->pmd); -+ -+ for_each_set_bit(i, args->bitmap, MIN_BATCH_SIZE) -+ set_page_dirty(pte_page(pte[i])); -+ -+ bitmap_zero(args->bitmap, MIN_BATCH_SIZE); -+} -+ -+/****************************************************************************** - * state change - ******************************************************************************/ - -@@ -3412,6 +4349,12 @@ static int __init init_lru_gen(void) - }; - late_initcall(init_lru_gen); - -+#else -+ -+static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) -+{ -+} -+ - #endif /* CONFIG_LRU_GEN */ - - static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) -@@ -4266,6 +5209,11 @@ static void age_active_anon(struct pglis - struct mem_cgroup *memcg; - struct lruvec *lruvec; - -+ if (lru_gen_enabled()) { -+ lru_gen_age_node(pgdat, sc); -+ return; -+ } -+ - if (!can_age_anon_pages(pgdat, sc)) - return; - diff --git a/target/linux/generic/backport-5.15/020-v6.1-07-mm-multi-gen-LRU-exploit-locality-in-rmap.patch b/target/linux/generic/backport-5.15/020-v6.1-07-mm-multi-gen-LRU-exploit-locality-in-rmap.patch new file mode 100644 index 00000000000..5cd6e03dc60 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-07-mm-multi-gen-LRU-exploit-locality-in-rmap.patch @@ -0,0 +1,491 @@ +From e4277535f6d6708bb19b88c4bad155832671d69b Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:04 -0600 +Subject: [PATCH 07/29] mm: multi-gen LRU: exploit locality in rmap +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +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. + +This patch exploits spatial locality to reduce the trips into the rmap. +When shrink_page_list() walks the rmap and finds a young PTE, a new +function lru_gen_look_around() scans at most BITS_PER_LONG-1 adjacent +PTEs. On finding another young PTE, it clears the accessed bit and +updates the gen counter of the page mapped by this PTE to +(max_seq%MAX_NR_GENS)+1. + +Server benchmark results: + Single workload: + fio (buffered I/O): no change + + Single workload: + memcached (anon): +[3, 5]% + Ops/sec KB/sec + patch1-6: 1106168.46 43025.04 + patch1-7: 1147696.57 44640.29 + + Configurations: + no change + +Client benchmark results: + kswapd profiles: + patch1-6 + 39.03% lzo1x_1_do_compress (real work) + 18.47% page_vma_mapped_walk (overhead) + 6.74% _raw_spin_unlock_irq + 3.97% do_raw_spin_lock + 2.49% ptep_clear_flush + 2.48% anon_vma_interval_tree_iter_first + 1.92% page_referenced_one + 1.88% __zram_bvec_write + 1.48% memmove + 1.31% vma_interval_tree_iter_next + + patch1-7 + 48.16% lzo1x_1_do_compress (real work) + 8.20% page_vma_mapped_walk (overhead) + 7.06% _raw_spin_unlock_irq + 2.92% ptep_clear_flush + 2.53% __zram_bvec_write + 2.11% do_raw_spin_lock + 2.02% memmove + 1.93% lru_gen_look_around + 1.56% free_unref_page_list + 1.40% memset + + Configurations: + no change + +Link: https://lkml.kernel.org/r/20220918080010.2920238-8-yuzhao@google.com +Signed-off-by: Yu Zhao +Acked-by: Barry Song +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + include/linux/memcontrol.h | 31 +++++++ + include/linux/mmzone.h | 6 ++ + mm/internal.h | 1 + + mm/memcontrol.c | 1 + + mm/rmap.c | 7 ++ + mm/swap.c | 4 +- + mm/vmscan.c | 184 +++++++++++++++++++++++++++++++++++++ + 7 files changed, 232 insertions(+), 2 deletions(-) + +--- a/include/linux/memcontrol.h ++++ b/include/linux/memcontrol.h +@@ -442,6 +442,7 @@ static inline struct obj_cgroup *__page_ + * - LRU isolation + * - lock_page_memcg() + * - exclusive reference ++ * - mem_cgroup_trylock_pages() + * + * For a kmem page a caller should hold an rcu read lock to protect memcg + * associated with a kmem page from being released. +@@ -497,6 +498,7 @@ static inline struct mem_cgroup *page_me + * - LRU isolation + * - lock_page_memcg() + * - exclusive reference ++ * - mem_cgroup_trylock_pages() + * + * For a kmem page a caller should hold an rcu read lock to protect memcg + * associated with a kmem page from being released. +@@ -953,6 +955,23 @@ void unlock_page_memcg(struct page *page + + void __mod_memcg_state(struct mem_cgroup *memcg, int idx, int val); + ++/* try to stablize page_memcg() for all the pages in a memcg */ ++static inline bool mem_cgroup_trylock_pages(struct mem_cgroup *memcg) ++{ ++ rcu_read_lock(); ++ ++ if (mem_cgroup_disabled() || !atomic_read(&memcg->moving_account)) ++ return true; ++ ++ rcu_read_unlock(); ++ return false; ++} ++ ++static inline void mem_cgroup_unlock_pages(void) ++{ ++ rcu_read_unlock(); ++} ++ + /* idx can be of type enum memcg_stat_item or node_stat_item */ + static inline void mod_memcg_state(struct mem_cgroup *memcg, + int idx, int val) +@@ -1369,6 +1388,18 @@ static inline void unlock_page_memcg(str + { + } + ++static inline bool mem_cgroup_trylock_pages(struct mem_cgroup *memcg) ++{ ++ /* to match page_memcg_rcu() */ ++ rcu_read_lock(); ++ return true; ++} ++ ++static inline void mem_cgroup_unlock_pages(void) ++{ ++ rcu_read_unlock(); ++} ++ + static inline void mem_cgroup_handle_over_high(void) + { + } +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -352,6 +352,7 @@ enum lruvec_flags { + #ifndef __GENERATING_BOUNDS_H + + struct lruvec; ++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) +@@ -407,6 +408,7 @@ struct lru_gen_struct { + }; + + void lru_gen_init_lruvec(struct lruvec *lruvec); ++void lru_gen_look_around(struct page_vma_mapped_walk *pvmw); + + #ifdef CONFIG_MEMCG + void lru_gen_init_memcg(struct mem_cgroup *memcg); +@@ -419,6 +421,10 @@ static inline void lru_gen_init_lruvec(s + { + } + ++static inline void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) ++{ ++} ++ + #ifdef CONFIG_MEMCG + static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) + { +--- a/mm/internal.h ++++ b/mm/internal.h +@@ -35,6 +35,7 @@ + void page_writeback_init(void); + + vm_fault_t do_swap_page(struct vm_fault *vmf); ++void activate_page(struct page *page); + + void free_pgtables(struct mmu_gather *tlb, struct vm_area_struct *start_vma, + unsigned long floor, unsigned long ceiling); +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -2798,6 +2798,7 @@ static void commit_charge(struct page *p + * - LRU isolation + * - lock_page_memcg() + * - exclusive reference ++ * - mem_cgroup_trylock_pages() + */ + page->memcg_data = (unsigned long)memcg; + } +--- a/mm/rmap.c ++++ b/mm/rmap.c +@@ -73,6 +73,7 @@ + #include + #include + #include ++#include + + #include + +@@ -793,6 +794,12 @@ static bool page_referenced_one(struct p + } + + if (pvmw.pte) { ++ if (lru_gen_enabled() && pte_young(*pvmw.pte) && ++ !(vma->vm_flags & (VM_SEQ_READ | VM_RAND_READ))) { ++ lru_gen_look_around(&pvmw); ++ referenced++; ++ } ++ + if (ptep_clear_flush_young_notify(vma, address, + pvmw.pte)) { + /* +--- a/mm/swap.c ++++ b/mm/swap.c +@@ -325,7 +325,7 @@ static bool need_activate_page_drain(int + return pagevec_count(&per_cpu(lru_pvecs.activate_page, cpu)) != 0; + } + +-static void activate_page(struct page *page) ++void activate_page(struct page *page) + { + page = compound_head(page); + if (PageLRU(page) && !PageActive(page) && !PageUnevictable(page)) { +@@ -345,7 +345,7 @@ static inline void activate_page_drain(i + { + } + +-static void activate_page(struct page *page) ++void activate_page(struct page *page) + { + struct lruvec *lruvec; + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -1409,6 +1409,11 @@ retry: + if (!sc->may_unmap && page_mapped(page)) + goto keep_locked; + ++ /* page_update_gen() tried to promote this page? */ ++ if (lru_gen_enabled() && !ignore_references && ++ page_mapped(page) && PageReferenced(page)) ++ goto keep_locked; ++ + may_enter_fs = (sc->gfp_mask & __GFP_FS) || + (PageSwapCache(page) && (sc->gfp_mask & __GFP_IO)); + +@@ -2990,6 +2995,29 @@ static bool positive_ctrl_err(struct ctr + * the aging + ******************************************************************************/ + ++/* promote pages accessed through page tables */ ++static int page_update_gen(struct page *page, int gen) ++{ ++ unsigned long new_flags, old_flags = READ_ONCE(page->flags); ++ ++ VM_WARN_ON_ONCE(gen >= MAX_NR_GENS); ++ VM_WARN_ON_ONCE(!rcu_read_lock_held()); ++ ++ do { ++ /* lru_gen_del_page() has isolated this page? */ ++ if (!(old_flags & LRU_GEN_MASK)) { ++ /* for shrink_page_list() */ ++ new_flags = old_flags | BIT(PG_referenced); ++ continue; ++ } ++ ++ new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_MASK | LRU_REFS_FLAGS); ++ new_flags |= (gen + 1UL) << LRU_GEN_PGOFF; ++ } while (!try_cmpxchg(&page->flags, &old_flags, new_flags)); ++ ++ return ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; ++} ++ + /* protect pages accessed multiple times through file descriptors */ + static int page_inc_gen(struct lruvec *lruvec, struct page *page, bool reclaiming) + { +@@ -3001,6 +3029,11 @@ static int page_inc_gen(struct lruvec *l + VM_WARN_ON_ONCE_PAGE(!(old_flags & LRU_GEN_MASK), page); + + do { ++ new_gen = ((old_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; ++ /* page_update_gen() has promoted this page? */ ++ if (new_gen >= 0 && new_gen != old_gen) ++ return new_gen; ++ + new_gen = (old_gen + 1) % MAX_NR_GENS; + + new_flags = old_flags & ~(LRU_GEN_MASK | LRU_REFS_MASK | LRU_REFS_FLAGS); +@@ -3015,6 +3048,43 @@ static int page_inc_gen(struct lruvec *l + return new_gen; + } + ++static unsigned long get_pte_pfn(pte_t pte, struct vm_area_struct *vma, unsigned long addr) ++{ ++ unsigned long pfn = pte_pfn(pte); ++ ++ VM_WARN_ON_ONCE(addr < vma->vm_start || addr >= vma->vm_end); ++ ++ if (!pte_present(pte) || is_zero_pfn(pfn)) ++ return -1; ++ ++ if (WARN_ON_ONCE(pte_devmap(pte) || pte_special(pte))) ++ return -1; ++ ++ if (WARN_ON_ONCE(!pfn_valid(pfn))) ++ return -1; ++ ++ return pfn; ++} ++ ++static struct page *get_pfn_page(unsigned long pfn, struct mem_cgroup *memcg, ++ struct pglist_data *pgdat) ++{ ++ struct page *page; ++ ++ /* try to avoid unnecessary memory loads */ ++ if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) ++ return NULL; ++ ++ page = compound_head(pfn_to_page(pfn)); ++ if (page_to_nid(page) != pgdat->node_id) ++ return NULL; ++ ++ if (page_memcg_rcu(page) != memcg) ++ return NULL; ++ ++ return page; ++} ++ + static void inc_min_seq(struct lruvec *lruvec, int type) + { + struct lru_gen_struct *lrugen = &lruvec->lrugen; +@@ -3214,6 +3284,114 @@ static void lru_gen_age_node(struct pgli + } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); + } + ++/* ++ * This function exploits spatial locality when shrink_page_list() walks the ++ * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages. ++ */ ++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; ++ unsigned long bitmap[BITS_TO_LONGS(MIN_LRU_BATCH)] = {}; ++ struct page *page = pvmw->page; ++ struct mem_cgroup *memcg = page_memcg(page); ++ struct pglist_data *pgdat = page_pgdat(page); ++ struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); ++ DEFINE_MAX_SEQ(lruvec); ++ int old_gen, new_gen = lru_gen_from_seq(max_seq); ++ ++ lockdep_assert_held(pvmw->ptl); ++ VM_WARN_ON_ONCE_PAGE(PageLRU(page), page); ++ ++ if (spin_is_contended(pvmw->ptl)) ++ return; ++ ++ start = max(pvmw->address & PMD_MASK, pvmw->vma->vm_start); ++ end = min(pvmw->address | ~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) ++ end = start + MIN_LRU_BATCH * PAGE_SIZE; ++ else if (end - pvmw->address < 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; ++ } ++ } ++ ++ pte = pvmw->pte - (pvmw->address - start) / PAGE_SIZE; ++ ++ rcu_read_lock(); ++ arch_enter_lazy_mmu_mode(); ++ ++ for (i = 0, addr = start; addr != end; i++, addr += PAGE_SIZE) { ++ unsigned long pfn; ++ ++ pfn = get_pte_pfn(pte[i], pvmw->vma, addr); ++ if (pfn == -1) ++ continue; ++ ++ if (!pte_young(pte[i])) ++ continue; ++ ++ page = get_pfn_page(pfn, memcg, pgdat); ++ if (!page) ++ continue; ++ ++ if (!ptep_test_and_clear_young(pvmw->vma, addr, pte + i)) ++ VM_WARN_ON_ONCE(true); ++ ++ if (pte_dirty(pte[i]) && !PageDirty(page) && ++ !(PageAnon(page) && PageSwapBacked(page) && ++ !PageSwapCache(page))) ++ set_page_dirty(page); ++ ++ old_gen = page_lru_gen(page); ++ if (old_gen < 0) ++ SetPageReferenced(page); ++ else if (old_gen != new_gen) ++ __set_bit(i, bitmap); ++ } ++ ++ arch_leave_lazy_mmu_mode(); ++ rcu_read_unlock(); ++ ++ if (bitmap_weight(bitmap, MIN_LRU_BATCH) < PAGEVEC_SIZE) { ++ for_each_set_bit(i, bitmap, MIN_LRU_BATCH) { ++ page = pte_page(pte[i]); ++ activate_page(page); ++ } ++ return; ++ } ++ ++ /* page_update_gen() requires stable page_memcg() */ ++ if (!mem_cgroup_trylock_pages(memcg)) ++ return; ++ ++ 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) { ++ page = compound_head(pte_page(pte[i])); ++ if (page_memcg_rcu(page) != memcg) ++ continue; ++ ++ old_gen = page_update_gen(page, new_gen); ++ if (old_gen < 0 || old_gen == new_gen) ++ continue; ++ ++ lru_gen_update_size(lruvec, page, old_gen, new_gen); ++ } ++ ++ spin_unlock_irq(&lruvec->lru_lock); ++ ++ mem_cgroup_unlock_pages(); ++} ++ + /****************************************************************************** + * the eviction + ******************************************************************************/ +@@ -3250,6 +3428,12 @@ static bool sort_page(struct lruvec *lru + return true; + } + ++ /* promoted */ ++ if (gen != lru_gen_from_seq(lrugen->min_seq[type])) { ++ list_move(&page->lru, &lrugen->lists[gen][type][zone]); ++ return true; ++ } ++ + /* protected */ + if (tier > tier_idx) { + int hist = lru_hist_from_seq(lrugen->min_seq[type]); diff --git a/target/linux/generic/backport-5.15/020-v6.1-07-mm-multigenerational-lru-eviction.patch b/target/linux/generic/backport-5.15/020-v6.1-07-mm-multigenerational-lru-eviction.patch deleted file mode 100644 index a75fedecaaa..00000000000 --- a/target/linux/generic/backport-5.15/020-v6.1-07-mm-multigenerational-lru-eviction.patch +++ /dev/null @@ -1,1002 +0,0 @@ -From f4b881ce07ccb2a519f664afaa2a68225b612ca3 Mon Sep 17 00:00:00 2001 -From: Yu Zhao -Date: Tue, 29 Jun 2021 20:46:47 -0600 -Subject: [PATCH 07/10] mm: multigenerational lru: eviction - -The eviction consumes old generations. Given an lruvec, the eviction -scans pages on lrugen->lists indexed by anon and file min_seq[] -(modulo MAX_NR_GENS). It first tries to select a type based on the -values of min_seq[]. If they are equal, it selects the type that has -a lower refaulted %. The eviction sorts a page according to its -updated generation number if the aging has found this page accessed. -It also moves a page to the next generation if this page is from an -upper tier that has a higher refaulted % than the base tier. The -eviction increments min_seq[] of a selected type when it finds -lrugen->lists indexed by min_seq[] of this selected type are empty. - -Each generation is divided into multiple tiers. Tiers represent -different ranges of numbers of accesses from file descriptors only. -Pages accessed N times via file descriptors belong to tier -order_base_2(N). Each generation contains at most MAX_NR_TIERS tiers, -and they require additional MAX_NR_TIERS-2 bits in page->flags. In -contrast to moving between generations which requires list operations, -moving between tiers only involves operations on page->flags and -therefore has a negligible cost. A feedback loop modeled after the PID -controller monitors refaulted % across all tiers and decides when to -protect pages from which tiers. - -Unmapped pages are initially added to the oldest generation and then -conditionally protected by tiers. Each tier keeps track of how many -pages from it have refaulted. Tier 0 is the base tier and pages from -it are evicted unconditionally because there are no better candidates. -Pages from an upper tier are either evicted or moved to the next -generation, depending on whether this upper tier has a higher -refaulted % than the base tier. This model has the following -advantages: - 1) It removes the cost in the buffered access path and reduces the - overall cost of protection because pages are conditionally protected - in the reclaim path. - 2) It takes mapped pages into account and avoids overprotecting - pages accessed multiple times via file descriptors. - 3 Additional tiers improve the protection of pages accessed more - than twice. - -Signed-off-by: Yu Zhao -Tested-by: Konstantin Kharlamov -Change-Id: I64c06d8f2cdb83ac7d56c7e1d07f043483956cac ---- - include/linux/mm_inline.h | 10 + - include/linux/mmzone.h | 33 +++ - mm/swap.c | 42 +++ - mm/vmscan.c | 555 +++++++++++++++++++++++++++++++++++++- - mm/workingset.c | 120 ++++++++- - 5 files changed, 757 insertions(+), 3 deletions(-) - ---- a/include/linux/mm_inline.h -+++ b/include/linux/mm_inline.h -@@ -106,6 +106,14 @@ static inline int lru_hist_from_seq(unsi - return seq % NR_HIST_GENS; - } - -+/* Convert the number of accesses to a tier. See the comment on MAX_NR_TIERS. */ -+static inline int lru_tier_from_refs(int refs) -+{ -+ VM_BUG_ON(refs > BIT(LRU_REFS_WIDTH)); -+ -+ return order_base_2(refs + 1); -+} -+ - /* The youngest and the second youngest generations are counted as active. */ - static inline bool lru_gen_is_active(struct lruvec *lruvec, int gen) - { -@@ -226,6 +234,8 @@ static inline bool lru_gen_del_page(stru - gen = ((new_flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; - - new_flags &= ~LRU_GEN_MASK; -+ if ((new_flags & LRU_REFS_FLAGS) != LRU_REFS_FLAGS) -+ new_flags &= ~(LRU_REFS_MASK | LRU_REFS_FLAGS); - /* for shrink_page_list() */ - if (reclaiming) - new_flags &= ~(BIT(PG_referenced) | BIT(PG_reclaim)); ---- a/include/linux/mmzone.h -+++ b/include/linux/mmzone.h -@@ -319,6 +319,30 @@ struct page_vma_mapped_walk; - #define MIN_NR_GENS 2 - #define MAX_NR_GENS ((unsigned int)CONFIG_NR_LRU_GENS) - -+/* -+ * Each generation is divided into multiple tiers. Tiers represent different -+ * ranges of numbers of accesses from file descriptors, i.e., -+ * mark_page_accessed(). In contrast to moving between generations which -+ * requires the lru lock, moving between tiers only involves an atomic -+ * operation on page->flags and therefore has a negligible cost. -+ * -+ * The purposes of tiers are to: -+ * 1) estimate whether pages accessed multiple times via file descriptors are -+ * more active than pages accessed only via page tables by separating the two -+ * access types into upper tiers and the base tier, and comparing refaulted % -+ * across all tiers. -+ * 2) improve buffered io performance by deferring the protection of pages -+ * accessed multiple times until the eviction. That is the protection happens -+ * in the reclaim path, not the access path. -+ * -+ * Pages accessed N times via file descriptors belong to tier order_base_2(N). -+ * The base tier may be marked by PageReferenced(). All upper tiers are marked -+ * by PageReferenced() && PageWorkingset(). Additional bits from page->flags are -+ * used to support more than one upper tier. -+ */ -+#define MAX_NR_TIERS ((unsigned int)CONFIG_TIERS_PER_GEN) -+#define LRU_REFS_FLAGS (BIT(PG_referenced) | BIT(PG_workingset)) -+ - /* Whether to keep stats for historical generations. */ - #ifdef CONFIG_LRU_GEN_STATS - #define NR_HIST_GENS ((unsigned int)CONFIG_NR_LRU_GENS) -@@ -337,6 +361,15 @@ struct lrugen { - struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; - /* the sizes of the multigenerational lru lists in pages */ - unsigned long sizes[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; -+ /* the exponential moving average of refaulted */ -+ unsigned long avg_refaulted[ANON_AND_FILE][MAX_NR_TIERS]; -+ /* the exponential moving average of protected+evicted */ -+ unsigned long avg_total[ANON_AND_FILE][MAX_NR_TIERS]; -+ /* the base tier isn't protected, hence the minus one */ -+ unsigned long protected[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS - 1]; -+ /* incremented without holding the lru lock */ -+ atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; -+ atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; - /* whether the multigenerational lru is enabled */ - bool enabled[ANON_AND_FILE]; - }; ---- a/mm/swap.c -+++ b/mm/swap.c -@@ -389,6 +389,43 @@ static void __lru_cache_activate_page(st - local_unlock(&lru_pvecs.lock); - } - -+#ifdef CONFIG_LRU_GEN -+static void page_inc_refs(struct page *page) -+{ -+ unsigned long refs; -+ unsigned long old_flags, new_flags; -+ -+ if (PageUnevictable(page)) -+ return; -+ -+ /* see the comment on MAX_NR_TIERS */ -+ do { -+ new_flags = old_flags = READ_ONCE(page->flags); -+ -+ if (!(new_flags & BIT(PG_referenced))) { -+ new_flags |= BIT(PG_referenced); -+ continue; -+ } -+ -+ if (!(new_flags & BIT(PG_workingset))) { -+ new_flags |= BIT(PG_workingset); -+ continue; -+ } -+ -+ refs = new_flags & LRU_REFS_MASK; -+ refs = min(refs + BIT(LRU_REFS_PGOFF), LRU_REFS_MASK); -+ -+ new_flags &= ~LRU_REFS_MASK; -+ new_flags |= refs; -+ } while (new_flags != old_flags && -+ cmpxchg(&page->flags, old_flags, new_flags) != old_flags); -+} -+#else -+static void page_inc_refs(struct page *page) -+{ -+} -+#endif /* CONFIG_LRU_GEN */ -+ - /* - * Mark a page as having seen activity. - * -@@ -403,6 +440,11 @@ void mark_page_accessed(struct page *pag - { - page = compound_head(page); - -+ if (lru_gen_enabled()) { -+ page_inc_refs(page); -+ return; -+ } -+ - if (!PageReferenced(page)) { - SetPageReferenced(page); - } else if (PageUnevictable(page)) { ---- a/mm/vmscan.c -+++ b/mm/vmscan.c -@@ -1145,9 +1145,11 @@ static int __remove_mapping(struct addre - - if (PageSwapCache(page)) { - swp_entry_t swap = { .val = page_private(page) }; -- mem_cgroup_swapout(page, swap); -+ -+ /* get a shadow entry before page_memcg() is cleared */ - if (reclaimed && !mapping_exiting(mapping)) - shadow = workingset_eviction(page, target_memcg); -+ mem_cgroup_swapout(page, swap); - __delete_from_swap_cache(page, swap, shadow); - xa_unlock_irq(&mapping->i_pages); - put_swap_page(page, swap); -@@ -1410,6 +1412,11 @@ retry: - if (!sc->may_unmap && page_mapped(page)) - goto keep_locked; - -+ /* lru_gen_look_around() has updated this page? */ -+ if (lru_gen_enabled() && !ignore_references && -+ page_mapped(page) && PageReferenced(page)) -+ goto keep_locked; -+ - may_enter_fs = (sc->gfp_mask & __GFP_FS) || - (PageSwapCache(page) && (sc->gfp_mask & __GFP_IO)); - -@@ -2505,6 +2512,9 @@ static void prepare_scan_count(pg_data_t - unsigned long file; - struct lruvec *target_lruvec; - -+ if (lru_gen_enabled()) -+ return; -+ - target_lruvec = mem_cgroup_lruvec(sc->target_mem_cgroup, pgdat); - - /* -@@ -2845,6 +2855,17 @@ static int page_lru_gen(struct page *pag - return ((flags & LRU_GEN_MASK) >> LRU_GEN_PGOFF) - 1; - } - -+static int page_lru_tier(struct page *page) -+{ -+ int refs; -+ unsigned long flags = READ_ONCE(page->flags); -+ -+ refs = (flags & LRU_REFS_FLAGS) == LRU_REFS_FLAGS ? -+ ((flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF) + 1 : 0; -+ -+ return lru_tier_from_refs(refs); -+} -+ - static int get_swappiness(struct mem_cgroup *memcg) - { - return mem_cgroup_get_nr_swap_pages(memcg) >= MIN_BATCH_SIZE ? -@@ -3181,6 +3202,91 @@ done: - } - - /****************************************************************************** -+ * refault feedback loop -+ ******************************************************************************/ -+ -+/* -+ * A feedback loop modeled after the PID controller. Currently supports the -+ * proportional (P) and the integral (I) terms; the derivative (D) term can be -+ * added if necessary. The setpoint (SP) is the desired position; the process -+ * variable (PV) is the measured position. The error is the difference between -+ * the SP and the PV. A positive error results in a positive control output -+ * correction, which, in our case, is to allow eviction. -+ * -+ * The P term is refaulted % of the current generation being evicted. The I -+ * term is the exponential moving average of refaulted % of previously evicted -+ * generations, using the smoothing factor 1/2. -+ * -+ * Our goal is to maintain proportional refaulted % across all tiers. -+ */ -+struct ctrl_pos { -+ unsigned long refaulted; -+ unsigned long total; -+ int gain; -+}; -+ -+static void read_ctrl_pos(struct lruvec *lruvec, int type, int tier, int gain, -+ struct ctrl_pos *pos) -+{ -+ struct lrugen *lrugen = &lruvec->evictable; -+ int hist = lru_hist_from_seq(lrugen->min_seq[type]); -+ -+ pos->refaulted = lrugen->avg_refaulted[type][tier] + -+ atomic_long_read(&lrugen->refaulted[hist][type][tier]); -+ pos->total = lrugen->avg_total[type][tier] + -+ atomic_long_read(&lrugen->evicted[hist][type][tier]); -+ if (tier) -+ pos->total += lrugen->protected[hist][type][tier - 1]; -+ pos->gain = gain; -+} -+ -+static void reset_ctrl_pos(struct lruvec *lruvec, int gen, int type) -+{ -+ int tier; -+ int hist = lru_hist_from_seq(gen); -+ struct lrugen *lrugen = &lruvec->evictable; -+ bool carryover = gen == lru_gen_from_seq(lrugen->min_seq[type]); -+ bool clear = carryover ? NR_HIST_GENS == 1 : NR_HIST_GENS > 1; -+ -+ if (!carryover && !clear) -+ return; -+ -+ for (tier = 0; tier < MAX_NR_TIERS; tier++) { -+ if (carryover) { -+ unsigned long sum; -+ -+ sum = lrugen->avg_refaulted[type][tier] + -+ atomic_long_read(&lrugen->refaulted[hist][type][tier]); -+ WRITE_ONCE(lrugen->avg_refaulted[type][tier], sum / 2); -+ -+ sum = lrugen->avg_total[type][tier] + -+ atomic_long_read(&lrugen->evicted[hist][type][tier]); -+ if (tier) -+ sum += lrugen->protected[hist][type][tier - 1]; -+ WRITE_ONCE(lrugen->avg_total[type][tier], sum / 2); -+ } -+ -+ if (clear) { -+ atomic_long_set(&lrugen->refaulted[hist][type][tier], 0); -+ atomic_long_set(&lrugen->evicted[hist][type][tier], 0); -+ if (tier) -+ WRITE_ONCE(lrugen->protected[hist][type][tier - 1], 0); -+ } -+ } -+} -+ -+static bool positive_ctrl_err(struct ctrl_pos *sp, struct ctrl_pos *pv) -+{ -+ /* -+ * Allow eviction if the PV has a limited number of refaulted pages or a -+ * lower refaulted % than the SP. -+ */ -+ return pv->refaulted < MIN_BATCH_SIZE || -+ pv->refaulted * max(sp->total, 1UL) * sp->gain <= -+ sp->refaulted * max(pv->total, 1UL) * pv->gain; -+} -+ -+/****************************************************************************** - * the aging - ******************************************************************************/ - -@@ -3200,6 +3306,7 @@ static int page_update_gen(struct page * - - new_flags &= ~LRU_GEN_MASK; - new_flags |= (gen + 1UL) << LRU_GEN_PGOFF; -+ new_flags &= ~(LRU_REFS_MASK | LRU_REFS_FLAGS); - } while (new_flags != old_flags && - cmpxchg(&page->flags, old_flags, new_flags) != old_flags); - -@@ -3231,6 +3338,7 @@ static void page_inc_gen(struct page *pa - - new_flags &= ~LRU_GEN_MASK; - new_flags |= (new_gen + 1UL) << LRU_GEN_PGOFF; -+ new_flags &= ~(LRU_REFS_MASK | LRU_REFS_FLAGS); - /* for end_page_writeback() */ - if (reclaiming) - new_flags |= BIT(PG_reclaim); -@@ -3722,6 +3830,7 @@ static bool inc_min_seq(struct lruvec *l - } - } - -+ reset_ctrl_pos(lruvec, gen, type); - WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1); - - return true; -@@ -3759,6 +3868,8 @@ next: - if (min_seq[type] == lrugen->min_seq[type]) - continue; - -+ gen = lru_gen_from_seq(lrugen->min_seq[type]); -+ reset_ctrl_pos(lruvec, gen, type); - WRITE_ONCE(lrugen->min_seq[type], min_seq[type]); - success = true; - } -@@ -3820,6 +3931,9 @@ static void inc_max_seq(struct lruvec *l - } - } - -+ for (type = 0; type < ANON_AND_FILE; type++) -+ reset_ctrl_pos(lruvec, gen, type); -+ - WRITE_ONCE(lrugen->timestamps[gen], jiffies); - /* make sure all preceding modifications appear first */ - smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); -@@ -4101,6 +4215,433 @@ void lru_gen_look_around(struct page_vma - } - - /****************************************************************************** -+ * the eviction -+ ******************************************************************************/ -+ -+static bool sort_page(struct page *page, struct lruvec *lruvec, int tier_idx) -+{ -+ bool success; -+ int gen = page_lru_gen(page); -+ int type = page_is_file_lru(page); -+ int zone = page_zonenum(page); -+ int tier = page_lru_tier(page); -+ int delta = thp_nr_pages(page); -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ VM_BUG_ON_PAGE(gen >= MAX_NR_GENS, page); -+ -+ /* an mlocked page? */ -+ if (!page_evictable(page)) { -+ success = lru_gen_del_page(page, lruvec, true); -+ VM_BUG_ON_PAGE(!success, page); -+ SetPageUnevictable(page); -+ add_page_to_lru_list(page, lruvec); -+ __count_vm_events(UNEVICTABLE_PGCULLED, delta); -+ return true; -+ } -+ -+ /* a lazy-free page that has been written into? */ -+ if (type && PageDirty(page) && PageAnon(page)) { -+ success = lru_gen_del_page(page, lruvec, true); -+ VM_BUG_ON_PAGE(!success, page); -+ SetPageSwapBacked(page); -+ add_page_to_lru_list_tail(page, lruvec); -+ return true; -+ } -+ -+ /* page_update_gen() has updated this page? */ -+ if (gen != lru_gen_from_seq(lrugen->min_seq[type])) { -+ list_move(&page->lru, &lrugen->lists[gen][type][zone]); -+ return true; -+ } -+ -+ /* protect this page if its tier has a higher refaulted % */ -+ if (tier > tier_idx) { -+ int hist = lru_hist_from_seq(gen); -+ -+ page_inc_gen(page, lruvec, false); -+ WRITE_ONCE(lrugen->protected[hist][type][tier - 1], -+ lrugen->protected[hist][type][tier - 1] + delta); -+ __mod_lruvec_state(lruvec, WORKINGSET_ACTIVATE_BASE + type, delta); -+ return true; -+ } -+ -+ /* mark this page for reclaim if it's pending writeback */ -+ if (PageWriteback(page) || (type && PageDirty(page))) { -+ page_inc_gen(page, lruvec, true); -+ return true; -+ } -+ -+ return false; -+} -+ -+static bool isolate_page(struct page *page, struct lruvec *lruvec, struct scan_control *sc) -+{ -+ bool success; -+ -+ if (!sc->may_unmap && page_mapped(page)) -+ return false; -+ -+ if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) && -+ (PageDirty(page) || (PageAnon(page) && !PageSwapCache(page)))) -+ return false; -+ -+ if (!get_page_unless_zero(page)) -+ return false; -+ -+ if (!TestClearPageLRU(page)) { -+ put_page(page); -+ return false; -+ } -+ -+ success = lru_gen_del_page(page, lruvec, true); -+ VM_BUG_ON_PAGE(!success, page); -+ -+ return true; -+} -+ -+static int scan_pages(struct lruvec *lruvec, struct scan_control *sc, -+ int type, int tier, struct list_head *list) -+{ -+ int gen, zone; -+ enum vm_event_item item; -+ int sorted = 0; -+ int scanned = 0; -+ int isolated = 0; -+ int remaining = MAX_BATCH_SIZE; -+ struct lrugen *lrugen = &lruvec->evictable; -+ struct mem_cgroup *memcg = lruvec_memcg(lruvec); -+ -+ VM_BUG_ON(!list_empty(list)); -+ -+ if (get_nr_gens(lruvec, type) == MIN_NR_GENS) -+ return 0; -+ -+ gen = lru_gen_from_seq(lrugen->min_seq[type]); -+ -+ for (zone = sc->reclaim_idx; zone >= 0; zone--) { -+ LIST_HEAD(moved); -+ int skipped = 0; -+ struct list_head *head = &lrugen->lists[gen][type][zone]; -+ -+ while (!list_empty(head)) { -+ struct page *page = lru_to_page(head); -+ int delta = thp_nr_pages(page); -+ -+ VM_BUG_ON_PAGE(PageTail(page), page); -+ VM_BUG_ON_PAGE(PageUnevictable(page), page); -+ VM_BUG_ON_PAGE(PageActive(page), page); -+ VM_BUG_ON_PAGE(page_is_file_lru(page) != type, page); -+ VM_BUG_ON_PAGE(page_zonenum(page) != zone, page); -+ -+ prefetchw_prev_lru_page(page, head, flags); -+ -+ scanned += delta; -+ -+ if (sort_page(page, lruvec, tier)) -+ sorted += delta; -+ else if (isolate_page(page, lruvec, sc)) { -+ list_add(&page->lru, list); -+ isolated += delta; -+ } else { -+ list_move(&page->lru, &moved); -+ skipped += delta; -+ } -+ -+ if (!--remaining || max(isolated, skipped) >= MIN_BATCH_SIZE) -+ break; -+ } -+ -+ if (skipped) { -+ list_splice(&moved, head); -+ __count_zid_vm_events(PGSCAN_SKIP, zone, skipped); -+ } -+ -+ if (!remaining || isolated >= MIN_BATCH_SIZE) -+ break; -+ } -+ -+ item = current_is_kswapd() ? PGSCAN_KSWAPD : PGSCAN_DIRECT; -+ if (!cgroup_reclaim(sc)) { -+ __count_vm_events(item, isolated); -+ __count_vm_events(PGREFILL, sorted); -+ } -+ __count_memcg_events(memcg, item, isolated); -+ __count_memcg_events(memcg, PGREFILL, sorted); -+ __count_vm_events(PGSCAN_ANON + type, isolated); -+ -+ /* -+ * We may have trouble finding eligible pages due to reclaim_idx, -+ * may_unmap and may_writepage. Check `remaining` to make sure we won't -+ * be stuck if we aren't making enough progress. -+ */ -+ return isolated || !remaining ? scanned : 0; -+} -+ -+static int get_tier_idx(struct lruvec *lruvec, int type) -+{ -+ int tier; -+ struct ctrl_pos sp, pv; -+ -+ /* -+ * Ideally we don't want to evict upper tiers that have higher refaulted -+ * %. However, we need to leave a margin for the fluctuation in -+ * refaulted %. So we use a larger gain factor to make sure upper tiers -+ * are indeed more active. We choose 2 because the lowest upper tier -+ * would have twice of refaulted % of the base tier, according to their -+ * numbers of accesses. -+ */ -+ read_ctrl_pos(lruvec, type, 0, 1, &sp); -+ for (tier = 1; tier < MAX_NR_TIERS; tier++) { -+ read_ctrl_pos(lruvec, type, tier, 2, &pv); -+ if (!positive_ctrl_err(&sp, &pv)) -+ break; -+ } -+ -+ return tier - 1; -+} -+ -+static int get_type_to_scan(struct lruvec *lruvec, int swappiness, int *tier_idx) -+{ -+ int type, tier; -+ struct ctrl_pos sp, pv; -+ int gain[ANON_AND_FILE] = { swappiness, 200 - swappiness }; -+ -+ /* -+ * Compare refaulted % between the base tiers of anon and file to -+ * determine which type to evict. Also need to compare refaulted % of -+ * the upper tiers of the selected type with that of the base tier of -+ * the other type to determine which tier of the selected type to evict. -+ */ -+ read_ctrl_pos(lruvec, 0, 0, gain[0], &sp); -+ read_ctrl_pos(lruvec, 1, 0, gain[1], &pv); -+ type = positive_ctrl_err(&sp, &pv); -+ -+ read_ctrl_pos(lruvec, !type, 0, gain[!type], &sp); -+ for (tier = 1; tier < MAX_NR_TIERS; tier++) { -+ read_ctrl_pos(lruvec, type, tier, gain[type], &pv); -+ if (!positive_ctrl_err(&sp, &pv)) -+ break; -+ } -+ -+ *tier_idx = tier - 1; -+ -+ return type; -+} -+ -+static int isolate_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness, -+ int *type_scanned, struct list_head *list) -+{ -+ int i; -+ int type; -+ int scanned; -+ int tier = -1; -+ DEFINE_MIN_SEQ(lruvec); -+ -+ VM_BUG_ON(!seq_is_valid(lruvec)); -+ -+ /* -+ * Try to select a type based on generations and swappiness, and if that -+ * fails, fall back to get_type_to_scan(). When anon and file are both -+ * available from the same generation, swappiness 200 is interpreted as -+ * anon first and swappiness 1 is interpreted as file first. -+ */ -+ if (!swappiness) -+ type = 1; -+ else if (min_seq[0] < min_seq[1]) -+ type = 0; -+ else if (swappiness == 1) -+ type = 1; -+ else if (swappiness == 200) -+ type = 0; -+ else -+ type = get_type_to_scan(lruvec, swappiness, &tier); -+ -+ for (i = !swappiness; i < ANON_AND_FILE; i++) { -+ if (tier < 0) -+ tier = get_tier_idx(lruvec, type); -+ -+ scanned = scan_pages(lruvec, sc, type, tier, list); -+ if (scanned) -+ break; -+ -+ type = !type; -+ tier = -1; -+ } -+ -+ *type_scanned = type; -+ -+ return scanned; -+} -+ -+/* Main function used by the foreground, the background and the user-triggered eviction. */ -+static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness) -+{ -+ int type; -+ int scanned; -+ int reclaimed; -+ LIST_HEAD(list); -+ struct page *page; -+ enum vm_event_item item; -+ struct reclaim_stat stat; -+ struct mm_walk_args *args; -+ struct mem_cgroup *memcg = lruvec_memcg(lruvec); -+ struct pglist_data *pgdat = lruvec_pgdat(lruvec); -+ -+ spin_lock_irq(&lruvec->lru_lock); -+ -+ scanned = isolate_pages(lruvec, sc, swappiness, &type, &list); -+ -+ if (try_to_inc_min_seq(lruvec, swappiness)) -+ scanned++; -+ -+ if (get_nr_gens(lruvec, 1) == MIN_NR_GENS) -+ scanned = 0; -+ -+ spin_unlock_irq(&lruvec->lru_lock); -+ -+ if (list_empty(&list)) -+ return scanned; -+ -+ reclaimed = shrink_page_list(&list, pgdat, sc, &stat, false); -+ /* -+ * We need to prevent rejected pages from being added back to the same -+ * lists they were isolated from. Otherwise we may risk looping on them -+ * forever. -+ */ -+ list_for_each_entry(page, &list, lru) { -+ if (!PageReclaim(page) || !(PageDirty(page) || PageWriteback(page))) -+ SetPageActive(page); -+ -+ ClearPageReferenced(page); -+ ClearPageWorkingset(page); -+ } -+ -+ spin_lock_irq(&lruvec->lru_lock); -+ -+ move_pages_to_lru(lruvec, &list); -+ -+ args = current->reclaim_state ? current->reclaim_state->mm_walk_args : NULL; -+ if (args && args->batch_size) -+ reset_batch_size(lruvec, args); -+ -+ item = current_is_kswapd() ? PGSTEAL_KSWAPD : PGSTEAL_DIRECT; -+ if (!cgroup_reclaim(sc)) -+ __count_vm_events(item, reclaimed); -+ __count_memcg_events(memcg, item, reclaimed); -+ __count_vm_events(PGSTEAL_ANON + type, reclaimed); -+ -+ spin_unlock_irq(&lruvec->lru_lock); -+ -+ mem_cgroup_uncharge_list(&list); -+ free_unref_page_list(&list); -+ -+ sc->nr_reclaimed += reclaimed; -+ -+ return scanned; -+} -+ -+static long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, int swappiness) -+{ -+ bool low; -+ long nr_to_scan; -+ struct mem_cgroup *memcg = lruvec_memcg(lruvec); -+ int priority = sc->priority; -+ 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; -+ -+ if (sc->nr_reclaimed >= sc->nr_to_reclaim) { -+ priority = DEF_PRIORITY; -+ sc->force_deactivate = 0; -+ } -+ -+ nr_to_scan = get_nr_evictable(lruvec, sc, swappiness, max_seq, min_seq, &low); -+ if (!nr_to_scan) -+ return 0; -+ -+ nr_to_scan >>= priority; -+ -+ if (!mem_cgroup_online(memcg)) -+ nr_to_scan++; -+ -+ if (!nr_to_scan) -+ return 0; -+ -+ if (current_is_kswapd()) { -+ /* leave the work to lru_gen_age_node() */ -+ if (max_seq - min_seq[1] < MIN_NR_GENS) -+ return 0; -+ -+ if (!low) -+ sc->force_deactivate = 0; -+ -+ return nr_to_scan; -+ } -+ -+ if (max_seq - min_seq[1] >= MIN_NR_GENS) -+ return nr_to_scan; -+ -+ /* move onto slab and other memcgs if we haven't tried them all */ -+ if (!sc->force_deactivate) { -+ sc->skipped_deactivate = 1; -+ return 0; -+ } -+ -+ return try_to_inc_max_seq(lruvec, sc, swappiness, max_seq, true) ? nr_to_scan : 0; -+} -+ -+static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) -+{ -+ struct blk_plug plug; -+ long scanned = 0; -+ struct mem_cgroup *memcg = lruvec_memcg(lruvec); -+ struct pglist_data *pgdat = lruvec_pgdat(lruvec); -+ -+ lru_add_drain(); -+ -+ if (current_is_kswapd()) -+ current->reclaim_state->mm_walk_args = &pgdat->mm_walk_args; -+ -+ blk_start_plug(&plug); -+ -+ while (true) { -+ int delta; -+ int swappiness; -+ long nr_to_scan; -+ -+ if (sc->may_swap) -+ swappiness = get_swappiness(memcg); -+ else if (!cgroup_reclaim(sc) && get_swappiness(memcg)) -+ swappiness = 1; -+ else -+ swappiness = 0; -+ -+ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness); -+ if (!nr_to_scan) -+ break; -+ -+ delta = evict_pages(lruvec, sc, swappiness); -+ if (!delta) -+ break; -+ -+ scanned += delta; -+ if (scanned >= nr_to_scan) -+ break; -+ -+ cond_resched(); -+ } -+ -+ blk_finish_plug(&plug); -+ -+ if (current_is_kswapd()) -+ current->reclaim_state->mm_walk_args = NULL; -+} -+ -+/****************************************************************************** - * state change - ******************************************************************************/ - -@@ -4355,6 +4896,10 @@ static void lru_gen_age_node(struct pgli - { - } - -+static void lru_gen_shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) -+{ -+} -+ - #endif /* CONFIG_LRU_GEN */ - - static void shrink_lruvec(struct lruvec *lruvec, struct scan_control *sc) -@@ -4368,6 +4913,11 @@ static void shrink_lruvec(struct lruvec - bool proportional_reclaim; - struct blk_plug plug; - -+ if (lru_gen_enabled()) { -+ lru_gen_shrink_lruvec(lruvec, sc); -+ return; -+ } -+ - get_scan_count(lruvec, sc, nr); - - /* Record the original scan target for proportional adjustments later */ -@@ -4839,6 +5389,9 @@ static void snapshot_refaults(struct mem - struct lruvec *target_lruvec; - unsigned long refaults; - -+ if (lru_gen_enabled()) -+ return; -+ - target_lruvec = mem_cgroup_lruvec(target_memcg, pgdat); - refaults = lruvec_page_state(target_lruvec, WORKINGSET_ACTIVATE_ANON); - target_lruvec->refaults[0] = refaults; ---- a/mm/workingset.c -+++ b/mm/workingset.c -@@ -187,7 +187,6 @@ static unsigned int bucket_order __read_ - static void *pack_shadow(int memcgid, pg_data_t *pgdat, unsigned long eviction, - bool workingset) - { -- eviction >>= bucket_order; - eviction &= EVICTION_MASK; - eviction = (eviction << MEM_CGROUP_ID_SHIFT) | memcgid; - eviction = (eviction << NODES_SHIFT) | pgdat->node_id; -@@ -212,10 +211,117 @@ static void unpack_shadow(void *shadow, - - *memcgidp = memcgid; - *pgdat = NODE_DATA(nid); -- *evictionp = entry << bucket_order; -+ *evictionp = entry; - *workingsetp = workingset; - } - -+#ifdef CONFIG_LRU_GEN -+ -+static int page_lru_refs(struct page *page) -+{ -+ unsigned long flags = READ_ONCE(page->flags); -+ -+ BUILD_BUG_ON(LRU_GEN_WIDTH + LRU_REFS_WIDTH > BITS_PER_LONG - EVICTION_SHIFT); -+ -+ /* see the comment on MAX_NR_TIERS */ -+ return flags & BIT(PG_workingset) ? (flags & LRU_REFS_MASK) >> LRU_REFS_PGOFF : 0; -+} -+ -+/* Return a token to be stored in the shadow entry of a page being evicted. */ -+static void *lru_gen_eviction(struct page *page) -+{ -+ int hist, tier; -+ unsigned long token; -+ unsigned long min_seq; -+ struct lruvec *lruvec; -+ struct lrugen *lrugen; -+ int type = page_is_file_lru(page); -+ int refs = page_lru_refs(page); -+ int delta = thp_nr_pages(page); -+ bool workingset = PageWorkingset(page); -+ struct mem_cgroup *memcg = page_memcg(page); -+ struct pglist_data *pgdat = page_pgdat(page); -+ -+ lruvec = mem_cgroup_lruvec(memcg, pgdat); -+ lrugen = &lruvec->evictable; -+ min_seq = READ_ONCE(lrugen->min_seq[type]); -+ token = (min_seq << LRU_REFS_WIDTH) | refs; -+ -+ hist = lru_hist_from_seq(min_seq); -+ tier = lru_tier_from_refs(refs + workingset); -+ atomic_long_add(delta, &lrugen->evicted[hist][type][tier]); -+ -+ return pack_shadow(mem_cgroup_id(memcg), pgdat, token, workingset); -+} -+ -+/* Count a refaulted page based on the token stored in its shadow entry. */ -+static void lru_gen_refault(struct page *page, void *shadow) -+{ -+ int hist, tier, refs; -+ int memcg_id; -+ bool workingset; -+ unsigned long token; -+ unsigned long min_seq; -+ struct lruvec *lruvec; -+ struct lrugen *lrugen; -+ struct mem_cgroup *memcg; -+ struct pglist_data *pgdat; -+ int type = page_is_file_lru(page); -+ int delta = thp_nr_pages(page); -+ -+ unpack_shadow(shadow, &memcg_id, &pgdat, &token, &workingset); -+ if (page_pgdat(page) != pgdat) -+ return; -+ -+ rcu_read_lock(); -+ memcg = page_memcg_rcu(page); -+ if (mem_cgroup_id(memcg) != memcg_id) -+ goto unlock; -+ -+ refs = token & (BIT(LRU_REFS_WIDTH) - 1); -+ if (refs && !workingset) -+ goto unlock; -+ -+ token >>= LRU_REFS_WIDTH; -+ lruvec = mem_cgroup_lruvec(memcg, pgdat); -+ lrugen = &lruvec->evictable; -+ min_seq = READ_ONCE(lrugen->min_seq[type]); -+ if (token != (min_seq & (EVICTION_MASK >> LRU_REFS_WIDTH))) -+ goto unlock; -+ -+ hist = lru_hist_from_seq(min_seq); -+ tier = lru_tier_from_refs(refs + workingset); -+ atomic_long_add(delta, &lrugen->refaulted[hist][type][tier]); -+ mod_lruvec_state(lruvec, WORKINGSET_REFAULT_BASE + type, delta); -+ -+ /* -+ * Tiers don't offer any protection to pages accessed via page tables. -+ * That's what generations do. Tiers can't fully protect pages after -+ * their numbers of accesses has exceeded the max value. Conservatively -+ * count these two conditions as stalls even though they might not -+ * indicate any real memory pressure. -+ */ -+ if (task_in_nonseq_fault() || refs + workingset == BIT(LRU_REFS_WIDTH)) { -+ SetPageWorkingset(page); -+ mod_lruvec_state(lruvec, WORKINGSET_RESTORE_BASE + type, delta); -+ } -+unlock: -+ rcu_read_unlock(); -+} -+ -+#else -+ -+static void *lru_gen_eviction(struct page *page) -+{ -+ return NULL; -+} -+ -+static void lru_gen_refault(struct page *page, void *shadow) -+{ -+} -+ -+#endif /* CONFIG_LRU_GEN */ -+ - /** - * workingset_age_nonresident - age non-resident entries as LRU ages - * @lruvec: the lruvec that was aged -@@ -264,10 +370,14 @@ void *workingset_eviction(struct page *p - VM_BUG_ON_PAGE(page_count(page), page); - VM_BUG_ON_PAGE(!PageLocked(page), page); - -+ if (lru_gen_enabled()) -+ return lru_gen_eviction(page); -+ - lruvec = mem_cgroup_lruvec(target_memcg, pgdat); - /* XXX: target_memcg can be NULL, go through lruvec */ - memcgid = mem_cgroup_id(lruvec_memcg(lruvec)); - eviction = atomic_long_read(&lruvec->nonresident_age); -+ eviction >>= bucket_order; - workingset_age_nonresident(lruvec, thp_nr_pages(page)); - return pack_shadow(memcgid, pgdat, eviction, PageWorkingset(page)); - } -@@ -296,7 +406,13 @@ void workingset_refault(struct page *pag - bool workingset; - int memcgid; - -+ if (lru_gen_enabled()) { -+ lru_gen_refault(page, shadow); -+ return; -+ } -+ - unpack_shadow(shadow, &memcgid, &pgdat, &eviction, &workingset); -+ eviction <<= bucket_order; - - rcu_read_lock(); - /* diff --git a/target/linux/generic/backport-5.15/020-v6.1-08-mm-multi-gen-LRU-support-page-table-walks.patch b/target/linux/generic/backport-5.15/020-v6.1-08-mm-multi-gen-LRU-support-page-table-walks.patch new file mode 100644 index 00000000000..b0df223b7c4 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-08-mm-multi-gen-LRU-support-page-table-walks.patch @@ -0,0 +1,1687 @@ +From 05223c4e80b34e29f2255c04ffebc2c4475e7593 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:05 -0600 +Subject: [PATCH 08/29] mm: multi-gen LRU: support page table walks +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +To further exploit spatial locality, the aging prefers to walk page tables +to search for young PTEs and promote hot pages. A kill switch will be +added in the next patch to disable this behavior. When disabled, the +aging relies on the rmap only. + +NB: this behavior has nothing similar with the page table scanning in the +2.4 kernel [1], which searches page tables for old PTEs, adds cold pages +to swapcache and unmaps them. + +To avoid confusion, the term "iteration" specifically means the traversal +of an entire mm_struct list; the term "walk" will be applied to page +tables and the rmap, as usual. + +An mm_struct list is maintained for each memcg, and an mm_struct follows +its owner task to the new memcg when this task is migrated. Given an +lruvec, the aging iterates lruvec_memcg()->mm_list and calls +walk_page_range() with each mm_struct on this list to promote hot pages +before it increments max_seq. + +When multiple page table walkers iterate the same list, each of them gets +a unique mm_struct; therefore they can run concurrently. Page table +walkers ignore any misplaced pages, e.g., if an mm_struct was migrated, +pages it left in the previous memcg will not be promoted when its current +memcg is under reclaim. Similarly, page table walkers will not promote +pages from nodes other than the one under reclaim. + +This patch uses the following optimizations when walking page tables: +1. It tracks the usage of mm_struct's between context switches so that + page table walkers can skip processes that have been sleeping since + the last iteration. +2. It uses generational Bloom filters to record populated branches so + that page table walkers can reduce their search space based on the + query results, e.g., to skip page tables containing mostly holes or + misplaced pages. +3. It takes advantage of the accessed bit in non-leaf PMD entries when + CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y. +4. It does not zigzag between a PGD table and the same PMD table + spanning multiple VMAs. IOW, it finishes all the VMAs within the + range of the same PMD table before it returns to a PGD table. This + improves the cache performance for workloads that have large + numbers of tiny VMAs [2], especially when CONFIG_PGTABLE_LEVELS=5. + +Server benchmark results: + Single workload: + fio (buffered I/O): no change + + Single workload: + memcached (anon): +[8, 10]% + Ops/sec KB/sec + patch1-7: 1147696.57 44640.29 + patch1-8: 1245274.91 48435.66 + + Configurations: + no change + +Client benchmark results: + kswapd profiles: + patch1-7 + 48.16% lzo1x_1_do_compress (real work) + 8.20% page_vma_mapped_walk (overhead) + 7.06% _raw_spin_unlock_irq + 2.92% ptep_clear_flush + 2.53% __zram_bvec_write + 2.11% do_raw_spin_lock + 2.02% memmove + 1.93% lru_gen_look_around + 1.56% free_unref_page_list + 1.40% memset + + patch1-8 + 49.44% lzo1x_1_do_compress (real work) + 6.19% page_vma_mapped_walk (overhead) + 5.97% _raw_spin_unlock_irq + 3.13% get_pfn_page + 2.85% ptep_clear_flush + 2.42% __zram_bvec_write + 2.08% do_raw_spin_lock + 1.92% memmove + 1.44% alloc_zspage + 1.36% memset + + Configurations: + no change + +Thanks to the following developers for their efforts [3]. + kernel test robot + +[1] https://lwn.net/Articles/23732/ +[2] https://llvm.org/docs/ScudoHardenedAllocator.html +[3] https://lore.kernel.org/r/202204160827.ekEARWQo-lkp@intel.com/ + +Link: https://lkml.kernel.org/r/20220918080010.2920238-9-yuzhao@google.com +Signed-off-by: Yu Zhao +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Barry Song +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + fs/exec.c | 2 + + include/linux/memcontrol.h | 5 + + include/linux/mm_types.h | 76 +++ + include/linux/mmzone.h | 56 +- + include/linux/swap.h | 4 + + kernel/exit.c | 1 + + kernel/fork.c | 9 + + kernel/sched/core.c | 1 + + mm/memcontrol.c | 25 + + mm/vmscan.c | 1010 +++++++++++++++++++++++++++++++++++- + 10 files changed, 1172 insertions(+), 17 deletions(-) + +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1013,6 +1013,7 @@ static int exec_mmap(struct mm_struct *m + active_mm = tsk->active_mm; + tsk->active_mm = mm; + tsk->mm = mm; ++ lru_gen_add_mm(mm); + /* + * This prevents preemption while active_mm is being loaded and + * it and mm are being updated, which could cause problems for +@@ -1028,6 +1029,7 @@ static int exec_mmap(struct mm_struct *m + tsk->mm->vmacache_seqnum = 0; + vmacache_flush(tsk); + task_unlock(tsk); ++ lru_gen_use_mm(mm); + if (old_mm) { + mmap_read_unlock(old_mm); + BUG_ON(active_mm != old_mm); +--- a/include/linux/memcontrol.h ++++ b/include/linux/memcontrol.h +@@ -348,6 +348,11 @@ struct mem_cgroup { + struct deferred_split deferred_split_queue; + #endif + ++#ifdef CONFIG_LRU_GEN ++ /* per-memcg mm_struct list */ ++ struct lru_gen_mm_list mm_list; ++#endif ++ + struct mem_cgroup_per_node *nodeinfo[]; + }; + +--- a/include/linux/mm_types.h ++++ b/include/linux/mm_types.h +@@ -580,6 +580,22 @@ struct mm_struct { + #ifdef CONFIG_IOMMU_SUPPORT + u32 pasid; + #endif ++#ifdef CONFIG_LRU_GEN ++ struct { ++ /* this mm_struct is on lru_gen_mm_list */ ++ struct list_head list; ++ /* ++ * Set when switching to this mm_struct, as a hint of ++ * whether it has been used since the last time per-node ++ * page table walkers cleared the corresponding bits. ++ */ ++ unsigned long bitmap; ++#ifdef CONFIG_MEMCG ++ /* points to the memcg of "owner" above */ ++ struct mem_cgroup *memcg; ++#endif ++ } lru_gen; ++#endif /* CONFIG_LRU_GEN */ + } __randomize_layout; + + /* +@@ -606,6 +622,66 @@ static inline cpumask_t *mm_cpumask(stru + return (struct cpumask *)&mm->cpu_bitmap; + } + ++#ifdef CONFIG_LRU_GEN ++ ++struct lru_gen_mm_list { ++ /* mm_struct list for page table walkers */ ++ struct list_head fifo; ++ /* protects the list above */ ++ spinlock_t lock; ++}; ++ ++void lru_gen_add_mm(struct mm_struct *mm); ++void lru_gen_del_mm(struct mm_struct *mm); ++#ifdef CONFIG_MEMCG ++void lru_gen_migrate_mm(struct mm_struct *mm); ++#endif ++ ++static inline void lru_gen_init_mm(struct mm_struct *mm) ++{ ++ INIT_LIST_HEAD(&mm->lru_gen.list); ++ mm->lru_gen.bitmap = 0; ++#ifdef CONFIG_MEMCG ++ mm->lru_gen.memcg = NULL; ++#endif ++} ++ ++static inline void lru_gen_use_mm(struct mm_struct *mm) ++{ ++ /* ++ * When the bitmap is set, page reclaim knows this mm_struct has been ++ * used since the last time it cleared the bitmap. So it might be worth ++ * walking the page tables of this mm_struct to clear the accessed bit. ++ */ ++ WRITE_ONCE(mm->lru_gen.bitmap, -1); ++} ++ ++#else /* !CONFIG_LRU_GEN */ ++ ++static inline void lru_gen_add_mm(struct mm_struct *mm) ++{ ++} ++ ++static inline void lru_gen_del_mm(struct mm_struct *mm) ++{ ++} ++ ++#ifdef CONFIG_MEMCG ++static inline void lru_gen_migrate_mm(struct mm_struct *mm) ++{ ++} ++#endif ++ ++static inline void lru_gen_init_mm(struct mm_struct *mm) ++{ ++} ++ ++static inline void lru_gen_use_mm(struct mm_struct *mm) ++{ ++} ++ ++#endif /* CONFIG_LRU_GEN */ ++ + struct mmu_gather; + extern void tlb_gather_mmu(struct mmu_gather *tlb, struct mm_struct *mm); + extern void tlb_gather_mmu_fullmm(struct mmu_gather *tlb, struct mm_struct *mm); +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -385,7 +385,7 @@ enum { + * min_seq behind. + * + * The number of pages in each generation is eventually consistent and therefore +- * can be transiently negative. ++ * can be transiently negative when reset_batch_size() is pending. + */ + struct lru_gen_struct { + /* the aging increments the youngest generation number */ +@@ -407,6 +407,53 @@ struct lru_gen_struct { + atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; + }; + ++enum { ++ MM_LEAF_TOTAL, /* total leaf entries */ ++ MM_LEAF_OLD, /* old leaf entries */ ++ MM_LEAF_YOUNG, /* young leaf entries */ ++ MM_NONLEAF_TOTAL, /* total non-leaf entries */ ++ MM_NONLEAF_FOUND, /* non-leaf entries found in Bloom filters */ ++ MM_NONLEAF_ADDED, /* non-leaf entries added to Bloom filters */ ++ NR_MM_STATS ++}; ++ ++/* double-buffering Bloom filters */ ++#define NR_BLOOM_FILTERS 2 ++ ++struct lru_gen_mm_state { ++ /* set to max_seq after each iteration */ ++ unsigned long seq; ++ /* where the current iteration continues (inclusive) */ ++ struct list_head *head; ++ /* where the last iteration ended (exclusive) */ ++ 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 { ++ /* the lruvec under reclaim */ ++ struct lruvec *lruvec; ++ /* unstable max_seq from lru_gen_struct */ ++ unsigned long max_seq; ++ /* the next address within an mm to scan */ ++ unsigned long next_addr; ++ /* to batch promoted pages */ ++ int nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++ /* to batch the mm stats */ ++ int mm_stats[NR_MM_STATS]; ++ /* total batched items */ ++ int batched; ++ bool can_swap; ++ bool force_scan; ++}; ++ + void lru_gen_init_lruvec(struct lruvec *lruvec); + void lru_gen_look_around(struct page_vma_mapped_walk *pvmw); + +@@ -457,6 +504,8 @@ struct lruvec { + #ifdef CONFIG_LRU_GEN + /* evictable pages divided into generations */ + struct lru_gen_struct lrugen; ++ /* to concurrently iterate lru_gen_mm_list */ ++ struct lru_gen_mm_state mm_state; + #endif + #ifdef CONFIG_MEMCG + struct pglist_data *pgdat; +@@ -1042,6 +1091,11 @@ typedef struct pglist_data { + + unsigned long flags; + ++#ifdef CONFIG_LRU_GEN ++ /* kswap mm walk data */ ++ struct lru_gen_mm_walk mm_walk; ++#endif ++ + ZONE_PADDING(_pad2_) + + /* Per-node vmstats */ +--- a/include/linux/swap.h ++++ b/include/linux/swap.h +@@ -137,6 +137,10 @@ union swap_header { + */ + struct reclaim_state { + unsigned long reclaimed_slab; ++#ifdef CONFIG_LRU_GEN ++ /* per-thread mm walk data */ ++ struct lru_gen_mm_walk *mm_walk; ++#endif + }; + + #ifdef __KERNEL__ +--- a/kernel/exit.c ++++ b/kernel/exit.c +@@ -469,6 +469,7 @@ assign_new_owner: + goto retry; + } + WRITE_ONCE(mm->owner, c); ++ lru_gen_migrate_mm(mm); + task_unlock(c); + put_task_struct(c); + } +--- a/kernel/fork.c ++++ b/kernel/fork.c +@@ -1083,6 +1083,7 @@ static struct mm_struct *mm_init(struct + goto fail_nocontext; + + mm->user_ns = get_user_ns(user_ns); ++ lru_gen_init_mm(mm); + return mm; + + fail_nocontext: +@@ -1125,6 +1126,7 @@ static inline void __mmput(struct mm_str + } + if (mm->binfmt) + module_put(mm->binfmt->module); ++ lru_gen_del_mm(mm); + mmdrop(mm); + } + +@@ -2622,6 +2624,13 @@ pid_t kernel_clone(struct kernel_clone_a + get_task_struct(p); + } + ++ if (IS_ENABLED(CONFIG_LRU_GEN) && !(clone_flags & CLONE_VM)) { ++ /* lock the task to synchronize with memcg migration */ ++ task_lock(p); ++ lru_gen_add_mm(p->mm); ++ task_unlock(p); ++ } ++ + wake_up_new_task(p); + + /* forking complete and child started to run, tell ptracer */ +--- a/kernel/sched/core.c ++++ b/kernel/sched/core.c +@@ -5007,6 +5007,7 @@ context_switch(struct rq *rq, struct tas + * finish_task_switch()'s mmdrop(). + */ + switch_mm_irqs_off(prev->active_mm, next->mm, next); ++ lru_gen_use_mm(next->mm); + + if (!prev->mm) { // from kernel + /* will mmdrop() in finish_task_switch(). */ +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -6212,6 +6212,30 @@ static void mem_cgroup_move_task(void) + } + #endif + ++#ifdef CONFIG_LRU_GEN ++static void mem_cgroup_attach(struct cgroup_taskset *tset) ++{ ++ struct task_struct *task; ++ struct cgroup_subsys_state *css; ++ ++ /* find the first leader if there is any */ ++ cgroup_taskset_for_each_leader(task, css, tset) ++ break; ++ ++ if (!task) ++ return; ++ ++ task_lock(task); ++ if (task->mm && READ_ONCE(task->mm->owner) == task) ++ lru_gen_migrate_mm(task->mm); ++ task_unlock(task); ++} ++#else ++static void mem_cgroup_attach(struct cgroup_taskset *tset) ++{ ++} ++#endif /* CONFIG_LRU_GEN */ ++ + static int seq_puts_memcg_tunable(struct seq_file *m, unsigned long value) + { + if (value == PAGE_COUNTER_MAX) +@@ -6555,6 +6579,7 @@ struct cgroup_subsys memory_cgrp_subsys + .css_reset = mem_cgroup_css_reset, + .css_rstat_flush = mem_cgroup_css_rstat_flush, + .can_attach = mem_cgroup_can_attach, ++ .attach = mem_cgroup_attach, + .cancel_attach = mem_cgroup_cancel_attach, + .post_attach = mem_cgroup_move_task, + .dfl_cftypes = memory_files, +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -50,6 +50,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -2853,7 +2855,7 @@ static bool can_age_anon_pages(struct pg + for ((type) = 0; (type) < ANON_AND_FILE; (type)++) \ + for ((zone) = 0; (zone) < MAX_NR_ZONES; (zone)++) + +-static struct lruvec __maybe_unused *get_lruvec(struct mem_cgroup *memcg, int nid) ++static struct lruvec *get_lruvec(struct mem_cgroup *memcg, int nid) + { + struct pglist_data *pgdat = NODE_DATA(nid); + +@@ -2899,6 +2901,371 @@ static bool __maybe_unused seq_is_valid( + } + + /****************************************************************************** ++ * mm_struct list ++ ******************************************************************************/ ++ ++static struct lru_gen_mm_list *get_mm_list(struct mem_cgroup *memcg) ++{ ++ static struct lru_gen_mm_list mm_list = { ++ .fifo = LIST_HEAD_INIT(mm_list.fifo), ++ .lock = __SPIN_LOCK_UNLOCKED(mm_list.lock), ++ }; ++ ++#ifdef CONFIG_MEMCG ++ if (memcg) ++ return &memcg->mm_list; ++#endif ++ VM_WARN_ON_ONCE(!mem_cgroup_disabled()); ++ ++ return &mm_list; ++} ++ ++void lru_gen_add_mm(struct mm_struct *mm) ++{ ++ int nid; ++ struct mem_cgroup *memcg = get_mem_cgroup_from_mm(mm); ++ struct lru_gen_mm_list *mm_list = get_mm_list(memcg); ++ ++ VM_WARN_ON_ONCE(!list_empty(&mm->lru_gen.list)); ++#ifdef CONFIG_MEMCG ++ VM_WARN_ON_ONCE(mm->lru_gen.memcg); ++ mm->lru_gen.memcg = memcg; ++#endif ++ spin_lock(&mm_list->lock); ++ ++ for_each_node_state(nid, N_MEMORY) { ++ struct lruvec *lruvec = get_lruvec(memcg, nid); ++ ++ if (!lruvec) ++ continue; ++ ++ /* the first addition since the last iteration */ ++ if (lruvec->mm_state.tail == &mm_list->fifo) ++ lruvec->mm_state.tail = &mm->lru_gen.list; ++ } ++ ++ list_add_tail(&mm->lru_gen.list, &mm_list->fifo); ++ ++ spin_unlock(&mm_list->lock); ++} ++ ++void lru_gen_del_mm(struct mm_struct *mm) ++{ ++ int nid; ++ struct lru_gen_mm_list *mm_list; ++ struct mem_cgroup *memcg = NULL; ++ ++ if (list_empty(&mm->lru_gen.list)) ++ return; ++ ++#ifdef CONFIG_MEMCG ++ memcg = mm->lru_gen.memcg; ++#endif ++ mm_list = get_mm_list(memcg); ++ ++ spin_lock(&mm_list->lock); ++ ++ for_each_node(nid) { ++ struct lruvec *lruvec = get_lruvec(memcg, nid); ++ ++ if (!lruvec) ++ continue; ++ ++ /* where the last iteration ended (exclusive) */ ++ 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); ++ ++ spin_unlock(&mm_list->lock); ++ ++#ifdef CONFIG_MEMCG ++ mem_cgroup_put(mm->lru_gen.memcg); ++ mm->lru_gen.memcg = NULL; ++#endif ++} ++ ++#ifdef CONFIG_MEMCG ++void lru_gen_migrate_mm(struct mm_struct *mm) ++{ ++ struct mem_cgroup *memcg; ++ struct task_struct *task = rcu_dereference_protected(mm->owner, true); ++ ++ VM_WARN_ON_ONCE(task->mm != mm); ++ lockdep_assert_held(&task->alloc_lock); ++ ++ /* for mm_update_next_owner() */ ++ if (mem_cgroup_disabled()) ++ return; ++ ++ rcu_read_lock(); ++ memcg = mem_cgroup_from_task(task); ++ rcu_read_unlock(); ++ if (memcg == mm->lru_gen.memcg) ++ return; ++ ++ VM_WARN_ON_ONCE(!mm->lru_gen.memcg); ++ VM_WARN_ON_ONCE(list_empty(&mm->lru_gen.list)); ++ ++ lru_gen_del_mm(mm); ++ lru_gen_add_mm(mm); ++} ++#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; ++ int hist; ++ ++ lockdep_assert_held(&get_mm_list(lruvec_memcg(lruvec))->lock); ++ ++ if (walk) { ++ hist = lru_hist_from_seq(walk->max_seq); ++ ++ for (i = 0; i < NR_MM_STATS; i++) { ++ WRITE_ONCE(lruvec->mm_state.stats[hist][i], ++ lruvec->mm_state.stats[hist][i] + walk->mm_stats[i]); ++ walk->mm_stats[i] = 0; ++ } ++ } ++ ++ if (NR_HIST_GENS > 1 && last) { ++ hist = lru_hist_from_seq(lruvec->mm_state.seq + 1); ++ ++ for (i = 0; i < NR_MM_STATS; i++) ++ WRITE_ONCE(lruvec->mm_state.stats[hist][i], 0); ++ } ++} ++ ++static bool should_skip_mm(struct mm_struct *mm, struct lru_gen_mm_walk *walk) ++{ ++ int type; ++ unsigned long size = 0; ++ struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec); ++ int key = pgdat->node_id % BITS_PER_TYPE(mm->lru_gen.bitmap); ++ ++ if (!walk->force_scan && !test_bit(key, &mm->lru_gen.bitmap)) ++ return true; ++ ++ clear_bit(key, &mm->lru_gen.bitmap); ++ ++ for (type = !walk->can_swap; type < ANON_AND_FILE; type++) { ++ size += type ? get_mm_counter(mm, MM_FILEPAGES) : ++ get_mm_counter(mm, MM_ANONPAGES) + ++ get_mm_counter(mm, MM_SHMEMPAGES); ++ } ++ ++ if (size < MIN_LRU_BATCH) ++ return true; ++ ++ return !mmget_not_zero(mm); ++} ++ ++static bool iterate_mm_list(struct lruvec *lruvec, struct lru_gen_mm_walk *walk, ++ struct mm_struct **iter) ++{ ++ bool first = false; ++ bool last = true; ++ 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. ++ */ ++ 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; ++ goto done; ++ } ++ ++ if (!mm_state->nr_walkers) { ++ VM_WARN_ON_ONCE(mm_state->head && mm_state->head != &mm_list->fifo); ++ ++ mm_state->head = mm_list->fifo.next; ++ first = true; ++ } ++ ++ while (!mm && mm_state->head != &mm_list->fifo) { ++ mm = list_entry(mm_state->head, struct mm_struct, lru_gen.list); ++ ++ mm_state->head = mm_state->head->next; ++ ++ /* 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; ++ walk->force_scan = true; ++ } ++ ++ if (should_skip_mm(mm, walk)) ++ mm = NULL; ++ } ++ ++ if (mm_state->head == &mm_list->fifo) ++ WRITE_ONCE(mm_state->seq, mm_state->seq + 1); ++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); ++ ++ spin_unlock(&mm_list->lock); ++ ++ if (mm && first) ++ reset_bloom_filter(lruvec, walk->max_seq + 1); ++ ++ if (*iter) ++ mmput_async(*iter); ++ ++ *iter = mm; ++ ++ return last; ++} ++ ++static bool iterate_mm_list_nowalk(struct lruvec *lruvec, unsigned long max_seq) ++{ ++ bool success = false; ++ 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; ++ ++ spin_lock(&mm_list->lock); ++ ++ 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); ++ ++ WRITE_ONCE(mm_state->seq, mm_state->seq + 1); ++ reset_mm_stats(lruvec, NULL, true); ++ success = true; ++ } ++ ++ spin_unlock(&mm_list->lock); ++ ++ return success; ++} ++ ++/****************************************************************************** + * refault feedback loop + ******************************************************************************/ + +@@ -3048,6 +3415,118 @@ static int page_inc_gen(struct lruvec *l + return new_gen; + } + ++static void update_batch_size(struct lru_gen_mm_walk *walk, struct page *page, ++ int old_gen, int new_gen) ++{ ++ int type = page_is_file_lru(page); ++ int zone = page_zonenum(page); ++ int delta = thp_nr_pages(page); ++ ++ VM_WARN_ON_ONCE(old_gen >= MAX_NR_GENS); ++ VM_WARN_ON_ONCE(new_gen >= MAX_NR_GENS); ++ ++ walk->batched++; ++ ++ walk->nr_pages[old_gen][type][zone] -= delta; ++ walk->nr_pages[new_gen][type][zone] += delta; ++} ++ ++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; ++ ++ walk->batched = 0; ++ ++ for_each_gen_type_zone(gen, type, zone) { ++ enum lru_list lru = type * LRU_INACTIVE_FILE; ++ int delta = walk->nr_pages[gen][type][zone]; ++ ++ if (!delta) ++ continue; ++ ++ walk->nr_pages[gen][type][zone] = 0; ++ WRITE_ONCE(lrugen->nr_pages[gen][type][zone], ++ lrugen->nr_pages[gen][type][zone] + delta); ++ ++ if (lru_gen_is_active(lruvec, gen)) ++ lru += LRU_ACTIVE; ++ __update_lru_size(lruvec, lru, zone, delta); ++ } ++} ++ ++static int should_skip_vma(unsigned long start, unsigned long end, struct mm_walk *args) ++{ ++ struct address_space *mapping; ++ struct vm_area_struct *vma = args->vma; ++ struct lru_gen_mm_walk *walk = args->private; ++ ++ if (!vma_is_accessible(vma)) ++ return true; ++ ++ if (is_vm_hugetlb_page(vma)) ++ return true; ++ ++ if (vma->vm_flags & (VM_LOCKED | VM_SPECIAL | VM_SEQ_READ | VM_RAND_READ)) ++ return true; ++ ++ if (vma == get_gate_vma(vma->vm_mm)) ++ return true; ++ ++ if (vma_is_anonymous(vma)) ++ return !walk->can_swap; ++ ++ if (WARN_ON_ONCE(!vma->vm_file || !vma->vm_file->f_mapping)) ++ return true; ++ ++ mapping = vma->vm_file->f_mapping; ++ if (mapping_unevictable(mapping)) ++ return true; ++ ++ if (shmem_mapping(mapping)) ++ return !walk->can_swap; ++ ++ /* to exclude special mappings like dax, etc. */ ++ return !mapping->a_ops->readpage; ++} ++ ++/* ++ * Some userspace memory allocators map many single-page VMAs. Instead of ++ * returning back to the PGD table for each of such VMAs, finish an entire PMD ++ * table to reduce zigzags and improve cache performance. ++ */ ++static bool get_next_vma(unsigned long mask, unsigned long size, struct mm_walk *args, ++ unsigned long *vm_start, unsigned long *vm_end) ++{ ++ unsigned long start = round_up(*vm_end, size); ++ unsigned long end = (start | ~mask) + 1; ++ ++ VM_WARN_ON_ONCE(mask & size); ++ VM_WARN_ON_ONCE((start & mask) != (*vm_start & mask)); ++ ++ while (args->vma) { ++ if (start >= args->vma->vm_end) { ++ args->vma = args->vma->vm_next; ++ continue; ++ } ++ ++ if (end && end <= args->vma->vm_start) ++ return false; ++ ++ if (should_skip_vma(args->vma->vm_start, args->vma->vm_end, args)) { ++ args->vma = args->vma->vm_next; ++ continue; ++ } ++ ++ *vm_start = max(start, args->vma->vm_start); ++ *vm_end = min(end - 1, args->vma->vm_end - 1) + 1; ++ ++ return true; ++ } ++ ++ return false; ++} ++ + static unsigned long get_pte_pfn(pte_t pte, struct vm_area_struct *vma, unsigned long addr) + { + unsigned long pfn = pte_pfn(pte); +@@ -3066,8 +3545,28 @@ static unsigned long get_pte_pfn(pte_t p + return pfn; + } + ++#if defined(CONFIG_TRANSPARENT_HUGEPAGE) || defined(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) ++static unsigned long get_pmd_pfn(pmd_t pmd, struct vm_area_struct *vma, unsigned long addr) ++{ ++ unsigned long pfn = pmd_pfn(pmd); ++ ++ VM_WARN_ON_ONCE(addr < vma->vm_start || addr >= vma->vm_end); ++ ++ if (!pmd_present(pmd) || is_huge_zero_pmd(pmd)) ++ return -1; ++ ++ if (WARN_ON_ONCE(pmd_devmap(pmd))) ++ return -1; ++ ++ if (WARN_ON_ONCE(!pfn_valid(pfn))) ++ return -1; ++ ++ return pfn; ++} ++#endif ++ + static struct page *get_pfn_page(unsigned long pfn, struct mem_cgroup *memcg, +- struct pglist_data *pgdat) ++ struct pglist_data *pgdat, bool can_swap) + { + struct page *page; + +@@ -3082,9 +3581,375 @@ static struct page *get_pfn_page(unsigne + if (page_memcg_rcu(page) != memcg) + return NULL; + ++ /* file VMAs can contain anon pages from COW */ ++ if (!page_is_file_lru(page) && !can_swap) ++ return NULL; ++ + return page; + } + ++static bool suitable_to_scan(int total, int young) ++{ ++ int n = clamp_t(int, cache_line_size() / sizeof(pte_t), 2, 8); ++ ++ /* suitable if the average number of young PTEs per cacheline is >=1 */ ++ return young * n >= total; ++} ++ ++static bool walk_pte_range(pmd_t *pmd, unsigned long start, unsigned long end, ++ struct mm_walk *args) ++{ ++ int i; ++ pte_t *pte; ++ spinlock_t *ptl; ++ unsigned long addr; ++ int total = 0; ++ int young = 0; ++ struct lru_gen_mm_walk *walk = args->private; ++ struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec); ++ struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec); ++ int old_gen, new_gen = lru_gen_from_seq(walk->max_seq); ++ ++ VM_WARN_ON_ONCE(pmd_leaf(*pmd)); ++ ++ ptl = pte_lockptr(args->mm, pmd); ++ if (!spin_trylock(ptl)) ++ return false; ++ ++ arch_enter_lazy_mmu_mode(); ++ ++ pte = pte_offset_map(pmd, start & PMD_MASK); ++restart: ++ for (i = pte_index(start), addr = start; addr != end; i++, addr += PAGE_SIZE) { ++ unsigned long pfn; ++ struct page *page; ++ ++ total++; ++ walk->mm_stats[MM_LEAF_TOTAL]++; ++ ++ pfn = get_pte_pfn(pte[i], args->vma, addr); ++ if (pfn == -1) ++ continue; ++ ++ if (!pte_young(pte[i])) { ++ walk->mm_stats[MM_LEAF_OLD]++; ++ continue; ++ } ++ ++ page = get_pfn_page(pfn, memcg, pgdat, walk->can_swap); ++ if (!page) ++ continue; ++ ++ if (!ptep_test_and_clear_young(args->vma, addr, pte + i)) ++ VM_WARN_ON_ONCE(true); ++ ++ young++; ++ walk->mm_stats[MM_LEAF_YOUNG]++; ++ ++ if (pte_dirty(pte[i]) && !PageDirty(page) && ++ !(PageAnon(page) && PageSwapBacked(page) && ++ !PageSwapCache(page))) ++ set_page_dirty(page); ++ ++ old_gen = page_update_gen(page, new_gen); ++ if (old_gen >= 0 && old_gen != new_gen) ++ update_batch_size(walk, page, old_gen, new_gen); ++ } ++ ++ if (i < PTRS_PER_PTE && get_next_vma(PMD_MASK, PAGE_SIZE, args, &start, &end)) ++ goto restart; ++ ++ pte_unmap(pte); ++ ++ arch_leave_lazy_mmu_mode(); ++ spin_unlock(ptl); ++ ++ return suitable_to_scan(total, young); ++} ++ ++#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) ++{ ++ int i; ++ pmd_t *pmd; ++ spinlock_t *ptl; ++ struct lru_gen_mm_walk *walk = args->private; ++ struct mem_cgroup *memcg = lruvec_memcg(walk->lruvec); ++ struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec); ++ int old_gen, new_gen = lru_gen_from_seq(walk->max_seq); ++ ++ VM_WARN_ON_ONCE(pud_leaf(*pud)); ++ ++ /* try to batch at most 1+MIN_LRU_BATCH+1 entries */ ++ if (*start == -1) { ++ *start = next; ++ return; ++ } ++ ++ i = next == -1 ? 0 : pmd_index(next) - pmd_index(*start); ++ if (i && i <= MIN_LRU_BATCH) { ++ __set_bit(i - 1, bitmap); ++ return; ++ } ++ ++ pmd = pmd_offset(pud, *start); ++ ++ ptl = pmd_lockptr(args->mm, pmd); ++ if (!spin_trylock(ptl)) ++ goto done; ++ ++ arch_enter_lazy_mmu_mode(); ++ ++ do { ++ unsigned long pfn; ++ struct page *page; ++ unsigned long addr = i ? (*start & PMD_MASK) + i * PMD_SIZE : *start; ++ ++ pfn = get_pmd_pfn(pmd[i], vma, addr); ++ if (pfn == -1) ++ goto next; ++ ++ if (!pmd_trans_huge(pmd[i])) { ++ if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)) ++ pmdp_test_and_clear_young(vma, addr, pmd + i); ++ goto next; ++ } ++ ++ page = get_pfn_page(pfn, memcg, pgdat, walk->can_swap); ++ if (!page) ++ goto next; ++ ++ if (!pmdp_test_and_clear_young(vma, addr, pmd + i)) ++ goto next; ++ ++ walk->mm_stats[MM_LEAF_YOUNG]++; ++ ++ if (pmd_dirty(pmd[i]) && !PageDirty(page) && ++ !(PageAnon(page) && PageSwapBacked(page) && ++ !PageSwapCache(page))) ++ set_page_dirty(page); ++ ++ old_gen = page_update_gen(page, new_gen); ++ if (old_gen >= 0 && old_gen != new_gen) ++ update_batch_size(walk, page, old_gen, new_gen); ++next: ++ i = i > MIN_LRU_BATCH ? 0 : find_next_bit(bitmap, MIN_LRU_BATCH, i) + 1; ++ } while (i <= MIN_LRU_BATCH); ++ ++ arch_leave_lazy_mmu_mode(); ++ spin_unlock(ptl); ++done: ++ *start = -1; ++ bitmap_zero(bitmap, MIN_LRU_BATCH); ++} ++#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) ++{ ++} ++#endif ++ ++static void walk_pmd_range(pud_t *pud, unsigned long start, unsigned long end, ++ struct mm_walk *args) ++{ ++ int i; ++ pmd_t *pmd; ++ unsigned long next; ++ unsigned long addr; ++ struct vm_area_struct *vma; ++ unsigned long pos = -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)); ++ ++ /* ++ * Finish an entire PMD in two passes: the first only reaches to PTE ++ * tables to avoid taking the PMD lock; the second, if necessary, takes ++ * the PMD lock to clear the accessed bit in PMD entries. ++ */ ++ pmd = pmd_offset(pud, start & PUD_MASK); ++restart: ++ /* walk_pte_range() may call get_next_vma() */ ++ vma = args->vma; ++ for (i = pmd_index(start), addr = start; addr != end; i++, addr = next) { ++ pmd_t val = pmd_read_atomic(pmd + i); ++ ++ /* for pmd_read_atomic() */ ++ barrier(); ++ ++ next = pmd_addr_end(addr, end); ++ ++ if (!pmd_present(val) || is_huge_zero_pmd(val)) { ++ walk->mm_stats[MM_LEAF_TOTAL]++; ++ continue; ++ } ++ ++#ifdef CONFIG_TRANSPARENT_HUGEPAGE ++ if (pmd_trans_huge(val)) { ++ unsigned long pfn = pmd_pfn(val); ++ struct pglist_data *pgdat = lruvec_pgdat(walk->lruvec); ++ ++ walk->mm_stats[MM_LEAF_TOTAL]++; ++ ++ if (!pmd_young(val)) { ++ walk->mm_stats[MM_LEAF_OLD]++; ++ continue; ++ } ++ ++ /* try to avoid unnecessary memory loads */ ++ if (pfn < pgdat->node_start_pfn || pfn >= pgdat_end_pfn(pgdat)) ++ continue; ++ ++ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos); ++ continue; ++ } ++#endif ++ walk->mm_stats[MM_NONLEAF_TOTAL]++; ++ ++#ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG ++ if (!pmd_young(val)) ++ continue; ++ ++ walk_pmd_range_locked(pud, addr, vma, args, bitmap, &pos); ++#endif ++ if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i)) ++ continue; ++ ++ walk->mm_stats[MM_NONLEAF_FOUND]++; ++ ++ if (!walk_pte_range(&val, addr, next, args)) ++ continue; ++ ++ walk->mm_stats[MM_NONLEAF_ADDED]++; ++ ++ /* carry over to the next generation */ ++ update_bloom_filter(walk->lruvec, walk->max_seq + 1, pmd + i); ++ } ++ ++ walk_pmd_range_locked(pud, -1, vma, args, bitmap, &pos); ++ ++ if (i < PTRS_PER_PMD && get_next_vma(PUD_MASK, PMD_SIZE, args, &start, &end)) ++ goto restart; ++} ++ ++static int walk_pud_range(p4d_t *p4d, unsigned long start, unsigned long end, ++ struct mm_walk *args) ++{ ++ int i; ++ pud_t *pud; ++ unsigned long addr; ++ unsigned long next; ++ struct lru_gen_mm_walk *walk = args->private; ++ ++ VM_WARN_ON_ONCE(p4d_leaf(*p4d)); ++ ++ pud = pud_offset(p4d, start & P4D_MASK); ++restart: ++ for (i = pud_index(start), addr = start; addr != end; i++, addr = next) { ++ pud_t val = READ_ONCE(pud[i]); ++ ++ next = pud_addr_end(addr, end); ++ ++ if (!pud_present(val) || WARN_ON_ONCE(pud_leaf(val))) ++ continue; ++ ++ 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; ++ } ++ } ++ ++ if (i < PTRS_PER_PUD && get_next_vma(P4D_MASK, PUD_SIZE, args, &start, &end)) ++ goto restart; ++ ++ end = round_up(end, P4D_SIZE); ++done: ++ if (!end || !args->vma) ++ return 1; ++ ++ walk->next_addr = max(end, args->vma->vm_start); ++ ++ return -EAGAIN; ++} ++ ++static void walk_mm(struct lruvec *lruvec, struct mm_struct *mm, struct lru_gen_mm_walk *walk) ++{ ++ static const struct mm_walk_ops mm_walk_ops = { ++ .test_walk = should_skip_vma, ++ .p4d_entry = walk_pud_range, ++ }; ++ ++ int err; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ ++ walk->next_addr = FIRST_USER_ADDRESS; ++ ++ do { ++ err = -EBUSY; ++ ++ /* page_update_gen() requires stable page_memcg() */ ++ if (!mem_cgroup_trylock_pages(memcg)) ++ break; ++ ++ /* the caller might be holding the lock for write */ ++ if (mmap_read_trylock(mm)) { ++ err = walk_page_range(mm, walk->next_addr, ULONG_MAX, &mm_walk_ops, walk); ++ ++ mmap_read_unlock(mm); ++ } ++ ++ mem_cgroup_unlock_pages(); ++ ++ if (walk->batched) { ++ spin_lock_irq(&lruvec->lru_lock); ++ reset_batch_size(lruvec, walk); ++ spin_unlock_irq(&lruvec->lru_lock); ++ } ++ ++ cond_resched(); ++ } while (err == -EAGAIN); ++} ++ ++static struct lru_gen_mm_walk *set_mm_walk(struct pglist_data *pgdat) ++{ ++ struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk; ++ ++ if (pgdat && current_is_kswapd()) { ++ VM_WARN_ON_ONCE(walk); ++ ++ walk = &pgdat->mm_walk; ++ } else if (!pgdat && !walk) { ++ VM_WARN_ON_ONCE(current_is_kswapd()); ++ ++ walk = kzalloc(sizeof(*walk), __GFP_HIGH | __GFP_NOMEMALLOC | __GFP_NOWARN); ++ } ++ ++ current->reclaim_state->mm_walk = walk; ++ ++ return walk; ++} ++ ++static void clear_mm_walk(void) ++{ ++ struct lru_gen_mm_walk *walk = current->reclaim_state->mm_walk; ++ ++ VM_WARN_ON_ONCE(walk && memchr_inv(walk->nr_pages, 0, sizeof(walk->nr_pages))); ++ VM_WARN_ON_ONCE(walk && memchr_inv(walk->mm_stats, 0, sizeof(walk->mm_stats))); ++ ++ current->reclaim_state->mm_walk = NULL; ++ ++ if (!current_is_kswapd()) ++ kfree(walk); ++} ++ + static void inc_min_seq(struct lruvec *lruvec, int type) + { + struct lru_gen_struct *lrugen = &lruvec->lrugen; +@@ -3136,7 +4001,7 @@ next: + return success; + } + +-static void inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, bool can_swap) ++static void inc_max_seq(struct lruvec *lruvec, bool can_swap) + { + int prev, next; + int type, zone; +@@ -3146,9 +4011,6 @@ static void inc_max_seq(struct lruvec *l + + VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); + +- if (max_seq != lrugen->max_seq) +- goto unlock; +- + for (type = ANON_AND_FILE - 1; type >= 0; type--) { + if (get_nr_gens(lruvec, type) != MAX_NR_GENS) + continue; +@@ -3186,10 +4048,76 @@ static void inc_max_seq(struct lruvec *l + + /* make sure preceding modifications appear */ + smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); +-unlock: ++ + spin_unlock_irq(&lruvec->lru_lock); + } + ++static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, ++ struct scan_control *sc, bool can_swap) ++{ ++ bool success; ++ struct lru_gen_mm_walk *walk; ++ struct mm_struct *mm = NULL; ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ ++ VM_WARN_ON_ONCE(max_seq > READ_ONCE(lrugen->max_seq)); ++ ++ /* see the comment in iterate_mm_list() */ ++ if (max_seq <= READ_ONCE(lruvec->mm_state.seq)) { ++ success = false; ++ goto done; ++ } ++ ++ /* ++ * If the hardware doesn't automatically set the accessed bit, fallback ++ * to lru_gen_look_around(), which only clears the accessed bit in a ++ * handful of PTEs. Spreading the work out over a period of time usually ++ * is less efficient, but it avoids bursty page faults. ++ */ ++ if (!arch_has_hw_pte_young()) { ++ success = iterate_mm_list_nowalk(lruvec, max_seq); ++ goto done; ++ } ++ ++ walk = set_mm_walk(NULL); ++ if (!walk) { ++ success = iterate_mm_list_nowalk(lruvec, max_seq); ++ goto done; ++ } ++ ++ walk->lruvec = lruvec; ++ walk->max_seq = max_seq; ++ walk->can_swap = can_swap; ++ walk->force_scan = false; ++ ++ do { ++ 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 max_seq < READ_ONCE(lrugen->max_seq); ++ } ++ ++ VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq)); ++ ++ inc_max_seq(lruvec, can_swap); ++ /* 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); ++ ++ wakeup_flusher_threads(WB_REASON_VMSCAN); ++ ++ return true; ++} ++ + static bool should_run_aging(struct lruvec *lruvec, unsigned long max_seq, unsigned long *min_seq, + struct scan_control *sc, bool can_swap, unsigned long *nr_to_scan) + { +@@ -3265,7 +4193,7 @@ static void age_lruvec(struct lruvec *lr + + need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan); + if (need_aging) +- inc_max_seq(lruvec, max_seq, swappiness); ++ try_to_inc_max_seq(lruvec, max_seq, sc, swappiness); + } + + static void lru_gen_age_node(struct pglist_data *pgdat, struct scan_control *sc) +@@ -3274,6 +4202,8 @@ static void lru_gen_age_node(struct pgli + + VM_WARN_ON_ONCE(!current_is_kswapd()); + ++ set_mm_walk(pgdat); ++ + memcg = mem_cgroup_iter(NULL, NULL, NULL); + do { + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); +@@ -3282,11 +4212,16 @@ static void lru_gen_age_node(struct pgli + + cond_resched(); + } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); ++ ++ clear_mm_walk(); + } + + /* + * This function exploits spatial locality when shrink_page_list() walks the +- * rmap. It scans the adjacent PTEs of a young PTE and promotes hot pages. ++ * 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. + */ + void lru_gen_look_around(struct page_vma_mapped_walk *pvmw) + { +@@ -3295,6 +4230,8 @@ void lru_gen_look_around(struct page_vma + 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)] = {}; + struct page *page = pvmw->page; + struct mem_cgroup *memcg = page_memcg(page); +@@ -3309,6 +4246,9 @@ void lru_gen_look_around(struct page_vma + if (spin_is_contended(pvmw->ptl)) + return; + ++ /* 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; + +@@ -3338,13 +4278,15 @@ void lru_gen_look_around(struct page_vma + if (!pte_young(pte[i])) + continue; + +- page = get_pfn_page(pfn, memcg, pgdat); ++ page = get_pfn_page(pfn, memcg, pgdat, !walk || walk->can_swap); + if (!page) + continue; + + if (!ptep_test_and_clear_young(pvmw->vma, addr, pte + i)) + VM_WARN_ON_ONCE(true); + ++ young++; ++ + if (pte_dirty(pte[i]) && !PageDirty(page) && + !(PageAnon(page) && PageSwapBacked(page) && + !PageSwapCache(page))) +@@ -3360,7 +4302,11 @@ void lru_gen_look_around(struct page_vma + arch_leave_lazy_mmu_mode(); + rcu_read_unlock(); + +- if (bitmap_weight(bitmap, MIN_LRU_BATCH) < PAGEVEC_SIZE) { ++ /* 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) { + page = pte_page(pte[i]); + activate_page(page); +@@ -3372,8 +4318,10 @@ void lru_gen_look_around(struct page_vma + if (!mem_cgroup_trylock_pages(memcg)) + return; + +- spin_lock_irq(&lruvec->lru_lock); +- new_gen = lru_gen_from_seq(lruvec->lrugen.max_seq); ++ 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) { + page = compound_head(pte_page(pte[i])); +@@ -3384,10 +4332,14 @@ void lru_gen_look_around(struct page_vma + if (old_gen < 0 || old_gen == new_gen) + continue; + +- lru_gen_update_size(lruvec, page, old_gen, new_gen); ++ if (walk) ++ update_batch_size(walk, page, old_gen, new_gen); ++ else ++ lru_gen_update_size(lruvec, page, old_gen, new_gen); + } + +- spin_unlock_irq(&lruvec->lru_lock); ++ if (!walk) ++ spin_unlock_irq(&lruvec->lru_lock); + + mem_cgroup_unlock_pages(); + } +@@ -3670,6 +4622,7 @@ static int evict_pages(struct lruvec *lr + struct page *page; + enum vm_event_item item; + struct reclaim_stat stat; ++ struct lru_gen_mm_walk *walk; + struct mem_cgroup *memcg = lruvec_memcg(lruvec); + struct pglist_data *pgdat = lruvec_pgdat(lruvec); + +@@ -3706,6 +4659,10 @@ static int evict_pages(struct lruvec *lr + + move_pages_to_lru(lruvec, &list); + ++ walk = current->reclaim_state->mm_walk; ++ if (walk && walk->batched) ++ reset_batch_size(lruvec, walk); ++ + item = current_is_kswapd() ? PGSTEAL_KSWAPD : PGSTEAL_DIRECT; + if (!cgroup_reclaim(sc)) + __count_vm_events(item, reclaimed); +@@ -3722,6 +4679,11 @@ static int evict_pages(struct lruvec *lr + return scanned; + } + ++/* ++ * For future optimizations: ++ * 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) + { +@@ -3747,7 +4709,8 @@ static unsigned long get_nr_to_scan(stru + if (current_is_kswapd()) + return 0; + +- inc_max_seq(lruvec, max_seq, can_swap); ++ if (try_to_inc_max_seq(lruvec, max_seq, sc, can_swap)) ++ return nr_to_scan; + done: + return min_seq[!can_swap] + MIN_NR_GENS <= max_seq ? nr_to_scan : 0; + } +@@ -3761,6 +4724,8 @@ static void lru_gen_shrink_lruvec(struct + + blk_start_plug(&plug); + ++ set_mm_walk(lruvec_pgdat(lruvec)); ++ + while (true) { + int delta; + int swappiness; +@@ -3788,6 +4753,8 @@ static void lru_gen_shrink_lruvec(struct + cond_resched(); + } + ++ clear_mm_walk(); ++ + blk_finish_plug(&plug); + } + +@@ -3804,15 +4771,21 @@ void lru_gen_init_lruvec(struct lruvec * + + for_each_gen_type_zone(gen, type, zone) + INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); ++ ++ lruvec->mm_state.seq = MIN_NR_GENS; ++ init_waitqueue_head(&lruvec->mm_state.wait); + } + + #ifdef CONFIG_MEMCG + void lru_gen_init_memcg(struct mem_cgroup *memcg) + { ++ INIT_LIST_HEAD(&memcg->mm_list.fifo); ++ spin_lock_init(&memcg->mm_list.lock); + } + + void lru_gen_exit_memcg(struct mem_cgroup *memcg) + { ++ int i; + int nid; + + for_each_node(nid) { +@@ -3820,6 +4793,11 @@ void lru_gen_exit_memcg(struct mem_cgrou + + VM_WARN_ON_ONCE(memchr_inv(lruvec->lrugen.nr_pages, 0, + sizeof(lruvec->lrugen.nr_pages))); ++ ++ for (i = 0; i < NR_BLOOM_FILTERS; i++) { ++ bitmap_free(lruvec->mm_state.filters[i]); ++ lruvec->mm_state.filters[i] = NULL; ++ } + } + } + #endif diff --git a/target/linux/generic/backport-5.15/020-v6.1-08-mm-multigenerational-lru-user-interface.patch b/target/linux/generic/backport-5.15/020-v6.1-08-mm-multigenerational-lru-user-interface.patch deleted file mode 100644 index f0753ea8028..00000000000 --- a/target/linux/generic/backport-5.15/020-v6.1-08-mm-multigenerational-lru-user-interface.patch +++ /dev/null @@ -1,496 +0,0 @@ -From 5cc7fdec54e87e32b4fb0f07d84b21769d5f8d92 Mon Sep 17 00:00:00 2001 -From: Yu Zhao -Date: Mon, 25 Jan 2021 21:38:02 -0700 -Subject: [PATCH 08/10] mm: multigenerational lru: user interface - -Add /sys/kernel/mm/lru_gen/enabled to enable and disable the -multigenerational lru at runtime. - -Add /sys/kernel/mm/lru_gen/min_ttl_ms to protect the working set of a -given number of milliseconds. The OOM killer is invoked if this -working set cannot be kept in memory. - -Add /sys/kernel/debug/lru_gen to monitor the multigenerational lru and -invoke the aging and the eviction. This file has the following output: - memcg memcg_id memcg_path - node node_id - min_gen birth_time anon_size file_size - ... - max_gen birth_time anon_size file_size - -min_gen is the oldest generation number and max_gen is the youngest -generation number. birth_time is in milliseconds. anon_size and -file_size are in pages. - -This file takes the following input: - + memcg_id node_id max_gen [swappiness] [use_bloom_filter] - - memcg_id node_id min_gen [swappiness] [nr_to_reclaim] - -The first command line invokes the aging, which scans PTEs for -accessed pages and then creates the next generation max_gen+1. A swap -file and a non-zero swappiness, which overrides vm.swappiness, are -required to scan PTEs mapping anon pages. The second command line -invokes the eviction, which evicts generations less than or equal to -min_gen. min_gen should be less than max_gen-1 as max_gen and -max_gen-1 are not fully aged and therefore cannot be evicted. -Setting nr_to_reclaim to N limits the number of pages to evict. -Setting use_bloom_filter to 0 overrides the default behavior which -only scans PTE tables found populated. Multiple command lines are -supported, as is concatenation with delimiters "," and ";". - -Signed-off-by: Yu Zhao -Tested-by: Konstantin Kharlamov -Change-Id: I4448e60029badbe347aa3b624f429b280cc3a3d3 ---- - include/linux/nodemask.h | 1 + - mm/vmscan.c | 415 +++++++++++++++++++++++++++++++++++++++ - 2 files changed, 416 insertions(+) - ---- a/include/linux/nodemask.h -+++ b/include/linux/nodemask.h -@@ -485,6 +485,7 @@ static inline int num_node_state(enum no - #define first_online_node 0 - #define first_memory_node 0 - #define next_online_node(nid) (MAX_NUMNODES) -+#define next_memory_node(nid) (MAX_NUMNODES) - #define nr_node_ids 1U - #define nr_online_nodes 1U - ---- a/mm/vmscan.c -+++ b/mm/vmscan.c -@@ -53,6 +53,8 @@ - #include - #include - #include -+#include -+#include - - #include - #include -@@ -4817,6 +4819,413 @@ unlock: - } - - /****************************************************************************** -+ * sysfs interface -+ ******************************************************************************/ -+ -+static ssize_t show_min_ttl(struct kobject *kobj, struct kobj_attribute *attr, char *buf) -+{ -+ return sprintf(buf, "%u\n", jiffies_to_msecs(READ_ONCE(lru_gen_min_ttl))); -+} -+ -+static ssize_t store_min_ttl(struct kobject *kobj, struct kobj_attribute *attr, -+ const char *buf, size_t len) -+{ -+ unsigned int msecs; -+ -+ if (kstrtouint(buf, 10, &msecs)) -+ return -EINVAL; -+ -+ WRITE_ONCE(lru_gen_min_ttl, msecs_to_jiffies(msecs)); -+ -+ return len; -+} -+ -+static struct kobj_attribute lru_gen_min_ttl_attr = __ATTR( -+ min_ttl_ms, 0644, show_min_ttl, store_min_ttl -+); -+ -+static ssize_t show_enable(struct kobject *kobj, struct kobj_attribute *attr, char *buf) -+{ -+ return snprintf(buf, PAGE_SIZE, "%d\n", lru_gen_enabled()); -+} -+ -+static ssize_t store_enable(struct kobject *kobj, struct kobj_attribute *attr, -+ const char *buf, size_t len) -+{ -+ bool enable; -+ -+ if (kstrtobool(buf, &enable)) -+ return -EINVAL; -+ -+ lru_gen_change_state(enable, true, false); -+ -+ return len; -+} -+ -+static struct kobj_attribute lru_gen_enabled_attr = __ATTR( -+ enabled, 0644, show_enable, store_enable -+); -+ -+static struct attribute *lru_gen_attrs[] = { -+ &lru_gen_min_ttl_attr.attr, -+ &lru_gen_enabled_attr.attr, -+ NULL -+}; -+ -+static struct attribute_group lru_gen_attr_group = { -+ .name = "lru_gen", -+ .attrs = lru_gen_attrs, -+}; -+ -+/****************************************************************************** -+ * debugfs interface -+ ******************************************************************************/ -+ -+static void *lru_gen_seq_start(struct seq_file *m, loff_t *pos) -+{ -+ struct mem_cgroup *memcg; -+ loff_t nr_to_skip = *pos; -+ -+ m->private = kvmalloc(PATH_MAX, GFP_KERNEL); -+ if (!m->private) -+ return ERR_PTR(-ENOMEM); -+ -+ memcg = mem_cgroup_iter(NULL, NULL, NULL); -+ do { -+ int nid; -+ -+ for_each_node_state(nid, N_MEMORY) { -+ if (!nr_to_skip--) -+ return get_lruvec(nid, memcg); -+ } -+ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); -+ -+ return NULL; -+} -+ -+static void lru_gen_seq_stop(struct seq_file *m, void *v) -+{ -+ if (!IS_ERR_OR_NULL(v)) -+ mem_cgroup_iter_break(NULL, lruvec_memcg(v)); -+ -+ kvfree(m->private); -+ m->private = NULL; -+} -+ -+static void *lru_gen_seq_next(struct seq_file *m, void *v, loff_t *pos) -+{ -+ int nid = lruvec_pgdat(v)->node_id; -+ struct mem_cgroup *memcg = lruvec_memcg(v); -+ -+ ++*pos; -+ -+ nid = next_memory_node(nid); -+ if (nid == MAX_NUMNODES) { -+ memcg = mem_cgroup_iter(NULL, memcg, NULL); -+ if (!memcg) -+ return NULL; -+ -+ nid = first_memory_node; -+ } -+ -+ return get_lruvec(nid, memcg); -+} -+ -+static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, -+ unsigned long max_seq, unsigned long *min_seq, -+ unsigned long seq) -+{ -+ int i; -+ int type, tier; -+ int hist = lru_hist_from_seq(seq); -+ struct lrugen *lrugen = &lruvec->evictable; -+ -+ for (tier = 0; tier < MAX_NR_TIERS; tier++) { -+ seq_printf(m, " %10d", tier); -+ for (type = 0; type < ANON_AND_FILE; type++) { -+ unsigned long n[3] = {}; -+ -+ if (seq == max_seq) { -+ n[0] = READ_ONCE(lrugen->avg_refaulted[type][tier]); -+ n[1] = READ_ONCE(lrugen->avg_total[type][tier]); -+ -+ seq_printf(m, " %10luR %10luT %10lu ", n[0], n[1], n[2]); -+ } else if (seq == min_seq[type] || NR_HIST_GENS > 1) { -+ n[0] = atomic_long_read(&lrugen->refaulted[hist][type][tier]); -+ n[1] = atomic_long_read(&lrugen->evicted[hist][type][tier]); -+ if (tier) -+ n[2] = READ_ONCE(lrugen->protected[hist][type][tier - 1]); -+ -+ seq_printf(m, " %10lur %10lue %10lup", n[0], n[1], n[2]); -+ } else -+ seq_puts(m, " 0 0 0 "); -+ } -+ seq_putc(m, '\n'); -+ } -+ -+ seq_puts(m, " "); -+ for (i = 0; i < NR_MM_STATS; i++) { -+ if (seq == max_seq && NR_HIST_GENS == 1) -+ seq_printf(m, " %10lu%c", READ_ONCE(lruvec->mm_walk.stats[hist][i]), -+ toupper(MM_STAT_CODES[i])); -+ else if (seq != max_seq && NR_HIST_GENS > 1) -+ seq_printf(m, " %10lu%c", READ_ONCE(lruvec->mm_walk.stats[hist][i]), -+ MM_STAT_CODES[i]); -+ else -+ seq_puts(m, " 0 "); -+ } -+ seq_putc(m, '\n'); -+} -+ -+static int lru_gen_seq_show(struct seq_file *m, void *v) -+{ -+ unsigned long seq; -+ bool full = !debugfs_real_fops(m->file)->write; -+ struct lruvec *lruvec = v; -+ struct lrugen *lrugen = &lruvec->evictable; -+ int nid = lruvec_pgdat(lruvec)->node_id; -+ struct mem_cgroup *memcg = lruvec_memcg(lruvec); -+ DEFINE_MAX_SEQ(lruvec); -+ DEFINE_MIN_SEQ(lruvec); -+ -+ if (nid == first_memory_node) { -+ const char *path = memcg ? m->private : ""; -+ -+#ifdef CONFIG_MEMCG -+ if (memcg) -+ cgroup_path(memcg->css.cgroup, m->private, PATH_MAX); -+#endif -+ seq_printf(m, "memcg %5hu %s\n", mem_cgroup_id(memcg), path); -+ } -+ -+ seq_printf(m, " node %5d\n", nid); -+ -+ if (!full) -+ seq = min_seq[0]; -+ else if (max_seq >= MAX_NR_GENS) -+ seq = max_seq - MAX_NR_GENS + 1; -+ else -+ seq = 0; -+ -+ for (; seq <= max_seq; seq++) { -+ int gen, type, zone; -+ unsigned int msecs; -+ -+ gen = lru_gen_from_seq(seq); -+ msecs = jiffies_to_msecs(jiffies - READ_ONCE(lrugen->timestamps[gen])); -+ -+ seq_printf(m, " %10lu %10u", seq, msecs); -+ -+ for (type = 0; type < ANON_AND_FILE; type++) { -+ long size = 0; -+ -+ if (seq < min_seq[type]) { -+ seq_puts(m, " -0 "); -+ continue; -+ } -+ -+ for (zone = 0; zone < MAX_NR_ZONES; zone++) -+ size += READ_ONCE(lrugen->sizes[gen][type][zone]); -+ -+ seq_printf(m, " %10lu ", max(size, 0L)); -+ } -+ -+ seq_putc(m, '\n'); -+ -+ if (full) -+ lru_gen_seq_show_full(m, lruvec, max_seq, min_seq, seq); -+ } -+ -+ return 0; -+} -+ -+static const struct seq_operations lru_gen_seq_ops = { -+ .start = lru_gen_seq_start, -+ .stop = lru_gen_seq_stop, -+ .next = lru_gen_seq_next, -+ .show = lru_gen_seq_show, -+}; -+ -+static int run_aging(struct lruvec *lruvec, struct scan_control *sc, int swappiness, -+ unsigned long seq, bool use_filter) -+{ -+ DEFINE_MAX_SEQ(lruvec); -+ -+ if (seq == max_seq) -+ try_to_inc_max_seq(lruvec, sc, swappiness, max_seq, use_filter); -+ -+ return seq > max_seq ? -EINVAL : 0; -+} -+ -+static int run_eviction(struct lruvec *lruvec, struct scan_control *sc, int swappiness, -+ unsigned long seq, unsigned long nr_to_reclaim) -+{ -+ struct blk_plug plug; -+ int err = -EINTR; -+ DEFINE_MAX_SEQ(lruvec); -+ -+ if (seq >= max_seq - 1) -+ return -EINVAL; -+ -+ sc->nr_reclaimed = 0; -+ -+ blk_start_plug(&plug); -+ -+ while (!signal_pending(current)) { -+ DEFINE_MIN_SEQ(lruvec); -+ -+ if (seq < min_seq[!swappiness] || sc->nr_reclaimed >= nr_to_reclaim || -+ !evict_pages(lruvec, sc, swappiness)) { -+ err = 0; -+ break; -+ } -+ -+ cond_resched(); -+ } -+ -+ blk_finish_plug(&plug); -+ -+ return err; -+} -+ -+static int run_cmd(char cmd, int memcg_id, int nid, struct scan_control *sc, -+ int swappiness, unsigned long seq, unsigned long opt) -+{ -+ struct lruvec *lruvec; -+ int err = -EINVAL; -+ struct mem_cgroup *memcg = NULL; -+ -+ if (!mem_cgroup_disabled()) { -+ rcu_read_lock(); -+ memcg = mem_cgroup_from_id(memcg_id); -+#ifdef CONFIG_MEMCG -+ if (memcg && !css_tryget(&memcg->css)) -+ memcg = NULL; -+#endif -+ rcu_read_unlock(); -+ -+ if (!memcg) -+ goto done; -+ } -+ if (memcg_id != mem_cgroup_id(memcg)) -+ goto done; -+ -+ if (nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY)) -+ goto done; -+ -+ lruvec = get_lruvec(nid, memcg); -+ -+ if (swappiness < 0) -+ swappiness = get_swappiness(memcg); -+ else if (swappiness > 200) -+ goto done; -+ -+ switch (cmd) { -+ case '+': -+ err = run_aging(lruvec, sc, swappiness, seq, opt); -+ break; -+ case '-': -+ err = run_eviction(lruvec, sc, swappiness, seq, opt); -+ break; -+ } -+done: -+ mem_cgroup_put(memcg); -+ -+ return err; -+} -+ -+static ssize_t lru_gen_seq_write(struct file *file, const char __user *src, -+ size_t len, loff_t *pos) -+{ -+ void *buf; -+ char *cur, *next; -+ unsigned int flags; -+ int err = 0; -+ struct scan_control sc = { -+ .may_writepage = 1, -+ .may_unmap = 1, -+ .may_swap = 1, -+ .reclaim_idx = MAX_NR_ZONES - 1, -+ .gfp_mask = GFP_KERNEL, -+ }; -+ -+ buf = kvmalloc(len + 1, GFP_KERNEL); -+ if (!buf) -+ return -ENOMEM; -+ -+ if (copy_from_user(buf, src, len)) { -+ kvfree(buf); -+ return -EFAULT; -+ } -+ -+ next = buf; -+ next[len] = '\0'; -+ -+ sc.reclaim_state.mm_walk_args = alloc_mm_walk_args(); -+ if (!sc.reclaim_state.mm_walk_args) { -+ kvfree(buf); -+ return -ENOMEM; -+ } -+ -+ flags = memalloc_noreclaim_save(); -+ set_task_reclaim_state(current, &sc.reclaim_state); -+ -+ while ((cur = strsep(&next, ",;\n"))) { -+ int n; -+ int end; -+ char cmd; -+ unsigned int memcg_id; -+ unsigned int nid; -+ unsigned long seq; -+ unsigned int swappiness = -1; -+ unsigned long opt = -1; -+ -+ cur = skip_spaces(cur); -+ if (!*cur) -+ continue; -+ -+ n = sscanf(cur, "%c %u %u %lu %n %u %n %lu %n", &cmd, &memcg_id, &nid, -+ &seq, &end, &swappiness, &end, &opt, &end); -+ if (n < 4 || cur[end]) { -+ err = -EINVAL; -+ break; -+ } -+ -+ err = run_cmd(cmd, memcg_id, nid, &sc, swappiness, seq, opt); -+ if (err) -+ break; -+ } -+ -+ set_task_reclaim_state(current, NULL); -+ memalloc_noreclaim_restore(flags); -+ -+ free_mm_walk_args(sc.reclaim_state.mm_walk_args); -+ kvfree(buf); -+ -+ return err ? : len; -+} -+ -+static int lru_gen_seq_open(struct inode *inode, struct file *file) -+{ -+ return seq_open(file, &lru_gen_seq_ops); -+} -+ -+static const struct file_operations lru_gen_rw_fops = { -+ .open = lru_gen_seq_open, -+ .read = seq_read, -+ .write = lru_gen_seq_write, -+ .llseek = seq_lseek, -+ .release = seq_release, -+}; -+ -+static const struct file_operations lru_gen_ro_fops = { -+ .open = lru_gen_seq_open, -+ .read = seq_read, -+ .llseek = seq_lseek, -+ .release = seq_release, -+}; -+ -+/****************************************************************************** - * initialization - ******************************************************************************/ - -@@ -4886,6 +5295,12 @@ static int __init init_lru_gen(void) - BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS); - BUILD_BUG_ON(sizeof(MM_STAT_CODES) != NR_MM_STATS + 1); - -+ if (sysfs_create_group(mm_kobj, &lru_gen_attr_group)) -+ pr_err("lru_gen: failed to create sysfs group\n"); -+ -+ debugfs_create_file("lru_gen", 0644, NULL, NULL, &lru_gen_rw_fops); -+ debugfs_create_file("lru_gen_full", 0444, NULL, NULL, &lru_gen_ro_fops); -+ - return 0; - }; - late_initcall(init_lru_gen); diff --git a/target/linux/generic/backport-5.15/020-v6.1-09-mm-multi-gen-LRU-optimize-multiple-memcgs.patch b/target/linux/generic/backport-5.15/020-v6.1-09-mm-multi-gen-LRU-optimize-multiple-memcgs.patch new file mode 100644 index 00000000000..b5fb1951514 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-09-mm-multi-gen-LRU-optimize-multiple-memcgs.patch @@ -0,0 +1,315 @@ +From 36a18a68ea458e8f4db2ca86b00091daf32c6c74 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:06 -0600 +Subject: [PATCH 09/29] mm: multi-gen LRU: optimize multiple memcgs +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When multiple memcgs are available, it is possible to use generations as a +frame of reference to make better choices and improve overall performance +under global memory pressure. This patch adds a basic optimization to +select memcgs that can drop single-use unmapped clean pages first. Doing +so reduces the chance of going into the aging path or swapping, which can +be costly. + +A typical example that benefits from this optimization is a server running +mixed types of workloads, e.g., heavy anon workload in one memcg and heavy +buffered I/O workload in the other. + +Though this optimization can be applied to both kswapd and direct reclaim, +it is only added to kswapd to keep the patchset manageable. Later +improvements may cover the direct reclaim path. + +While ensuring certain fairness to all eligible memcgs, proportional scans +of individual memcgs also require proper backoff to avoid overshooting +their aggregate reclaim target by too much. Otherwise it can cause high +direct reclaim latency. The conditions for backoff are: + +1. At low priorities, for direct reclaim, if aging fairness or direct + reclaim latency is at risk, i.e., aging one memcg multiple times or + swapping after the target is met. +2. At high priorities, for global reclaim, if per-zone free pages are + above respective watermarks. + +Server benchmark results: + Mixed workloads: + fio (buffered I/O): +[19, 21]% + IOPS BW + patch1-8: 1880k 7343MiB/s + patch1-9: 2252k 8796MiB/s + + memcached (anon): +[119, 123]% + Ops/sec KB/sec + patch1-8: 862768.65 33514.68 + patch1-9: 1911022.12 74234.54 + + Mixed workloads: + fio (buffered I/O): +[75, 77]% + IOPS BW + 5.19-rc1: 1279k 4996MiB/s + patch1-9: 2252k 8796MiB/s + + memcached (anon): +[13, 15]% + Ops/sec KB/sec + 5.19-rc1: 1673524.04 65008.87 + patch1-9: 1911022.12 74234.54 + + Configurations: + (changes since patch 6) + + cat mixed.sh + modprobe brd rd_nr=2 rd_size=56623104 + + swapoff -a + mkswap /dev/ram0 + swapon /dev/ram0 + + mkfs.ext4 /dev/ram1 + mount -t ext4 /dev/ram1 /mnt + + memtier_benchmark -S /var/run/memcached/memcached.sock \ + -P memcache_binary -n allkeys --key-minimum=1 \ + --key-maximum=50000000 --key-pattern=P:P -c 1 -t 36 \ + --ratio 1:0 --pipeline 8 -d 2000 + + fio -name=mglru --numjobs=36 --directory=/mnt --size=1408m \ + --buffered=1 --ioengine=io_uring --iodepth=128 \ + --iodepth_batch_submit=32 --iodepth_batch_complete=32 \ + --rw=randread --random_distribution=random --norandommap \ + --time_based --ramp_time=10m --runtime=90m --group_reporting & + pid=$! + + sleep 200 + + memtier_benchmark -S /var/run/memcached/memcached.sock \ + -P memcache_binary -n allkeys --key-minimum=1 \ + --key-maximum=50000000 --key-pattern=R:R -c 1 -t 36 \ + --ratio 0:1 --pipeline 8 --randomize --distinct-client-seed + + kill -INT $pid + wait + +Client benchmark results: + no change (CONFIG_MEMCG=n) + +Link: https://lkml.kernel.org/r/20220918080010.2920238-10-yuzhao@google.com +Signed-off-by: Yu Zhao +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Barry Song +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 105 +++++++++++++++++++++++++++++++++++++++++++++++----- + 1 file changed, 96 insertions(+), 9 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -127,6 +127,12 @@ 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 int memcgs_need_aging:1; ++ unsigned long last_reclaimed; ++#endif ++ + /* Allocation order */ + s8 order; + +@@ -4202,6 +4208,19 @@ static void lru_gen_age_node(struct pgli + + 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; ++ return; ++ } ++ + set_mm_walk(pgdat); + + memcg = mem_cgroup_iter(NULL, NULL, NULL); +@@ -4613,7 +4632,8 @@ static int isolate_pages(struct lruvec * + return scanned; + } + +-static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness) ++static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness, ++ bool *need_swapping) + { + int type; + int scanned; +@@ -4676,6 +4696,9 @@ static int evict_pages(struct lruvec *lr + + sc->nr_reclaimed += reclaimed; + ++ if (need_swapping && type == LRU_GEN_ANON) ++ *need_swapping = true; ++ + return scanned; + } + +@@ -4685,9 +4708,8 @@ static int evict_pages(struct lruvec *lr + * reclaim. + */ + static unsigned long get_nr_to_scan(struct lruvec *lruvec, struct scan_control *sc, +- bool can_swap) ++ bool can_swap, bool *need_aging) + { +- bool need_aging; + unsigned long nr_to_scan; + struct mem_cgroup *memcg = lruvec_memcg(lruvec); + DEFINE_MAX_SEQ(lruvec); +@@ -4697,8 +4719,8 @@ static unsigned long get_nr_to_scan(stru + (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) ++ *need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, can_swap, &nr_to_scan); ++ if (!*need_aging) + return nr_to_scan; + + /* skip the aging path at the default priority */ +@@ -4715,10 +4737,68 @@ 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) ++{ ++ int i; ++ DEFINE_MAX_SEQ(lruvec); ++ ++ if (!current_is_kswapd()) { ++ /* age each memcg 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 target isn't ++ * met, and yet the allocation may still succeed, since kswapd may have ++ * caught up. In either case, it's better to stop now, and restart 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; ++ } ++ ++ sc->nr_reclaimed += MIN_LRU_BATCH; ++ ++ return true; ++} ++ + 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); + + lru_add_drain(); + +@@ -4738,21 +4818,28 @@ static void lru_gen_shrink_lruvec(struct + else + swappiness = 0; + +- nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness); ++ nr_to_scan = get_nr_to_scan(lruvec, sc, swappiness, &need_aging); + if (!nr_to_scan) +- break; ++ goto done; + +- delta = evict_pages(lruvec, sc, swappiness); ++ delta = evict_pages(lruvec, sc, swappiness, &need_swapping); + if (!delta) +- break; ++ goto done; + + scanned += delta; + if (scanned >= nr_to_scan) + break; + ++ if (should_abort_scan(lruvec, max_seq, sc, need_swapping)) ++ break; ++ + 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); diff --git a/target/linux/generic/backport-5.15/020-v6.1-09-mm-multigenerational-lru-Kconfig.patch b/target/linux/generic/backport-5.15/020-v6.1-09-mm-multigenerational-lru-Kconfig.patch deleted file mode 100644 index 4462549f99b..00000000000 --- a/target/linux/generic/backport-5.15/020-v6.1-09-mm-multigenerational-lru-Kconfig.patch +++ /dev/null @@ -1,80 +0,0 @@ -From 3008095eb835d207dd7e5b60899aad17f32aa9f7 Mon Sep 17 00:00:00 2001 -From: Yu Zhao -Date: Mon, 25 Jan 2021 21:47:24 -0700 -Subject: [PATCH 09/10] mm: multigenerational lru: Kconfig - -Add configuration options for the multigenerational lru. - -Signed-off-by: Yu Zhao -Tested-by: Konstantin Kharlamov -Change-Id: Ic74ea07f8fb5f56e6904a1b80c3c286bc2911635 ---- - mm/Kconfig | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 59 insertions(+) - ---- a/mm/Kconfig -+++ b/mm/Kconfig -@@ -899,4 +899,63 @@ config SECRETMEM - - source "mm/damon/Kconfig" - -+# the multigenerational lru { -+config LRU_GEN -+ bool "Multigenerational LRU" -+ depends on MMU -+ # the following options may leave not enough spare bits in page->flags -+ depends on !MAXSMP && (64BIT || !SPARSEMEM || SPARSEMEM_VMEMMAP) -+ help -+ A high performance LRU implementation to heavily overcommit workloads -+ that are not IO bound. See Documentation/vm/multigen_lru.rst for -+ details. -+ -+ Warning: do not enable this option unless you plan to use it because -+ it introduces a small per-process and per-memcg and per-node memory -+ overhead. -+ -+config LRU_GEN_ENABLED -+ bool "Turn on by default" -+ depends on LRU_GEN -+ help -+ The default value of /sys/kernel/mm/lru_gen/enabled is 0. This option -+ changes it to 1. -+ -+ Warning: the default value is the fast path. See -+ Documentation/static-keys.txt for details. -+ -+config LRU_GEN_STATS -+ bool "Full stats for debugging" -+ depends on LRU_GEN -+ help -+ This option keeps full stats for each generation, which can be read -+ from /sys/kernel/debug/lru_gen_full. -+ -+ Warning: do not enable this option unless you plan to use it because -+ it introduces an additional small per-process and per-memcg and -+ per-node memory overhead. -+ -+config NR_LRU_GENS -+ int "Max number of generations" -+ depends on LRU_GEN -+ range 4 31 -+ default 7 -+ help -+ This will use order_base_2(N+1) spare bits from page flags. -+ -+ Warning: do not use numbers larger than necessary because each -+ generation introduces a small per-node and per-memcg memory overhead. -+ -+config TIERS_PER_GEN -+ int "Number of tiers per generation" -+ depends on LRU_GEN -+ range 2 5 -+ default 4 -+ help -+ This will use N-2 spare bits from page flags. -+ -+ Larger values generally offer better protection to active pages under -+ heavy buffered I/O workloads. -+# } -+ - endmenu diff --git a/target/linux/generic/backport-5.15/020-v6.1-10-mm-multi-gen-LRU-kill-switch.patch b/target/linux/generic/backport-5.15/020-v6.1-10-mm-multi-gen-LRU-kill-switch.patch new file mode 100644 index 00000000000..cf5b8f0e9f7 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-10-mm-multi-gen-LRU-kill-switch.patch @@ -0,0 +1,498 @@ +From 640db3a029dca909af47157ca18f52b29d34a1b9 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:07 -0600 +Subject: [PATCH 10/29] mm: multi-gen LRU: kill switch +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add /sys/kernel/mm/lru_gen/enabled as a kill switch. Components that +can be disabled include: + 0x0001: the multi-gen LRU core + 0x0002: walking page table, when arch_has_hw_pte_young() returns + true + 0x0004: clearing the accessed bit in non-leaf PMD entries, when + CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG=y + [yYnN]: apply to all the components above +E.g., + echo y >/sys/kernel/mm/lru_gen/enabled + cat /sys/kernel/mm/lru_gen/enabled + 0x0007 + echo 5 >/sys/kernel/mm/lru_gen/enabled + cat /sys/kernel/mm/lru_gen/enabled + 0x0005 + +NB: the page table walks happen on the scale of seconds under heavy memory +pressure, in which case the mmap_lock contention is a lesser concern, +compared with the LRU lock contention and the I/O congestion. So far the +only well-known case of the mmap_lock contention happens on Android, due +to Scudo [1] which allocates several thousand VMAs for merely a few +hundred MBs. The SPF and the Maple Tree also have provided their own +assessments [2][3]. However, if walking page tables does worsen the +mmap_lock contention, the kill switch can be used to disable it. In this +case the multi-gen LRU will suffer a minor performance degradation, as +shown previously. + +Clearing the accessed bit in non-leaf PMD entries can also be disabled, +since this behavior was not tested on x86 varieties other than Intel and +AMD. + +[1] https://source.android.com/devices/tech/debug/scudo +[2] https://lore.kernel.org/r/20220128131006.67712-1-michel@lespinasse.org/ +[3] https://lore.kernel.org/r/20220426150616.3937571-1-Liam.Howlett@oracle.com/ + +Link: https://lkml.kernel.org/r/20220918080010.2920238-11-yuzhao@google.com +Signed-off-by: Yu Zhao +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Barry Song +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + include/linux/cgroup.h | 15 ++- + include/linux/mm_inline.h | 15 ++- + include/linux/mmzone.h | 9 ++ + kernel/cgroup/cgroup-internal.h | 1 - + mm/Kconfig | 6 + + mm/vmscan.c | 228 +++++++++++++++++++++++++++++++- + 6 files changed, 265 insertions(+), 9 deletions(-) + +--- a/include/linux/cgroup.h ++++ b/include/linux/cgroup.h +@@ -433,6 +433,18 @@ static inline void cgroup_put(struct cgr + css_put(&cgrp->self); + } + ++extern struct mutex cgroup_mutex; ++ ++static inline void cgroup_lock(void) ++{ ++ mutex_lock(&cgroup_mutex); ++} ++ ++static inline void cgroup_unlock(void) ++{ ++ mutex_unlock(&cgroup_mutex); ++} ++ + /** + * task_css_set_check - obtain a task's css_set with extra access conditions + * @task: the task to obtain css_set for +@@ -447,7 +459,6 @@ static inline void cgroup_put(struct cgr + * as locks used during the cgroup_subsys::attach() methods. + */ + #ifdef CONFIG_PROVE_RCU +-extern struct mutex cgroup_mutex; + extern spinlock_t css_set_lock; + #define task_css_set_check(task, __c) \ + rcu_dereference_check((task)->cgroups, \ +@@ -708,6 +719,8 @@ struct cgroup; + static inline u64 cgroup_id(const struct cgroup *cgrp) { return 1; } + static inline void css_get(struct cgroup_subsys_state *css) {} + static inline void css_put(struct cgroup_subsys_state *css) {} ++static inline void cgroup_lock(void) {} ++static inline void cgroup_unlock(void) {} + static inline int cgroup_attach_task_all(struct task_struct *from, + struct task_struct *t) { return 0; } + static inline int cgroupstats_build(struct cgroupstats *stats, +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -91,10 +91,21 @@ static __always_inline enum lru_list pag + + #ifdef CONFIG_LRU_GEN + ++#ifdef CONFIG_LRU_GEN_ENABLED + static inline bool lru_gen_enabled(void) + { +- return true; ++ DECLARE_STATIC_KEY_TRUE(lru_gen_caps[NR_LRU_GEN_CAPS]); ++ ++ return static_branch_likely(&lru_gen_caps[LRU_GEN_CORE]); ++} ++#else ++static inline bool lru_gen_enabled(void) ++{ ++ DECLARE_STATIC_KEY_FALSE(lru_gen_caps[NR_LRU_GEN_CAPS]); ++ ++ return static_branch_unlikely(&lru_gen_caps[LRU_GEN_CORE]); + } ++#endif + + static inline bool lru_gen_in_fault(void) + { +@@ -207,7 +218,7 @@ static inline bool lru_gen_add_page(stru + + VM_WARN_ON_ONCE_PAGE(gen != -1, page); + +- if (PageUnevictable(page)) ++ if (PageUnevictable(page) || !lrugen->enabled) + return false; + /* + * There are three common cases for this page: +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -364,6 +364,13 @@ enum { + LRU_GEN_FILE, + }; + ++enum { ++ LRU_GEN_CORE, ++ LRU_GEN_MM_WALK, ++ LRU_GEN_NONLEAF_YOUNG, ++ NR_LRU_GEN_CAPS ++}; ++ + #define MIN_LRU_BATCH BITS_PER_LONG + #define MAX_LRU_BATCH (MIN_LRU_BATCH * 64) + +@@ -405,6 +412,8 @@ struct lru_gen_struct { + /* can be modified without holding the LRU lock */ + atomic_long_t evicted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; + atomic_long_t refaulted[NR_HIST_GENS][ANON_AND_FILE][MAX_NR_TIERS]; ++ /* whether the multi-gen LRU is enabled */ ++ bool enabled; + }; + + enum { +--- a/kernel/cgroup/cgroup-internal.h ++++ b/kernel/cgroup/cgroup-internal.h +@@ -165,7 +165,6 @@ struct cgroup_mgctx { + #define DEFINE_CGROUP_MGCTX(name) \ + struct cgroup_mgctx name = CGROUP_MGCTX_INIT(name) + +-extern struct mutex cgroup_mutex; + extern spinlock_t css_set_lock; + extern struct cgroup_subsys *cgroup_subsys[]; + extern struct list_head cgroup_roots; +--- a/mm/Kconfig ++++ b/mm/Kconfig +@@ -906,6 +906,12 @@ config LRU_GEN + help + A high performance LRU implementation to overcommit memory. + ++config LRU_GEN_ENABLED ++ bool "Enable by default" ++ depends on LRU_GEN ++ help ++ This option enables the multi-gen LRU by default. ++ + config LRU_GEN_STATS + bool "Full stats for debugging" + depends on LRU_GEN +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -52,6 +52,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -2841,6 +2842,14 @@ static bool can_age_anon_pages(struct pg + + #ifdef CONFIG_LRU_GEN + ++#ifdef CONFIG_LRU_GEN_ENABLED ++DEFINE_STATIC_KEY_ARRAY_TRUE(lru_gen_caps, NR_LRU_GEN_CAPS); ++#define get_cap(cap) static_branch_likely(&lru_gen_caps[cap]) ++#else ++DEFINE_STATIC_KEY_ARRAY_FALSE(lru_gen_caps, NR_LRU_GEN_CAPS); ++#define get_cap(cap) static_branch_unlikely(&lru_gen_caps[cap]) ++#endif ++ + /****************************************************************************** + * shorthand helpers + ******************************************************************************/ +@@ -3717,7 +3726,8 @@ static void walk_pmd_range_locked(pud_t + goto next; + + if (!pmd_trans_huge(pmd[i])) { +- if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG)) ++ if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) && ++ get_cap(LRU_GEN_NONLEAF_YOUNG)) + pmdp_test_and_clear_young(vma, addr, pmd + i); + goto next; + } +@@ -3815,10 +3825,12 @@ restart: + walk->mm_stats[MM_NONLEAF_TOTAL]++; + + #ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG +- if (!pmd_young(val)) +- continue; ++ if (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, &pos); ++ } + #endif + if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i)) + continue; +@@ -4080,7 +4092,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 (!arch_has_hw_pte_young()) { ++ if (!(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) { + success = iterate_mm_list_nowalk(lruvec, max_seq); + goto done; + } +@@ -4846,6 +4858,208 @@ done: + } + + /****************************************************************************** ++ * state change ++ ******************************************************************************/ ++ ++static bool __maybe_unused state_is_valid(struct lruvec *lruvec) ++{ ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ ++ if (lrugen->enabled) { ++ enum lru_list lru; ++ ++ for_each_evictable_lru(lru) { ++ if (!list_empty(&lruvec->lists[lru])) ++ return false; ++ } ++ } else { ++ int gen, type, zone; ++ ++ for_each_gen_type_zone(gen, type, zone) { ++ if (!list_empty(&lrugen->lists[gen][type][zone])) ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static bool fill_evictable(struct lruvec *lruvec) ++{ ++ enum lru_list lru; ++ int remaining = MAX_LRU_BATCH; ++ ++ for_each_evictable_lru(lru) { ++ int type = is_file_lru(lru); ++ bool active = is_active_lru(lru); ++ struct list_head *head = &lruvec->lists[lru]; ++ ++ while (!list_empty(head)) { ++ bool success; ++ struct page *page = lru_to_page(head); ++ ++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page); ++ VM_WARN_ON_ONCE_PAGE(PageActive(page) != active, page); ++ VM_WARN_ON_ONCE_PAGE(page_is_file_lru(page) != type, page); ++ VM_WARN_ON_ONCE_PAGE(page_lru_gen(page) != -1, page); ++ ++ del_page_from_lru_list(page, lruvec); ++ success = lru_gen_add_page(lruvec, page, false); ++ VM_WARN_ON_ONCE(!success); ++ ++ if (!--remaining) ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static bool drain_evictable(struct lruvec *lruvec) ++{ ++ int gen, type, zone; ++ int remaining = MAX_LRU_BATCH; ++ ++ for_each_gen_type_zone(gen, type, zone) { ++ struct list_head *head = &lruvec->lrugen.lists[gen][type][zone]; ++ ++ while (!list_empty(head)) { ++ bool success; ++ struct page *page = lru_to_page(head); ++ ++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page); ++ VM_WARN_ON_ONCE_PAGE(PageActive(page), page); ++ VM_WARN_ON_ONCE_PAGE(page_is_file_lru(page) != type, page); ++ VM_WARN_ON_ONCE_PAGE(page_zonenum(page) != zone, page); ++ ++ success = lru_gen_del_page(lruvec, page, false); ++ VM_WARN_ON_ONCE(!success); ++ add_page_to_lru_list(page, lruvec); ++ ++ if (!--remaining) ++ return false; ++ } ++ } ++ ++ return true; ++} ++ ++static void lru_gen_change_state(bool enabled) ++{ ++ static DEFINE_MUTEX(state_mutex); ++ ++ struct mem_cgroup *memcg; ++ ++ cgroup_lock(); ++ cpus_read_lock(); ++ get_online_mems(); ++ mutex_lock(&state_mutex); ++ ++ if (enabled == lru_gen_enabled()) ++ goto unlock; ++ ++ if (enabled) ++ static_branch_enable_cpuslocked(&lru_gen_caps[LRU_GEN_CORE]); ++ else ++ static_branch_disable_cpuslocked(&lru_gen_caps[LRU_GEN_CORE]); ++ ++ memcg = mem_cgroup_iter(NULL, NULL, NULL); ++ do { ++ int nid; ++ ++ for_each_node(nid) { ++ struct lruvec *lruvec = get_lruvec(memcg, nid); ++ ++ if (!lruvec) ++ continue; ++ ++ spin_lock_irq(&lruvec->lru_lock); ++ ++ VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); ++ VM_WARN_ON_ONCE(!state_is_valid(lruvec)); ++ ++ lruvec->lrugen.enabled = enabled; ++ ++ while (!(enabled ? fill_evictable(lruvec) : drain_evictable(lruvec))) { ++ spin_unlock_irq(&lruvec->lru_lock); ++ cond_resched(); ++ spin_lock_irq(&lruvec->lru_lock); ++ } ++ ++ spin_unlock_irq(&lruvec->lru_lock); ++ } ++ ++ cond_resched(); ++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); ++unlock: ++ mutex_unlock(&state_mutex); ++ put_online_mems(); ++ cpus_read_unlock(); ++ cgroup_unlock(); ++} ++ ++/****************************************************************************** ++ * sysfs interface ++ ******************************************************************************/ ++ ++static ssize_t show_enabled(struct kobject *kobj, struct kobj_attribute *attr, char *buf) ++{ ++ unsigned int caps = 0; ++ ++ if (get_cap(LRU_GEN_CORE)) ++ caps |= BIT(LRU_GEN_CORE); ++ ++ if (arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK)) ++ caps |= BIT(LRU_GEN_MM_WALK); ++ ++ if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) && get_cap(LRU_GEN_NONLEAF_YOUNG)) ++ caps |= BIT(LRU_GEN_NONLEAF_YOUNG); ++ ++ return snprintf(buf, PAGE_SIZE, "0x%04x\n", caps); ++} ++ ++static ssize_t store_enabled(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t len) ++{ ++ int i; ++ unsigned int caps; ++ ++ if (tolower(*buf) == 'n') ++ caps = 0; ++ else if (tolower(*buf) == 'y') ++ caps = -1; ++ else if (kstrtouint(buf, 0, &caps)) ++ return -EINVAL; ++ ++ for (i = 0; i < NR_LRU_GEN_CAPS; i++) { ++ bool enabled = caps & BIT(i); ++ ++ if (i == LRU_GEN_CORE) ++ lru_gen_change_state(enabled); ++ else if (enabled) ++ static_branch_enable(&lru_gen_caps[i]); ++ else ++ static_branch_disable(&lru_gen_caps[i]); ++ } ++ ++ return len; ++} ++ ++static struct kobj_attribute lru_gen_enabled_attr = __ATTR( ++ enabled, 0644, show_enabled, store_enabled ++); ++ ++static struct attribute *lru_gen_attrs[] = { ++ &lru_gen_enabled_attr.attr, ++ NULL ++}; ++ ++static struct attribute_group lru_gen_attr_group = { ++ .name = "lru_gen", ++ .attrs = lru_gen_attrs, ++}; ++ ++/****************************************************************************** + * initialization + ******************************************************************************/ + +@@ -4855,6 +5069,7 @@ void lru_gen_init_lruvec(struct lruvec * + struct lru_gen_struct *lrugen = &lruvec->lrugen; + + lrugen->max_seq = MIN_NR_GENS + 1; ++ lrugen->enabled = lru_gen_enabled(); + + for_each_gen_type_zone(gen, type, zone) + INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); +@@ -4894,6 +5109,9 @@ static int __init init_lru_gen(void) + BUILD_BUG_ON(MIN_NR_GENS + 1 >= MAX_NR_GENS); + BUILD_BUG_ON(BIT(LRU_GEN_WIDTH) <= MAX_NR_GENS); + ++ if (sysfs_create_group(mm_kobj, &lru_gen_attr_group)) ++ pr_err("lru_gen: failed to create sysfs group\n"); ++ + return 0; + }; + late_initcall(init_lru_gen); diff --git a/target/linux/generic/backport-5.15/020-v6.1-10-mm-multigenerational-lru-documentation.patch b/target/linux/generic/backport-5.15/020-v6.1-10-mm-multigenerational-lru-documentation.patch deleted file mode 100644 index f4716fb68d6..00000000000 --- a/target/linux/generic/backport-5.15/020-v6.1-10-mm-multigenerational-lru-documentation.patch +++ /dev/null @@ -1,161 +0,0 @@ -From f59c618ed70a1e48accc4cad91a200966f2569c9 Mon Sep 17 00:00:00 2001 -From: Yu Zhao -Date: Tue, 2 Feb 2021 01:27:45 -0700 -Subject: [PATCH 10/10] mm: multigenerational lru: documentation - -Add Documentation/vm/multigen_lru.rst. - -Signed-off-by: Yu Zhao -Tested-by: Konstantin Kharlamov -Change-Id: I1902178bcbb5adfa0a748c4d284a6456059bdd7e ---- - Documentation/vm/index.rst | 1 + - Documentation/vm/multigen_lru.rst | 132 ++++++++++++++++++++++++++++++ - 2 files changed, 133 insertions(+) - create mode 100644 Documentation/vm/multigen_lru.rst - ---- a/Documentation/vm/index.rst -+++ b/Documentation/vm/index.rst -@@ -17,6 +17,7 @@ various features of the Linux memory man - - swap_numa - zswap -+ multigen_lru - - Kernel developers MM documentation - ================================== ---- /dev/null -+++ b/Documentation/vm/multigen_lru.rst -@@ -0,0 +1,132 @@ -+.. SPDX-License-Identifier: GPL-2.0 -+ -+===================== -+Multigenerational LRU -+===================== -+ -+Quick Start -+=========== -+Build Configurations -+-------------------- -+:Required: Set ``CONFIG_LRU_GEN=y``. -+ -+:Optional: Set ``CONFIG_LRU_GEN_ENABLED=y`` to turn the feature on by -+ default. -+ -+Runtime Configurations -+---------------------- -+:Required: Write ``1`` to ``/sys/kernel/mm/lru_gen/enable`` if the -+ feature was not turned on by default. -+ -+:Optional: Write ``N`` to ``/sys/kernel/mm/lru_gen/min_ttl_ms`` to -+ protect the working set of ``N`` milliseconds. The OOM killer is -+ invoked if this working set cannot be kept in memory. -+ -+:Optional: Read ``/sys/kernel/debug/lru_gen`` to confirm the feature -+ is turned on. This file has the following output: -+ -+:: -+ -+ memcg memcg_id memcg_path -+ node node_id -+ min_gen birth_time anon_size file_size -+ ... -+ max_gen birth_time anon_size file_size -+ -+``min_gen`` is the oldest generation number and ``max_gen`` is the -+youngest generation number. ``birth_time`` is in milliseconds. -+``anon_size`` and ``file_size`` are in pages. -+ -+Phones/Laptops/Workstations -+--------------------------- -+No additional configurations required. -+ -+Servers/Data Centers -+-------------------- -+:To support more generations: Change ``CONFIG_NR_LRU_GENS`` to a -+ larger number. -+ -+:To support more tiers: Change ``CONFIG_TIERS_PER_GEN`` to a larger -+ number. -+ -+:To support full stats: Set ``CONFIG_LRU_GEN_STATS=y``. -+ -+:Working set estimation: Write ``+ memcg_id node_id max_gen -+ [swappiness] [use_bloom_filter]`` to ``/sys/kernel/debug/lru_gen`` to -+ invoke the aging, which scans PTEs for accessed pages and then -+ creates the next generation ``max_gen+1``. A swap file and a non-zero -+ ``swappiness``, which overrides ``vm.swappiness``, are required to -+ scan PTEs mapping anon pages. Set ``use_bloom_filter`` to 0 to -+ override the default behavior which only scans PTE tables found -+ populated. -+ -+:Proactive reclaim: Write ``- memcg_id node_id min_gen [swappiness] -+ [nr_to_reclaim]`` to ``/sys/kernel/debug/lru_gen`` to invoke the -+ eviction, which evicts generations less than or equal to ``min_gen``. -+ ``min_gen`` should be less than ``max_gen-1`` as ``max_gen`` and -+ ``max_gen-1`` are not fully aged and therefore cannot be evicted. -+ Use ``nr_to_reclaim`` to limit the number of pages to evict. Multiple -+ command lines are supported, so does concatenation with delimiters -+ ``,`` and ``;``. -+ -+Framework -+========= -+For each ``lruvec``, evictable pages are divided into multiple -+generations. The youngest generation number is stored in -+``lrugen->max_seq`` for both anon and file types as they are aged on -+an equal footing. The oldest generation numbers are stored in -+``lrugen->min_seq[]`` separately for anon and file types as clean -+file pages can be evicted regardless of swap and writeback -+constraints. These three variables are monotonically increasing. -+Generation numbers are truncated into -+``order_base_2(CONFIG_NR_LRU_GENS+1)`` bits in order to fit into -+``page->flags``. The sliding window technique is used to prevent -+truncated generation numbers from overlapping. Each truncated -+generation number is an index to an array of per-type and per-zone -+lists ``lrugen->lists``. -+ -+Each generation is divided into multiple tiers. Tiers represent -+different ranges of numbers of accesses from file descriptors only. -+Pages accessed ``N`` times via file descriptors belong to tier -+``order_base_2(N)``. Each generation contains at most -+``CONFIG_TIERS_PER_GEN`` tiers, and they require additional -+``CONFIG_TIERS_PER_GEN-2`` bits in ``page->flags``. In contrast to -+moving between generations which requires list operations, moving -+between tiers only involves operations on ``page->flags`` and -+therefore has a negligible cost. A feedback loop modeled after the PID -+controller monitors refaulted % across all tiers and decides when to -+protect pages from which tiers. -+ -+The framework comprises two conceptually independent components: the -+aging and the eviction, which can be invoked separately from user -+space for the purpose of working set estimation and proactive reclaim. -+ -+Aging -+----- -+The aging produces young generations. Given an ``lruvec``, the aging -+traverses ``lruvec_memcg()->mm_list`` and calls ``walk_page_range()`` -+to scan PTEs for accessed pages (a ``mm_struct`` list is maintained -+for each ``memcg``). Upon finding one, the aging updates its -+generation number to ``max_seq`` (modulo ``CONFIG_NR_LRU_GENS``). -+After each round of traversal, the aging increments ``max_seq``. The -+aging is due when ``min_seq[]`` reaches ``max_seq-1``. -+ -+Eviction -+-------- -+The eviction consumes old generations. Given an ``lruvec``, the -+eviction scans pages on the per-zone lists indexed by anon and file -+``min_seq[]`` (modulo ``CONFIG_NR_LRU_GENS``). It first tries to -+select a type based on the values of ``min_seq[]``. If they are -+equal, it selects the type that has a lower refaulted %. The eviction -+sorts a page according to its updated generation number if the aging -+has found this page accessed. It also moves a page to the next -+generation if this page is from an upper tier that has a higher -+refaulted % than the base tier. The eviction increments ``min_seq[]`` -+of a selected type when it finds all the per-zone lists indexed by -+``min_seq[]`` of this selected type are empty. -+ -+To-do List -+========== -+KVM Optimization -+---------------- -+Support shadow page table walk. diff --git a/target/linux/generic/backport-5.15/020-v6.1-11-mm-multi-gen-LRU-thrashing-prevention.patch b/target/linux/generic/backport-5.15/020-v6.1-11-mm-multi-gen-LRU-thrashing-prevention.patch new file mode 100644 index 00000000000..30e20aff6ed --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-11-mm-multi-gen-LRU-thrashing-prevention.patch @@ -0,0 +1,226 @@ +From 73d1ff551760f0c79c47ab70faa4c2ca91413f5c Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:08 -0600 +Subject: [PATCH 11/29] mm: multi-gen LRU: thrashing prevention +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add /sys/kernel/mm/lru_gen/min_ttl_ms for thrashing prevention, as +requested by many desktop users [1]. + +When set to value N, it prevents the working set of N milliseconds from +getting evicted. The OOM killer is triggered if this working set cannot +be kept in memory. Based on the average human detectable lag (~100ms), +N=1000 usually eliminates intolerable lags due to thrashing. Larger +values like N=3000 make lags less noticeable at the risk of premature OOM +kills. + +Compared with the size-based approach [2], 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. + +[1] https://lore.kernel.org/r/Ydza%2FzXKY9ATRoh6@google.com/ +[2] https://lore.kernel.org/r/20101028191523.GA14972@google.com/ + +Link: https://lkml.kernel.org/r/20220918080010.2920238-12-yuzhao@google.com +Signed-off-by: Yu Zhao +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Barry Song +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Qi Zheng +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + include/linux/mmzone.h | 2 ++ + mm/vmscan.c | 74 ++++++++++++++++++++++++++++++++++++++++-- + 2 files changed, 73 insertions(+), 3 deletions(-) + +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -399,6 +399,8 @@ struct lru_gen_struct { + unsigned long max_seq; + /* the eviction increments the oldest generation numbers */ + unsigned long min_seq[ANON_AND_FILE]; ++ /* the birth time of each generation in jiffies */ ++ unsigned long timestamps[MAX_NR_GENS]; + /* the multi-gen LRU lists, lazily sorted on eviction */ + struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; + /* the multi-gen LRU sizes, eventually consistent */ +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -4064,6 +4064,7 @@ static void inc_max_seq(struct lruvec *l + for (type = 0; type < ANON_AND_FILE; type++) + reset_ctrl_pos(lruvec, type, false); + ++ WRITE_ONCE(lrugen->timestamps[next], jiffies); + /* make sure preceding modifications appear */ + smp_store_release(&lrugen->max_seq, lrugen->max_seq + 1); + +@@ -4193,7 +4194,7 @@ static bool should_run_aging(struct lruv + return false; + } + +-static void age_lruvec(struct lruvec *lruvec, struct scan_control *sc) ++static bool age_lruvec(struct lruvec *lruvec, struct scan_control *sc, unsigned long min_ttl) + { + bool need_aging; + unsigned long nr_to_scan; +@@ -4207,16 +4208,36 @@ static void age_lruvec(struct lruvec *lr + mem_cgroup_calculate_protection(NULL, memcg); + + if (mem_cgroup_below_min(memcg)) +- return; ++ return false; + + need_aging = should_run_aging(lruvec, max_seq, min_seq, sc, swappiness, &nr_to_scan); ++ ++ if (min_ttl) { ++ int gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]); ++ unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]); ++ ++ if (time_is_after_jiffies(birth + min_ttl)) ++ return false; ++ ++ /* the size is likely too small to be helpful */ ++ if (!nr_to_scan && sc->priority != DEF_PRIORITY) ++ return false; ++ } ++ + if (need_aging) + try_to_inc_max_seq(lruvec, max_seq, sc, swappiness); ++ ++ return true; + } + ++/* to protect the working set of the last N jiffies */ ++static unsigned long lru_gen_min_ttl __read_mostly; ++ + 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()); + +@@ -4239,12 +4260,32 @@ static void lru_gen_age_node(struct pgli + do { + struct lruvec *lruvec = mem_cgroup_lruvec(memcg, pgdat); + +- age_lruvec(lruvec, sc); ++ if (age_lruvec(lruvec, sc, min_ttl)) ++ success = true; + + 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. ++ */ ++ if (mutex_trylock(&oom_lock)) { ++ struct oom_control oc = { ++ .gfp_mask = sc->gfp_mask, ++ }; ++ ++ out_of_memory(&oc); ++ ++ mutex_unlock(&oom_lock); ++ } + } + + /* +@@ -5002,6 +5043,28 @@ unlock: + * sysfs interface + ******************************************************************************/ + ++static ssize_t show_min_ttl(struct kobject *kobj, struct kobj_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%u\n", jiffies_to_msecs(READ_ONCE(lru_gen_min_ttl))); ++} ++ ++static ssize_t store_min_ttl(struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t len) ++{ ++ unsigned int msecs; ++ ++ if (kstrtouint(buf, 0, &msecs)) ++ return -EINVAL; ++ ++ WRITE_ONCE(lru_gen_min_ttl, msecs_to_jiffies(msecs)); ++ ++ return len; ++} ++ ++static struct kobj_attribute lru_gen_min_ttl_attr = __ATTR( ++ min_ttl_ms, 0644, show_min_ttl, store_min_ttl ++); ++ + static ssize_t show_enabled(struct kobject *kobj, struct kobj_attribute *attr, char *buf) + { + unsigned int caps = 0; +@@ -5050,6 +5113,7 @@ static struct kobj_attribute lru_gen_ena + ); + + static struct attribute *lru_gen_attrs[] = { ++ &lru_gen_min_ttl_attr.attr, + &lru_gen_enabled_attr.attr, + NULL + }; +@@ -5065,12 +5129,16 @@ static struct attribute_group lru_gen_at + + void lru_gen_init_lruvec(struct lruvec *lruvec) + { ++ int i; + int gen, type, zone; + struct lru_gen_struct *lrugen = &lruvec->lrugen; + + lrugen->max_seq = MIN_NR_GENS + 1; + lrugen->enabled = lru_gen_enabled(); + ++ for (i = 0; i <= MIN_NR_GENS + 1; i++) ++ lrugen->timestamps[i] = jiffies; ++ + for_each_gen_type_zone(gen, type, zone) + INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); + diff --git a/target/linux/generic/backport-5.15/020-v6.1-12-mm-multi-gen-LRU-debugfs-interface.patch b/target/linux/generic/backport-5.15/020-v6.1-12-mm-multi-gen-LRU-debugfs-interface.patch new file mode 100644 index 00000000000..482e714bb6a --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-12-mm-multi-gen-LRU-debugfs-interface.patch @@ -0,0 +1,579 @@ +From 530716d008ca26315f246cd70dc1cefc636beaa4 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 18 Sep 2022 02:00:09 -0600 +Subject: [PATCH 12/29] mm: multi-gen LRU: debugfs interface +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add /sys/kernel/debug/lru_gen for working set estimation and proactive +reclaim. These techniques are commonly used to optimize job scheduling +(bin packing) in data centers [1][2]. + +Compared with the page table-based approach and the PFN-based +approach, this lruvec-based approach has the following advantages: +1. It offers better choices because it is aware of memcgs, NUMA nodes, + shared mappings and unmapped page cache. +2. It is more scalable because it is O(nr_hot_pages), whereas the + PFN-based approach is O(nr_total_pages). + +Add /sys/kernel/debug/lru_gen_full for debugging. + +[1] https://dl.acm.org/doi/10.1145/3297858.3304053 +[2] https://dl.acm.org/doi/10.1145/3503222.3507731 + +Link: https://lkml.kernel.org/r/20220918080010.2920238-13-yuzhao@google.com +Signed-off-by: Yu Zhao +Reviewed-by: Qi Zheng +Acked-by: Brian Geffon +Acked-by: Jan Alexander Steffens (heftig) +Acked-by: Oleksandr Natalenko +Acked-by: Steven Barrett +Acked-by: Suleiman Souhlal +Tested-by: Daniel Byrne +Tested-by: Donald Carr +Tested-by: Holger Hoffstätte +Tested-by: Konstantin Kharlamov +Tested-by: Shuang Zhai +Tested-by: Sofia Trinh +Tested-by: Vaibhav Jain +Cc: Andi Kleen +Cc: Aneesh Kumar K.V +Cc: Barry Song +Cc: Catalin Marinas +Cc: Dave Hansen +Cc: Hillf Danton +Cc: Jens Axboe +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Linus Torvalds +Cc: Matthew Wilcox +Cc: Mel Gorman +Cc: Miaohe Lin +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Mike Rapoport +Cc: Peter Zijlstra +Cc: Tejun Heo +Cc: Vlastimil Babka +Cc: Will Deacon +Signed-off-by: Andrew Morton +--- + include/linux/nodemask.h | 1 + + mm/vmscan.c | 411 ++++++++++++++++++++++++++++++++++++++- + 2 files changed, 402 insertions(+), 10 deletions(-) + +--- a/include/linux/nodemask.h ++++ b/include/linux/nodemask.h +@@ -485,6 +485,7 @@ static inline int num_node_state(enum no + #define first_online_node 0 + #define first_memory_node 0 + #define next_online_node(nid) (MAX_NUMNODES) ++#define next_memory_node(nid) (MAX_NUMNODES) + #define nr_node_ids 1U + #define nr_online_nodes 1U + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -53,6 +53,7 @@ + #include + #include + #include ++#include + + #include + #include +@@ -3968,12 +3969,40 @@ static void clear_mm_walk(void) + kfree(walk); + } + +-static void inc_min_seq(struct lruvec *lruvec, int type) ++static bool inc_min_seq(struct lruvec *lruvec, int type, bool can_swap) + { ++ int zone; ++ int remaining = MAX_LRU_BATCH; + struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]); ++ ++ if (type == LRU_GEN_ANON && !can_swap) ++ goto done; ++ ++ /* prevent cold/hot inversion if force_scan is true */ ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) { ++ struct list_head *head = &lrugen->lists[old_gen][type][zone]; ++ ++ while (!list_empty(head)) { ++ struct page *page = lru_to_page(head); ++ ++ VM_WARN_ON_ONCE_PAGE(PageUnevictable(page), page); ++ VM_WARN_ON_ONCE_PAGE(PageActive(page), page); ++ VM_WARN_ON_ONCE_PAGE(page_is_file_lru(page) != type, page); ++ VM_WARN_ON_ONCE_PAGE(page_zonenum(page) != zone, page); + ++ new_gen = page_inc_gen(lruvec, page, false); ++ list_move_tail(&page->lru, &lrugen->lists[new_gen][type][zone]); ++ ++ if (!--remaining) ++ return false; ++ } ++ } ++done: + reset_ctrl_pos(lruvec, type, true); + WRITE_ONCE(lrugen->min_seq[type], lrugen->min_seq[type] + 1); ++ ++ return true; + } + + static bool try_to_inc_min_seq(struct lruvec *lruvec, bool can_swap) +@@ -4019,7 +4048,7 @@ next: + return success; + } + +-static void inc_max_seq(struct lruvec *lruvec, bool can_swap) ++static void inc_max_seq(struct lruvec *lruvec, bool can_swap, bool force_scan) + { + int prev, next; + int type, zone; +@@ -4033,9 +4062,13 @@ static void inc_max_seq(struct lruvec *l + if (get_nr_gens(lruvec, type) != MAX_NR_GENS) + continue; + +- VM_WARN_ON_ONCE(type == LRU_GEN_FILE || can_swap); ++ VM_WARN_ON_ONCE(!force_scan && (type == LRU_GEN_FILE || can_swap)); + +- inc_min_seq(lruvec, type); ++ while (!inc_min_seq(lruvec, type, can_swap)) { ++ spin_unlock_irq(&lruvec->lru_lock); ++ cond_resched(); ++ spin_lock_irq(&lruvec->lru_lock); ++ } + } + + /* +@@ -4072,7 +4105,7 @@ static void inc_max_seq(struct lruvec *l + } + + static bool try_to_inc_max_seq(struct lruvec *lruvec, unsigned long max_seq, +- struct scan_control *sc, bool can_swap) ++ struct scan_control *sc, bool can_swap, bool force_scan) + { + bool success; + struct lru_gen_mm_walk *walk; +@@ -4093,7 +4126,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 (!(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) { ++ if (!force_scan && !(arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK))) { + success = iterate_mm_list_nowalk(lruvec, max_seq); + goto done; + } +@@ -4107,7 +4140,7 @@ static bool try_to_inc_max_seq(struct lr + walk->lruvec = lruvec; + walk->max_seq = max_seq; + walk->can_swap = can_swap; +- walk->force_scan = false; ++ walk->force_scan = force_scan; + + do { + success = iterate_mm_list(lruvec, walk, &mm); +@@ -4127,7 +4160,7 @@ done: + + VM_WARN_ON_ONCE(max_seq != READ_ONCE(lrugen->max_seq)); + +- inc_max_seq(lruvec, can_swap); ++ 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); +@@ -4225,7 +4258,7 @@ static bool age_lruvec(struct lruvec *lr + } + + if (need_aging) +- try_to_inc_max_seq(lruvec, max_seq, sc, swappiness); ++ try_to_inc_max_seq(lruvec, max_seq, sc, swappiness, false); + + return true; + } +@@ -4784,7 +4817,7 @@ static unsigned long get_nr_to_scan(stru + if (current_is_kswapd()) + return 0; + +- if (try_to_inc_max_seq(lruvec, max_seq, sc, can_swap)) ++ 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; +@@ -5124,6 +5157,361 @@ static struct attribute_group lru_gen_at + }; + + /****************************************************************************** ++ * debugfs interface ++ ******************************************************************************/ ++ ++static void *lru_gen_seq_start(struct seq_file *m, loff_t *pos) ++{ ++ struct mem_cgroup *memcg; ++ loff_t nr_to_skip = *pos; ++ ++ m->private = kvmalloc(PATH_MAX, GFP_KERNEL); ++ if (!m->private) ++ return ERR_PTR(-ENOMEM); ++ ++ memcg = mem_cgroup_iter(NULL, NULL, NULL); ++ do { ++ int nid; ++ ++ for_each_node_state(nid, N_MEMORY) { ++ if (!nr_to_skip--) ++ return get_lruvec(memcg, nid); ++ } ++ } while ((memcg = mem_cgroup_iter(NULL, memcg, NULL))); ++ ++ return NULL; ++} ++ ++static void lru_gen_seq_stop(struct seq_file *m, void *v) ++{ ++ if (!IS_ERR_OR_NULL(v)) ++ mem_cgroup_iter_break(NULL, lruvec_memcg(v)); ++ ++ kvfree(m->private); ++ m->private = NULL; ++} ++ ++static void *lru_gen_seq_next(struct seq_file *m, void *v, loff_t *pos) ++{ ++ int nid = lruvec_pgdat(v)->node_id; ++ struct mem_cgroup *memcg = lruvec_memcg(v); ++ ++ ++*pos; ++ ++ nid = next_memory_node(nid); ++ if (nid == MAX_NUMNODES) { ++ memcg = mem_cgroup_iter(NULL, memcg, NULL); ++ if (!memcg) ++ return NULL; ++ ++ nid = first_memory_node; ++ } ++ ++ return get_lruvec(memcg, nid); ++} ++ ++static void lru_gen_seq_show_full(struct seq_file *m, struct lruvec *lruvec, ++ unsigned long max_seq, unsigned long *min_seq, ++ unsigned long seq) ++{ ++ int i; ++ int type, tier; ++ int hist = lru_hist_from_seq(seq); ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ ++ for (tier = 0; tier < MAX_NR_TIERS; tier++) { ++ seq_printf(m, " %10d", tier); ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ const char *s = " "; ++ unsigned long n[3] = {}; ++ ++ if (seq == max_seq) { ++ s = "RT "; ++ n[0] = READ_ONCE(lrugen->avg_refaulted[type][tier]); ++ n[1] = READ_ONCE(lrugen->avg_total[type][tier]); ++ } else if (seq == min_seq[type] || NR_HIST_GENS > 1) { ++ s = "rep"; ++ n[0] = atomic_long_read(&lrugen->refaulted[hist][type][tier]); ++ n[1] = atomic_long_read(&lrugen->evicted[hist][type][tier]); ++ if (tier) ++ n[2] = READ_ONCE(lrugen->protected[hist][type][tier - 1]); ++ } ++ ++ for (i = 0; i < 3; i++) ++ seq_printf(m, " %10lu%c", n[i], s[i]); ++ } ++ seq_putc(m, '\n'); ++ } ++ ++ seq_puts(m, " "); ++ for (i = 0; i < NR_MM_STATS; i++) { ++ const char *s = " "; ++ unsigned long n = 0; ++ ++ if (seq == max_seq && NR_HIST_GENS == 1) { ++ s = "LOYNFA"; ++ n = READ_ONCE(lruvec->mm_state.stats[hist][i]); ++ } else if (seq != max_seq && NR_HIST_GENS > 1) { ++ s = "loynfa"; ++ n = READ_ONCE(lruvec->mm_state.stats[hist][i]); ++ } ++ ++ seq_printf(m, " %10lu%c", n, s[i]); ++ } ++ seq_putc(m, '\n'); ++} ++ ++static int lru_gen_seq_show(struct seq_file *m, void *v) ++{ ++ unsigned long seq; ++ bool full = !debugfs_real_fops(m->file)->write; ++ struct lruvec *lruvec = v; ++ struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ int nid = lruvec_pgdat(lruvec)->node_id; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ DEFINE_MAX_SEQ(lruvec); ++ DEFINE_MIN_SEQ(lruvec); ++ ++ if (nid == first_memory_node) { ++ const char *path = memcg ? m->private : ""; ++ ++#ifdef CONFIG_MEMCG ++ if (memcg) ++ cgroup_path(memcg->css.cgroup, m->private, PATH_MAX); ++#endif ++ seq_printf(m, "memcg %5hu %s\n", mem_cgroup_id(memcg), path); ++ } ++ ++ seq_printf(m, " node %5d\n", nid); ++ ++ if (!full) ++ seq = min_seq[LRU_GEN_ANON]; ++ else if (max_seq >= MAX_NR_GENS) ++ seq = max_seq - MAX_NR_GENS + 1; ++ else ++ seq = 0; ++ ++ for (; seq <= max_seq; seq++) { ++ int type, zone; ++ int gen = lru_gen_from_seq(seq); ++ unsigned long birth = READ_ONCE(lruvec->lrugen.timestamps[gen]); ++ ++ seq_printf(m, " %10lu %10u", seq, jiffies_to_msecs(jiffies - birth)); ++ ++ for (type = 0; type < ANON_AND_FILE; type++) { ++ unsigned long size = 0; ++ char mark = full && seq < min_seq[type] ? 'x' : ' '; ++ ++ for (zone = 0; zone < MAX_NR_ZONES; zone++) ++ size += max(READ_ONCE(lrugen->nr_pages[gen][type][zone]), 0L); ++ ++ seq_printf(m, " %10lu%c", size, mark); ++ } ++ ++ seq_putc(m, '\n'); ++ ++ if (full) ++ lru_gen_seq_show_full(m, lruvec, max_seq, min_seq, seq); ++ } ++ ++ return 0; ++} ++ ++static const struct seq_operations lru_gen_seq_ops = { ++ .start = lru_gen_seq_start, ++ .stop = lru_gen_seq_stop, ++ .next = lru_gen_seq_next, ++ .show = lru_gen_seq_show, ++}; ++ ++static int run_aging(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc, ++ bool can_swap, bool force_scan) ++{ ++ DEFINE_MAX_SEQ(lruvec); ++ DEFINE_MIN_SEQ(lruvec); ++ ++ if (seq < max_seq) ++ return 0; ++ ++ if (seq > max_seq) ++ return -EINVAL; ++ ++ if (!force_scan && min_seq[!can_swap] + MAX_NR_GENS - 1 <= max_seq) ++ return -ERANGE; ++ ++ try_to_inc_max_seq(lruvec, max_seq, sc, can_swap, force_scan); ++ ++ return 0; ++} ++ ++static int run_eviction(struct lruvec *lruvec, unsigned long seq, struct scan_control *sc, ++ int swappiness, unsigned long nr_to_reclaim) ++{ ++ DEFINE_MAX_SEQ(lruvec); ++ ++ if (seq + MIN_NR_GENS > max_seq) ++ return -EINVAL; ++ ++ sc->nr_reclaimed = 0; ++ ++ while (!signal_pending(current)) { ++ DEFINE_MIN_SEQ(lruvec); ++ ++ if (seq < min_seq[!swappiness]) ++ return 0; ++ ++ if (sc->nr_reclaimed >= nr_to_reclaim) ++ return 0; ++ ++ if (!evict_pages(lruvec, sc, swappiness, NULL)) ++ return 0; ++ ++ cond_resched(); ++ } ++ ++ return -EINTR; ++} ++ ++static int run_cmd(char cmd, int memcg_id, int nid, unsigned long seq, ++ struct scan_control *sc, int swappiness, unsigned long opt) ++{ ++ struct lruvec *lruvec; ++ int err = -EINVAL; ++ struct mem_cgroup *memcg = NULL; ++ ++ if (nid < 0 || nid >= MAX_NUMNODES || !node_state(nid, N_MEMORY)) ++ return -EINVAL; ++ ++ if (!mem_cgroup_disabled()) { ++ rcu_read_lock(); ++ memcg = mem_cgroup_from_id(memcg_id); ++#ifdef CONFIG_MEMCG ++ if (memcg && !css_tryget(&memcg->css)) ++ memcg = NULL; ++#endif ++ rcu_read_unlock(); ++ ++ if (!memcg) ++ return -EINVAL; ++ } ++ ++ if (memcg_id != mem_cgroup_id(memcg)) ++ goto done; ++ ++ lruvec = get_lruvec(memcg, nid); ++ ++ if (swappiness < 0) ++ swappiness = get_swappiness(lruvec, sc); ++ else if (swappiness > 200) ++ goto done; ++ ++ switch (cmd) { ++ case '+': ++ err = run_aging(lruvec, seq, sc, swappiness, opt); ++ break; ++ case '-': ++ err = run_eviction(lruvec, seq, sc, swappiness, opt); ++ break; ++ } ++done: ++ mem_cgroup_put(memcg); ++ ++ return err; ++} ++ ++static ssize_t lru_gen_seq_write(struct file *file, const char __user *src, ++ size_t len, loff_t *pos) ++{ ++ void *buf; ++ char *cur, *next; ++ unsigned int flags; ++ struct blk_plug plug; ++ int err = -EINVAL; ++ struct scan_control sc = { ++ .may_writepage = true, ++ .may_unmap = true, ++ .may_swap = true, ++ .reclaim_idx = MAX_NR_ZONES - 1, ++ .gfp_mask = GFP_KERNEL, ++ }; ++ ++ buf = kvmalloc(len + 1, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ if (copy_from_user(buf, src, len)) { ++ kvfree(buf); ++ return -EFAULT; ++ } ++ ++ set_task_reclaim_state(current, &sc.reclaim_state); ++ flags = memalloc_noreclaim_save(); ++ blk_start_plug(&plug); ++ if (!set_mm_walk(NULL)) { ++ err = -ENOMEM; ++ goto done; ++ } ++ ++ next = buf; ++ next[len] = '\0'; ++ ++ while ((cur = strsep(&next, ",;\n"))) { ++ int n; ++ int end; ++ char cmd; ++ unsigned int memcg_id; ++ unsigned int nid; ++ unsigned long seq; ++ unsigned int swappiness = -1; ++ unsigned long opt = -1; ++ ++ cur = skip_spaces(cur); ++ if (!*cur) ++ continue; ++ ++ n = sscanf(cur, "%c %u %u %lu %n %u %n %lu %n", &cmd, &memcg_id, &nid, ++ &seq, &end, &swappiness, &end, &opt, &end); ++ if (n < 4 || cur[end]) { ++ err = -EINVAL; ++ break; ++ } ++ ++ err = run_cmd(cmd, memcg_id, nid, seq, &sc, swappiness, opt); ++ if (err) ++ break; ++ } ++done: ++ clear_mm_walk(); ++ blk_finish_plug(&plug); ++ memalloc_noreclaim_restore(flags); ++ set_task_reclaim_state(current, NULL); ++ ++ kvfree(buf); ++ ++ return err ? : len; ++} ++ ++static int lru_gen_seq_open(struct inode *inode, struct file *file) ++{ ++ return seq_open(file, &lru_gen_seq_ops); ++} ++ ++static const struct file_operations lru_gen_rw_fops = { ++ .open = lru_gen_seq_open, ++ .read = seq_read, ++ .write = lru_gen_seq_write, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++static const struct file_operations lru_gen_ro_fops = { ++ .open = lru_gen_seq_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = seq_release, ++}; ++ ++/****************************************************************************** + * initialization + ******************************************************************************/ + +@@ -5180,6 +5568,9 @@ static int __init init_lru_gen(void) + if (sysfs_create_group(mm_kobj, &lru_gen_attr_group)) + pr_err("lru_gen: failed to create sysfs group\n"); + ++ debugfs_create_file("lru_gen", 0644, NULL, NULL, &lru_gen_rw_fops); ++ debugfs_create_file("lru_gen_full", 0444, NULL, NULL, &lru_gen_ro_fops); ++ + return 0; + }; + late_initcall(init_lru_gen); diff --git a/target/linux/generic/backport-5.15/021-v6.1-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch b/target/linux/generic/backport-5.15/020-v6.1-13-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch similarity index 75% rename from target/linux/generic/backport-5.15/021-v6.1-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch rename to target/linux/generic/backport-5.15/020-v6.1-13-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch index 6cc4b3368f3..fd4aa727473 100644 --- a/target/linux/generic/backport-5.15/021-v6.1-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch +++ b/target/linux/generic/backport-5.15/020-v6.1-13-mm-mglru-don-t-sync-disk-for-each-aging-cycle.patch @@ -1,7 +1,7 @@ -From 14aa8b2d5c2ebead01b542f62d68029023054774 Mon Sep 17 00:00:00 2001 +From 92d430e8955c976eacb7cc91d7ff849c0dd009af Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Wed, 28 Sep 2022 13:36:58 -0600 -Subject: [PATCH 1/1] mm/mglru: don't sync disk for each aging cycle +Subject: [PATCH 13/29] mm/mglru: don't sync disk for each aging cycle wakeup_flusher_threads() was added under the assumption that if a system runs out of clean cold pages, it might want to write back dirty pages more @@ -21,9 +21,9 @@ Signed-off-by: Andrew Morton --- a/mm/vmscan.c +++ b/mm/vmscan.c -@@ -4007,8 +4007,6 @@ static bool try_to_inc_max_seq(struct lr - if (wq_has_sleeper(&lruvec->mm_walk.wait)) - wake_up_all(&lruvec->mm_walk.wait); +@@ -4165,8 +4165,6 @@ done: + if (wq_has_sleeper(&lruvec->mm_state.wait)) + wake_up_all(&lruvec->mm_state.wait); - wakeup_flusher_threads(WB_REASON_VMSCAN); - diff --git a/target/linux/generic/backport-5.15/020-v6.1-14-mm-multi-gen-LRU-retry-pages-written-back-while-isol.patch b/target/linux/generic/backport-5.15/020-v6.1-14-mm-multi-gen-LRU-retry-pages-written-back-while-isol.patch new file mode 100644 index 00000000000..31b35cbc4b6 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-14-mm-multi-gen-LRU-retry-pages-written-back-while-isol.patch @@ -0,0 +1,124 @@ +From 6f315879ad750391a0b1fab8c9170bc054a5f5d7 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Tue, 15 Nov 2022 18:38:07 -0700 +Subject: [PATCH 14/29] mm: multi-gen LRU: retry pages written back while + isolated + +The page reclaim isolates a batch of pages from the tail of one of the +LRU lists and works on those pages one by one. For a suitable +swap-backed page, if the swap device is async, it queues that page for +writeback. After the page reclaim finishes an entire batch, it puts back +the pages it queued for writeback to the head of the original LRU list. + +In the meantime, the page writeback flushes the queued pages also by +batches. Its batching logic is independent from that of the page reclaim. +For each of the pages it writes back, the page writeback calls +rotate_reclaimable_page() which tries to rotate a page to the tail. + +rotate_reclaimable_page() only works for a page after the page reclaim +has put it back. If an async swap device is fast enough, the page +writeback can finish with that page while the page reclaim is still +working on the rest of the batch containing it. In this case, that page +will remain at the head and the page reclaim will not retry it before +reaching there. + +This patch adds a retry to evict_pages(). After evict_pages() has +finished an entire batch and before it puts back pages it cannot free +immediately, it retries those that may have missed the rotation. + +Before this patch, ~60% of pages swapped to an Intel Optane missed +rotate_reclaimable_page(). After this patch, ~99% of missed pages were +reclaimed upon retry. + +This problem affects relatively slow async swap devices like Samsung 980 +Pro much less and does not affect sync swap devices like zram or zswap at +all. + +Link: https://lkml.kernel.org/r/20221116013808.3995280-1-yuzhao@google.com +Fixes: ac35a4902374 ("mm: multi-gen LRU: minimal implementation") +Signed-off-by: Yu Zhao +Cc: "Yin, Fengwei" +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 48 +++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 37 insertions(+), 11 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -4723,10 +4723,13 @@ static int evict_pages(struct lruvec *lr + int scanned; + int reclaimed; + LIST_HEAD(list); ++ LIST_HEAD(clean); + struct page *page; ++ struct page *next; + enum vm_event_item item; + struct reclaim_stat stat; + struct lru_gen_mm_walk *walk; ++ bool skip_retry = false; + struct mem_cgroup *memcg = lruvec_memcg(lruvec); + struct pglist_data *pgdat = lruvec_pgdat(lruvec); + +@@ -4743,20 +4746,37 @@ static int evict_pages(struct lruvec *lr + + if (list_empty(&list)) + return scanned; +- ++retry: + reclaimed = shrink_page_list(&list, pgdat, sc, &stat, false); ++ sc->nr_reclaimed += reclaimed; + +- list_for_each_entry(page, &list, lru) { +- /* restore LRU_REFS_FLAGS cleared by isolate_page() */ +- if (PageWorkingset(page)) +- SetPageReferenced(page); ++ list_for_each_entry_safe_reverse(page, next, &list, lru) { ++ if (!page_evictable(page)) { ++ list_del(&page->lru); ++ putback_lru_page(page); ++ continue; ++ } + +- /* don't add rejected pages to the oldest generation */ + if (PageReclaim(page) && +- (PageDirty(page) || PageWriteback(page))) +- ClearPageActive(page); +- else +- SetPageActive(page); ++ (PageDirty(page) || PageWriteback(page))) { ++ /* restore LRU_REFS_FLAGS cleared by isolate_page() */ ++ if (PageWorkingset(page)) ++ SetPageReferenced(page); ++ continue; ++ } ++ ++ if (skip_retry || PageActive(page) || PageReferenced(page) || ++ page_mapped(page) || PageLocked(page) || ++ PageDirty(page) || PageWriteback(page)) { ++ /* don't add rejected pages to the oldest generation */ ++ set_mask_bits(&page->flags, LRU_REFS_MASK | LRU_REFS_FLAGS, ++ BIT(PG_active)); ++ continue; ++ } ++ ++ /* retry pages that may have missed rotate_reclaimable_page() */ ++ list_move(&page->lru, &clean); ++ sc->nr_scanned -= thp_nr_pages(page); + } + + spin_lock_irq(&lruvec->lru_lock); +@@ -4778,7 +4798,13 @@ static int evict_pages(struct lruvec *lr + mem_cgroup_uncharge_list(&list); + free_unref_page_list(&list); + +- sc->nr_reclaimed += reclaimed; ++ INIT_LIST_HEAD(&list); ++ list_splice_init(&clean, &list); ++ ++ if (!list_empty(&list)) { ++ skip_retry = true; ++ goto retry; ++ } + + if (need_swapping && type == LRU_GEN_ANON) + *need_swapping = true; diff --git a/target/linux/generic/backport-5.15/020-v6.1-15-mm-multi-gen-LRU-move-lru_gen_add_mm-out-of-IRQ-off-.patch b/target/linux/generic/backport-5.15/020-v6.1-15-mm-multi-gen-LRU-move-lru_gen_add_mm-out-of-IRQ-off-.patch new file mode 100644 index 00000000000..5b1d378504a --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-15-mm-multi-gen-LRU-move-lru_gen_add_mm-out-of-IRQ-off-.patch @@ -0,0 +1,49 @@ +From 255bb0ac393f1c2818cd75af45a9226300ab3daf Mon Sep 17 00:00:00 2001 +From: Sebastian Andrzej Siewior +Date: Wed, 26 Oct 2022 15:48:30 +0200 +Subject: [PATCH 15/29] mm: multi-gen LRU: move lru_gen_add_mm() out of IRQ-off + region + +lru_gen_add_mm() has been added within an IRQ-off region in the commit +mentioned below. The other invocations of lru_gen_add_mm() are not within +an IRQ-off region. + +The invocation within IRQ-off region is problematic on PREEMPT_RT because +the function is using a spin_lock_t which must not be used within +IRQ-disabled regions. + +The other invocations of lru_gen_add_mm() occur while +task_struct::alloc_lock is acquired. Move lru_gen_add_mm() after +interrupts are enabled and before task_unlock(). + +Link: https://lkml.kernel.org/r/20221026134830.711887-1-bigeasy@linutronix.de +Fixes: bd74fdaea1460 ("mm: multi-gen LRU: support page table walks") +Signed-off-by: Sebastian Andrzej Siewior +Acked-by: Yu Zhao +Cc: Al Viro +Cc: "Eric W . Biederman" +Cc: Kees Cook +Cc: Thomas Gleixner +Signed-off-by: Andrew Morton +--- + fs/exec.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/fs/exec.c ++++ b/fs/exec.c +@@ -1013,7 +1013,6 @@ static int exec_mmap(struct mm_struct *m + active_mm = tsk->active_mm; + tsk->active_mm = mm; + tsk->mm = mm; +- lru_gen_add_mm(mm); + /* + * This prevents preemption while active_mm is being loaded and + * it and mm are being updated, which could cause problems for +@@ -1028,6 +1027,7 @@ static int exec_mmap(struct mm_struct *m + local_irq_enable(); + tsk->mm->vmacache_seqnum = 0; + vmacache_flush(tsk); ++ lru_gen_add_mm(mm); + task_unlock(tsk); + lru_gen_use_mm(mm); + if (old_mm) { diff --git a/target/linux/generic/backport-5.15/020-v6.1-17-mm-add-dummy-pmd_young-for-architectures-not-having-.patch b/target/linux/generic/backport-5.15/020-v6.1-17-mm-add-dummy-pmd_young-for-architectures-not-having-.patch new file mode 100644 index 00000000000..c91252eb6b2 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-17-mm-add-dummy-pmd_young-for-architectures-not-having-.patch @@ -0,0 +1,96 @@ +From c5ec455ebd2b488d91de9d8915a0c8036a2a04dd Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Wed, 30 Nov 2022 14:49:41 -0800 +Subject: [PATCH 17/29] mm: add dummy pmd_young() for architectures not having + it + +In order to avoid #ifdeffery add a dummy pmd_young() implementation as a +fallback. This is required for the later patch "mm: introduce +arch_has_hw_nonleaf_pmd_young()". + +Link: https://lkml.kernel.org/r/fd3ac3cd-7349-6bbd-890a-71a9454ca0b3@suse.com +Signed-off-by: Juergen Gross +Acked-by: Yu Zhao +Cc: Borislav Petkov +Cc: Dave Hansen +Cc: Geert Uytterhoeven +Cc: "H. Peter Anvin" +Cc: Ingo Molnar +Cc: Sander Eikelenboom +Cc: Thomas Gleixner +Signed-off-by: Andrew Morton +--- + arch/mips/include/asm/pgtable.h | 1 + + arch/riscv/include/asm/pgtable.h | 1 + + arch/s390/include/asm/pgtable.h | 1 + + arch/sparc/include/asm/pgtable_64.h | 1 + + arch/x86/include/asm/pgtable.h | 1 + + include/linux/pgtable.h | 7 +++++++ + 6 files changed, 12 insertions(+) + +--- a/arch/mips/include/asm/pgtable.h ++++ b/arch/mips/include/asm/pgtable.h +@@ -632,6 +632,7 @@ static inline pmd_t pmd_mkdirty(pmd_t pm + return pmd; + } + ++#define pmd_young pmd_young + static inline int pmd_young(pmd_t pmd) + { + return !!(pmd_val(pmd) & _PAGE_ACCESSED); +--- a/arch/riscv/include/asm/pgtable.h ++++ b/arch/riscv/include/asm/pgtable.h +@@ -531,6 +531,7 @@ static inline int pmd_dirty(pmd_t pmd) + return pte_dirty(pmd_pte(pmd)); + } + ++#define pmd_young pmd_young + static inline int pmd_young(pmd_t pmd) + { + return pte_young(pmd_pte(pmd)); +--- a/arch/s390/include/asm/pgtable.h ++++ b/arch/s390/include/asm/pgtable.h +@@ -748,6 +748,7 @@ static inline int pmd_dirty(pmd_t pmd) + return (pmd_val(pmd) & _SEGMENT_ENTRY_DIRTY) != 0; + } + ++#define pmd_young pmd_young + static inline int pmd_young(pmd_t pmd) + { + return (pmd_val(pmd) & _SEGMENT_ENTRY_YOUNG) != 0; +--- a/arch/sparc/include/asm/pgtable_64.h ++++ b/arch/sparc/include/asm/pgtable_64.h +@@ -712,6 +712,7 @@ static inline unsigned long pmd_dirty(pm + return pte_dirty(pte); + } + ++#define pmd_young pmd_young + static inline unsigned long pmd_young(pmd_t pmd) + { + pte_t pte = __pte(pmd_val(pmd)); +--- a/arch/x86/include/asm/pgtable.h ++++ b/arch/x86/include/asm/pgtable.h +@@ -136,6 +136,7 @@ static inline int pmd_dirty(pmd_t pmd) + return pmd_flags(pmd) & _PAGE_DIRTY; + } + ++#define pmd_young pmd_young + static inline int pmd_young(pmd_t pmd) + { + return pmd_flags(pmd) & _PAGE_ACCESSED; +--- a/include/linux/pgtable.h ++++ b/include/linux/pgtable.h +@@ -164,6 +164,13 @@ static inline pte_t *virt_to_kpte(unsign + return pmd_none(*pmd) ? NULL : pte_offset_kernel(pmd, vaddr); + } + ++#ifndef pmd_young ++static inline int pmd_young(pmd_t pmd) ++{ ++ return 0; ++} ++#endif ++ + #ifndef __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS + extern int ptep_set_access_flags(struct vm_area_struct *vma, + unsigned long address, pte_t *ptep, diff --git a/target/linux/generic/backport-5.15/020-v6.1-18-mm-introduce-arch_has_hw_nonleaf_pmd_young.patch b/target/linux/generic/backport-5.15/020-v6.1-18-mm-introduce-arch_has_hw_nonleaf_pmd_young.patch new file mode 100644 index 00000000000..9a1f9bead6c --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.1-18-mm-introduce-arch_has_hw_nonleaf_pmd_young.patch @@ -0,0 +1,113 @@ +From 46cbda7b65998a5af4493f745d94417af697bd68 Mon Sep 17 00:00:00 2001 +From: Juergen Gross +Date: Wed, 23 Nov 2022 07:45:10 +0100 +Subject: [PATCH 18/29] mm: introduce arch_has_hw_nonleaf_pmd_young() + +When running as a Xen PV guests commit eed9a328aa1a ("mm: x86: add +CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG") can cause a protection violation in +pmdp_test_and_clear_young(): + + BUG: unable to handle page fault for address: ffff8880083374d0 + #PF: supervisor write access in kernel mode + #PF: error_code(0x0003) - permissions violation + PGD 3026067 P4D 3026067 PUD 3027067 PMD 7fee5067 PTE 8010000008337065 + Oops: 0003 [#1] PREEMPT SMP NOPTI + CPU: 7 PID: 158 Comm: kswapd0 Not tainted 6.1.0-rc5-20221118-doflr+ #1 + RIP: e030:pmdp_test_and_clear_young+0x25/0x40 + +This happens because the Xen hypervisor can't emulate direct writes to +page table entries other than PTEs. + +This can easily be fixed by introducing arch_has_hw_nonleaf_pmd_young() +similar to arch_has_hw_pte_young() and test that instead of +CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG. + +Link: https://lkml.kernel.org/r/20221123064510.16225-1-jgross@suse.com +Fixes: eed9a328aa1a ("mm: x86: add CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG") +Signed-off-by: Juergen Gross +Reported-by: Sander Eikelenboom +Acked-by: Yu Zhao +Tested-by: Sander Eikelenboom +Acked-by: David Hildenbrand [core changes] +Signed-off-by: Andrew Morton +--- + arch/x86/include/asm/pgtable.h | 8 ++++++++ + include/linux/pgtable.h | 11 +++++++++++ + mm/vmscan.c | 10 +++++----- + 3 files changed, 24 insertions(+), 5 deletions(-) + +--- a/arch/x86/include/asm/pgtable.h ++++ b/arch/x86/include/asm/pgtable.h +@@ -1405,6 +1405,14 @@ static inline bool arch_has_hw_pte_young + return true; + } + ++#ifdef CONFIG_XEN_PV ++#define arch_has_hw_nonleaf_pmd_young arch_has_hw_nonleaf_pmd_young ++static inline bool arch_has_hw_nonleaf_pmd_young(void) ++{ ++ return !cpu_feature_enabled(X86_FEATURE_XENPV); ++} ++#endif ++ + #endif /* __ASSEMBLY__ */ + + #endif /* _ASM_X86_PGTABLE_H */ +--- a/include/linux/pgtable.h ++++ b/include/linux/pgtable.h +@@ -266,6 +266,17 @@ static inline int pmdp_clear_flush_young + #endif /* CONFIG_TRANSPARENT_HUGEPAGE */ + #endif + ++#ifndef arch_has_hw_nonleaf_pmd_young ++/* ++ * Return whether the accessed bit in non-leaf PMD entries is supported on the ++ * local CPU. ++ */ ++static inline bool arch_has_hw_nonleaf_pmd_young(void) ++{ ++ return IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG); ++} ++#endif ++ + #ifndef arch_has_hw_pte_young + /* + * Return whether the accessed bit is supported on the local CPU. +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -3727,7 +3727,7 @@ static void walk_pmd_range_locked(pud_t + goto next; + + if (!pmd_trans_huge(pmd[i])) { +- if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_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; +@@ -3825,14 +3825,14 @@ restart: + #endif + walk->mm_stats[MM_NONLEAF_TOTAL]++; + +-#ifdef CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG +- if (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); + } +-#endif ++ + if (!walk->force_scan && !test_bloom_filter(walk->lruvec, walk->max_seq, pmd + i)) + continue; + +@@ -5132,7 +5132,7 @@ static ssize_t show_enabled(struct kobje + if (arch_has_hw_pte_young() && get_cap(LRU_GEN_MM_WALK)) + caps |= BIT(LRU_GEN_MM_WALK); + +- if (IS_ENABLED(CONFIG_ARCH_HAS_NONLEAF_PMD_YOUNG) && get_cap(LRU_GEN_NONLEAF_YOUNG)) ++ if (arch_has_hw_nonleaf_pmd_young() && get_cap(LRU_GEN_NONLEAF_YOUNG)) + caps |= BIT(LRU_GEN_NONLEAF_YOUNG); + + return snprintf(buf, PAGE_SIZE, "0x%04x\n", caps); diff --git a/target/linux/generic/backport-5.15/020-v6.2-16-mm-multi-gen-LRU-fix-crash-during-cgroup-migration.patch b/target/linux/generic/backport-5.15/020-v6.2-16-mm-multi-gen-LRU-fix-crash-during-cgroup-migration.patch new file mode 100644 index 00000000000..e37386abdf1 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.2-16-mm-multi-gen-LRU-fix-crash-during-cgroup-migration.patch @@ -0,0 +1,56 @@ +From c7dfefd4bdfba3d5171038d1cc2d4160288e6ee4 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Sun, 15 Jan 2023 20:44:05 -0700 +Subject: [PATCH 16/29] mm: multi-gen LRU: fix crash during cgroup migration + +lru_gen_migrate_mm() assumes lru_gen_add_mm() runs prior to itself. This +isn't true for the following scenario: + + CPU 1 CPU 2 + + clone() + cgroup_can_fork() + cgroup_procs_write() + cgroup_post_fork() + task_lock() + lru_gen_migrate_mm() + task_unlock() + task_lock() + lru_gen_add_mm() + task_unlock() + +And when the above happens, kernel crashes because of linked list +corruption (mm_struct->lru_gen.list). + +Link: https://lore.kernel.org/r/20230115134651.30028-1-msizanoen@qtmlabs.xyz/ +Link: https://lkml.kernel.org/r/20230116034405.2960276-1-yuzhao@google.com +Fixes: bd74fdaea146 ("mm: multi-gen LRU: support page table walks") +Signed-off-by: Yu Zhao +Reported-by: msizanoen +Tested-by: msizanoen +Cc: [6.1+] +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 5 ++++- + 1 file changed, 4 insertions(+), 1 deletion(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -3024,13 +3024,16 @@ void lru_gen_migrate_mm(struct mm_struct + if (mem_cgroup_disabled()) + return; + ++ /* migration can happen before addition */ ++ if (!mm->lru_gen.memcg) ++ return; ++ + rcu_read_lock(); + memcg = mem_cgroup_from_task(task); + rcu_read_unlock(); + if (memcg == mm->lru_gen.memcg) + return; + +- VM_WARN_ON_ONCE(!mm->lru_gen.memcg); + VM_WARN_ON_ONCE(list_empty(&mm->lru_gen.list)); + + lru_gen_del_mm(mm); diff --git a/target/linux/generic/backport-5.15/020-v6.3-19-mm-add-vma_has_recency.patch b/target/linux/generic/backport-5.15/020-v6.3-19-mm-add-vma_has_recency.patch new file mode 100644 index 00000000000..6154bbe3528 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-19-mm-add-vma_has_recency.patch @@ -0,0 +1,196 @@ +From 6c7f552a48b49a8612786a28a2239fbc24fac289 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Fri, 30 Dec 2022 14:52:51 -0700 +Subject: [PATCH 19/29] 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 +Signed-off-by: Yu Zhao +Cc: Alexander Viro +Cc: Andrea Righi +Cc: Johannes Weiner +Cc: Michael Larabel +Signed-off-by: Andrew Morton +--- + include/linux/mm_inline.h | 9 +++++++++ + mm/memory.c | 8 ++++---- + mm/rmap.c | 42 +++++++++++++++++---------------------- + mm/vmscan.c | 5 ++++- + 4 files changed, 35 insertions(+), 29 deletions(-) + +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -333,4 +333,13 @@ static __always_inline void del_page_fro + update_lru_size(lruvec, page_lru(page), page_zonenum(page), + -thp_nr_pages(page)); + } ++ ++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 +@@ -41,6 +41,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -1353,8 +1354,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)]--; +@@ -4781,8 +4781,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 +@@ -794,25 +794,14 @@ static bool page_referenced_one(struct p + } + + 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 page has been used in another mapping, +- * we will catch it; if this other mapping is +- * already gone, the unmap path will have set +- * PG_referenced or activated the page. +- */ +- 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)) +@@ -846,7 +835,20 @@ static bool invalid_page_referenced_vma( + struct page_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 ++ * page 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 page 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; +@@ -876,6 +878,7 @@ int page_referenced(struct page *page, + .rmap_one = page_referenced_one, + .arg = (void *)&pra, + .anon_lock = page_lock_anon_vma_read, ++ .invalid_vma = invalid_page_referenced_vma, + }; + + *vm_flags = 0; +@@ -891,15 +894,6 @@ int page_referenced(struct page *page, + 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_page_referenced_vma; +- } +- + rmap_walk(page, &rwc); + *vm_flags = pra.vm_flags; + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -3486,7 +3486,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)) diff --git a/target/linux/generic/backport-5.15/020-v6.3-20-mm-support-POSIX_FADV_NOREUSE.patch b/target/linux/generic/backport-5.15/020-v6.3-20-mm-support-POSIX_FADV_NOREUSE.patch new file mode 100644 index 00000000000..3bb075bf367 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-20-mm-support-POSIX_FADV_NOREUSE.patch @@ -0,0 +1,125 @@ +From 686c3d4f71de9e0e7a27f03a5617a712385f90cd Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Fri, 30 Dec 2022 14:52:52 -0700 +Subject: [PATCH 20/29] 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 mark_page_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 < #include + 35d35 + < #include /* _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 +Signed-off-by: Yu Zhao +Cc: Alexander Viro +Cc: Andrea Righi +Cc: Johannes Weiner +Cc: Michael Larabel +Signed-off-by: Andrew Morton +--- + 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 +@@ -167,6 +167,8 @@ typedef int (dio_iodone_t)(struct kiocb + /* File is stream-like */ + #define FMODE_STREAM ((__force fmode_t)0x200000) + ++#define FMODE_NOREUSE ((__force fmode_t)0x400000) ++ + /* 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 +@@ -339,6 +339,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: + if (!inode_write_congested(mapping->host)) diff --git a/target/linux/generic/backport-5.15/020-v6.3-21-mm-multi-gen-LRU-rename-lru_gen_struct-to-lru_gen_pa.patch b/target/linux/generic/backport-5.15/020-v6.3-21-mm-multi-gen-LRU-rename-lru_gen_struct-to-lru_gen_pa.patch new file mode 100644 index 00000000000..4e09173681f --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-21-mm-multi-gen-LRU-rename-lru_gen_struct-to-lru_gen_pa.patch @@ -0,0 +1,348 @@ +From 348fdbada9fb3f0bf1a53651be46319105af187f Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 21 Dec 2022 21:18:59 -0700 +Subject: [PATCH 21/29] mm: multi-gen LRU: rename lru_gen_struct to + lru_gen_page + +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 pages (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 pages): +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_page 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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + 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 +@@ -168,7 +168,7 @@ static inline void lru_gen_update_size(s + int zone = page_zonenum(page); + int delta = thp_nr_pages(page); + enum lru_list lru = type * LRU_INACTIVE_FILE; +- struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ struct lru_gen_page *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); +@@ -214,7 +214,7 @@ static inline bool lru_gen_add_page(stru + int gen = page_lru_gen(page); + int type = page_is_file_lru(page); + int zone = page_zonenum(page); +- struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ struct lru_gen_page *lrugen = &lruvec->lrugen; + + VM_WARN_ON_ONCE_PAGE(gen != -1, page); + +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -394,7 +394,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_page { + /* the aging increments the youngest generation number */ + unsigned long max_seq; + /* the eviction increments the oldest generation numbers */ +@@ -451,7 +451,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_page */ + unsigned long max_seq; + /* the next address within an mm to scan */ + unsigned long next_addr; +@@ -514,7 +514,7 @@ struct lruvec { + unsigned long flags; + #ifdef CONFIG_LRU_GEN + /* evictable pages divided into generations */ +- struct lru_gen_struct lrugen; ++ struct lru_gen_page lrugen; + /* to concurrently iterate lru_gen_mm_list */ + struct lru_gen_mm_state mm_state; + #endif +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -2910,7 +2910,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_page */ + 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; +@@ -3316,7 +3316,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_page *lrugen = &lruvec->lrugen; + int hist = lru_hist_from_seq(lrugen->min_seq[type]); + + pos->refaulted = lrugen->avg_refaulted[type][tier] + +@@ -3331,7 +3331,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_page *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; + +@@ -3408,7 +3408,7 @@ static int page_update_gen(struct page * + static int page_inc_gen(struct lruvec *lruvec, struct page *page, bool reclaiming) + { + int type = page_is_file_lru(page); +- struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ struct lru_gen_page *lrugen = &lruvec->lrugen; + int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]); + unsigned long new_flags, old_flags = READ_ONCE(page->flags); + +@@ -3453,7 +3453,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_page *lrugen = &lruvec->lrugen; + + walk->batched = 0; + +@@ -3979,7 +3979,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_page *lrugen = &lruvec->lrugen; + int new_gen, old_gen = lru_gen_from_seq(lrugen->min_seq[type]); + + if (type == LRU_GEN_ANON && !can_swap) +@@ -4015,7 +4015,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_page *lrugen = &lruvec->lrugen; + DEFINE_MIN_SEQ(lruvec); + + VM_WARN_ON_ONCE(!seq_is_valid(lruvec)); +@@ -4036,7 +4036,7 @@ next: + ; + } + +- /* see the comment on lru_gen_struct */ ++ /* see the comment on lru_gen_page */ + 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]); +@@ -4058,7 +4058,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_page *lrugen = &lruvec->lrugen; + + spin_lock_irq(&lruvec->lru_lock); + +@@ -4116,7 +4116,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_page *lrugen = &lruvec->lrugen; + + VM_WARN_ON_ONCE(max_seq > READ_ONCE(lrugen->max_seq)); + +@@ -4181,7 +4181,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_page *lrugen = &lruvec->lrugen; + struct mem_cgroup *memcg = lruvec_memcg(lruvec); + + for (type = !can_swap; type < ANON_AND_FILE; type++) { +@@ -4466,7 +4466,7 @@ static bool sort_page(struct lruvec *lru + int delta = thp_nr_pages(page); + int refs = page_lru_refs(page); + int tier = lru_tier_from_refs(refs); +- struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ struct lru_gen_page *lrugen = &lruvec->lrugen; + + VM_WARN_ON_ONCE_PAGE(gen >= MAX_NR_GENS, page); + +@@ -4566,7 +4566,7 @@ static int scan_pages(struct lruvec *lru + int scanned = 0; + int isolated = 0; + int remaining = MAX_LRU_BATCH; +- struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ struct lru_gen_page *lrugen = &lruvec->lrugen; + struct mem_cgroup *memcg = lruvec_memcg(lruvec); + + VM_WARN_ON_ONCE(!list_empty(list)); +@@ -4967,7 +4967,7 @@ done: + + static bool __maybe_unused state_is_valid(struct lruvec *lruvec) + { +- struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ struct lru_gen_page *lrugen = &lruvec->lrugen; + + if (lrugen->enabled) { + enum lru_list lru; +@@ -5247,7 +5247,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_page *lrugen = &lruvec->lrugen; + + for (tier = 0; tier < MAX_NR_TIERS; tier++) { + seq_printf(m, " %10d", tier); +@@ -5296,7 +5296,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_page *lrugen = &lruvec->lrugen; + int nid = lruvec_pgdat(lruvec)->node_id; + struct mem_cgroup *memcg = lruvec_memcg(lruvec); + DEFINE_MAX_SEQ(lruvec); +@@ -5549,7 +5549,7 @@ void lru_gen_init_lruvec(struct lruvec * + { + int i; + int gen, type, zone; +- struct lru_gen_struct *lrugen = &lruvec->lrugen; ++ struct lru_gen_page *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 pag + unsigned long token; + unsigned long min_seq; + struct lruvec *lruvec; +- struct lru_gen_struct *lrugen; ++ struct lru_gen_page *lrugen; + int type = page_is_file_lru(page); + int delta = thp_nr_pages(page); + int refs = page_lru_refs(page); +@@ -252,7 +252,7 @@ static void lru_gen_refault(struct page + unsigned long token; + unsigned long min_seq; + struct lruvec *lruvec; +- struct lru_gen_struct *lrugen; ++ struct lru_gen_page *lrugen; + struct mem_cgroup *memcg; + struct pglist_data *pgdat; + int type = page_is_file_lru(page); diff --git a/target/linux/generic/backport-5.15/020-v6.3-22-mm-multi-gen-LRU-rename-lrugen-lists-to-lrugen-pages.patch b/target/linux/generic/backport-5.15/020-v6.3-22-mm-multi-gen-LRU-rename-lrugen-lists-to-lrugen-pages.patch new file mode 100644 index 00000000000..b548c1c8b3d --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-22-mm-multi-gen-LRU-rename-lrugen-lists-to-lrugen-pages.patch @@ -0,0 +1,162 @@ +From afd37e73db04c7e6b47411120ac5f6a7eca51fec Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 21 Dec 2022 21:19:00 -0700 +Subject: [PATCH 22/29] mm: multi-gen LRU: rename lrugen->lists[] to + lrugen->pages[] + +lru_gen_page will be chained into per-node lists by the coming +lrugen->list. + +Link: https://lkml.kernel.org/r/20221222041905.2431096-3-yuzhao@google.com +Signed-off-by: Yu Zhao +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + include/linux/mm_inline.h | 4 ++-- + include/linux/mmzone.h | 8 ++++---- + mm/vmscan.c | 20 ++++++++++---------- + 3 files changed, 16 insertions(+), 16 deletions(-) + +--- a/include/linux/mm_inline.h ++++ b/include/linux/mm_inline.h +@@ -246,9 +246,9 @@ static inline bool lru_gen_add_page(stru + lru_gen_update_size(lruvec, page, -1, gen); + /* for rotate_reclaimable_page() */ + if (reclaiming) +- list_add_tail(&page->lru, &lrugen->lists[gen][type][zone]); ++ list_add_tail(&page->lru, &lrugen->pages[gen][type][zone]); + else +- list_add(&page->lru, &lrugen->lists[gen][type][zone]); ++ list_add(&page->lru, &lrugen->pages[gen][type][zone]); + + return true; + } +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -302,7 +302,7 @@ enum lruvec_flags { + * They form a sliding window of a variable size [MIN_NR_GENS, MAX_NR_GENS]. An + * offset within MAX_NR_GENS, i.e., gen, indexes the LRU list of the + * corresponding generation. The gen counter in page->flags stores gen+1 while +- * a page is on one of lrugen->lists[]. Otherwise it stores 0. ++ * a page is on one of lrugen->pages[]. Otherwise it stores 0. + * + * A page is added to the youngest generation on faulting. The aging needs to + * check the accessed bit at least twice before handing this page over to the +@@ -314,8 +314,8 @@ enum lruvec_flags { + * rest of generations, if they exist, are considered inactive. See + * lru_gen_is_active(). + * +- * PG_active is always cleared while a page is on one of lrugen->lists[] so that +- * the aging needs not to worry about it. And it's set again when a page ++ * PG_active is always cleared while a page is on one of lrugen->pages[] so ++ * that the aging needs not to worry about it. And it's set again when a page + * considered active is isolated for non-reclaiming purposes, e.g., migration. + * See lru_gen_add_page() and lru_gen_del_page(). + * +@@ -402,7 +402,7 @@ struct lru_gen_page { + /* the birth time of each generation in jiffies */ + unsigned long timestamps[MAX_NR_GENS]; + /* the multi-gen LRU lists, lazily sorted on eviction */ +- struct list_head lists[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; ++ struct list_head pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; + /* the multi-gen LRU sizes, eventually consistent */ + long nr_pages[MAX_NR_GENS][ANON_AND_FILE][MAX_NR_ZONES]; + /* the exponential moving average of refaulted */ +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -3987,7 +3987,7 @@ static bool inc_min_seq(struct lruvec *l + + /* prevent cold/hot inversion if force_scan is true */ + for (zone = 0; zone < MAX_NR_ZONES; zone++) { +- struct list_head *head = &lrugen->lists[old_gen][type][zone]; ++ struct list_head *head = &lrugen->pages[old_gen][type][zone]; + + while (!list_empty(head)) { + struct page *page = lru_to_page(head); +@@ -3998,7 +3998,7 @@ static bool inc_min_seq(struct lruvec *l + VM_WARN_ON_ONCE_PAGE(page_zonenum(page) != zone, page); + + new_gen = page_inc_gen(lruvec, page, false); +- list_move_tail(&page->lru, &lrugen->lists[new_gen][type][zone]); ++ list_move_tail(&page->lru, &lrugen->pages[new_gen][type][zone]); + + if (!--remaining) + return false; +@@ -4026,7 +4026,7 @@ static bool try_to_inc_min_seq(struct lr + gen = lru_gen_from_seq(min_seq[type]); + + for (zone = 0; zone < MAX_NR_ZONES; zone++) { +- if (!list_empty(&lrugen->lists[gen][type][zone])) ++ if (!list_empty(&lrugen->pages[gen][type][zone])) + goto next; + } + +@@ -4491,7 +4491,7 @@ static bool sort_page(struct lruvec *lru + + /* promoted */ + if (gen != lru_gen_from_seq(lrugen->min_seq[type])) { +- list_move(&page->lru, &lrugen->lists[gen][type][zone]); ++ list_move(&page->lru, &lrugen->pages[gen][type][zone]); + return true; + } + +@@ -4500,7 +4500,7 @@ static bool sort_page(struct lruvec *lru + int hist = lru_hist_from_seq(lrugen->min_seq[type]); + + gen = page_inc_gen(lruvec, page, false); +- list_move_tail(&page->lru, &lrugen->lists[gen][type][zone]); ++ list_move_tail(&page->lru, &lrugen->pages[gen][type][zone]); + + WRITE_ONCE(lrugen->protected[hist][type][tier - 1], + lrugen->protected[hist][type][tier - 1] + delta); +@@ -4512,7 +4512,7 @@ static bool sort_page(struct lruvec *lru + if (PageLocked(page) || PageWriteback(page) || + (type == LRU_GEN_FILE && PageDirty(page))) { + gen = page_inc_gen(lruvec, page, true); +- list_move(&page->lru, &lrugen->lists[gen][type][zone]); ++ list_move(&page->lru, &lrugen->pages[gen][type][zone]); + return true; + } + +@@ -4579,7 +4579,7 @@ static int scan_pages(struct lruvec *lru + for (zone = sc->reclaim_idx; zone >= 0; zone--) { + LIST_HEAD(moved); + int skipped = 0; +- struct list_head *head = &lrugen->lists[gen][type][zone]; ++ struct list_head *head = &lrugen->pages[gen][type][zone]; + + while (!list_empty(head)) { + struct page *page = lru_to_page(head); +@@ -4980,7 +4980,7 @@ static bool __maybe_unused state_is_vali + int gen, type, zone; + + for_each_gen_type_zone(gen, type, zone) { +- if (!list_empty(&lrugen->lists[gen][type][zone])) ++ if (!list_empty(&lrugen->pages[gen][type][zone])) + return false; + } + } +@@ -5025,7 +5025,7 @@ static bool drain_evictable(struct lruve + int remaining = MAX_LRU_BATCH; + + for_each_gen_type_zone(gen, type, zone) { +- struct list_head *head = &lruvec->lrugen.lists[gen][type][zone]; ++ struct list_head *head = &lruvec->lrugen.pages[gen][type][zone]; + + while (!list_empty(head)) { + bool success; +@@ -5558,7 +5558,7 @@ void lru_gen_init_lruvec(struct lruvec * + lrugen->timestamps[i] = jiffies; + + for_each_gen_type_zone(gen, type, zone) +- INIT_LIST_HEAD(&lrugen->lists[gen][type][zone]); ++ INIT_LIST_HEAD(&lrugen->pages[gen][type][zone]); + + lruvec->mm_state.seq = MIN_NR_GENS; + init_waitqueue_head(&lruvec->mm_state.wait); diff --git a/target/linux/generic/backport-5.15/020-v6.3-23-mm-multi-gen-LRU-remove-eviction-fairness-safeguard.patch b/target/linux/generic/backport-5.15/020-v6.3-23-mm-multi-gen-LRU-remove-eviction-fairness-safeguard.patch new file mode 100644 index 00000000000..2bb6e12a5ae --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-23-mm-multi-gen-LRU-remove-eviction-fairness-safeguard.patch @@ -0,0 +1,188 @@ +From ce45f1c4b32cf69b166f56ef5bc6c761e06ed4e5 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 21 Dec 2022 21:19:01 -0700 +Subject: [PATCH 23/29] mm: multi-gen LRU: remove eviction fairness safeguard + +Recall that the eviction consumes the oldest generation: first it +bucket-sorts pages 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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 82 +++++++++++++++-------------------------------------- + 1 file changed, 23 insertions(+), 59 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -443,6 +443,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 +@@ -493,6 +498,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; +@@ -4722,8 +4732,7 @@ static int isolate_pages(struct lruvec * + return scanned; + } + +-static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness, +- bool *need_swapping) ++static int evict_pages(struct lruvec *lruvec, struct scan_control *sc, int swappiness) + { + int type; + int scanned; +@@ -4812,9 +4821,6 @@ retry: + goto retry; + } + +- if (need_swapping && type == LRU_GEN_ANON) +- *need_swapping = true; +- + return scanned; + } + +@@ -4853,68 +4859,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 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 target isn't +- * met, and yet the allocation may still succeed, since kswapd may have +- * caught up. In either case, it's better to stop now, and restart 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(); + +@@ -4938,7 +4902,7 @@ static void lru_gen_shrink_lruvec(struct + if (!nr_to_scan) + goto done; + +- delta = evict_pages(lruvec, sc, swappiness, &need_swapping); ++ delta = evict_pages(lruvec, sc, swappiness); + if (!delta) + goto done; + +@@ -4946,7 +4910,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(); +@@ -5393,7 +5357,7 @@ static int run_eviction(struct lruvec *l + if (sc->nr_reclaimed >= nr_to_reclaim) + return 0; + +- if (!evict_pages(lruvec, sc, swappiness, NULL)) ++ if (!evict_pages(lruvec, sc, swappiness)) + return 0; + + cond_resched(); diff --git a/target/linux/generic/backport-5.15/020-v6.3-24-mm-multi-gen-LRU-remove-aging-fairness-safeguard.patch b/target/linux/generic/backport-5.15/020-v6.3-24-mm-multi-gen-LRU-remove-aging-fairness-safeguard.patch new file mode 100644 index 00000000000..316217ed028 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-24-mm-multi-gen-LRU-remove-aging-fairness-safeguard.patch @@ -0,0 +1,287 @@ +From e20b7386fccc18c791796eb1dc1a91eee3ccf801 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 21 Dec 2022 21:19:02 -0700 +Subject: [PATCH 24/29] mm: multi-gen LRU: remove aging fairness safeguard + +Recall that the aging produces the youngest generation: first it scans +for accessed pages 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 pages. 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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 126 ++++++++++++++++++++++++---------------------------- + 1 file changed, 59 insertions(+), 67 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -131,7 +131,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 + +@@ -4184,7 +4183,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; +@@ -4193,6 +4192,13 @@ static bool should_run_aging(struct lruv + unsigned long total = 0; + struct lru_gen_page *lrugen = &lruvec->lrugen; + struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ DEFINE_MIN_SEQ(lruvec); ++ ++ /* whether this lruvec is completely out of cold pages */ ++ 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; +@@ -4221,8 +4227,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; + +@@ -4241,40 +4245,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_page *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_page */ ++ 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 */ +@@ -4283,46 +4301,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 = { +@@ -4830,33 +4834,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 pages */ ++ return 0; + } + + static unsigned long get_nr_to_reclaim(struct scan_control *sc) +@@ -4875,9 +4873,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(); +@@ -4898,13 +4894,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_pages(lruvec, sc, swappiness); + if (!delta) +- goto done; ++ break; + + scanned += delta; + if (scanned >= nr_to_scan) +@@ -4916,10 +4912,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); diff --git a/target/linux/generic/backport-5.15/020-v6.3-25-mm-multi-gen-LRU-shuffle-should_run_aging.patch b/target/linux/generic/backport-5.15/020-v6.3-25-mm-multi-gen-LRU-shuffle-should_run_aging.patch new file mode 100644 index 00000000000..391ee6e67ce --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-25-mm-multi-gen-LRU-shuffle-should_run_aging.patch @@ -0,0 +1,161 @@ +From 107d54931df3c28d81648122e219bf0034ef4e99 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 21 Dec 2022 21:19:03 -0700 +Subject: [PATCH 25/29] 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 +Signed-off-by: Yu Zhao +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 124 ++++++++++++++++++++++++++-------------------------- + 1 file changed, 62 insertions(+), 62 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -4183,68 +4183,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_page *lrugen = &lruvec->lrugen; +- struct mem_cgroup *memcg = lruvec_memcg(lruvec); +- DEFINE_MIN_SEQ(lruvec); +- +- /* whether this lruvec is completely out of cold pages */ +- 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; +@@ -4828,6 +4766,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_page *lrugen = &lruvec->lrugen; ++ struct mem_cgroup *memcg = lruvec_memcg(lruvec); ++ DEFINE_MIN_SEQ(lruvec); ++ ++ /* whether this lruvec is completely out of cold pages */ ++ 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 diff --git a/target/linux/generic/backport-5.15/020-v6.3-26-mm-multi-gen-LRU-per-node-lru_gen_page-lists.patch b/target/linux/generic/backport-5.15/020-v6.3-26-mm-multi-gen-LRU-per-node-lru_gen_page-lists.patch new file mode 100644 index 00000000000..cf01c3997a2 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-26-mm-multi-gen-LRU-per-node-lru_gen_page-lists.patch @@ -0,0 +1,868 @@ +From fa6363828d314e837c5f79e97ea5e8c0d2f7f062 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 21 Dec 2022 21:19:04 -0700 +Subject: [PATCH 26/29] mm: multi-gen LRU: per-node lru_gen_page 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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + 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 | 373 +++++++++++++++++++++++++++++++++---- + 6 files changed, 499 insertions(+), 35 deletions(-) + +--- a/include/linux/memcontrol.h ++++ b/include/linux/memcontrol.h +@@ -818,6 +818,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) +@@ -1283,6 +1288,11 @@ struct mem_cgroup *mem_cgroup_from_css(s + return NULL; + } + ++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 +@@ -112,6 +112,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; +@@ -287,6 +299,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_page(struct lruvec *lruvec, struct page *page, bool reclaiming) + { + return false; +--- a/include/linux/mmzone.h ++++ b/include/linux/mmzone.h +@@ -7,6 +7,7 @@ + + #include + #include ++#include + #include + #include + #include +@@ -357,6 +358,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 { +@@ -416,6 +426,14 @@ struct lru_gen_page { + 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_page belongs to */ ++ u8 gen; ++ /* the list segment this lru_gen_page belongs to */ ++ u8 seg; ++ /* per-node lru_gen_page list for global reclaim */ ++ struct hlist_nulls_node list; ++#endif + }; + + enum { +@@ -469,12 +487,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_page per node */ ++ unsigned long nr_memcgs[MEMCG_NR_GENS]; ++ /* per-node lru_gen_page 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) + { + } +@@ -484,6 +577,7 @@ static inline void lru_gen_look_around(s + } + + #ifdef CONFIG_MEMCG ++ + static inline void lru_gen_init_memcg(struct mem_cgroup *memcg) + { + } +@@ -491,7 +585,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 */ + +@@ -1105,6 +1216,8 @@ typedef struct pglist_data { + #ifdef CONFIG_LRU_GEN + /* kswap mm walk data */ + struct lru_gen_mm_walk mm_walk; ++ /* lru_gen_page list */ ++ struct lru_gen_memcg memcg_lru; + #endif + + ZONE_PADDING(_pad2_) +--- a/mm/memcontrol.c ++++ b/mm/memcontrol.c +@@ -549,6 +549,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 = &mem_cgroup_page_nodeinfo(memcg, page)->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_from_page(page); + if (!mctz) + return; +@@ -3433,6 +3443,9 @@ unsigned long mem_cgroup_soft_limit_recl + unsigned long excess; + unsigned long nr_scanned; + ++ if (lru_gen_enabled()) ++ return 0; ++ + if (order > 0) + return 0; + +@@ -5321,6 +5334,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; + } + +@@ -5347,6 +5361,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); + +@@ -5358,6 +5373,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 +@@ -7645,6 +7645,7 @@ static void __init free_area_init_node(i + pgdat_set_deferred_range(pgdat); + + free_area_init_core(pgdat); ++ lru_gen_init_pgdat(pgdat); + } + + void __init free_area_init_memoryless_node(int nid) +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -54,6 +54,8 @@ + #include + #include + #include ++#include ++#include + + #include + #include +@@ -129,11 +131,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; + +@@ -2880,6 +2877,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); +@@ -4169,8 +4169,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)); +@@ -4243,8 +4242,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; +@@ -4833,8 +4830,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); +@@ -4851,10 +4847,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 pages */ +- 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) +@@ -4863,29 +4857,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); +@@ -4895,7 +4878,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_pages(lruvec, sc, swappiness); +@@ -4912,10 +4895,250 @@ 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); ++ ++ 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_page *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 = prandom_u32_max(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 = prandom_u32_max(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 +@@ -5370,11 +5593,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) +@@ -5521,6 +5744,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); +@@ -5544,7 +5780,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 = prandom_u32_max(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) + { +@@ -5571,6 +5869,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) +@@ -5584,7 +5886,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; + } +@@ -5826,6 +6128,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: diff --git a/target/linux/generic/backport-5.15/020-v6.3-27-mm-multi-gen-LRU-clarify-scan_control-flags.patch b/target/linux/generic/backport-5.15/020-v6.3-27-mm-multi-gen-LRU-clarify-scan_control-flags.patch new file mode 100644 index 00000000000..882f29e9892 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-27-mm-multi-gen-LRU-clarify-scan_control-flags.patch @@ -0,0 +1,196 @@ +From 93147736b5b3a21bea24313bfc7a696829932009 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 21 Dec 2022 21:19:05 -0700 +Subject: [PATCH 27/29] 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 pages are more + likely to exist. +4. sc->may_writepage and sc->may_unmap, which indicates opportunistic + reclaim, are rejected, since unmapped clean pages 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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 55 +++++++++++++++++++++++++++-------------------------- + 1 file changed, 28 insertions(+), 27 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -2905,6 +2905,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; +@@ -3952,7 +3955,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; + +@@ -3960,7 +3963,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); +@@ -4146,7 +4149,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; +@@ -4215,8 +4218,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_page */ + gen = lru_gen_from_seq(min_seq[LRU_GEN_FILE]); + birth = READ_ONCE(lruvec->lrugen.timestamps[gen]); +@@ -4472,12 +4473,8 @@ static bool isolate_page(struct lruvec * + { + bool success; + +- /* unmapping inhibited */ +- if (!sc->may_unmap && page_mapped(page)) +- return false; +- + /* swapping inhibited */ +- if (!(sc->may_writepage && (sc->gfp_mask & __GFP_IO)) && ++ if (!(sc->gfp_mask & __GFP_IO) && + (PageDirty(page) || + (PageAnon(page) && !PageSwapCache(page)))) + return false; +@@ -4574,9 +4571,8 @@ static int scan_pages(struct lruvec *lru + __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 pages due to reclaim_idx. Check the ++ * remaining to prevent livelock if it's not making progress. + */ + return isolated || !remaining ? scanned : 0; + } +@@ -4836,8 +4832,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)) +@@ -4865,17 +4860,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 pages 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) +@@ -5005,12 +4997,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, false); + + if (try_to_shrink_lruvec(lruvec, sc)) + lru_gen_rotate_memcg(lruvec, MEMCG_LRU_YOUNG); +@@ -5066,11 +5059,19 @@ static void lru_gen_shrink_node(struct p + + VM_WARN_ON_ONCE(!global_reclaim(sc)); + ++ /* ++ * Unmapped clean pages 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, false); + + set_initial_priority(pgdat, sc); + +@@ -5088,7 +5089,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; + } +@@ -5656,7 +5657,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; + } diff --git a/target/linux/generic/backport-5.15/020-v6.3-28-mm-multi-gen-LRU-simplify-arch_has_hw_pte_young-chec.patch b/target/linux/generic/backport-5.15/020-v6.3-28-mm-multi-gen-LRU-simplify-arch_has_hw_pte_young-chec.patch new file mode 100644 index 00000000000..38d0e5496cc --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-28-mm-multi-gen-LRU-simplify-arch_has_hw_pte_young-chec.patch @@ -0,0 +1,34 @@ +From cf3297e4c7a928da8b2b2f0baff2f9c69ea57952 Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Wed, 21 Dec 2022 21:19:06 -0700 +Subject: [PATCH 28/29] 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 +Cc: Johannes Weiner +Cc: Jonathan Corbet +Cc: Michael Larabel +Cc: Michal Hocko +Cc: Mike Rapoport +Cc: Roman Gushchin +Cc: Suren Baghdasaryan +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -4144,7 +4144,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; + } diff --git a/target/linux/generic/backport-5.15/020-v6.3-29-mm-multi-gen-LRU-avoid-futile-retries.patch b/target/linux/generic/backport-5.15/020-v6.3-29-mm-multi-gen-LRU-avoid-futile-retries.patch new file mode 100644 index 00000000000..2d19d0d7da1 --- /dev/null +++ b/target/linux/generic/backport-5.15/020-v6.3-29-mm-multi-gen-LRU-avoid-futile-retries.patch @@ -0,0 +1,88 @@ +From cc67f962cc53f6e1dfa92eb85b7b26fe83a3c66f Mon Sep 17 00:00:00 2001 +From: Yu Zhao +Date: Mon, 13 Feb 2023 00:53:22 -0700 +Subject: [PATCH 29/29] 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 +Reported-by: T.J. Mercier +Signed-off-by: Andrew Morton +--- + mm/vmscan.c | 25 +++++++++++++++---------- + 1 file changed, 15 insertions(+), 10 deletions(-) + +--- a/mm/vmscan.c ++++ b/mm/vmscan.c +@@ -4934,18 +4934,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_page *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 = prandom_u32_max(MEMCG_NR_BINS); + restart: ++ op = 0; ++ memcg = NULL; + gen = get_memcg_gen(READ_ONCE(pgdat->memcg_lru.seq)); + + rcu_read_lock(); +@@ -4969,14 +4971,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; +@@ -4985,11 +4995,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) diff --git a/target/linux/generic/backport-5.15/060-v6.0-01-tools-build-Add-feature-test-for-init_disassemble_in.patch b/target/linux/generic/backport-5.15/060-v6.0-01-tools-build-Add-feature-test-for-init_disassemble_in.patch deleted file mode 100644 index f25e9ff917b..00000000000 --- a/target/linux/generic/backport-5.15/060-v6.0-01-tools-build-Add-feature-test-for-init_disassemble_in.patch +++ /dev/null @@ -1,95 +0,0 @@ -From 598ada195606eb0e577db0487dd59a2536f206ce Mon Sep 17 00:00:00 2001 -From: Andres Freund -Date: Sun, 31 Jul 2022 18:38:27 -0700 -Subject: [PATCH 1/5] tools build: Add feature test for init_disassemble_info - API changes - -binutils changed the signature of init_disassemble_info(), which now causes -compilation failures for tools/{perf,bpf}, e.g. on debian unstable. - -Relevant binutils commit: - - https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07 - -This commit adds a feature test to detect the new signature. Subsequent -commits will use it to fix the build failures. - -Signed-off-by: Andres Freund -Acked-by: Quentin Monnet -Cc: Alexei Starovoitov -Cc: Ben Hutchings -Cc: Jiri Olsa -Cc: Quentin Monnet -Cc: Sedat Dilek -Cc: bpf@vger.kernel.org -Link: http://lore.kernel.org/lkml/20220622181918.ykrs5rsnmx3og4sv@alap3.anarazel.de -Link: https://lore.kernel.org/r/20220801013834.156015-2-andres@anarazel.de -Signed-off-by: Arnaldo Carvalho de Melo -(cherry picked from commit cfd59ca91467056bb2c36907b2fa67b8e1af9952) ---- - tools/build/Makefile.feature | 1 + - tools/build/feature/Makefile | 4 ++++ - tools/build/feature/test-all.c | 4 ++++ - tools/build/feature/test-disassembler-init-styled.c | 13 +++++++++++++ - 4 files changed, 22 insertions(+) - create mode 100644 tools/build/feature/test-disassembler-init-styled.c - ---- a/tools/build/Makefile.feature -+++ b/tools/build/Makefile.feature -@@ -69,6 +69,7 @@ FEATURE_TESTS_BASIC := - libaio \ - libzstd \ - disassembler-four-args \ -+ disassembler-init-styled \ - file-handle - - # FEATURE_TESTS_BASIC + FEATURE_TESTS_EXTRA is the complete list ---- a/tools/build/feature/Makefile -+++ b/tools/build/feature/Makefile -@@ -18,6 +18,7 @@ FILES= - test-libbfd.bin \ - test-libbfd-buildid.bin \ - test-disassembler-four-args.bin \ -+ test-disassembler-init-styled.bin \ - test-reallocarray.bin \ - test-libbfd-liberty.bin \ - test-libbfd-liberty-z.bin \ -@@ -239,6 +240,9 @@ $(OUTPUT)test-libbfd-buildid.bin: - $(OUTPUT)test-disassembler-four-args.bin: - $(BUILD) -DPACKAGE='"perf"' -lbfd -lopcodes - -+$(OUTPUT)test-disassembler-init-styled.bin: -+ $(BUILD) -DPACKAGE='"perf"' -lbfd -lopcodes -+ - $(OUTPUT)test-reallocarray.bin: - $(BUILD) - ---- a/tools/build/feature/test-all.c -+++ b/tools/build/feature/test-all.c -@@ -166,6 +166,10 @@ - # include "test-disassembler-four-args.c" - #undef main - -+#define main main_test_disassembler_init_styled -+# include "test-disassembler-init-styled.c" -+#undef main -+ - #define main main_test_libzstd - # include "test-libzstd.c" - #undef main ---- /dev/null -+++ b/tools/build/feature/test-disassembler-init-styled.c -@@ -0,0 +1,13 @@ -+// SPDX-License-Identifier: GPL-2.0 -+#include -+#include -+ -+int main(void) -+{ -+ struct disassemble_info info; -+ -+ init_disassemble_info(&info, stdout, -+ NULL, NULL); -+ -+ return 0; -+} diff --git a/target/linux/generic/backport-5.15/060-v6.0-02-tools-include-add-dis-asm-compat.h-to-handle-version.patch b/target/linux/generic/backport-5.15/060-v6.0-02-tools-include-add-dis-asm-compat.h-to-handle-version.patch deleted file mode 100644 index 8c6ba187180..00000000000 --- a/target/linux/generic/backport-5.15/060-v6.0-02-tools-include-add-dis-asm-compat.h-to-handle-version.patch +++ /dev/null @@ -1,96 +0,0 @@ -From 08ec5766e5cf7b24fdebefb83b6f760bceeddf40 Mon Sep 17 00:00:00 2001 -From: Andres Freund -Date: Sun, 31 Jul 2022 18:38:29 -0700 -Subject: [PATCH 2/5] tools include: add dis-asm-compat.h to handle version - differences - -binutils changed the signature of init_disassemble_info(), which now causes -compilation failures for tools/{perf,bpf}, e.g. on debian unstable. - -Relevant binutils commit: - - https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07 - -This commit introduces a wrapper for init_disassemble_info(), to avoid -spreading #ifdef DISASM_INIT_STYLED to a bunch of places. Subsequent -commits will use it to fix the build failures. - -It likely is worth adding a wrapper for disassember(), to avoid the already -existing DISASM_FOUR_ARGS_SIGNATURE ifdefery. - -Signed-off-by: Andres Freund -Signed-off-by: Ben Hutchings -Acked-by: Quentin Monnet -Cc: Alexei Starovoitov -Cc: Ben Hutchings -Cc: Jiri Olsa -Cc: Quentin Monnet -Cc: Sedat Dilek -Cc: bpf@vger.kernel.org -Link: http://lore.kernel.org/lkml/20220622181918.ykrs5rsnmx3og4sv@alap3.anarazel.de -Link: https://lore.kernel.org/r/20220801013834.156015-4-andres@anarazel.de -Signed-off-by: Arnaldo Carvalho de Melo -(cherry picked from commit a45b3d6926231c3d024ea0de4f7bd967f83709ee) ---- - tools/include/tools/dis-asm-compat.h | 55 ++++++++++++++++++++++++++++ - 1 file changed, 55 insertions(+) - create mode 100644 tools/include/tools/dis-asm-compat.h - ---- /dev/null -+++ b/tools/include/tools/dis-asm-compat.h -@@ -0,0 +1,55 @@ -+/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ -+#ifndef _TOOLS_DIS_ASM_COMPAT_H -+#define _TOOLS_DIS_ASM_COMPAT_H -+ -+#include -+#include -+ -+/* define types for older binutils version, to centralize ifdef'ery a bit */ -+#ifndef DISASM_INIT_STYLED -+enum disassembler_style {DISASSEMBLER_STYLE_NOT_EMPTY}; -+typedef int (*fprintf_styled_ftype) (void *, enum disassembler_style, const char*, ...); -+#endif -+ -+/* -+ * Trivial fprintf wrapper to be used as the fprintf_styled_func argument to -+ * init_disassemble_info_compat() when normal fprintf suffices. -+ */ -+static inline int fprintf_styled(void *out, -+ enum disassembler_style style, -+ const char *fmt, ...) -+{ -+ va_list args; -+ int r; -+ -+ (void)style; -+ -+ va_start(args, fmt); -+ r = vfprintf(out, fmt, args); -+ va_end(args); -+ -+ return r; -+} -+ -+/* -+ * Wrapper for init_disassemble_info() that hides version -+ * differences. Depending on binutils version and architecture either -+ * fprintf_func or fprintf_styled_func will be called. -+ */ -+static inline void init_disassemble_info_compat(struct disassemble_info *info, -+ void *stream, -+ fprintf_ftype unstyled_func, -+ fprintf_styled_ftype styled_func) -+{ -+#ifdef DISASM_INIT_STYLED -+ init_disassemble_info(info, stream, -+ unstyled_func, -+ styled_func); -+#else -+ (void)styled_func; -+ init_disassemble_info(info, stream, -+ unstyled_func); -+#endif -+} -+ -+#endif /* _TOOLS_DIS_ASM_COMPAT_H */ diff --git a/target/linux/generic/backport-5.15/060-v6.0-03-tools-perf-Fix-compilation-error-with-new-binutils.patch b/target/linux/generic/backport-5.15/060-v6.0-03-tools-perf-Fix-compilation-error-with-new-binutils.patch deleted file mode 100644 index 27fb4011fc9..00000000000 --- a/target/linux/generic/backport-5.15/060-v6.0-03-tools-perf-Fix-compilation-error-with-new-binutils.patch +++ /dev/null @@ -1,111 +0,0 @@ -From 3bc373152a3a00742750dbbe974d541af78231e6 Mon Sep 17 00:00:00 2001 -From: Andres Freund -Date: Sun, 31 Jul 2022 18:38:30 -0700 -Subject: [PATCH 3/5] tools perf: Fix compilation error with new binutils - -binutils changed the signature of init_disassemble_info(), which now causes -compilation failures for tools/perf/util/annotate.c, e.g. on debian -unstable. - -Relevant binutils commit: - - https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07 - -Wire up the feature test and switch to init_disassemble_info_compat(), -which were introduced in prior commits, fixing the compilation failure. - -I verified that perf can still disassemble bpf programs by using bpftrace -under load, recording a perf trace, and then annotating the bpf "function" -with and without the changes. With old binutils there's no change in output -before/after this patch. When comparing the output from old binutils (2.35) -to new bintuils with the patch (upstream snapshot) there are a few output -differences, but they are unrelated to this patch. An example hunk is: - - 1.15 : 55:mov %rbp,%rdx - 0.00 : 58:add $0xfffffffffffffff8,%rdx - 0.00 : 5c:xor %ecx,%ecx - - 1.03 : 5e:callq 0xffffffffe12aca3c - + 1.03 : 5e:call 0xffffffffe12aca3c - 0.00 : 63:xor %eax,%eax - - 2.18 : 65:leaveq - - 2.82 : 66:retq - + 2.18 : 65:leave - + 2.82 : 66:ret - -Signed-off-by: Andres Freund -Acked-by: Quentin Monnet -Cc: Alexei Starovoitov -Cc: Ben Hutchings -Cc: Jiri Olsa -Cc: Sedat Dilek -Cc: bpf@vger.kernel.org -Link: http://lore.kernel.org/lkml/20220622181918.ykrs5rsnmx3og4sv@alap3.anarazel.de -Link: https://lore.kernel.org/r/20220801013834.156015-5-andres@anarazel.de -Signed-off-by: Arnaldo Carvalho de Melo -(cherry picked from commit 83aa0120487e8bc3f231e72c460add783f71f17c) ---- - tools/perf/Makefile.config | 8 ++++++++ - tools/perf/util/annotate.c | 7 ++++--- - 2 files changed, 12 insertions(+), 3 deletions(-) - ---- a/tools/perf/Makefile.config -+++ b/tools/perf/Makefile.config -@@ -296,6 +296,7 @@ FEATURE_CHECK_LDFLAGS-libpython := $(PYT - FEATURE_CHECK_LDFLAGS-libaio = -lrt - - FEATURE_CHECK_LDFLAGS-disassembler-four-args = -lbfd -lopcodes -ldl -+FEATURE_CHECK_LDFLAGS-disassembler-init-styled = -lbfd -lopcodes -ldl - - CORE_CFLAGS += -fno-omit-frame-pointer - CORE_CFLAGS += -ggdb3 -@@ -872,13 +873,16 @@ ifndef NO_LIBBFD - ifeq ($(feature-libbfd-liberty), 1) - EXTLIBS += -lbfd -lopcodes -liberty - FEATURE_CHECK_LDFLAGS-disassembler-four-args += -liberty -ldl -+ FEATURE_CHECK_LDFLAGS-disassembler-init-styled += -liberty -ldl - else - ifeq ($(feature-libbfd-liberty-z), 1) - EXTLIBS += -lbfd -lopcodes -liberty -lz - FEATURE_CHECK_LDFLAGS-disassembler-four-args += -liberty -lz -ldl -+ FEATURE_CHECK_LDFLAGS-disassembler-init-styled += -liberty -lz -ldl - endif - endif - $(call feature_check,disassembler-four-args) -+ $(call feature_check,disassembler-init-styled) - endif - - ifeq ($(feature-libbfd-buildid), 1) -@@ -992,6 +996,10 @@ ifeq ($(feature-disassembler-four-args), - CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE - endif - -+ifeq ($(feature-disassembler-init-styled), 1) -+ CFLAGS += -DDISASM_INIT_STYLED -+endif -+ - ifeq (${IS_64_BIT}, 1) - ifndef NO_PERF_READ_VDSO32 - $(call feature_check,compile-32) ---- a/tools/perf/util/annotate.c -+++ b/tools/perf/util/annotate.c -@@ -1694,6 +1694,7 @@ fallback: - #include - #include - #include -+#include - - static int symbol__disassemble_bpf(struct symbol *sym, - struct annotate_args *args) -@@ -1736,9 +1737,9 @@ static int symbol__disassemble_bpf(struc - ret = errno; - goto out; - } -- init_disassemble_info(&info, s, -- (fprintf_ftype) fprintf); -- -+ init_disassemble_info_compat(&info, s, -+ (fprintf_ftype) fprintf, -+ fprintf_styled); - info.arch = bfd_get_arch(bfdf); - info.mach = bfd_get_mach(bfdf); - diff --git a/target/linux/generic/backport-5.15/060-v6.0-04-tools-bpf_jit_disasm-Fix-compilation-error-with-new-.patch b/target/linux/generic/backport-5.15/060-v6.0-04-tools-bpf_jit_disasm-Fix-compilation-error-with-new-.patch deleted file mode 100644 index f8f5bcc6b80..00000000000 --- a/target/linux/generic/backport-5.15/060-v6.0-04-tools-bpf_jit_disasm-Fix-compilation-error-with-new-.patch +++ /dev/null @@ -1,102 +0,0 @@ -From 042e7f11769adac0736d77d76262912b90724d7d Mon Sep 17 00:00:00 2001 -From: Andres Freund -Date: Sun, 31 Jul 2022 18:38:31 -0700 -Subject: [PATCH 4/5] tools bpf_jit_disasm: Fix compilation error with new - binutils - -binutils changed the signature of init_disassemble_info(), which now causes -compilation to fail for tools/bpf/bpf_jit_disasm.c, e.g. on debian -unstable. - -Relevant binutils commit: - - https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07 - -Wire up the feature test and switch to init_disassemble_info_compat(), -which were introduced in prior commits, fixing the compilation failure. - -I verified that bpf_jit_disasm can still disassemble bpf programs, both -with the old and new dis-asm.h API. With old binutils there's no change in -output before/after this patch. When comparing the output from old -binutils (2.35) to new bintuils with the patch (upstream snapshot) there -are a few output differences, but they are unrelated to this patch. An -example hunk is: - - f4: mov %r14,%rsi - f7: mov %r15,%rdx - fa: mov $0x2a,%ecx - - ff: callq 0xffffffffea8c4988 - + ff: call 0xffffffffea8c4988 - 104: test %rax,%rax - 107: jge 0x0000000000000110 - 109: xor %eax,%eax - - 10b: jmpq 0x0000000000000073 - + 10b: jmp 0x0000000000000073 - 110: cmp $0x16,%rax - -However, I had to use an older kernel to generate the bpf_jit_enabled = -2 output, as that has been broken since 5.18 / 1022a5498f6f745c ("bpf, -x86_64: Use bpf_jit_binary_pack_alloc"). - - https://lore.kernel.org/20220703030210.pmjft7qc2eajzi6c@alap3.anarazel.de - -Signed-off-by: Andres Freund -Acked-by: Quentin Monnet -Cc: Alexei Starovoitov -Cc: Ben Hutchings -Cc: Daniel Borkmann -Cc: Jiri Olsa -Cc: Quentin Monnet -Cc: Sedat Dilek -Cc: bpf@vger.kernel.org -Link: http://lore.kernel.org/lkml/20220622181918.ykrs5rsnmx3og4sv@alap3.anarazel.de -Link: https://lore.kernel.org/r/20220801013834.156015-6-andres@anarazel.de -Signed-off-by: Arnaldo Carvalho de Melo -(cherry picked from commit 96ed066054abf11c7d3e106e3011a51f3f1227a3) ---- - tools/bpf/Makefile | 5 ++++- - tools/bpf/bpf_jit_disasm.c | 5 ++++- - 2 files changed, 8 insertions(+), 2 deletions(-) - ---- a/tools/bpf/Makefile -+++ b/tools/bpf/Makefile -@@ -34,7 +34,7 @@ else - endif - - FEATURE_USER = .bpf --FEATURE_TESTS = libbfd disassembler-four-args -+FEATURE_TESTS = libbfd disassembler-four-args disassembler-init-styled - FEATURE_DISPLAY = libbfd disassembler-four-args - - check_feat := 1 -@@ -56,6 +56,9 @@ endif - ifeq ($(feature-disassembler-four-args), 1) - CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE - endif -+ifeq ($(feature-disassembler-init-styled), 1) -+CFLAGS += -DDISASM_INIT_STYLED -+endif - - $(OUTPUT)%.yacc.c: $(srctree)/tools/bpf/%.y - $(QUIET_BISON)$(YACC) -o $@ -d $< ---- a/tools/bpf/bpf_jit_disasm.c -+++ b/tools/bpf/bpf_jit_disasm.c -@@ -28,6 +28,7 @@ - #include - #include - #include -+#include - - #define CMD_ACTION_SIZE_BUFFER 10 - #define CMD_ACTION_READ_ALL 3 -@@ -64,7 +65,9 @@ static void get_asm_insns(uint8_t *image - assert(bfdf); - assert(bfd_check_format(bfdf, bfd_object)); - -- init_disassemble_info(&info, stdout, (fprintf_ftype) fprintf); -+ init_disassemble_info_compat(&info, stdout, -+ (fprintf_ftype) fprintf, -+ fprintf_styled); - info.arch = bfd_get_arch(bfdf); - info.mach = bfd_get_mach(bfdf); - info.buffer = image; diff --git a/target/linux/generic/backport-5.15/060-v6.0-05-tools-bpftool-Fix-compilation-error-with-new-binutil.patch b/target/linux/generic/backport-5.15/060-v6.0-05-tools-bpftool-Fix-compilation-error-with-new-binutil.patch deleted file mode 100644 index 66ac3d94096..00000000000 --- a/target/linux/generic/backport-5.15/060-v6.0-05-tools-bpftool-Fix-compilation-error-with-new-binutil.patch +++ /dev/null @@ -1,146 +0,0 @@ -From a82db18ab34ba7f9d38319e8cc01ffe382e3e55e Mon Sep 17 00:00:00 2001 -From: Andres Freund -Date: Sun, 31 Jul 2022 18:38:33 -0700 -Subject: [PATCH 5/5] tools bpftool: Fix compilation error with new binutils - -binutils changed the signature of init_disassemble_info(), which now causes -compilation to fail for tools/bpf/bpftool/jit_disasm.c, e.g. on debian -unstable. - -Relevant binutils commit: - - https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07 - -Wire up the feature test and switch to init_disassemble_info_compat(), -which were introduced in prior commits, fixing the compilation failure. - -I verified that bpftool can still disassemble bpf programs, both with an -old and new dis-asm.h API. There are no output changes for plain and json -formats. When comparing the output from old binutils (2.35) -to new bintuils with the patch (upstream snapshot) there are a few output -differences, but they are unrelated to this patch. An example hunk is: - - 2f: pop %r14 - 31: pop %r13 - 33: pop %rbx - - 34: leaveq - - 35: retq - + 34: leave - + 35: ret - -Signed-off-by: Andres Freund -Acked-by: Quentin Monnet -Cc: Alexei Starovoitov -Cc: Ben Hutchings -Cc: Jiri Olsa -Cc: Quentin Monnet -Cc: Sedat Dilek -Cc: bpf@vger.kernel.org -Link: http://lore.kernel.org/lkml/20220622181918.ykrs5rsnmx3og4sv@alap3.anarazel.de -Link: https://lore.kernel.org/r/20220801013834.156015-8-andres@anarazel.de -Signed-off-by: Arnaldo Carvalho de Melo -(cherry picked from commit 600b7b26c07a070d0153daa76b3806c1e52c9e00) ---- - tools/bpf/bpftool/Makefile | 5 +++- - tools/bpf/bpftool/jit_disasm.c | 42 +++++++++++++++++++++++++++------- - 2 files changed, 38 insertions(+), 9 deletions(-) - ---- a/tools/bpf/bpftool/Makefile -+++ b/tools/bpf/bpftool/Makefile -@@ -76,7 +76,7 @@ INSTALL ?= install - RM ?= rm -f - - FEATURE_USER = .bpftool --FEATURE_TESTS = libbfd disassembler-four-args reallocarray zlib libcap \ -+FEATURE_TESTS = libbfd disassembler-four-args disassembler-init-styled reallocarray zlib libcap \ - clang-bpf-co-re - FEATURE_DISPLAY = libbfd disassembler-four-args zlib libcap \ - clang-bpf-co-re -@@ -100,6 +100,9 @@ endif - ifeq ($(feature-disassembler-four-args), 1) - CFLAGS += -DDISASM_FOUR_ARGS_SIGNATURE - endif -+ifeq ($(feature-disassembler-init-styled), 1) -+ CFLAGS += -DDISASM_INIT_STYLED -+endif - - ifeq ($(feature-reallocarray), 0) - CFLAGS += -DCOMPAT_NEED_REALLOCARRAY ---- a/tools/bpf/bpftool/jit_disasm.c -+++ b/tools/bpf/bpftool/jit_disasm.c -@@ -24,6 +24,7 @@ - #include - #include - #include -+#include - - #include "json_writer.h" - #include "main.h" -@@ -39,15 +40,12 @@ static void get_exec_path(char *tpath, s - } - - static int oper_count; --static int fprintf_json(void *out, const char *fmt, ...) -+static int printf_json(void *out, const char *fmt, va_list ap) - { -- va_list ap; - char *s; - int err; - -- va_start(ap, fmt); - err = vasprintf(&s, fmt, ap); -- va_end(ap); - if (err < 0) - return -1; - -@@ -73,6 +71,32 @@ static int fprintf_json(void *out, const - return 0; - } - -+static int fprintf_json(void *out, const char *fmt, ...) -+{ -+ va_list ap; -+ int r; -+ -+ va_start(ap, fmt); -+ r = printf_json(out, fmt, ap); -+ va_end(ap); -+ -+ return r; -+} -+ -+static int fprintf_json_styled(void *out, -+ enum disassembler_style style __maybe_unused, -+ const char *fmt, ...) -+{ -+ va_list ap; -+ int r; -+ -+ va_start(ap, fmt); -+ r = printf_json(out, fmt, ap); -+ va_end(ap); -+ -+ return r; -+} -+ - void disasm_print_insn(unsigned char *image, ssize_t len, int opcodes, - const char *arch, const char *disassembler_options, - const struct btf *btf, -@@ -99,11 +123,13 @@ void disasm_print_insn(unsigned char *im - assert(bfd_check_format(bfdf, bfd_object)); - - if (json_output) -- init_disassemble_info(&info, stdout, -- (fprintf_ftype) fprintf_json); -+ init_disassemble_info_compat(&info, stdout, -+ (fprintf_ftype) fprintf_json, -+ fprintf_json_styled); - else -- init_disassemble_info(&info, stdout, -- (fprintf_ftype) fprintf); -+ init_disassemble_info_compat(&info, stdout, -+ (fprintf_ftype) fprintf, -+ fprintf_styled); - - /* Update architecture info for offload. */ - if (arch) { diff --git a/target/linux/generic/backport-5.15/350-v5.18-regmap-add-configurable-downshift-for-addresses.patch b/target/linux/generic/backport-5.15/350-v5.18-regmap-add-configurable-downshift-for-addresses.patch new file mode 100644 index 00000000000..99cd89ea002 --- /dev/null +++ b/target/linux/generic/backport-5.15/350-v5.18-regmap-add-configurable-downshift-for-addresses.patch @@ -0,0 +1,90 @@ +From 86fc59ef818beb0e1945d17f8e734898baba7e4e Mon Sep 17 00:00:00 2001 +From: Colin Foster +Date: Sun, 13 Mar 2022 15:45:23 -0700 +Subject: [PATCH 1/2] regmap: add configurable downshift for addresses + +Add an additional reg_downshift to be applied to register addresses before +any register accesses. An example of a device that uses this is a VSC7514 +chip, which require each register address to be downshifted by two if the +access is performed over a SPI bus. + +Signed-off-by: Colin Foster +Link: https://lore.kernel.org/r/20220313224524.399947-2-colin.foster@in-advantage.com +Signed-off-by: Mark Brown +--- + drivers/base/regmap/internal.h | 1 + + drivers/base/regmap/regmap.c | 5 +++++ + include/linux/regmap.h | 3 +++ + 3 files changed, 9 insertions(+) + +--- a/drivers/base/regmap/internal.h ++++ b/drivers/base/regmap/internal.h +@@ -31,6 +31,7 @@ struct regmap_format { + size_t buf_size; + size_t reg_bytes; + size_t pad_bytes; ++ size_t reg_downshift; + size_t val_bytes; + void (*format_write)(struct regmap *map, + unsigned int reg, unsigned int val); +--- a/drivers/base/regmap/regmap.c ++++ b/drivers/base/regmap/regmap.c +@@ -823,6 +823,7 @@ struct regmap *__regmap_init(struct devi + + map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); + map->format.pad_bytes = config->pad_bits / 8; ++ map->format.reg_downshift = config->reg_downshift; + map->format.val_bytes = DIV_ROUND_UP(config->val_bits, 8); + map->format.buf_size = DIV_ROUND_UP(config->reg_bits + + config->val_bits + config->pad_bits, 8); +@@ -1735,6 +1736,7 @@ static int _regmap_raw_write_impl(struct + return ret; + } + ++ reg >>= map->format.reg_downshift; + map->format.format_reg(map->work_buf, reg, map->reg_shift); + regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, + map->write_flag_mask); +@@ -1905,6 +1907,7 @@ static int _regmap_bus_formatted_write(v + return ret; + } + ++ reg >>= map->format.reg_downshift; + map->format.format_write(map, reg, val); + + trace_regmap_hw_write_start(map, reg, 1); +@@ -2346,6 +2349,7 @@ static int _regmap_raw_multi_reg_write(s + unsigned int reg = regs[i].reg; + unsigned int val = regs[i].def; + trace_regmap_hw_write_start(map, reg, 1); ++ reg >>= map->format.reg_downshift; + map->format.format_reg(u8, reg, map->reg_shift); + u8 += reg_bytes + pad_bytes; + map->format.format_val(u8, val, 0); +@@ -2673,6 +2677,7 @@ static int _regmap_raw_read(struct regma + return ret; + } + ++ reg >>= map->format.reg_downshift; + map->format.format_reg(map->work_buf, reg, map->reg_shift); + regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, + map->read_flag_mask); +--- a/include/linux/regmap.h ++++ b/include/linux/regmap.h +@@ -237,6 +237,8 @@ typedef void (*regmap_unlock)(void *); + * @reg_stride: The register address stride. Valid register addresses are a + * multiple of this value. If set to 0, a value of 1 will be + * used. ++ * @reg_downshift: The number of bits to downshift the register before ++ * performing any operations. + * @pad_bits: Number of bits of padding between register and value. + * @val_bits: Number of bits in a register value, mandatory. + * +@@ -360,6 +362,7 @@ struct regmap_config { + + int reg_bits; + int reg_stride; ++ int reg_downshift; + int pad_bits; + int val_bits; + diff --git a/target/linux/generic/backport-5.15/351-v5.18-regmap-allow-a-defined-reg_base-to-be-added-to-every.patch b/target/linux/generic/backport-5.15/351-v5.18-regmap-allow-a-defined-reg_base-to-be-added-to-every.patch new file mode 100644 index 00000000000..0f32288fcab --- /dev/null +++ b/target/linux/generic/backport-5.15/351-v5.18-regmap-allow-a-defined-reg_base-to-be-added-to-every.patch @@ -0,0 +1,95 @@ +From 0074f3f2b1e43d3cedd97e47fb6980db6d2ba79e Mon Sep 17 00:00:00 2001 +From: Colin Foster +Date: Sun, 13 Mar 2022 15:45:24 -0700 +Subject: [PATCH 2/2] regmap: allow a defined reg_base to be added to every + address + +There's an inconsistency that arises when a register set can be accessed +internally via MMIO, or externally via SPI. The VSC7514 chip allows both +modes of operation. When internally accessed, the system utilizes __iomem, +devm_ioremap_resource, and devm_regmap_init_mmio. + +For SPI it isn't possible to utilize memory-mapped IO. To properly operate, +the resource base must be added to the register before every operation. + +Signed-off-by: Colin Foster +Link: https://lore.kernel.org/r/20220313224524.399947-3-colin.foster@in-advantage.com +Signed-off-by: Mark Brown +--- + drivers/base/regmap/internal.h | 1 + + drivers/base/regmap/regmap.c | 6 ++++++ + include/linux/regmap.h | 3 +++ + 3 files changed, 10 insertions(+) + +--- a/drivers/base/regmap/internal.h ++++ b/drivers/base/regmap/internal.h +@@ -63,6 +63,7 @@ struct regmap { + regmap_unlock unlock; + void *lock_arg; /* This is passed to lock/unlock functions */ + gfp_t alloc_flags; ++ unsigned int reg_base; + + struct device *dev; /* Device we do I/O on */ + void *work_buf; /* Scratch buffer used to format I/O */ +--- a/drivers/base/regmap/regmap.c ++++ b/drivers/base/regmap/regmap.c +@@ -821,6 +821,8 @@ struct regmap *__regmap_init(struct devi + else + map->alloc_flags = GFP_KERNEL; + ++ map->reg_base = config->reg_base; ++ + map->format.reg_bytes = DIV_ROUND_UP(config->reg_bits, 8); + map->format.pad_bytes = config->pad_bits / 8; + map->format.reg_downshift = config->reg_downshift; +@@ -1736,6 +1738,7 @@ static int _regmap_raw_write_impl(struct + return ret; + } + ++ reg += map->reg_base; + reg >>= map->format.reg_downshift; + map->format.format_reg(map->work_buf, reg, map->reg_shift); + regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, +@@ -1907,6 +1910,7 @@ static int _regmap_bus_formatted_write(v + return ret; + } + ++ reg += map->reg_base; + reg >>= map->format.reg_downshift; + map->format.format_write(map, reg, val); + +@@ -2349,6 +2353,7 @@ static int _regmap_raw_multi_reg_write(s + unsigned int reg = regs[i].reg; + unsigned int val = regs[i].def; + trace_regmap_hw_write_start(map, reg, 1); ++ reg += map->reg_base; + reg >>= map->format.reg_downshift; + map->format.format_reg(u8, reg, map->reg_shift); + u8 += reg_bytes + pad_bytes; +@@ -2677,6 +2682,7 @@ static int _regmap_raw_read(struct regma + return ret; + } + ++ reg += map->reg_base; + reg >>= map->format.reg_downshift; + map->format.format_reg(map->work_buf, reg, map->reg_shift); + regmap_set_work_buf_flag_mask(map, map->format.reg_bytes, +--- a/include/linux/regmap.h ++++ b/include/linux/regmap.h +@@ -239,6 +239,8 @@ typedef void (*regmap_unlock)(void *); + * used. + * @reg_downshift: The number of bits to downshift the register before + * performing any operations. ++ * @reg_base: Value to be added to every register address before performing any ++ * operation. + * @pad_bits: Number of bits of padding between register and value. + * @val_bits: Number of bits in a register value, mandatory. + * +@@ -363,6 +365,7 @@ struct regmap_config { + int reg_bits; + int reg_stride; + int reg_downshift; ++ unsigned int reg_base; + int pad_bits; + int val_bits; + diff --git a/target/linux/generic/backport-5.15/352-v6.3-regmap-apply-reg_base-and-reg_downshift-for-single-r.patch b/target/linux/generic/backport-5.15/352-v6.3-regmap-apply-reg_base-and-reg_downshift-for-single-r.patch new file mode 100644 index 00000000000..804f68d23c5 --- /dev/null +++ b/target/linux/generic/backport-5.15/352-v6.3-regmap-apply-reg_base-and-reg_downshift-for-single-r.patch @@ -0,0 +1,57 @@ +From 697c3892d825fb78f42ec8e53bed065dd728db3e Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 30 Jan 2023 02:04:57 +0000 +Subject: [PATCH] regmap: apply reg_base and reg_downshift for single register + ops + +reg_base and reg_downshift currently don't have any effect if used with +a regmap_bus or regmap_config which only offers single register +operations (ie. reg_read, reg_write and optionally reg_update_bits). + +Fix that and take them into account also for regmap_bus with only +reg_read and read_write operations by applying reg_base and +reg_downshift in _regmap_bus_reg_write, _regmap_bus_reg_read. + +Also apply reg_base and reg_downshift in _regmap_update_bits, but only +in case the operation is carried out with a reg_update_bits call +defined in either regmap_bus or regmap_config. + +Fixes: 0074f3f2b1e43d ("regmap: allow a defined reg_base to be added to every address") +Fixes: 86fc59ef818beb ("regmap: add configurable downshift for addresses") +Signed-off-by: Daniel Golle +Tested-by: Colin Foster +Link: https://lore.kernel.org/r/Y9clyVS3tQEHlUhA@makrotopia.org +Signed-off-by: Mark Brown +--- + drivers/base/regmap/regmap.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/base/regmap/regmap.c ++++ b/drivers/base/regmap/regmap.c +@@ -1929,6 +1929,8 @@ static int _regmap_bus_reg_write(void *c + { + struct regmap *map = context; + ++ reg += map->reg_base; ++ reg >>= map->format.reg_downshift; + return map->bus->reg_write(map->bus_context, reg, val); + } + +@@ -2703,6 +2705,8 @@ static int _regmap_bus_reg_read(void *co + { + struct regmap *map = context; + ++ reg += map->reg_base; ++ reg >>= map->format.reg_downshift; + return map->bus->reg_read(map->bus_context, reg, val); + } + +@@ -3078,6 +3082,8 @@ static int _regmap_update_bits(struct re + *change = false; + + if (regmap_volatile(map, reg) && map->reg_update_bits) { ++ reg += map->reg_base; ++ reg >>= map->format.reg_downshift; + ret = map->reg_update_bits(map->bus_context, reg, mask, val); + if (ret == 0 && change) + *change = true; diff --git a/target/linux/generic/backport-5.15/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch b/target/linux/generic/backport-5.15/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch new file mode 100644 index 00000000000..7dbc2717250 --- /dev/null +++ b/target/linux/generic/backport-5.15/423-v6.3-mtd-spinand-macronix-use-scratch-buffer-for-DMA-oper.patch @@ -0,0 +1,35 @@ +From ebed787a0becb9354f0a23620a5130cccd6c730c Mon Sep 17 00:00:00 2001 +From: Daniel Golle +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 +Reviewed-by: Dhruva Gole +Signed-off-by: Miquel Raynal +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; diff --git a/target/linux/generic/backport-5.15/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch b/target/linux/generic/backport-5.15/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch index e13616fd834..c83d659d1c0 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-00-net-ethernet-mtk_eth_soc-add-support-for-coherent-DM.patch @@ -20,7 +20,7 @@ Signed-off-by: Felix Fietkau #include #include #include -@@ -839,7 +840,7 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -840,7 +841,7 @@ static int mtk_init_fq_dma(struct mtk_et dma_addr_t dma_addr; int i; @@ -29,7 +29,7 @@ Signed-off-by: Felix Fietkau cnt * sizeof(struct mtk_tx_dma), ð->phy_scratch_ring, GFP_ATOMIC); -@@ -851,10 +852,10 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -852,10 +853,10 @@ static int mtk_init_fq_dma(struct mtk_et if (unlikely(!eth->scratch_head)) return -ENOMEM; @@ -42,7 +42,7 @@ Signed-off-by: Felix Fietkau return -ENOMEM; phy_ring_tail = eth->phy_scratch_ring + -@@ -908,26 +909,26 @@ static void mtk_tx_unmap(struct mtk_eth +@@ -909,26 +910,26 @@ static void mtk_tx_unmap(struct mtk_eth { if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { if (tx_buf->flags & MTK_TX_FLAGS_SINGLE0) { @@ -73,7 +73,7 @@ Signed-off-by: Felix Fietkau dma_unmap_addr(tx_buf, dma_addr1), dma_unmap_len(tx_buf, dma_len1), DMA_TO_DEVICE); -@@ -1005,9 +1006,9 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1006,9 +1007,9 @@ static int mtk_tx_map(struct sk_buff *sk if (skb_vlan_tag_present(skb)) txd4 |= TX_DMA_INS_VLAN | skb_vlan_tag_get(skb); @@ -85,7 +85,7 @@ Signed-off-by: Felix Fietkau return -ENOMEM; WRITE_ONCE(itxd->txd1, mapped_addr); -@@ -1046,10 +1047,10 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1047,10 +1048,10 @@ static int mtk_tx_map(struct sk_buff *sk frag_map_size = min(frag_size, MTK_TX_DMA_BUF_LEN); @@ -98,7 +98,7 @@ Signed-off-by: Felix Fietkau goto err_dma; if (i == nr_frags - 1 && -@@ -1330,18 +1331,18 @@ static int mtk_poll_rx(struct napi_struc +@@ -1331,18 +1332,18 @@ static int mtk_poll_rx(struct napi_struc netdev->stats.rx_dropped++; goto release_desc; } @@ -120,7 +120,7 @@ Signed-off-by: Felix Fietkau ring->buf_size, DMA_FROM_DEVICE); /* receive data */ -@@ -1614,7 +1615,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1615,7 +1616,7 @@ static int mtk_tx_alloc(struct mtk_eth * if (!ring->buf) goto no_tx_mem; @@ -129,7 +129,7 @@ Signed-off-by: Felix Fietkau &ring->phys, GFP_ATOMIC); if (!ring->dma) goto no_tx_mem; -@@ -1632,7 +1633,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1633,7 +1634,7 @@ static int mtk_tx_alloc(struct mtk_eth * * descriptors in ring->dma_pdma. */ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { @@ -138,7 +138,7 @@ Signed-off-by: Felix Fietkau &ring->phys_pdma, GFP_ATOMIC); if (!ring->dma_pdma) -@@ -1691,7 +1692,7 @@ static void mtk_tx_clean(struct mtk_eth +@@ -1692,7 +1693,7 @@ static void mtk_tx_clean(struct mtk_eth } if (ring->dma) { @@ -147,7 +147,7 @@ Signed-off-by: Felix Fietkau MTK_DMA_SIZE * sizeof(*ring->dma), ring->dma, ring->phys); -@@ -1699,7 +1700,7 @@ static void mtk_tx_clean(struct mtk_eth +@@ -1700,7 +1701,7 @@ static void mtk_tx_clean(struct mtk_eth } if (ring->dma_pdma) { @@ -156,7 +156,7 @@ Signed-off-by: Felix Fietkau MTK_DMA_SIZE * sizeof(*ring->dma_pdma), ring->dma_pdma, ring->phys_pdma); -@@ -1747,18 +1748,18 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1748,18 +1749,18 @@ static int mtk_rx_alloc(struct mtk_eth * return -ENOMEM; } @@ -178,7 +178,7 @@ Signed-off-by: Felix Fietkau return -ENOMEM; ring->dma[i].rxd1 = (unsigned int)dma_addr; -@@ -1794,7 +1795,7 @@ static void mtk_rx_clean(struct mtk_eth +@@ -1795,7 +1796,7 @@ static void mtk_rx_clean(struct mtk_eth continue; if (!ring->dma[i].rxd1) continue; @@ -187,7 +187,7 @@ Signed-off-by: Felix Fietkau ring->dma[i].rxd1, ring->buf_size, DMA_FROM_DEVICE); -@@ -1805,7 +1806,7 @@ static void mtk_rx_clean(struct mtk_eth +@@ -1806,7 +1807,7 @@ static void mtk_rx_clean(struct mtk_eth } if (ring->dma) { @@ -196,7 +196,7 @@ Signed-off-by: Felix Fietkau ring->dma_size * sizeof(*ring->dma), ring->dma, ring->phys); -@@ -2161,7 +2162,7 @@ static void mtk_dma_free(struct mtk_eth +@@ -2162,7 +2163,7 @@ static void mtk_dma_free(struct mtk_eth if (eth->netdev[i]) netdev_reset_queue(eth->netdev[i]); if (eth->scratch_ring) { @@ -205,7 +205,7 @@ Signed-off-by: Felix Fietkau MTK_DMA_SIZE * sizeof(struct mtk_tx_dma), eth->scratch_ring, eth->phy_scratch_ring); -@@ -2513,6 +2514,8 @@ static void mtk_dim_tx(struct work_struc +@@ -2514,6 +2515,8 @@ static void mtk_dim_tx(struct work_struc static int mtk_hw_init(struct mtk_eth *eth) { @@ -214,7 +214,7 @@ Signed-off-by: Felix Fietkau int i, val, ret; if (test_and_set_bit(MTK_HW_INIT, ð->state)) -@@ -2525,6 +2528,10 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2526,6 +2529,10 @@ static int mtk_hw_init(struct mtk_eth *e if (ret) goto err_disable_pm; @@ -225,7 +225,7 @@ Signed-off-by: Felix Fietkau if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { ret = device_reset(eth->dev); if (ret) { -@@ -3078,6 +3085,35 @@ free_netdev: +@@ -3079,6 +3086,35 @@ free_netdev: return err; } @@ -261,7 +261,7 @@ Signed-off-by: Felix Fietkau static int mtk_probe(struct platform_device *pdev) { struct device_node *mac_np; -@@ -3091,6 +3127,7 @@ static int mtk_probe(struct platform_dev +@@ -3092,6 +3128,7 @@ static int mtk_probe(struct platform_dev eth->soc = of_device_get_match_data(&pdev->dev); eth->dev = &pdev->dev; @@ -269,7 +269,7 @@ Signed-off-by: Felix Fietkau eth->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(eth->base)) return PTR_ERR(eth->base); -@@ -3139,6 +3176,16 @@ static int mtk_probe(struct platform_dev +@@ -3140,6 +3177,16 @@ static int mtk_probe(struct platform_dev } } @@ -288,7 +288,7 @@ Signed-off-by: Felix Fietkau GFP_KERNEL); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -462,6 +462,12 @@ +@@ -463,6 +463,12 @@ #define RSTCTRL_FE BIT(6) #define RSTCTRL_PPE BIT(31) @@ -301,7 +301,7 @@ Signed-off-by: Felix Fietkau /* SGMII subsystem config registers */ /* Register to auto-negotiation restart */ #define SGMSYS_PCS_CONTROL_1 0x0 -@@ -879,6 +885,7 @@ struct mtk_sgmii { +@@ -880,6 +886,7 @@ struct mtk_sgmii { /* struct mtk_eth - This is the main datasructure for holding the state * of the driver * @dev: The device pointer @@ -309,7 +309,7 @@ Signed-off-by: Felix Fietkau * @base: The mapped register i/o base * @page_lock: Make sure that register operations are atomic * @tx_irq__lock: Make sure that IRQ register operations are atomic -@@ -922,6 +929,7 @@ struct mtk_sgmii { +@@ -923,6 +930,7 @@ struct mtk_sgmii { struct mtk_eth { struct device *dev; @@ -317,7 +317,7 @@ Signed-off-by: Felix Fietkau void __iomem *base; spinlock_t page_lock; spinlock_t tx_irq_lock; -@@ -1020,6 +1028,7 @@ int mtk_gmac_rgmii_path_setup(struct mtk +@@ -1021,6 +1029,7 @@ int mtk_gmac_rgmii_path_setup(struct mtk int mtk_eth_offload_init(struct mtk_eth *eth); int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data); diff --git a/target/linux/generic/backport-5.15/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch b/target/linux/generic/backport-5.15/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch index a623aa14d78..2f3a0827fe4 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-02-net-ethernet-mtk_eth_soc-add-support-for-Wireless-Et.patch @@ -56,7 +56,7 @@ Signed-off-by: Felix Fietkau static int mtk_msg_level = -1; module_param_named(msg_level, mtk_msg_level, int, 0); -@@ -3208,6 +3209,22 @@ static int mtk_probe(struct platform_dev +@@ -3209,6 +3210,22 @@ static int mtk_probe(struct platform_dev } } diff --git a/target/linux/generic/backport-5.15/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch b/target/linux/generic/backport-5.15/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch index d5b1d214133..50d65b1eb6f 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch @@ -233,7 +233,7 @@ Signed-off-by: Felix Fietkau static inline void --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -870,6 +870,7 @@ enum net_device_path_type { +@@ -872,6 +872,7 @@ enum net_device_path_type { DEV_PATH_BRIDGE, DEV_PATH_PPPOE, DEV_PATH_DSA, @@ -241,7 +241,7 @@ Signed-off-by: Felix Fietkau }; struct net_device_path { -@@ -895,6 +896,12 @@ struct net_device_path { +@@ -897,6 +898,12 @@ struct net_device_path { int port; u16 proto; } dsa; diff --git a/target/linux/generic/backport-5.15/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch b/target/linux/generic/backport-5.15/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch index 5002c38dc9c..bc75d152114 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-07-net-ethernet-mtk_eth_soc-allocate-struct-mtk_ppe-sep.patch @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2334,7 +2334,7 @@ static int mtk_open(struct net_device *d +@@ -2335,7 +2335,7 @@ static int mtk_open(struct net_device *d return err; } @@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau gdm_config = MTK_GDMA_TO_PPE; mtk_gdm_config(eth, gdm_config); -@@ -2408,7 +2408,7 @@ static int mtk_stop(struct net_device *d +@@ -2409,7 +2409,7 @@ static int mtk_stop(struct net_device *d mtk_dma_free(eth); if (eth->soc->offload_version) @@ -28,7 +28,7 @@ Signed-off-by: Felix Fietkau return 0; } -@@ -3300,10 +3300,11 @@ static int mtk_probe(struct platform_dev +@@ -3301,10 +3301,11 @@ static int mtk_probe(struct platform_dev } if (eth->soc->offload_version) { @@ -45,7 +45,7 @@ Signed-off-by: Felix Fietkau if (err) --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -982,7 +982,7 @@ struct mtk_eth { +@@ -983,7 +983,7 @@ struct mtk_eth { u32 rx_dma_l4_valid; int ip_align; diff --git a/target/linux/generic/backport-5.15/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch b/target/linux/generic/backport-5.15/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch index 52b3add875d..2609cbedec2 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-08-net-ethernet-mtk_eth_soc-rework-hardware-flow-table-.patch @@ -33,7 +33,7 @@ Signed-off-by: Felix Fietkau #include #include "mtk_eth_soc.h" -@@ -1292,7 +1293,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1293,7 +1294,7 @@ static int mtk_poll_rx(struct napi_struc struct net_device *netdev; unsigned int pktlen; dma_addr_t dma_addr; @@ -42,7 +42,7 @@ Signed-off-by: Felix Fietkau int mac; ring = mtk_get_rx_ring(eth); -@@ -1371,6 +1372,11 @@ static int mtk_poll_rx(struct napi_struc +@@ -1372,6 +1373,11 @@ static int mtk_poll_rx(struct napi_struc skb_set_hash(skb, hash, PKT_HASH_TYPE_L4); } @@ -54,7 +54,7 @@ Signed-off-by: Felix Fietkau if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX && (trxd.rxd2 & RX_DMA_VTAG)) __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), -@@ -3300,7 +3306,7 @@ static int mtk_probe(struct platform_dev +@@ -3301,7 +3307,7 @@ static int mtk_probe(struct platform_dev } if (eth->soc->offload_version) { diff --git a/target/linux/generic/backport-5.15/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch b/target/linux/generic/backport-5.15/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch index c893d8c8bbe..a7c5f08f100 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-13-net-ethernet-mtk_eth_soc-use-standard-property-for-c.patch @@ -24,7 +24,7 @@ Signed-off-by: David S. Miller mediatek,hifsys = <&hifsys>; --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3187,7 +3187,7 @@ static int mtk_probe(struct platform_dev +@@ -3188,7 +3188,7 @@ static int mtk_probe(struct platform_dev struct regmap *cci; cci = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, diff --git a/target/linux/generic/backport-5.15/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch b/target/linux/generic/backport-5.15/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch index 97677670cc1..9b6321f1daf 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-17-net-ethernet-mtk_eth_soc-rely-on-GFP_KERNEL-for-dma_.patch @@ -13,7 +13,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -845,7 +845,7 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -846,7 +846,7 @@ static int mtk_init_fq_dma(struct mtk_et eth->scratch_ring = dma_alloc_coherent(eth->dma_dev, cnt * sizeof(struct mtk_tx_dma), ð->phy_scratch_ring, @@ -22,7 +22,7 @@ Signed-off-by: David S. Miller if (unlikely(!eth->scratch_ring)) return -ENOMEM; -@@ -1623,7 +1623,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1624,7 +1624,7 @@ static int mtk_tx_alloc(struct mtk_eth * goto no_tx_mem; ring->dma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, @@ -31,7 +31,7 @@ Signed-off-by: David S. Miller if (!ring->dma) goto no_tx_mem; -@@ -1641,8 +1641,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1642,8 +1642,7 @@ static int mtk_tx_alloc(struct mtk_eth * */ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, @@ -41,7 +41,7 @@ Signed-off-by: David S. Miller if (!ring->dma_pdma) goto no_tx_mem; -@@ -1757,7 +1756,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1758,7 +1757,7 @@ static int mtk_rx_alloc(struct mtk_eth * ring->dma = dma_alloc_coherent(eth->dma_dev, rx_dma_size * sizeof(*ring->dma), diff --git a/target/linux/generic/backport-5.15/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch b/target/linux/generic/backport-5.15/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch index 95f122f730f..8e16ea25566 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-18-net-ethernet-mtk_eth_soc-move-tx-dma-desc-configurat.patch @@ -14,7 +14,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -971,18 +971,51 @@ static void setup_tx_buf(struct mtk_eth +@@ -972,18 +972,51 @@ static void setup_tx_buf(struct mtk_eth } } @@ -69,7 +69,7 @@ Signed-off-by: David S. Miller int k = 0; itxd = ring->next_free; -@@ -990,49 +1023,32 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -991,49 +1024,32 @@ static int mtk_tx_map(struct sk_buff *sk if (itxd == ring->last_free) return -ENOMEM; @@ -126,7 +126,7 @@ Signed-off-by: David S. Miller bool new_desc = true; if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA) || -@@ -1047,23 +1063,17 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1048,23 +1064,17 @@ static int mtk_tx_map(struct sk_buff *sk new_desc = false; } @@ -159,7 +159,7 @@ Signed-off-by: David S. Miller tx_buf = mtk_desc_to_tx_buf(ring, txd); if (new_desc) -@@ -1073,20 +1083,17 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1074,20 +1084,17 @@ static int mtk_tx_map(struct sk_buff *sk tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1; @@ -186,7 +186,7 @@ Signed-off-by: David S. Miller txd_pdma->txd2 |= TX_DMA_LS0; --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -842,6 +842,17 @@ enum mkt_eth_capabilities { +@@ -843,6 +843,17 @@ enum mkt_eth_capabilities { MTK_MUX_U3_GMAC2_TO_QPHY | \ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA) diff --git a/target/linux/generic/backport-5.15/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch b/target/linux/generic/backport-5.15/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch index d3feef28eb8..f5206bba00b 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-19-net-ethernet-mtk_eth_soc-add-txd_size-to-mtk_soc_dat.patch @@ -14,7 +14,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -837,20 +837,20 @@ static void *mtk_max_lro_buf_alloc(gfp_t +@@ -838,20 +838,20 @@ static void *mtk_max_lro_buf_alloc(gfp_t /* the qdma core needs scratch memory to be setup */ static int mtk_init_fq_dma(struct mtk_eth *eth) { @@ -38,7 +38,7 @@ Signed-off-by: David S. Miller if (unlikely(!eth->scratch_head)) return -ENOMEM; -@@ -860,16 +860,19 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -861,16 +861,19 @@ static int mtk_init_fq_dma(struct mtk_et if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) return -ENOMEM; @@ -65,7 +65,7 @@ Signed-off-by: David S. Miller } mtk_w32(eth, eth->phy_scratch_ring, MTK_QDMA_FQ_HEAD); -@@ -2169,6 +2172,7 @@ static int mtk_dma_init(struct mtk_eth * +@@ -2170,6 +2173,7 @@ static int mtk_dma_init(struct mtk_eth * static void mtk_dma_free(struct mtk_eth *eth) { @@ -73,7 +73,7 @@ Signed-off-by: David S. Miller int i; for (i = 0; i < MTK_MAC_COUNT; i++) -@@ -2176,9 +2180,8 @@ static void mtk_dma_free(struct mtk_eth +@@ -2177,9 +2181,8 @@ static void mtk_dma_free(struct mtk_eth netdev_reset_queue(eth->netdev[i]); if (eth->scratch_ring) { dma_free_coherent(eth->dma_dev, @@ -85,7 +85,7 @@ Signed-off-by: David S. Miller eth->scratch_ring = NULL; eth->phy_scratch_ring = 0; } -@@ -3390,6 +3393,9 @@ static const struct mtk_soc_data mt2701_ +@@ -3391,6 +3394,9 @@ static const struct mtk_soc_data mt2701_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, @@ -95,7 +95,7 @@ Signed-off-by: David S. Miller }; static const struct mtk_soc_data mt7621_data = { -@@ -3398,6 +3404,9 @@ static const struct mtk_soc_data mt7621_ +@@ -3399,6 +3405,9 @@ static const struct mtk_soc_data mt7621_ .required_clks = MT7621_CLKS_BITMAP, .required_pctl = false, .offload_version = 2, @@ -105,7 +105,7 @@ Signed-off-by: David S. Miller }; static const struct mtk_soc_data mt7622_data = { -@@ -3407,6 +3416,9 @@ static const struct mtk_soc_data mt7622_ +@@ -3408,6 +3417,9 @@ static const struct mtk_soc_data mt7622_ .required_clks = MT7622_CLKS_BITMAP, .required_pctl = false, .offload_version = 2, @@ -115,7 +115,7 @@ Signed-off-by: David S. Miller }; static const struct mtk_soc_data mt7623_data = { -@@ -3415,6 +3427,9 @@ static const struct mtk_soc_data mt7623_ +@@ -3416,6 +3428,9 @@ static const struct mtk_soc_data mt7623_ .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, .offload_version = 2, @@ -125,7 +125,7 @@ Signed-off-by: David S. Miller }; static const struct mtk_soc_data mt7629_data = { -@@ -3423,6 +3438,9 @@ static const struct mtk_soc_data mt7629_ +@@ -3424,6 +3439,9 @@ static const struct mtk_soc_data mt7629_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7629_CLKS_BITMAP, .required_pctl = false, @@ -135,7 +135,7 @@ Signed-off-by: David S. Miller }; static const struct mtk_soc_data rt5350_data = { -@@ -3430,6 +3448,9 @@ static const struct mtk_soc_data rt5350_ +@@ -3431,6 +3449,9 @@ static const struct mtk_soc_data rt5350_ .hw_features = MTK_HW_FEATURES_MT7628, .required_clks = MT7628_CLKS_BITMAP, .required_pctl = false, @@ -147,7 +147,7 @@ Signed-off-by: David S. Miller const struct of_device_id of_mtk_match[] = { --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -863,6 +863,7 @@ struct mtk_tx_dma_desc_info { +@@ -864,6 +864,7 @@ struct mtk_tx_dma_desc_info { * the target SoC * @required_pctl A bool value to show whether the SoC requires * the extra setup for those pins used by GMAC. @@ -155,7 +155,7 @@ Signed-off-by: David S. Miller */ struct mtk_soc_data { u32 ana_rgc3; -@@ -871,6 +872,9 @@ struct mtk_soc_data { +@@ -872,6 +873,9 @@ struct mtk_soc_data { bool required_pctl; u8 offload_version; netdev_features_t hw_features; diff --git a/target/linux/generic/backport-5.15/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch b/target/linux/generic/backport-5.15/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch index 01dbca0753b..ebe1ee3d59a 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-20-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_tx_.patch @@ -12,7 +12,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1624,8 +1624,10 @@ static int mtk_napi_rx(struct napi_struc +@@ -1625,8 +1625,10 @@ static int mtk_napi_rx(struct napi_struc static int mtk_tx_alloc(struct mtk_eth *eth) { @@ -24,7 +24,7 @@ Signed-off-by: David S. Miller ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf), GFP_KERNEL); -@@ -1641,8 +1643,10 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1642,8 +1644,10 @@ static int mtk_tx_alloc(struct mtk_eth * int next = (i + 1) % MTK_DMA_SIZE; u32 next_ptr = ring->phys + next * sz; @@ -37,7 +37,7 @@ Signed-off-by: David S. Miller } /* On MT7688 (PDMA only) this driver uses the ring->dma structs -@@ -1664,7 +1668,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1665,7 +1669,7 @@ static int mtk_tx_alloc(struct mtk_eth * ring->dma_size = MTK_DMA_SIZE; atomic_set(&ring->free_count, MTK_DMA_SIZE - 2); ring->next_free = &ring->dma[0]; @@ -46,7 +46,7 @@ Signed-off-by: David S. Miller ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz)); ring->thresh = MAX_SKB_FRAGS; -@@ -1697,6 +1701,7 @@ no_tx_mem: +@@ -1698,6 +1702,7 @@ no_tx_mem: static void mtk_tx_clean(struct mtk_eth *eth) { @@ -54,7 +54,7 @@ Signed-off-by: David S. Miller struct mtk_tx_ring *ring = ð->tx_ring; int i; -@@ -1709,17 +1714,15 @@ static void mtk_tx_clean(struct mtk_eth +@@ -1710,17 +1715,15 @@ static void mtk_tx_clean(struct mtk_eth if (ring->dma) { dma_free_coherent(eth->dma_dev, diff --git a/target/linux/generic/backport-5.15/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch b/target/linux/generic/backport-5.15/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch index 1d23a178b4b..053412e7490 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-21-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-mtk_des.patch @@ -12,7 +12,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -890,10 +890,11 @@ static inline void *mtk_qdma_phys_to_vir +@@ -891,10 +891,11 @@ static inline void *mtk_qdma_phys_to_vir return ret + (desc - ring->phys); } @@ -27,7 +27,7 @@ Signed-off-by: David S. Miller return &ring->buf[idx]; } -@@ -1015,6 +1016,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1016,6 +1017,7 @@ static int mtk_tx_map(struct sk_buff *sk }; struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; @@ -35,7 +35,7 @@ Signed-off-by: David S. Miller struct mtk_tx_dma *itxd, *txd; struct mtk_tx_dma *itxd_pdma, *txd_pdma; struct mtk_tx_buf *itx_buf, *tx_buf; -@@ -1026,7 +1028,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1027,7 +1029,7 @@ static int mtk_tx_map(struct sk_buff *sk if (itxd == ring->last_free) return -ENOMEM; @@ -44,7 +44,7 @@ Signed-off-by: David S. Miller memset(itx_buf, 0, sizeof(*itx_buf)); txd_info.addr = dma_map_single(eth->dma_dev, skb->data, txd_info.size, -@@ -1054,7 +1056,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1055,7 +1057,7 @@ static int mtk_tx_map(struct sk_buff *sk while (frag_size) { bool new_desc = true; @@ -53,7 +53,7 @@ Signed-off-by: David S. Miller (i & 0x1)) { txd = mtk_qdma_phys_to_virt(ring, txd->txd2); txd_pdma = qdma_to_pdma(ring, txd); -@@ -1078,7 +1080,8 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1079,7 +1081,8 @@ static int mtk_tx_map(struct sk_buff *sk mtk_tx_set_dma_desc(dev, txd, &txd_info); @@ -63,7 +63,7 @@ Signed-off-by: David S. Miller if (new_desc) memset(tx_buf, 0, sizeof(*tx_buf)); tx_buf->skb = (struct sk_buff *)MTK_DMA_DUMMY_DESC; -@@ -1097,7 +1100,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1098,7 +1101,7 @@ static int mtk_tx_map(struct sk_buff *sk /* store skb to cleanup */ itx_buf->skb = skb; @@ -72,7 +72,7 @@ Signed-off-by: David S. Miller if (k & 0x1) txd_pdma->txd2 |= TX_DMA_LS0; else -@@ -1115,7 +1118,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1116,7 +1119,7 @@ static int mtk_tx_map(struct sk_buff *sk */ wmb(); @@ -81,7 +81,7 @@ Signed-off-by: David S. Miller if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !netdev_xmit_more()) mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR); -@@ -1129,13 +1132,13 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1130,13 +1133,13 @@ static int mtk_tx_map(struct sk_buff *sk err_dma: do { @@ -97,7 +97,7 @@ Signed-off-by: David S. Miller itxd_pdma->txd2 = TX_DMA_DESP2_DEF; itxd = mtk_qdma_phys_to_virt(ring, itxd->txd2); -@@ -1449,7 +1452,8 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -1450,7 +1453,8 @@ static int mtk_poll_tx_qdma(struct mtk_e if ((desc->txd3 & TX_DMA_OWNER_CPU) == 0) break; diff --git a/target/linux/generic/backport-5.15/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch b/target/linux/generic/backport-5.15/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch index 2989d190d82..251d583f296 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-22-net-ethernet-mtk_eth_soc-rely-on-txd_size-in-txd_to_.patch @@ -11,7 +11,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -905,9 +905,10 @@ static struct mtk_tx_dma *qdma_to_pdma(s +@@ -906,9 +906,10 @@ static struct mtk_tx_dma *qdma_to_pdma(s return ring->dma_pdma - ring->dma + dma; } @@ -24,7 +24,7 @@ Signed-off-by: David S. Miller } static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, -@@ -1123,8 +1124,10 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1124,8 +1125,10 @@ static int mtk_tx_map(struct sk_buff *sk !netdev_xmit_more()) mtk_w32(eth, txd->txd2, MTK_QTX_CTX_PTR); } else { diff --git a/target/linux/generic/backport-5.15/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch b/target/linux/generic/backport-5.15/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch index 5436e92dbea..ec206f28d64 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-23-net-ethernet-mtk_eth_soc-add-rxd_size-to-mtk_soc_dat.patch @@ -13,7 +13,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1775,7 +1775,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1776,7 +1776,7 @@ static int mtk_rx_alloc(struct mtk_eth * } ring->dma = dma_alloc_coherent(eth->dma_dev, @@ -22,7 +22,7 @@ Signed-off-by: David S. Miller &ring->phys, GFP_KERNEL); if (!ring->dma) return -ENOMEM; -@@ -1833,9 +1833,8 @@ static void mtk_rx_clean(struct mtk_eth +@@ -1834,9 +1834,8 @@ static void mtk_rx_clean(struct mtk_eth if (ring->dma) { dma_free_coherent(eth->dma_dev, @@ -34,7 +34,7 @@ Signed-off-by: David S. Miller ring->dma = NULL; } } -@@ -3405,6 +3404,7 @@ static const struct mtk_soc_data mt2701_ +@@ -3406,6 +3405,7 @@ static const struct mtk_soc_data mt2701_ .required_pctl = true, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), @@ -42,7 +42,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3416,6 +3416,7 @@ static const struct mtk_soc_data mt7621_ +@@ -3417,6 +3417,7 @@ static const struct mtk_soc_data mt7621_ .offload_version = 2, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), @@ -50,7 +50,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3428,6 +3429,7 @@ static const struct mtk_soc_data mt7622_ +@@ -3429,6 +3430,7 @@ static const struct mtk_soc_data mt7622_ .offload_version = 2, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), @@ -58,7 +58,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3439,6 +3441,7 @@ static const struct mtk_soc_data mt7623_ +@@ -3440,6 +3442,7 @@ static const struct mtk_soc_data mt7623_ .offload_version = 2, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), @@ -66,7 +66,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3450,6 +3453,7 @@ static const struct mtk_soc_data mt7629_ +@@ -3451,6 +3454,7 @@ static const struct mtk_soc_data mt7629_ .required_pctl = false, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), @@ -74,7 +74,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3460,6 +3464,7 @@ static const struct mtk_soc_data rt5350_ +@@ -3461,6 +3465,7 @@ static const struct mtk_soc_data rt5350_ .required_pctl = false, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), @@ -84,7 +84,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -864,6 +864,7 @@ struct mtk_tx_dma_desc_info { +@@ -865,6 +865,7 @@ struct mtk_tx_dma_desc_info { * @required_pctl A bool value to show whether the SoC requires * the extra setup for those pins used by GMAC. * @txd_size Tx DMA descriptor size. @@ -92,7 +92,7 @@ Signed-off-by: David S. Miller */ struct mtk_soc_data { u32 ana_rgc3; -@@ -874,6 +875,7 @@ struct mtk_soc_data { +@@ -875,6 +876,7 @@ struct mtk_soc_data { netdev_features_t hw_features; struct { u32 txd_size; diff --git a/target/linux/generic/backport-5.15/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch b/target/linux/generic/backport-5.15/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch index 53af586b6c0..eb92b8c7a22 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-24-net-ethernet-mtk_eth_soc-rely-on-txd_size-field-in-m.patch @@ -12,7 +12,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1264,9 +1264,12 @@ static struct mtk_rx_ring *mtk_get_rx_ri +@@ -1265,9 +1265,12 @@ static struct mtk_rx_ring *mtk_get_rx_ri return ð->rx_ring[0]; for (i = 0; i < MTK_MAX_RX_RING_NUM; i++) { @@ -26,7 +26,7 @@ Signed-off-by: David S. Miller ring->calc_idx_update = true; return ring; } -@@ -1317,7 +1320,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1318,7 +1321,7 @@ static int mtk_poll_rx(struct napi_struc goto rx_done; idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); @@ -35,7 +35,7 @@ Signed-off-by: David S. Miller data = ring->data[idx]; if (!mtk_rx_get_desc(&trxd, rxd)) -@@ -1509,7 +1512,7 @@ static int mtk_poll_tx_pdma(struct mtk_e +@@ -1510,7 +1513,7 @@ static int mtk_poll_tx_pdma(struct mtk_e mtk_tx_unmap(eth, tx_buf, true); diff --git a/target/linux/generic/backport-5.15/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch b/target/linux/generic/backport-5.15/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch index 1f4fa1dfb50..456eec247c5 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-25-net-ethernet-mtk_eth_soc-rely-on-rxd_size-field-in-m.patch @@ -14,7 +14,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1784,18 +1784,25 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1785,18 +1785,25 @@ static int mtk_rx_alloc(struct mtk_eth * return -ENOMEM; for (i = 0; i < rx_dma_size; i++) { @@ -43,7 +43,7 @@ Signed-off-by: David S. Miller } ring->dma_size = rx_dma_size; ring->calc_idx_update = false; -@@ -1820,14 +1827,17 @@ static void mtk_rx_clean(struct mtk_eth +@@ -1821,14 +1828,17 @@ static void mtk_rx_clean(struct mtk_eth if (ring->data && ring->dma) { for (i = 0; i < ring->dma_size; i++) { diff --git a/target/linux/generic/backport-5.15/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch b/target/linux/generic/backport-5.15/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch index 945d4d9bb2e..272f782877f 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-26-net-ethernet-mtk_eth_soc-introduce-device-register-m.patch @@ -73,7 +73,7 @@ Signed-off-by: David S. Miller /* strings used by ethtool */ static const struct mtk_ethtool_stats { char str[ETH_GSTRING_LEN]; -@@ -618,8 +671,8 @@ static inline void mtk_tx_irq_disable(st +@@ -619,8 +672,8 @@ static inline void mtk_tx_irq_disable(st u32 val; spin_lock_irqsave(ð->tx_irq_lock, flags); @@ -84,7 +84,7 @@ Signed-off-by: David S. Miller spin_unlock_irqrestore(ð->tx_irq_lock, flags); } -@@ -629,8 +682,8 @@ static inline void mtk_tx_irq_enable(str +@@ -630,8 +683,8 @@ static inline void mtk_tx_irq_enable(str u32 val; spin_lock_irqsave(ð->tx_irq_lock, flags); @@ -95,7 +95,7 @@ Signed-off-by: David S. Miller spin_unlock_irqrestore(ð->tx_irq_lock, flags); } -@@ -640,8 +693,8 @@ static inline void mtk_rx_irq_disable(st +@@ -641,8 +694,8 @@ static inline void mtk_rx_irq_disable(st u32 val; spin_lock_irqsave(ð->rx_irq_lock, flags); @@ -106,7 +106,7 @@ Signed-off-by: David S. Miller spin_unlock_irqrestore(ð->rx_irq_lock, flags); } -@@ -651,8 +704,8 @@ static inline void mtk_rx_irq_enable(str +@@ -652,8 +705,8 @@ static inline void mtk_rx_irq_enable(str u32 val; spin_lock_irqsave(ð->rx_irq_lock, flags); @@ -117,7 +117,7 @@ Signed-off-by: David S. Miller spin_unlock_irqrestore(ð->rx_irq_lock, flags); } -@@ -703,39 +756,39 @@ void mtk_stats_update_mac(struct mtk_mac +@@ -704,39 +757,39 @@ void mtk_stats_update_mac(struct mtk_mac hw_stats->rx_checksum_errors += mtk_r32(mac->hw, MT7628_SDM_CS_ERR); } else { @@ -172,7 +172,7 @@ Signed-off-by: David S. Miller } u64_stats_update_end(&hw_stats->syncp); -@@ -875,10 +928,10 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -876,10 +929,10 @@ static int mtk_init_fq_dma(struct mtk_et txd->txd4 = 0; } @@ -187,7 +187,7 @@ Signed-off-by: David S. Miller return 0; } -@@ -1122,7 +1175,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1123,7 +1176,7 @@ static int mtk_tx_map(struct sk_buff *sk if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { if (netif_xmit_stopped(netdev_get_tx_queue(dev, 0)) || !netdev_xmit_more()) @@ -196,7 +196,7 @@ Signed-off-by: David S. Miller } else { int next_idx; -@@ -1439,6 +1492,7 @@ rx_done: +@@ -1440,6 +1493,7 @@ rx_done: static int mtk_poll_tx_qdma(struct mtk_eth *eth, int budget, unsigned int *done, unsigned int *bytes) { @@ -204,7 +204,7 @@ Signed-off-by: David S. Miller struct mtk_tx_ring *ring = ð->tx_ring; struct mtk_tx_dma *desc; struct sk_buff *skb; -@@ -1446,7 +1500,7 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -1447,7 +1501,7 @@ static int mtk_poll_tx_qdma(struct mtk_e u32 cpu, dma; cpu = ring->last_free_ptr; @@ -213,7 +213,7 @@ Signed-off-by: David S. Miller desc = mtk_qdma_phys_to_virt(ring, cpu); -@@ -1481,7 +1535,7 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -1482,7 +1536,7 @@ static int mtk_poll_tx_qdma(struct mtk_e } ring->last_free_ptr = cpu; @@ -222,7 +222,7 @@ Signed-off-by: David S. Miller return budget; } -@@ -1574,24 +1628,25 @@ static void mtk_handle_status_irq(struct +@@ -1575,24 +1629,25 @@ static void mtk_handle_status_irq(struct static int mtk_napi_tx(struct napi_struct *napi, int budget) { struct mtk_eth *eth = container_of(napi, struct mtk_eth, tx_napi); @@ -252,7 +252,7 @@ Signed-off-by: David S. Miller return budget; if (napi_complete_done(napi, tx_done)) -@@ -1603,6 +1658,7 @@ static int mtk_napi_tx(struct napi_struc +@@ -1604,6 +1659,7 @@ static int mtk_napi_tx(struct napi_struc static int mtk_napi_rx(struct napi_struct *napi, int budget) { struct mtk_eth *eth = container_of(napi, struct mtk_eth, rx_napi); @@ -260,7 +260,7 @@ Signed-off-by: David S. Miller int rx_done_total = 0; mtk_handle_status_irq(eth); -@@ -1610,21 +1666,21 @@ static int mtk_napi_rx(struct napi_struc +@@ -1611,21 +1667,21 @@ static int mtk_napi_rx(struct napi_struc do { int rx_done; @@ -286,7 +286,7 @@ Signed-off-by: David S. Miller if (napi_complete_done(napi, rx_done_total)) mtk_rx_irq_enable(eth, MTK_RX_DONE_INT); -@@ -1687,20 +1743,20 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1688,20 +1744,20 @@ static int mtk_tx_alloc(struct mtk_eth * */ wmb(); @@ -314,7 +314,7 @@ Signed-off-by: David S. Miller } return 0; -@@ -1739,6 +1795,7 @@ static void mtk_tx_clean(struct mtk_eth +@@ -1740,6 +1796,7 @@ static void mtk_tx_clean(struct mtk_eth static int mtk_rx_alloc(struct mtk_eth *eth, int ring_no, int rx_flag) { @@ -322,7 +322,7 @@ Signed-off-by: David S. Miller struct mtk_rx_ring *ring; int rx_data_len, rx_dma_size; int i; -@@ -1807,16 +1864,18 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1808,16 +1865,18 @@ static int mtk_rx_alloc(struct mtk_eth * ring->dma_size = rx_dma_size; ring->calc_idx_update = false; ring->calc_idx = rx_dma_size - 1; @@ -345,7 +345,7 @@ Signed-off-by: David S. Miller return 0; } -@@ -2125,9 +2184,9 @@ static int mtk_dma_busy_wait(struct mtk_ +@@ -2126,9 +2185,9 @@ static int mtk_dma_busy_wait(struct mtk_ u32 val; if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) @@ -357,7 +357,7 @@ Signed-off-by: David S. Miller ret = readx_poll_timeout_atomic(__raw_readl, eth->base + reg, val, !(val & (MTK_RX_DMA_BUSY | MTK_TX_DMA_BUSY)), -@@ -2185,8 +2244,8 @@ static int mtk_dma_init(struct mtk_eth * +@@ -2186,8 +2245,8 @@ static int mtk_dma_init(struct mtk_eth * * automatically */ mtk_w32(eth, FC_THRES_DROP_MODE | FC_THRES_DROP_EN | @@ -368,7 +368,7 @@ Signed-off-by: David S. Miller } return 0; -@@ -2260,13 +2319,14 @@ static irqreturn_t mtk_handle_irq_tx(int +@@ -2261,13 +2320,14 @@ static irqreturn_t mtk_handle_irq_tx(int static irqreturn_t mtk_handle_irq(int irq, void *_eth) { struct mtk_eth *eth = _eth; @@ -387,7 +387,7 @@ Signed-off-by: David S. Miller mtk_handle_irq_tx(irq, _eth); } -@@ -2290,6 +2350,7 @@ static void mtk_poll_controller(struct n +@@ -2291,6 +2351,7 @@ static void mtk_poll_controller(struct n static int mtk_start_dma(struct mtk_eth *eth) { u32 rx_2b_offset = (NET_IP_ALIGN == 2) ? MTK_RX_2B_OFFSET : 0; @@ -395,7 +395,7 @@ Signed-off-by: David S. Miller int err; err = mtk_dma_init(eth); -@@ -2304,16 +2365,15 @@ static int mtk_start_dma(struct mtk_eth +@@ -2305,16 +2366,15 @@ static int mtk_start_dma(struct mtk_eth MTK_TX_BT_32DWORDS | MTK_NDP_CO_PRO | MTK_RX_DMA_EN | MTK_RX_2B_OFFSET | MTK_RX_BT_32DWORDS, @@ -415,7 +415,7 @@ Signed-off-by: David S. Miller } return 0; -@@ -2439,8 +2499,8 @@ static int mtk_stop(struct net_device *d +@@ -2440,8 +2500,8 @@ static int mtk_stop(struct net_device *d cancel_work_sync(ð->tx_dim.work); if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) @@ -426,7 +426,7 @@ Signed-off-by: David S. Miller mtk_dma_free(eth); -@@ -2494,6 +2554,7 @@ static void mtk_dim_rx(struct work_struc +@@ -2495,6 +2555,7 @@ static void mtk_dim_rx(struct work_struc { struct dim *dim = container_of(work, struct dim, work); struct mtk_eth *eth = container_of(dim, struct mtk_eth, rx_dim); @@ -434,7 +434,7 @@ Signed-off-by: David S. Miller struct dim_cq_moder cur_profile; u32 val, cur; -@@ -2501,7 +2562,7 @@ static void mtk_dim_rx(struct work_struc +@@ -2502,7 +2563,7 @@ static void mtk_dim_rx(struct work_struc dim->profile_ix); spin_lock_bh(ð->dim_lock); @@ -443,7 +443,7 @@ Signed-off-by: David S. Miller val &= MTK_PDMA_DELAY_TX_MASK; val |= MTK_PDMA_DELAY_RX_EN; -@@ -2511,9 +2572,9 @@ static void mtk_dim_rx(struct work_struc +@@ -2512,9 +2573,9 @@ static void mtk_dim_rx(struct work_struc cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK); val |= cur << MTK_PDMA_DELAY_RX_PINT_SHIFT; @@ -455,7 +455,7 @@ Signed-off-by: David S. Miller spin_unlock_bh(ð->dim_lock); -@@ -2524,6 +2585,7 @@ static void mtk_dim_tx(struct work_struc +@@ -2525,6 +2586,7 @@ static void mtk_dim_tx(struct work_struc { struct dim *dim = container_of(work, struct dim, work); struct mtk_eth *eth = container_of(dim, struct mtk_eth, tx_dim); @@ -463,7 +463,7 @@ Signed-off-by: David S. Miller struct dim_cq_moder cur_profile; u32 val, cur; -@@ -2531,7 +2593,7 @@ static void mtk_dim_tx(struct work_struc +@@ -2532,7 +2594,7 @@ static void mtk_dim_tx(struct work_struc dim->profile_ix); spin_lock_bh(ð->dim_lock); @@ -472,7 +472,7 @@ Signed-off-by: David S. Miller val &= MTK_PDMA_DELAY_RX_MASK; val |= MTK_PDMA_DELAY_TX_EN; -@@ -2541,9 +2603,9 @@ static void mtk_dim_tx(struct work_struc +@@ -2542,9 +2604,9 @@ static void mtk_dim_tx(struct work_struc cur = min_t(u32, cur_profile.pkts, MTK_PDMA_DELAY_PINT_MASK); val |= cur << MTK_PDMA_DELAY_TX_PINT_SHIFT; @@ -484,7 +484,7 @@ Signed-off-by: David S. Miller spin_unlock_bh(ð->dim_lock); -@@ -2554,6 +2616,7 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2555,6 +2617,7 @@ static int mtk_hw_init(struct mtk_eth *e { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | ETHSYS_DMA_AG_MAP_PPE; @@ -492,7 +492,7 @@ Signed-off-by: David S. Miller int i, val, ret; if (test_and_set_bit(MTK_HW_INIT, ð->state)) -@@ -2628,10 +2691,10 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2629,10 +2692,10 @@ static int mtk_hw_init(struct mtk_eth *e mtk_rx_irq_disable(eth, ~0); /* FE int grouping */ @@ -507,7 +507,7 @@ Signed-off-by: David S. Miller mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); return 0; -@@ -3170,14 +3233,6 @@ static int mtk_probe(struct platform_dev +@@ -3171,14 +3234,6 @@ static int mtk_probe(struct platform_dev if (IS_ERR(eth->base)) return PTR_ERR(eth->base); @@ -522,7 +522,7 @@ Signed-off-by: David S. Miller if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { eth->rx_dma_l4_valid = RX_DMA_L4_VALID_PDMA; eth->ip_align = NET_IP_ALIGN; -@@ -3411,6 +3466,7 @@ static int mtk_remove(struct platform_de +@@ -3412,6 +3467,7 @@ static int mtk_remove(struct platform_de } static const struct mtk_soc_data mt2701_data = { @@ -530,7 +530,7 @@ Signed-off-by: David S. Miller .caps = MT7623_CAPS | MTK_HWLRO, .hw_features = MTK_HW_FEATURES, .required_clks = MT7623_CLKS_BITMAP, -@@ -3422,6 +3478,7 @@ static const struct mtk_soc_data mt2701_ +@@ -3423,6 +3479,7 @@ static const struct mtk_soc_data mt2701_ }; static const struct mtk_soc_data mt7621_data = { @@ -538,7 +538,7 @@ Signed-off-by: David S. Miller .caps = MT7621_CAPS, .hw_features = MTK_HW_FEATURES, .required_clks = MT7621_CLKS_BITMAP, -@@ -3434,6 +3491,7 @@ static const struct mtk_soc_data mt7621_ +@@ -3435,6 +3492,7 @@ static const struct mtk_soc_data mt7621_ }; static const struct mtk_soc_data mt7622_data = { @@ -546,7 +546,7 @@ Signed-off-by: David S. Miller .ana_rgc3 = 0x2028, .caps = MT7622_CAPS | MTK_HWLRO, .hw_features = MTK_HW_FEATURES, -@@ -3447,6 +3505,7 @@ static const struct mtk_soc_data mt7622_ +@@ -3448,6 +3506,7 @@ static const struct mtk_soc_data mt7622_ }; static const struct mtk_soc_data mt7623_data = { @@ -554,7 +554,7 @@ Signed-off-by: David S. Miller .caps = MT7623_CAPS | MTK_HWLRO, .hw_features = MTK_HW_FEATURES, .required_clks = MT7623_CLKS_BITMAP, -@@ -3459,6 +3518,7 @@ static const struct mtk_soc_data mt7623_ +@@ -3460,6 +3519,7 @@ static const struct mtk_soc_data mt7623_ }; static const struct mtk_soc_data mt7629_data = { @@ -562,7 +562,7 @@ Signed-off-by: David S. Miller .ana_rgc3 = 0x128, .caps = MT7629_CAPS | MTK_HWLRO, .hw_features = MTK_HW_FEATURES, -@@ -3471,6 +3531,7 @@ static const struct mtk_soc_data mt7629_ +@@ -3472,6 +3532,7 @@ static const struct mtk_soc_data mt7629_ }; static const struct mtk_soc_data rt5350_data = { @@ -748,7 +748,7 @@ Signed-off-by: David S. Miller #define MTK_STAT_OFFSET 0x40 #define MTK_WDMA0_BASE 0x2800 -@@ -853,8 +762,46 @@ struct mtk_tx_dma_desc_info { +@@ -854,8 +763,46 @@ struct mtk_tx_dma_desc_info { u8 last:1; }; @@ -795,7 +795,7 @@ Signed-off-by: David S. Miller * @ana_rgc3: The offset for register ANA_RGC3 related to * sgmiisys syscon * @caps Flags shown the extra capability for the SoC -@@ -867,6 +814,7 @@ struct mtk_tx_dma_desc_info { +@@ -868,6 +815,7 @@ struct mtk_tx_dma_desc_info { * @rxd_size Rx DMA descriptor size. */ struct mtk_soc_data { @@ -803,7 +803,7 @@ Signed-off-by: David S. Miller u32 ana_rgc3; u32 caps; u32 required_clks; -@@ -994,8 +942,6 @@ struct mtk_eth { +@@ -995,8 +943,6 @@ struct mtk_eth { u32 tx_bytes; struct dim tx_dim; diff --git a/target/linux/generic/backport-5.15/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch b/target/linux/generic/backport-5.15/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch index d1f19d6a454..4d6c94b13b8 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-27-net-ethernet-mtk_eth_soc-introduce-MTK_NETSYS_V2-sup.patch @@ -13,7 +13,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -862,8 +862,8 @@ static inline int mtk_max_buf_size(int f +@@ -863,8 +863,8 @@ static inline int mtk_max_buf_size(int f return buf_size; } @@ -24,7 +24,7 @@ Signed-off-by: David S. Miller { rxd->rxd2 = READ_ONCE(dma_rxd->rxd2); if (!(rxd->rxd2 & RX_DMA_DONE)) -@@ -872,6 +872,10 @@ static inline bool mtk_rx_get_desc(struc +@@ -873,6 +873,10 @@ static inline bool mtk_rx_get_desc(struc rxd->rxd1 = READ_ONCE(dma_rxd->rxd1); rxd->rxd3 = READ_ONCE(dma_rxd->rxd3); rxd->rxd4 = READ_ONCE(dma_rxd->rxd4); @@ -35,7 +35,7 @@ Signed-off-by: David S. Miller return true; } -@@ -916,7 +920,7 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -917,7 +921,7 @@ static int mtk_init_fq_dma(struct mtk_et phy_ring_tail = eth->phy_scratch_ring + soc->txrx.txd_size * (cnt - 1); for (i = 0; i < cnt; i++) { @@ -44,7 +44,7 @@ Signed-off-by: David S. Miller txd = (void *)eth->scratch_ring + i * soc->txrx.txd_size; txd->txd1 = dma_addr + i * MTK_QDMA_PAGE_SIZE; -@@ -926,6 +930,12 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -927,6 +931,12 @@ static int mtk_init_fq_dma(struct mtk_et txd->txd3 = TX_DMA_PLEN0(MTK_QDMA_PAGE_SIZE); txd->txd4 = 0; @@ -57,7 +57,7 @@ Signed-off-by: David S. Miller } mtk_w32(eth, eth->phy_scratch_ring, soc->reg_map->qdma.fq_head); -@@ -1029,10 +1039,12 @@ static void setup_tx_buf(struct mtk_eth +@@ -1030,10 +1040,12 @@ static void setup_tx_buf(struct mtk_eth } } @@ -72,7 +72,7 @@ Signed-off-by: David S. Miller u32 data; WRITE_ONCE(desc->txd1, info->addr); -@@ -1056,6 +1068,59 @@ static void mtk_tx_set_dma_desc(struct n +@@ -1057,6 +1069,59 @@ static void mtk_tx_set_dma_desc(struct n WRITE_ONCE(desc->txd4, data); } @@ -132,7 +132,7 @@ Signed-off-by: David S. Miller static int mtk_tx_map(struct sk_buff *skb, struct net_device *dev, int tx_num, struct mtk_tx_ring *ring, bool gso) { -@@ -1064,6 +1129,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1065,6 +1130,7 @@ static int mtk_tx_map(struct sk_buff *sk .gso = gso, .csum = skb->ip_summed == CHECKSUM_PARTIAL, .vlan = skb_vlan_tag_present(skb), @@ -140,7 +140,7 @@ Signed-off-by: David S. Miller .vlan_tci = skb_vlan_tag_get(skb), .first = true, .last = !skb_is_nonlinear(skb), -@@ -1123,7 +1189,9 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1124,7 +1190,9 @@ static int mtk_tx_map(struct sk_buff *sk } memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); @@ -151,7 +151,7 @@ Signed-off-by: David S. Miller txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 && !(frag_size - txd_info.size); txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, -@@ -1204,17 +1272,16 @@ err_dma: +@@ -1205,17 +1273,16 @@ err_dma: return -ENOMEM; } @@ -172,7 +172,7 @@ Signed-off-by: David S. Miller } } else { nfrags += skb_shinfo(skb)->nr_frags; -@@ -1266,7 +1333,7 @@ static netdev_tx_t mtk_start_xmit(struct +@@ -1267,7 +1334,7 @@ static netdev_tx_t mtk_start_xmit(struct if (unlikely(test_bit(MTK_RESETTING, ð->state))) goto drop; @@ -181,7 +181,7 @@ Signed-off-by: David S. Miller if (unlikely(atomic_read(&ring->free_count) <= tx_num)) { netif_stop_queue(dev); netif_err(eth, tx_queued, dev, -@@ -1358,7 +1425,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1359,7 +1426,7 @@ static int mtk_poll_rx(struct napi_struc int idx; struct sk_buff *skb; u8 *data, *new_data; @@ -190,7 +190,7 @@ Signed-off-by: David S. Miller int done = 0, bytes = 0; while (done < budget) { -@@ -1366,7 +1433,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1367,7 +1434,7 @@ static int mtk_poll_rx(struct napi_struc unsigned int pktlen; dma_addr_t dma_addr; u32 hash, reason; @@ -199,7 +199,7 @@ Signed-off-by: David S. Miller ring = mtk_get_rx_ring(eth); if (unlikely(!ring)) -@@ -1376,16 +1443,15 @@ static int mtk_poll_rx(struct napi_struc +@@ -1377,16 +1444,15 @@ static int mtk_poll_rx(struct napi_struc rxd = (void *)ring->dma + idx * eth->soc->txrx.rxd_size; data = ring->data[idx]; @@ -222,7 +222,7 @@ Signed-off-by: David S. Miller if (unlikely(mac < 0 || mac >= MTK_MAC_COUNT || !eth->netdev[mac])) -@@ -1431,7 +1497,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1432,7 +1498,7 @@ static int mtk_poll_rx(struct napi_struc pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); skb->dev = netdev; skb_put(skb, pktlen); @@ -231,7 +231,7 @@ Signed-off-by: David S. Miller skb->ip_summed = CHECKSUM_UNNECESSARY; else skb_checksum_none_assert(skb); -@@ -1449,10 +1515,25 @@ static int mtk_poll_rx(struct napi_struc +@@ -1450,10 +1516,25 @@ static int mtk_poll_rx(struct napi_struc mtk_ppe_check_skb(eth->ppe, skb, trxd.rxd4 & MTK_RXD4_FOE_ENTRY); @@ -261,7 +261,7 @@ Signed-off-by: David S. Miller skb_record_rx_queue(skb, 0); napi_gro_receive(napi, skb); -@@ -1464,7 +1545,7 @@ release_desc: +@@ -1465,7 +1546,7 @@ release_desc: if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) rxd->rxd2 = RX_DMA_LSO; else @@ -270,7 +270,7 @@ Signed-off-by: David S. Miller ring->calc_idx = idx; -@@ -1666,7 +1747,8 @@ static int mtk_napi_rx(struct napi_struc +@@ -1667,7 +1748,8 @@ static int mtk_napi_rx(struct napi_struc do { int rx_done; @@ -280,7 +280,7 @@ Signed-off-by: David S. Miller rx_done = mtk_poll_rx(napi, budget - rx_done_total, eth); rx_done_total += rx_done; -@@ -1680,10 +1762,11 @@ static int mtk_napi_rx(struct napi_struc +@@ -1681,10 +1763,11 @@ static int mtk_napi_rx(struct napi_struc if (rx_done_total == budget) return budget; @@ -294,7 +294,7 @@ Signed-off-by: David S. Miller return rx_done_total; } -@@ -1693,7 +1776,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1694,7 +1777,7 @@ static int mtk_tx_alloc(struct mtk_eth * const struct mtk_soc_data *soc = eth->soc; struct mtk_tx_ring *ring = ð->tx_ring; int i, sz = soc->txrx.txd_size; @@ -303,7 +303,7 @@ Signed-off-by: David S. Miller ring->buf = kcalloc(MTK_DMA_SIZE, sizeof(*ring->buf), GFP_KERNEL); -@@ -1713,13 +1796,19 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1714,13 +1797,19 @@ static int mtk_tx_alloc(struct mtk_eth * txd->txd2 = next_ptr; txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; txd->txd4 = 0; @@ -324,7 +324,7 @@ Signed-off-by: David S. Miller ring->dma_pdma = dma_alloc_coherent(eth->dma_dev, MTK_DMA_SIZE * sz, &ring->phys_pdma, GFP_KERNEL); if (!ring->dma_pdma) -@@ -1799,13 +1888,11 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1800,13 +1889,11 @@ static int mtk_rx_alloc(struct mtk_eth * struct mtk_rx_ring *ring; int rx_data_len, rx_dma_size; int i; @@ -338,7 +338,7 @@ Signed-off-by: David S. Miller } else { ring = ð->rx_ring[ring_no]; } -@@ -1841,7 +1928,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1842,7 +1929,7 @@ static int mtk_rx_alloc(struct mtk_eth * return -ENOMEM; for (i = 0; i < rx_dma_size; i++) { @@ -347,7 +347,7 @@ Signed-off-by: David S. Miller dma_addr_t dma_addr = dma_map_single(eth->dma_dev, ring->data[i] + NET_SKB_PAD + eth->ip_align, -@@ -1856,26 +1943,47 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1857,26 +1944,47 @@ static int mtk_rx_alloc(struct mtk_eth * if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) rxd->rxd2 = RX_DMA_LSO; else @@ -403,7 +403,7 @@ Signed-off-by: David S. Miller return 0; } -@@ -2297,7 +2405,7 @@ static irqreturn_t mtk_handle_irq_rx(int +@@ -2298,7 +2406,7 @@ static irqreturn_t mtk_handle_irq_rx(int eth->rx_events++; if (likely(napi_schedule_prep(ð->rx_napi))) { __napi_schedule(ð->rx_napi); @@ -412,7 +412,7 @@ Signed-off-by: David S. Miller } return IRQ_HANDLED; -@@ -2321,8 +2429,10 @@ static irqreturn_t mtk_handle_irq(int ir +@@ -2322,8 +2430,10 @@ static irqreturn_t mtk_handle_irq(int ir struct mtk_eth *eth = _eth; const struct mtk_reg_map *reg_map = eth->soc->reg_map; @@ -425,7 +425,7 @@ Signed-off-by: David S. Miller mtk_handle_irq_rx(irq, _eth); } if (mtk_r32(eth, reg_map->tx_irq_mask) & MTK_TX_DONE_INT) { -@@ -2340,16 +2450,16 @@ static void mtk_poll_controller(struct n +@@ -2341,16 +2451,16 @@ static void mtk_poll_controller(struct n struct mtk_eth *eth = mac->hw; mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); @@ -445,7 +445,7 @@ Signed-off-by: David S. Miller const struct mtk_reg_map *reg_map = eth->soc->reg_map; int err; -@@ -2360,12 +2470,19 @@ static int mtk_start_dma(struct mtk_eth +@@ -2361,12 +2471,19 @@ static int mtk_start_dma(struct mtk_eth } if (MTK_HAS_CAPS(eth->soc->caps, MTK_QDMA)) { @@ -471,7 +471,7 @@ Signed-off-by: David S. Miller mtk_w32(eth, MTK_RX_DMA_EN | rx_2b_offset | MTK_RX_BT_32DWORDS | MTK_MULTI_EN, -@@ -2439,7 +2556,7 @@ static int mtk_open(struct net_device *d +@@ -2440,7 +2557,7 @@ static int mtk_open(struct net_device *d napi_enable(ð->tx_napi); napi_enable(ð->rx_napi); mtk_tx_irq_enable(eth, MTK_TX_DONE_INT); @@ -480,7 +480,7 @@ Signed-off-by: David S. Miller refcount_set(ð->dma_refcnt, 1); } else -@@ -2491,7 +2608,7 @@ static int mtk_stop(struct net_device *d +@@ -2492,7 +2609,7 @@ static int mtk_stop(struct net_device *d mtk_gdm_config(eth, MTK_GDMA_DROP_ALL); mtk_tx_irq_disable(eth, MTK_TX_DONE_INT); @@ -489,7 +489,7 @@ Signed-off-by: David S. Miller napi_disable(ð->tx_napi); napi_disable(ð->rx_napi); -@@ -2651,9 +2768,25 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2652,9 +2769,25 @@ static int mtk_hw_init(struct mtk_eth *e return 0; } @@ -518,7 +518,7 @@ Signed-off-by: David S. Miller if (eth->pctl) { /* Set GE2 driving and slew rate */ -@@ -2692,11 +2825,47 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -2693,11 +2826,47 @@ static int mtk_hw_init(struct mtk_eth *e /* FE int grouping */ mtk_w32(eth, MTK_TX_DONE_INT, reg_map->pdma.int_grp); @@ -568,7 +568,7 @@ Signed-off-by: David S. Miller return 0; err_disable_pm: -@@ -3233,12 +3402,8 @@ static int mtk_probe(struct platform_dev +@@ -3234,12 +3403,8 @@ static int mtk_probe(struct platform_dev if (IS_ERR(eth->base)) return PTR_ERR(eth->base); @@ -582,7 +582,7 @@ Signed-off-by: David S. Miller spin_lock_init(ð->page_lock); spin_lock_init(ð->tx_irq_lock); -@@ -3474,6 +3639,10 @@ static const struct mtk_soc_data mt2701_ +@@ -3475,6 +3640,10 @@ static const struct mtk_soc_data mt2701_ .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -593,7 +593,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3487,6 +3656,10 @@ static const struct mtk_soc_data mt7621_ +@@ -3488,6 +3657,10 @@ static const struct mtk_soc_data mt7621_ .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -604,7 +604,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3501,6 +3674,10 @@ static const struct mtk_soc_data mt7622_ +@@ -3502,6 +3675,10 @@ static const struct mtk_soc_data mt7622_ .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -615,7 +615,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3514,6 +3691,10 @@ static const struct mtk_soc_data mt7623_ +@@ -3515,6 +3692,10 @@ static const struct mtk_soc_data mt7623_ .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -626,7 +626,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3527,6 +3708,10 @@ static const struct mtk_soc_data mt7629_ +@@ -3528,6 +3709,10 @@ static const struct mtk_soc_data mt7629_ .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -637,7 +637,7 @@ Signed-off-by: David S. Miller }, }; -@@ -3539,6 +3724,10 @@ static const struct mtk_soc_data rt5350_ +@@ -3540,6 +3725,10 @@ static const struct mtk_soc_data rt5350_ .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), @@ -804,7 +804,7 @@ Signed-off-by: David S. Miller /* PHY Indirect Access Control registers */ #define MTK_PHY_IAC 0x10004 #define PHY_IAC_ACCESS BIT(31) -@@ -370,6 +434,16 @@ +@@ -371,6 +435,16 @@ #define ETHSYS_TRGMII_MT7621_DDR_PLL BIT(5) /* ethernet reset control register */ @@ -821,7 +821,7 @@ Signed-off-by: David S. Miller #define ETHSYS_RSTCTRL 0x34 #define RSTCTRL_FE BIT(6) #define RSTCTRL_PPE BIT(31) -@@ -453,6 +527,17 @@ struct mtk_rx_dma { +@@ -454,6 +528,17 @@ struct mtk_rx_dma { unsigned int rxd4; } __packed __aligned(4); @@ -839,7 +839,7 @@ Signed-off-by: David S. Miller struct mtk_tx_dma { unsigned int txd1; unsigned int txd2; -@@ -460,6 +545,17 @@ struct mtk_tx_dma { +@@ -461,6 +546,17 @@ struct mtk_tx_dma { unsigned int txd4; } __packed __aligned(4); @@ -857,7 +857,7 @@ Signed-off-by: David S. Miller struct mtk_eth; struct mtk_mac; -@@ -646,7 +742,9 @@ enum mkt_eth_capabilities { +@@ -647,7 +743,9 @@ enum mkt_eth_capabilities { MTK_SHARED_INT_BIT, MTK_TRGMII_MT7621_CLK_BIT, MTK_QDMA_BIT, @@ -867,7 +867,7 @@ Signed-off-by: David S. Miller /* MUX BITS*/ MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, -@@ -678,7 +776,9 @@ enum mkt_eth_capabilities { +@@ -679,7 +777,9 @@ enum mkt_eth_capabilities { #define MTK_SHARED_INT BIT(MTK_SHARED_INT_BIT) #define MTK_TRGMII_MT7621_CLK BIT(MTK_TRGMII_MT7621_CLK_BIT) #define MTK_QDMA BIT(MTK_QDMA_BIT) @@ -877,7 +877,7 @@ Signed-off-by: David S. Miller #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \ BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) -@@ -755,6 +855,7 @@ struct mtk_tx_dma_desc_info { +@@ -756,6 +856,7 @@ struct mtk_tx_dma_desc_info { dma_addr_t addr; u32 size; u16 vlan_tci; @@ -885,7 +885,7 @@ Signed-off-by: David S. Miller u8 gso:1; u8 csum:1; u8 vlan:1; -@@ -812,6 +913,10 @@ struct mtk_reg_map { +@@ -813,6 +914,10 @@ struct mtk_reg_map { * the extra setup for those pins used by GMAC. * @txd_size Tx DMA descriptor size. * @rxd_size Rx DMA descriptor size. @@ -896,7 +896,7 @@ Signed-off-by: David S. Miller */ struct mtk_soc_data { const struct mtk_reg_map *reg_map; -@@ -824,6 +929,10 @@ struct mtk_soc_data { +@@ -825,6 +930,10 @@ struct mtk_soc_data { struct { u32 txd_size; u32 rxd_size; @@ -907,7 +907,7 @@ Signed-off-by: David S. Miller } txrx; }; -@@ -942,7 +1051,6 @@ struct mtk_eth { +@@ -943,7 +1052,6 @@ struct mtk_eth { u32 tx_bytes; struct dim tx_dim; diff --git a/target/linux/generic/backport-5.15/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch b/target/linux/generic/backport-5.15/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch index a9566392391..1ecb5e71b3a 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-28-net-ethernet-mtk_eth_soc-convert-ring-dma-pointer-to.patch @@ -10,7 +10,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -946,18 +946,15 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -947,18 +947,15 @@ static int mtk_init_fq_dma(struct mtk_et return 0; } @@ -33,7 +33,7 @@ Signed-off-by: David S. Miller return &ring->buf[idx]; } -@@ -965,13 +962,12 @@ static struct mtk_tx_buf *mtk_desc_to_tx +@@ -966,13 +963,12 @@ static struct mtk_tx_buf *mtk_desc_to_tx static struct mtk_tx_dma *qdma_to_pdma(struct mtk_tx_ring *ring, struct mtk_tx_dma *dma) { @@ -50,7 +50,7 @@ Signed-off-by: David S. Miller } static void mtk_tx_unmap(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, -@@ -1388,7 +1384,7 @@ static struct mtk_rx_ring *mtk_get_rx_ri +@@ -1389,7 +1385,7 @@ static struct mtk_rx_ring *mtk_get_rx_ri ring = ð->rx_ring[i]; idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); @@ -59,7 +59,7 @@ Signed-off-by: David S. Miller if (rxd->rxd2 & RX_DMA_DONE) { ring->calc_idx_update = true; return ring; -@@ -1440,7 +1436,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1441,7 +1437,7 @@ static int mtk_poll_rx(struct napi_struc goto rx_done; idx = NEXT_DESP_IDX(ring->calc_idx, ring->dma_size); @@ -68,7 +68,7 @@ Signed-off-by: David S. Miller data = ring->data[idx]; if (!mtk_rx_get_desc(eth, &trxd, rxd)) -@@ -1647,7 +1643,7 @@ static int mtk_poll_tx_pdma(struct mtk_e +@@ -1648,7 +1644,7 @@ static int mtk_poll_tx_pdma(struct mtk_e mtk_tx_unmap(eth, tx_buf, true); @@ -77,7 +77,7 @@ Signed-off-by: David S. Miller ring->last_free = desc; atomic_inc(&ring->free_count); -@@ -1792,7 +1788,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1793,7 +1789,7 @@ static int mtk_tx_alloc(struct mtk_eth * int next = (i + 1) % MTK_DMA_SIZE; u32 next_ptr = ring->phys + next * sz; @@ -86,7 +86,7 @@ Signed-off-by: David S. Miller txd->txd2 = next_ptr; txd->txd3 = TX_DMA_LS0 | TX_DMA_OWNER_CPU; txd->txd4 = 0; -@@ -1822,7 +1818,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -1823,7 +1819,7 @@ static int mtk_tx_alloc(struct mtk_eth * ring->dma_size = MTK_DMA_SIZE; atomic_set(&ring->free_count, MTK_DMA_SIZE - 2); @@ -95,7 +95,7 @@ Signed-off-by: David S. Miller ring->last_free = (void *)txd; ring->last_free_ptr = (u32)(ring->phys + ((MTK_DMA_SIZE - 1) * sz)); ring->thresh = MAX_SKB_FRAGS; -@@ -1937,7 +1933,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1938,7 +1934,7 @@ static int mtk_rx_alloc(struct mtk_eth * if (unlikely(dma_mapping_error(eth->dma_dev, dma_addr))) return -ENOMEM; @@ -104,7 +104,7 @@ Signed-off-by: David S. Miller rxd->rxd1 = (unsigned int)dma_addr; if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) -@@ -1999,7 +1995,7 @@ static void mtk_rx_clean(struct mtk_eth +@@ -2000,7 +1996,7 @@ static void mtk_rx_clean(struct mtk_eth if (!ring->data[i]) continue; @@ -115,7 +115,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -688,7 +688,7 @@ struct mtk_tx_buf { +@@ -689,7 +689,7 @@ struct mtk_tx_buf { * are present */ struct mtk_tx_ring { @@ -124,7 +124,7 @@ Signed-off-by: David S. Miller struct mtk_tx_buf *buf; dma_addr_t phys; struct mtk_tx_dma *next_free; -@@ -718,7 +718,7 @@ enum mtk_rx_flags { +@@ -719,7 +719,7 @@ enum mtk_rx_flags { * @calc_idx: The current head of ring */ struct mtk_rx_ring { diff --git a/target/linux/generic/backport-5.15/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch b/target/linux/generic/backport-5.15/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch index 459ffd54067..f7318e68bbd 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-29-net-ethernet-mtk_eth_soc-convert-scratch_ring-pointe.patch @@ -11,7 +11,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -922,7 +922,7 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -923,7 +923,7 @@ static int mtk_init_fq_dma(struct mtk_et for (i = 0; i < cnt; i++) { struct mtk_tx_dma_v2 *txd; @@ -22,7 +22,7 @@ Signed-off-by: David S. Miller txd->txd2 = eth->phy_scratch_ring + --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1028,7 +1028,7 @@ struct mtk_eth { +@@ -1029,7 +1029,7 @@ struct mtk_eth { struct mtk_rx_ring rx_ring_qdma; struct napi_struct tx_napi; struct napi_struct rx_napi; diff --git a/target/linux/generic/backport-5.15/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch b/target/linux/generic/backport-5.15/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch index c14fcffcc54..a4698d7814d 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-30-net-ethernet-mtk_eth_soc-introduce-support-for-mt798.patch @@ -65,7 +65,7 @@ Signed-off-by: David S. Miller }; void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg) -@@ -3711,6 +3748,21 @@ static const struct mtk_soc_data mt7629_ +@@ -3712,6 +3749,21 @@ static const struct mtk_soc_data mt7629_ }, }; @@ -87,7 +87,7 @@ Signed-off-by: David S. Miller static const struct mtk_soc_data rt5350_data = { .reg_map = &mt7628_reg_map, .caps = MT7628_CAPS, -@@ -3733,6 +3785,7 @@ const struct of_device_id of_mtk_match[] +@@ -3734,6 +3786,7 @@ const struct of_device_id of_mtk_match[] { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data}, @@ -97,7 +97,7 @@ Signed-off-by: David S. Miller }; --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -623,6 +623,10 @@ enum mtk_clks_map { +@@ -624,6 +624,10 @@ enum mtk_clks_map { MTK_CLK_SGMII2_CDR_FB, MTK_CLK_SGMII_CK, MTK_CLK_ETH2PLL, @@ -108,7 +108,7 @@ Signed-off-by: David S. Miller MTK_CLK_MAX }; -@@ -653,6 +657,16 @@ enum mtk_clks_map { +@@ -654,6 +658,16 @@ enum mtk_clks_map { BIT(MTK_CLK_SGMII2_CDR_FB) | \ BIT(MTK_CLK_SGMII_CK) | \ BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP)) @@ -125,7 +125,7 @@ Signed-off-by: David S. Miller enum mtk_dev_state { MTK_HW_INIT, -@@ -851,6 +865,10 @@ enum mkt_eth_capabilities { +@@ -852,6 +866,10 @@ enum mkt_eth_capabilities { MTK_MUX_U3_GMAC2_TO_QPHY | \ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA) diff --git a/target/linux/generic/backport-5.15/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch b/target/linux/generic/backport-5.15/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch index 56d0efb9031..d76df75dda8 100644 --- a/target/linux/generic/backport-5.15/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch +++ b/target/linux/generic/backport-5.15/702-v5.19-33-net-ethernet-mtk_eth_soc-enable-rx-cksum-offload-for.patch @@ -12,7 +12,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1462,8 +1462,8 @@ static int mtk_poll_rx(struct napi_struc +@@ -1463,8 +1463,8 @@ static int mtk_poll_rx(struct napi_struc int done = 0, bytes = 0; while (done < budget) { @@ -22,7 +22,7 @@ Signed-off-by: Jakub Kicinski dma_addr_t dma_addr; u32 hash, reason; int mac = 0; -@@ -1530,7 +1530,13 @@ static int mtk_poll_rx(struct napi_struc +@@ -1531,7 +1531,13 @@ static int mtk_poll_rx(struct napi_struc pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); skb->dev = netdev; skb_put(skb, pktlen); @@ -37,7 +37,7 @@ Signed-off-by: Jakub Kicinski skb->ip_summed = CHECKSUM_UNNECESSARY; else skb_checksum_none_assert(skb); -@@ -3758,6 +3764,7 @@ static const struct mtk_soc_data mt7986_ +@@ -3759,6 +3765,7 @@ static const struct mtk_soc_data mt7986_ .txd_size = sizeof(struct mtk_tx_dma_v2), .rxd_size = sizeof(struct mtk_rx_dma_v2), .rx_irq_done_mask = MTK_RX_DONE_INT_V2, diff --git a/target/linux/generic/backport-5.15/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch b/target/linux/generic/backport-5.15/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch index a602ed5c9c0..b6fe0dad4c8 100644 --- a/target/linux/generic/backport-5.15/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch +++ b/target/linux/generic/backport-5.15/704-01-v5.17-net-mtk_eth_soc-populate-supported_interfaces-member.patch @@ -14,7 +14,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3354,6 +3354,26 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -3355,6 +3355,26 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.dev = ð->netdev[id]->dev; mac->phylink_config.type = PHYLINK_NETDEV; diff --git a/target/linux/generic/backport-5.15/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch b/target/linux/generic/backport-5.15/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch index 05a84c4f679..0a33ab00934 100644 --- a/target/linux/generic/backport-5.15/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch +++ b/target/linux/generic/backport-5.15/704-02-v5.17-net-mtk_eth_soc-remove-interface-checks-in-mtk_valid.patch @@ -16,7 +16,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -567,24 +567,8 @@ static void mtk_validate(struct phylink_ +@@ -568,24 +568,8 @@ static void mtk_validate(struct phylink_ unsigned long *supported, struct phylink_link_state *state) { @@ -41,7 +41,7 @@ Signed-off-by: David S. Miller phylink_set_port_modes(mask); phylink_set(mask, Autoneg); -@@ -611,7 +595,6 @@ static void mtk_validate(struct phylink_ +@@ -612,7 +596,6 @@ static void mtk_validate(struct phylink_ case PHY_INTERFACE_MODE_MII: case PHY_INTERFACE_MODE_RMII: case PHY_INTERFACE_MODE_REVMII: @@ -49,7 +49,7 @@ Signed-off-by: David S. Miller default: phylink_set(mask, 10baseT_Half); phylink_set(mask, 10baseT_Full); -@@ -620,23 +603,6 @@ static void mtk_validate(struct phylink_ +@@ -621,23 +604,6 @@ static void mtk_validate(struct phylink_ break; } diff --git a/target/linux/generic/backport-5.15/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch b/target/linux/generic/backport-5.15/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch index a3cfab7f881..f8cc8105a4c 100644 --- a/target/linux/generic/backport-5.15/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch +++ b/target/linux/generic/backport-5.15/704-03-v5.17-net-mtk_eth_soc-drop-use-of-phylink_helper_basex_spe.patch @@ -17,7 +17,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -577,8 +577,9 @@ static void mtk_validate(struct phylink_ +@@ -578,8 +578,9 @@ static void mtk_validate(struct phylink_ phylink_set(mask, 1000baseT_Full); break; case PHY_INTERFACE_MODE_1000BASEX: @@ -28,7 +28,7 @@ Signed-off-by: David S. Miller phylink_set(mask, 2500baseX_Full); break; case PHY_INTERFACE_MODE_GMII: -@@ -608,11 +609,6 @@ static void mtk_validate(struct phylink_ +@@ -609,11 +610,6 @@ static void mtk_validate(struct phylink_ linkmode_and(supported, supported, mask); linkmode_and(state->advertising, state->advertising, mask); diff --git a/target/linux/generic/backport-5.15/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch b/target/linux/generic/backport-5.15/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch index 2140a8267b3..f695991ec1f 100644 --- a/target/linux/generic/backport-5.15/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch +++ b/target/linux/generic/backport-5.15/704-04-v5.17-net-mtk_eth_soc-use-phylink_generic_validate.patch @@ -14,7 +14,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -563,56 +563,8 @@ static void mtk_mac_link_up(struct phyli +@@ -564,56 +564,8 @@ static void mtk_mac_link_up(struct phyli mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } @@ -72,7 +72,7 @@ Signed-off-by: David S. Miller .mac_pcs_get_state = mtk_mac_pcs_get_state, .mac_an_restart = mtk_mac_an_restart, .mac_config = mtk_mac_config, -@@ -3316,6 +3268,9 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -3317,6 +3269,9 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.dev = ð->netdev[id]->dev; mac->phylink_config.type = PHYLINK_NETDEV; diff --git a/target/linux/generic/backport-5.15/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch b/target/linux/generic/backport-5.15/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch index 8b9a249e40e..cbff1bfbbc4 100644 --- a/target/linux/generic/backport-5.15/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch +++ b/target/linux/generic/backport-5.15/704-05-v5.17-net-mtk_eth_soc-mark-as-a-legacy_pre_march2020-drive.patch @@ -16,7 +16,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3268,6 +3268,10 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -3269,6 +3269,10 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.dev = ð->netdev[id]->dev; mac->phylink_config.type = PHYLINK_NETDEV; diff --git a/target/linux/generic/backport-5.15/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch b/target/linux/generic/backport-5.15/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch index 97e0b3ccb06..c0b4a61cb6e 100644 --- a/target/linux/generic/backport-5.15/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch +++ b/target/linux/generic/backport-5.15/704-06-v5.19-eth-mtk_eth_soc-remove-a-copy-of-the-NAPI_POLL_WEIGH.patch @@ -16,7 +16,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3567,9 +3567,9 @@ static int mtk_probe(struct platform_dev +@@ -3568,9 +3568,9 @@ static int mtk_probe(struct platform_dev */ init_dummy_netdev(ð->dummy_dev); netif_napi_add(ð->dummy_dev, ð->tx_napi, mtk_napi_tx, diff --git a/target/linux/generic/backport-5.15/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch b/target/linux/generic/backport-5.15/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch index 9873edbc500..5940ac27df9 100644 --- a/target/linux/generic/backport-5.15/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch +++ b/target/linux/generic/backport-5.15/704-07-v5.19-mtk_eth_soc-remove-unused-mac-mode.patch @@ -15,7 +15,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3263,7 +3263,6 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -3264,7 +3264,6 @@ static int mtk_add_mac(struct mtk_eth *e /* mac config is not set */ mac->interface = PHY_INTERFACE_MODE_NA; @@ -25,7 +25,7 @@ Signed-off-by: Jakub Kicinski mac->phylink_config.dev = ð->netdev[id]->dev; --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1085,7 +1085,6 @@ struct mtk_eth { +@@ -1086,7 +1086,6 @@ struct mtk_eth { struct mtk_mac { int id; phy_interface_t interface; diff --git a/target/linux/generic/backport-5.15/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch b/target/linux/generic/backport-5.15/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch index 39aa24157ed..a15914bd553 100644 --- a/target/linux/generic/backport-5.15/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch +++ b/target/linux/generic/backport-5.15/704-08-v5.19-net-mtk_eth_soc-remove-unused-sgmii-flags.patch @@ -14,7 +14,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -956,23 +956,15 @@ struct mtk_soc_data { +@@ -957,23 +957,15 @@ struct mtk_soc_data { /* currently no SoC has more than 2 macs */ #define MTK_MAX_DEVS 2 diff --git a/target/linux/generic/backport-5.15/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch b/target/linux/generic/backport-5.15/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch index f2e1f86bacf..e16bc875e52 100644 --- a/target/linux/generic/backport-5.15/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch +++ b/target/linux/generic/backport-5.15/704-09-v5.19-net-mtk_eth_soc-add-mask-and-update-PCS-speed-defini.patch @@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski #include "mtk_ppe.h" #define MTK_QDMA_PAGE_SIZE 2048 -@@ -473,9 +474,10 @@ +@@ -474,9 +475,10 @@ #define SGMSYS_SGMII_MODE 0x20 #define SGMII_IF_MODE_BIT0 BIT(0) #define SGMII_SPEED_DUPLEX_AN BIT(1) diff --git a/target/linux/generic/backport-5.15/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch b/target/linux/generic/backport-5.15/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch index d4a4d511016..78444903a8c 100644 --- a/target/linux/generic/backport-5.15/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch +++ b/target/linux/generic/backport-5.15/704-11-v5.19-net-mtk_eth_soc-correct-802.3z-duplex-setting.patch @@ -18,7 +18,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -532,8 +532,18 @@ static void mtk_mac_link_up(struct phyli +@@ -533,8 +533,18 @@ static void mtk_mac_link_up(struct phyli { struct mtk_mac *mac = container_of(config, struct mtk_mac, phylink_config); @@ -38,7 +38,7 @@ Signed-off-by: Jakub Kicinski mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | MAC_MCR_FORCE_RX_FC); -@@ -3267,9 +3277,7 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -3268,9 +3278,7 @@ static int mtk_add_mac(struct mtk_eth *e mac->phylink_config.dev = ð->netdev[id]->dev; mac->phylink_config.type = PHYLINK_NETDEV; @@ -51,7 +51,7 @@ Signed-off-by: Jakub Kicinski MAC_10 | MAC_100 | MAC_1000 | MAC_2500FD; --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1103,6 +1103,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, +@@ -1104,6 +1104,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id); int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, const struct phylink_link_state *state); diff --git a/target/linux/generic/backport-5.15/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch b/target/linux/generic/backport-5.15/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch index 56b5e43e532..6556bb7d07d 100644 --- a/target/linux/generic/backport-5.15/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch +++ b/target/linux/generic/backport-5.15/704-12-v5.19-net-mtk_eth_soc-stop-passing-phylink-state-to-sgmii-.patch @@ -29,7 +29,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1102,7 +1102,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, +@@ -1103,7 +1103,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, u32 ana_rgc3); int mtk_sgmii_setup_mode_an(struct mtk_sgmii *ss, int id); int mtk_sgmii_setup_mode_force(struct mtk_sgmii *ss, int id, diff --git a/target/linux/generic/backport-5.15/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch b/target/linux/generic/backport-5.15/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch index 4c91cf68f4c..0e22c7fd674 100644 --- a/target/linux/generic/backport-5.15/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch +++ b/target/linux/generic/backport-5.15/704-13-v5.19-net-mtk_eth_soc-provide-mtk_sgmii_config.patch @@ -32,7 +32,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1100,9 +1100,8 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne +@@ -1101,9 +1101,8 @@ u32 mtk_r32(struct mtk_eth *eth, unsigne int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, u32 ana_rgc3); diff --git a/target/linux/generic/backport-5.15/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch b/target/linux/generic/backport-5.15/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch index 368db4cca26..337c6112d66 100644 --- a/target/linux/generic/backport-5.15/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch +++ b/target/linux/generic/backport-5.15/704-15-v5.19-net-mtk_eth_soc-move-MAC_MCR-setting-to-mac_finish.patch @@ -25,27 +25,22 @@ Signed-off-by: Jakub Kicinski /* MT76x8 has no hardware settings between for the MAC */ if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628) && -@@ -455,16 +455,6 @@ static void mtk_mac_config(struct phylin +@@ -455,6 +455,25 @@ static void mtk_mac_config(struct phylin return; } -- /* Setup gmac */ -- mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); -- mcr_new = mcr_cur; -- mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | -- MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; -- -- /* Only update control register when needed! */ -- if (mcr_new != mcr_cur) -- mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); -- - return; - - err_phy: -@@ -477,6 +467,26 @@ init_err: - mac->id, phy_modes(state->interface), err); - } - ++ return; ++ ++err_phy: ++ dev_err(eth->dev, "%s: GMAC%d mode %s not supported!\n", __func__, ++ mac->id, phy_modes(state->interface)); ++ return; ++ ++init_err: ++ dev_err(eth->dev, "%s: GMAC%d mode %s err: %d!\n", __func__, ++ mac->id, phy_modes(state->interface), err); ++} ++ +static int mtk_mac_finish(struct phylink_config *config, unsigned int mode, + phy_interface_t interface) +{ @@ -53,23 +48,28 @@ Signed-off-by: Jakub Kicinski + phylink_config); + u32 mcr_cur, mcr_new; + -+ /* Setup gmac */ -+ mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); -+ mcr_new = mcr_cur; -+ mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | -+ MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; -+ -+ /* Only update control register when needed! */ -+ if (mcr_new != mcr_cur) -+ mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); -+ + /* Setup gmac */ + mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); + mcr_new = mcr_cur; +@@ -466,16 +485,7 @@ static void mtk_mac_config(struct phylin + if (mcr_new != mcr_cur) + mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); + +- return; +- +-err_phy: +- dev_err(eth->dev, "%s: GMAC%d mode %s not supported!\n", __func__, +- mac->id, phy_modes(state->interface)); +- return; +- +-init_err: +- dev_err(eth->dev, "%s: GMAC%d mode %s err: %d!\n", __func__, +- mac->id, phy_modes(state->interface), err); + return 0; -+} -+ + } + static void mtk_mac_pcs_get_state(struct phylink_config *config, - struct phylink_link_state *state) - { -@@ -581,6 +591,7 @@ static const struct phylink_mac_ops mtk_ +@@ -582,6 +592,7 @@ static const struct phylink_mac_ops mtk_ .mac_pcs_get_state = mtk_mac_pcs_get_state, .mac_an_restart = mtk_mac_an_restart, .mac_config = mtk_mac_config, diff --git a/target/linux/generic/backport-5.15/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch b/target/linux/generic/backport-5.15/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch index ad6ec60288f..b03ef436bdb 100644 --- a/target/linux/generic/backport-5.15/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch +++ b/target/linux/generic/backport-5.15/704-16-v5.19-net-mtk_eth_soc-move-restoration-of-SYSCFG0-to-mac_f.patch @@ -47,7 +47,7 @@ Signed-off-by: Jakub Kicinski mcr_new = mcr_cur; --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1087,6 +1087,7 @@ struct mtk_mac { +@@ -1088,6 +1088,7 @@ struct mtk_mac { struct mtk_hw_stats *hw_stats; __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; int hwlro_ip_cnt; diff --git a/target/linux/generic/backport-5.15/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch b/target/linux/generic/backport-5.15/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch index 623658f459c..4c84703cd94 100644 --- a/target/linux/generic/backport-5.15/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch +++ b/target/linux/generic/backport-5.15/704-17-v5.19-net-mtk_eth_soc-convert-code-structure-to-suit-split.patch @@ -18,7 +18,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -958,16 +958,23 @@ struct mtk_soc_data { +@@ -959,16 +959,23 @@ struct mtk_soc_data { /* currently no SoC has more than 2 macs */ #define MTK_MAX_DEVS 2 diff --git a/target/linux/generic/backport-5.15/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch b/target/linux/generic/backport-5.15/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch index df675e28999..b585867935e 100644 --- a/target/linux/generic/backport-5.15/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch +++ b/target/linux/generic/backport-5.15/704-18-v5.19-net-mtk_eth_soc-partially-convert-to-phylink_pcs.patch @@ -69,7 +69,7 @@ Signed-off-by: Jakub Kicinski /* Save the syscfg0 value for mac_finish */ mac->syscfg0 = val; } else if (phylink_autoneg_inband(mode)) { -@@ -526,14 +536,6 @@ static void mtk_mac_pcs_get_state(struct +@@ -527,14 +537,6 @@ static void mtk_mac_pcs_get_state(struct state->pause |= MLO_PAUSE_TX; } @@ -84,7 +84,7 @@ Signed-off-by: Jakub Kicinski static void mtk_mac_link_down(struct phylink_config *config, unsigned int mode, phy_interface_t interface) { -@@ -554,15 +556,6 @@ static void mtk_mac_link_up(struct phyli +@@ -555,15 +557,6 @@ static void mtk_mac_link_up(struct phyli phylink_config); u32 mcr; @@ -100,7 +100,7 @@ Signed-off-by: Jakub Kicinski mcr = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); mcr &= ~(MAC_MCR_SPEED_100 | MAC_MCR_SPEED_1000 | MAC_MCR_FORCE_DPX | MAC_MCR_FORCE_TX_FC | -@@ -595,8 +588,8 @@ static void mtk_mac_link_up(struct phyli +@@ -596,8 +589,8 @@ static void mtk_mac_link_up(struct phyli static const struct phylink_mac_ops mtk_phylink_ops = { .validate = phylink_generic_validate, @@ -112,7 +112,7 @@ Signed-off-by: Jakub Kicinski .mac_link_down = mtk_mac_link_down, --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -963,10 +963,12 @@ struct mtk_soc_data { +@@ -964,10 +964,12 @@ struct mtk_soc_data { * @regmap: The register map pointing at the range used to setup * SGMII modes * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap @@ -125,7 +125,7 @@ Signed-off-by: Jakub Kicinski }; /* struct mtk_sgmii - This is the structure holding sgmii regmap and its -@@ -1106,12 +1108,9 @@ void mtk_stats_update_mac(struct mtk_mac +@@ -1107,12 +1109,9 @@ void mtk_stats_update_mac(struct mtk_mac void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg); u32 mtk_r32(struct mtk_eth *eth, unsigned reg); diff --git a/target/linux/generic/backport-5.15/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch b/target/linux/generic/backport-5.15/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch index a3fbf00629f..845f2fa4b20 100644 --- a/target/linux/generic/backport-5.15/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch +++ b/target/linux/generic/backport-5.15/705-01-v5.17-net-dsa-mt7530-iterate-using-dsa_switch_for_each_use.patch @@ -21,7 +21,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -1191,27 +1191,31 @@ static int +@@ -1194,27 +1194,31 @@ static int mt7530_port_bridge_join(struct dsa_switch *ds, int port, struct net_device *bridge) { @@ -65,7 +65,7 @@ Signed-off-by: Jakub Kicinski } /* Add the all other ports to this port matrix. */ -@@ -1316,24 +1320,28 @@ static void +@@ -1319,24 +1323,28 @@ static void mt7530_port_bridge_leave(struct dsa_switch *ds, int port, struct net_device *bridge) { diff --git a/target/linux/generic/backport-5.15/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch b/target/linux/generic/backport-5.15/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch index 6599f25c0fc..4ee326030dc 100644 --- a/target/linux/generic/backport-5.15/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch +++ b/target/linux/generic/backport-5.15/705-02-v5.19-net-dsa-mt7530-populate-supported_interfaces-and-mac.patch @@ -23,7 +23,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2402,6 +2402,32 @@ mt7531_setup(struct dsa_switch *ds) +@@ -2407,6 +2407,32 @@ mt7531_setup(struct dsa_switch *ds) return 0; } @@ -56,7 +56,7 @@ Signed-off-by: Paolo Abeni static bool mt7530_phy_mode_supported(struct dsa_switch *ds, int port, const struct phylink_link_state *state) -@@ -2438,6 +2464,37 @@ static bool mt7531_is_rgmii_port(struct +@@ -2443,6 +2469,37 @@ static bool mt7531_is_rgmii_port(struct return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII); } @@ -94,7 +94,7 @@ Signed-off-by: Paolo Abeni static bool mt7531_phy_mode_supported(struct dsa_switch *ds, int port, const struct phylink_link_state *state) -@@ -2914,6 +2971,18 @@ mt7531_cpu_port_config(struct dsa_switch +@@ -2919,6 +2976,18 @@ mt7531_cpu_port_config(struct dsa_switch return 0; } @@ -113,7 +113,7 @@ Signed-off-by: Paolo Abeni static void mt7530_mac_port_validate(struct dsa_switch *ds, int port, unsigned long *supported) -@@ -3149,6 +3218,7 @@ static const struct dsa_switch_ops mt753 +@@ -3154,6 +3223,7 @@ static const struct dsa_switch_ops mt753 .port_vlan_del = mt7530_port_vlan_del, .port_mirror_add = mt753x_port_mirror_add, .port_mirror_del = mt753x_port_mirror_del, @@ -121,7 +121,7 @@ Signed-off-by: Paolo Abeni .phylink_validate = mt753x_phylink_validate, .phylink_mac_link_state = mt753x_phylink_mac_link_state, .phylink_mac_config = mt753x_phylink_mac_config, -@@ -3166,6 +3236,7 @@ static const struct mt753x_info mt753x_t +@@ -3171,6 +3241,7 @@ static const struct mt753x_info mt753x_t .phy_read = mt7530_phy_read, .phy_write = mt7530_phy_write, .pad_setup = mt7530_pad_clk_setup, @@ -129,7 +129,7 @@ Signed-off-by: Paolo Abeni .phy_mode_supported = mt7530_phy_mode_supported, .mac_port_validate = mt7530_mac_port_validate, .mac_port_get_state = mt7530_phylink_mac_link_state, -@@ -3177,6 +3248,7 @@ static const struct mt753x_info mt753x_t +@@ -3182,6 +3253,7 @@ static const struct mt753x_info mt753x_t .phy_read = mt7530_phy_read, .phy_write = mt7530_phy_write, .pad_setup = mt7530_pad_clk_setup, @@ -137,7 +137,7 @@ Signed-off-by: Paolo Abeni .phy_mode_supported = mt7530_phy_mode_supported, .mac_port_validate = mt7530_mac_port_validate, .mac_port_get_state = mt7530_phylink_mac_link_state, -@@ -3189,6 +3261,7 @@ static const struct mt753x_info mt753x_t +@@ -3194,6 +3266,7 @@ static const struct mt753x_info mt753x_t .phy_write = mt7531_ind_phy_write, .pad_setup = mt7531_pad_setup, .cpu_port_config = mt7531_cpu_port_config, @@ -145,7 +145,7 @@ Signed-off-by: Paolo Abeni .phy_mode_supported = mt7531_phy_mode_supported, .mac_port_validate = mt7531_mac_port_validate, .mac_port_get_state = mt7531_phylink_mac_link_state, -@@ -3251,6 +3324,7 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3256,6 +3329,7 @@ mt7530_probe(struct mdio_device *mdiodev */ if (!priv->info->sw_setup || !priv->info->pad_setup || !priv->info->phy_read || !priv->info->phy_write || diff --git a/target/linux/generic/backport-5.15/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch b/target/linux/generic/backport-5.15/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch index 325152603a1..1d74aac8f54 100644 --- a/target/linux/generic/backport-5.15/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch +++ b/target/linux/generic/backport-5.15/705-03-v5.19-net-dsa-mt7530-remove-interface-checks.patch @@ -21,7 +21,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2428,37 +2428,6 @@ static void mt7530_mac_port_get_caps(str +@@ -2433,37 +2433,6 @@ static void mt7530_mac_port_get_caps(str } } @@ -59,7 +59,7 @@ Signed-off-by: Paolo Abeni static bool mt7531_is_rgmii_port(struct mt7530_priv *priv, u32 port) { return (port == 5) && (priv->p5_intf_sel != P5_INTF_SEL_GMAC5_SGMII); -@@ -2495,44 +2464,6 @@ static void mt7531_mac_port_get_caps(str +@@ -2500,44 +2469,6 @@ static void mt7531_mac_port_get_caps(str } } @@ -104,7 +104,7 @@ Signed-off-by: Paolo Abeni static int mt753x_pad_setup(struct dsa_switch *ds, const struct phylink_link_state *state) { -@@ -2787,9 +2718,6 @@ mt753x_phylink_mac_config(struct dsa_swi +@@ -2792,9 +2723,6 @@ mt753x_phylink_mac_config(struct dsa_swi struct mt7530_priv *priv = ds->priv; u32 mcr_cur, mcr_new; @@ -114,7 +114,7 @@ Signed-off-by: Paolo Abeni switch (port) { case 0 ... 4: /* Internal phy */ if (state->interface != PHY_INTERFACE_MODE_GMII) -@@ -3005,12 +2933,6 @@ mt753x_phylink_validate(struct dsa_switc +@@ -3010,12 +2938,6 @@ mt753x_phylink_validate(struct dsa_switc __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, }; struct mt7530_priv *priv = ds->priv; @@ -127,7 +127,7 @@ Signed-off-by: Paolo Abeni phylink_set_port_modes(mask); if (state->interface != PHY_INTERFACE_MODE_TRGMII && -@@ -3237,7 +3159,6 @@ static const struct mt753x_info mt753x_t +@@ -3242,7 +3164,6 @@ static const struct mt753x_info mt753x_t .phy_write = mt7530_phy_write, .pad_setup = mt7530_pad_clk_setup, .mac_port_get_caps = mt7530_mac_port_get_caps, @@ -135,7 +135,7 @@ Signed-off-by: Paolo Abeni .mac_port_validate = mt7530_mac_port_validate, .mac_port_get_state = mt7530_phylink_mac_link_state, .mac_port_config = mt7530_mac_config, -@@ -3249,7 +3170,6 @@ static const struct mt753x_info mt753x_t +@@ -3254,7 +3175,6 @@ static const struct mt753x_info mt753x_t .phy_write = mt7530_phy_write, .pad_setup = mt7530_pad_clk_setup, .mac_port_get_caps = mt7530_mac_port_get_caps, @@ -143,7 +143,7 @@ Signed-off-by: Paolo Abeni .mac_port_validate = mt7530_mac_port_validate, .mac_port_get_state = mt7530_phylink_mac_link_state, .mac_port_config = mt7530_mac_config, -@@ -3262,7 +3182,6 @@ static const struct mt753x_info mt753x_t +@@ -3267,7 +3187,6 @@ static const struct mt753x_info mt753x_t .pad_setup = mt7531_pad_setup, .cpu_port_config = mt7531_cpu_port_config, .mac_port_get_caps = mt7531_mac_port_get_caps, @@ -151,7 +151,7 @@ Signed-off-by: Paolo Abeni .mac_port_validate = mt7531_mac_port_validate, .mac_port_get_state = mt7531_phylink_mac_link_state, .mac_port_config = mt7531_mac_config, -@@ -3325,7 +3244,6 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3330,7 +3249,6 @@ mt7530_probe(struct mdio_device *mdiodev if (!priv->info->sw_setup || !priv->info->pad_setup || !priv->info->phy_read || !priv->info->phy_write || !priv->info->mac_port_get_caps || diff --git a/target/linux/generic/backport-5.15/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch b/target/linux/generic/backport-5.15/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch index 76d89d0c542..3e97ad4a43e 100644 --- a/target/linux/generic/backport-5.15/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch +++ b/target/linux/generic/backport-5.15/705-04-v5.19-net-dsa-mt7530-drop-use-of-phylink_helper_basex_spee.patch @@ -20,7 +20,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2957,11 +2957,6 @@ mt753x_phylink_validate(struct dsa_switc +@@ -2962,11 +2962,6 @@ mt753x_phylink_validate(struct dsa_switc linkmode_and(supported, supported, mask); linkmode_and(state->advertising, state->advertising, mask); diff --git a/target/linux/generic/backport-5.15/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch b/target/linux/generic/backport-5.15/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch index 6ee017ea72e..97d3ff9c98c 100644 --- a/target/linux/generic/backport-5.15/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch +++ b/target/linux/generic/backport-5.15/705-05-v5.19-net-dsa-mt7530-only-indicate-linkmodes-that-can-be-s.patch @@ -23,7 +23,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2535,12 +2535,13 @@ static int mt7531_rgmii_setup(struct mt7 +@@ -2540,12 +2540,13 @@ static int mt7531_rgmii_setup(struct mt7 } static void mt7531_sgmii_validate(struct mt7530_priv *priv, int port, @@ -38,7 +38,7 @@ Signed-off-by: Paolo Abeni phylink_set(supported, 2500baseX_Full); phylink_set(supported, 2500baseT_Full); } -@@ -2913,16 +2914,18 @@ static void mt753x_phylink_get_caps(stru +@@ -2918,16 +2919,18 @@ static void mt753x_phylink_get_caps(stru static void mt7530_mac_port_validate(struct dsa_switch *ds, int port, @@ -58,7 +58,7 @@ Signed-off-by: Paolo Abeni } static void -@@ -2945,12 +2948,13 @@ mt753x_phylink_validate(struct dsa_switc +@@ -2950,12 +2953,13 @@ mt753x_phylink_validate(struct dsa_switc } /* This switch only supports 1G full-duplex. */ diff --git a/target/linux/generic/backport-5.15/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch b/target/linux/generic/backport-5.15/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch index 670ac080cf0..009b2bb583f 100644 --- a/target/linux/generic/backport-5.15/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch +++ b/target/linux/generic/backport-5.15/705-06-v5.19-net-dsa-mt7530-switch-to-use-phylink_get_linkmodes.patch @@ -20,7 +20,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2534,19 +2534,6 @@ static int mt7531_rgmii_setup(struct mt7 +@@ -2539,19 +2539,6 @@ static int mt7531_rgmii_setup(struct mt7 return 0; } @@ -40,7 +40,7 @@ Signed-off-by: Paolo Abeni static void mt7531_sgmii_link_up_force(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface, -@@ -2913,51 +2900,21 @@ static void mt753x_phylink_get_caps(stru +@@ -2918,51 +2905,21 @@ static void mt753x_phylink_get_caps(stru } static void @@ -97,7 +97,7 @@ Signed-off-by: Paolo Abeni linkmode_and(supported, supported, mask); linkmode_and(state->advertising, state->advertising, mask); -@@ -3158,7 +3115,6 @@ static const struct mt753x_info mt753x_t +@@ -3163,7 +3120,6 @@ static const struct mt753x_info mt753x_t .phy_write = mt7530_phy_write, .pad_setup = mt7530_pad_clk_setup, .mac_port_get_caps = mt7530_mac_port_get_caps, @@ -105,7 +105,7 @@ Signed-off-by: Paolo Abeni .mac_port_get_state = mt7530_phylink_mac_link_state, .mac_port_config = mt7530_mac_config, }, -@@ -3169,7 +3125,6 @@ static const struct mt753x_info mt753x_t +@@ -3174,7 +3130,6 @@ static const struct mt753x_info mt753x_t .phy_write = mt7530_phy_write, .pad_setup = mt7530_pad_clk_setup, .mac_port_get_caps = mt7530_mac_port_get_caps, @@ -113,7 +113,7 @@ Signed-off-by: Paolo Abeni .mac_port_get_state = mt7530_phylink_mac_link_state, .mac_port_config = mt7530_mac_config, }, -@@ -3181,7 +3136,6 @@ static const struct mt753x_info mt753x_t +@@ -3186,7 +3141,6 @@ static const struct mt753x_info mt753x_t .pad_setup = mt7531_pad_setup, .cpu_port_config = mt7531_cpu_port_config, .mac_port_get_caps = mt7531_mac_port_get_caps, @@ -121,7 +121,7 @@ Signed-off-by: Paolo Abeni .mac_port_get_state = mt7531_phylink_mac_link_state, .mac_port_config = mt7531_mac_config, .mac_pcs_an_restart = mt7531_sgmii_restart_an, -@@ -3243,7 +3197,6 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3248,7 +3202,6 @@ mt7530_probe(struct mdio_device *mdiodev if (!priv->info->sw_setup || !priv->info->pad_setup || !priv->info->phy_read || !priv->info->phy_write || !priv->info->mac_port_get_caps || diff --git a/target/linux/generic/backport-5.15/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch b/target/linux/generic/backport-5.15/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch index 638127f56ce..c7e004e2e7b 100644 --- a/target/linux/generic/backport-5.15/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch +++ b/target/linux/generic/backport-5.15/705-07-v5.19-net-dsa-mt7530-partially-convert-to-phylink_pcs.patch @@ -33,7 +33,7 @@ Signed-off-by: Paolo Abeni /* String, offset, and register size in bytes if different from 4 bytes */ static const struct mt7530_mib_desc mt7530_mib[] = { MIB_DESC(1, 0x00, "TxDrop"), -@@ -2534,12 +2539,11 @@ static int mt7531_rgmii_setup(struct mt7 +@@ -2539,12 +2544,11 @@ static int mt7531_rgmii_setup(struct mt7 return 0; } @@ -50,7 +50,7 @@ Signed-off-by: Paolo Abeni unsigned int val; /* For adjusting speed and duplex of SGMII force mode. */ -@@ -2565,6 +2569,9 @@ mt7531_sgmii_link_up_force(struct dsa_sw +@@ -2570,6 +2574,9 @@ mt7531_sgmii_link_up_force(struct dsa_sw /* MT7531 SGMII 1G force mode can only work in full duplex mode, * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. @@ -60,7 +60,7 @@ Signed-off-by: Paolo Abeni */ if ((speed == SPEED_10 || speed == SPEED_100) && duplex != DUPLEX_FULL) -@@ -2640,9 +2647,10 @@ static int mt7531_sgmii_setup_mode_an(st +@@ -2645,9 +2652,10 @@ static int mt7531_sgmii_setup_mode_an(st return 0; } @@ -73,7 +73,7 @@ Signed-off-by: Paolo Abeni u32 val; /* Only restart AN when AN is enabled */ -@@ -2699,6 +2707,24 @@ mt753x_mac_config(struct dsa_switch *ds, +@@ -2704,6 +2712,24 @@ mt753x_mac_config(struct dsa_switch *ds, return priv->info->mac_port_config(ds, port, mode, state->interface); } @@ -98,7 +98,7 @@ Signed-off-by: Paolo Abeni static void mt753x_phylink_mac_config(struct dsa_switch *ds, int port, unsigned int mode, const struct phylink_link_state *state) -@@ -2760,17 +2786,6 @@ unsupported: +@@ -2765,17 +2791,6 @@ unsupported: mt7530_write(priv, MT7530_PMCR_P(port), mcr_new); } @@ -116,7 +116,7 @@ Signed-off-by: Paolo Abeni static void mt753x_phylink_mac_link_down(struct dsa_switch *ds, int port, unsigned int mode, phy_interface_t interface) -@@ -2780,16 +2795,13 @@ static void mt753x_phylink_mac_link_down +@@ -2785,16 +2800,13 @@ static void mt753x_phylink_mac_link_down mt7530_clear(priv, MT7530_PMCR_P(port), PMCR_LINK_SETTINGS_MASK); } @@ -139,7 +139,7 @@ Signed-off-by: Paolo Abeni } static void mt753x_phylink_mac_link_up(struct dsa_switch *ds, int port, -@@ -2802,8 +2814,6 @@ static void mt753x_phylink_mac_link_up(s +@@ -2807,8 +2819,6 @@ static void mt753x_phylink_mac_link_up(s struct mt7530_priv *priv = ds->priv; u32 mcr; @@ -148,7 +148,7 @@ Signed-off-by: Paolo Abeni mcr = PMCR_RX_EN | PMCR_TX_EN | PMCR_FORCE_LNK; /* MT753x MAC works in 1G full duplex mode for all up-clocked -@@ -2881,6 +2891,8 @@ mt7531_cpu_port_config(struct dsa_switch +@@ -2886,6 +2896,8 @@ mt7531_cpu_port_config(struct dsa_switch return ret; mt7530_write(priv, MT7530_PMCR_P(port), PMCR_CPU_PORT_SETTING(priv->id)); @@ -157,7 +157,7 @@ Signed-off-by: Paolo Abeni mt753x_phylink_mac_link_up(ds, port, MLO_AN_FIXED, interface, NULL, speed, DUPLEX_FULL, true, true); -@@ -2920,16 +2932,13 @@ mt753x_phylink_validate(struct dsa_switc +@@ -2925,16 +2937,13 @@ mt753x_phylink_validate(struct dsa_switc linkmode_and(state->advertising, state->advertising, mask); } @@ -178,7 +178,7 @@ Signed-off-by: Paolo Abeni pmsr = mt7530_read(priv, MT7530_PMSR_P(port)); state->link = (pmsr & PMSR_LINK); -@@ -2956,8 +2965,6 @@ mt7530_phylink_mac_link_state(struct dsa +@@ -2961,8 +2970,6 @@ mt7530_phylink_mac_link_state(struct dsa state->pause |= MLO_PAUSE_RX; if (pmsr & PMSR_TX_FC) state->pause |= MLO_PAUSE_TX; @@ -187,7 +187,7 @@ Signed-off-by: Paolo Abeni } static int -@@ -2999,32 +3006,49 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 +@@ -3004,32 +3011,49 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 return 0; } @@ -249,7 +249,7 @@ Signed-off-by: Paolo Abeni if (ret) return ret; -@@ -3037,6 +3061,13 @@ mt753x_setup(struct dsa_switch *ds) +@@ -3042,6 +3066,13 @@ mt753x_setup(struct dsa_switch *ds) if (ret && priv->irq) mt7530_free_irq_common(priv); @@ -263,7 +263,7 @@ Signed-off-by: Paolo Abeni return ret; } -@@ -3098,9 +3129,8 @@ static const struct dsa_switch_ops mt753 +@@ -3103,9 +3134,8 @@ static const struct dsa_switch_ops mt753 .port_mirror_del = mt753x_port_mirror_del, .phylink_get_caps = mt753x_phylink_get_caps, .phylink_validate = mt753x_phylink_validate, @@ -274,7 +274,7 @@ Signed-off-by: Paolo Abeni .phylink_mac_link_down = mt753x_phylink_mac_link_down, .phylink_mac_link_up = mt753x_phylink_mac_link_up, .get_mac_eee = mt753x_get_mac_eee, -@@ -3110,36 +3140,34 @@ static const struct dsa_switch_ops mt753 +@@ -3115,36 +3145,34 @@ static const struct dsa_switch_ops mt753 static const struct mt753x_info mt753x_table[] = { [ID_MT7621] = { .id = ID_MT7621, @@ -314,7 +314,7 @@ Signed-off-by: Paolo Abeni }, }; -@@ -3197,7 +3225,7 @@ mt7530_probe(struct mdio_device *mdiodev +@@ -3202,7 +3230,7 @@ mt7530_probe(struct mdio_device *mdiodev if (!priv->info->sw_setup || !priv->info->pad_setup || !priv->info->phy_read || !priv->info->phy_write || !priv->info->mac_port_get_caps || diff --git a/target/linux/generic/backport-5.15/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch b/target/linux/generic/backport-5.15/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch index 19d3258af5c..2ddfe8ccd04 100644 --- a/target/linux/generic/backport-5.15/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch +++ b/target/linux/generic/backport-5.15/705-08-v5.19-net-dsa-mt7530-move-autoneg-handling-to-PCS-validati.patch @@ -20,7 +20,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2911,25 +2911,16 @@ static void mt753x_phylink_get_caps(stru +@@ -2916,25 +2916,16 @@ static void mt753x_phylink_get_caps(stru priv->info->mac_port_get_caps(ds, port, config); } @@ -55,7 +55,7 @@ Signed-off-by: Paolo Abeni } static void mt7530_pcs_get_state(struct phylink_pcs *pcs, -@@ -3031,12 +3022,14 @@ static void mt7530_pcs_an_restart(struct +@@ -3036,12 +3027,14 @@ static void mt7530_pcs_an_restart(struct } static const struct phylink_pcs_ops mt7530_pcs_ops = { @@ -70,7 +70,7 @@ Signed-off-by: Paolo Abeni .pcs_get_state = mt7531_pcs_get_state, .pcs_config = mt753x_pcs_config, .pcs_an_restart = mt7531_pcs_an_restart, -@@ -3128,7 +3121,6 @@ static const struct dsa_switch_ops mt753 +@@ -3133,7 +3126,6 @@ static const struct dsa_switch_ops mt753 .port_mirror_add = mt753x_port_mirror_add, .port_mirror_del = mt753x_port_mirror_del, .phylink_get_caps = mt753x_phylink_get_caps, diff --git a/target/linux/generic/backport-5.15/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch b/target/linux/generic/backport-5.15/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch index 7d5e4ae6cec..e58089a3f85 100644 --- a/target/linux/generic/backport-5.15/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch +++ b/target/linux/generic/backport-5.15/705-09-v5.19-net-dsa-mt7530-mark-as-non-legacy.patch @@ -19,7 +19,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2908,6 +2908,12 @@ static void mt753x_phylink_get_caps(stru +@@ -2913,6 +2913,12 @@ static void mt753x_phylink_get_caps(stru config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; diff --git a/target/linux/generic/backport-5.15/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch b/target/linux/generic/backport-5.15/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch index 7066a87d71a..576d768fe21 100644 --- a/target/linux/generic/backport-5.15/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch +++ b/target/linux/generic/backport-5.15/705-10-v5.19-net-dsa-mt753x-fix-pcs-conversion-regression.patch @@ -81,7 +81,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -3046,9 +3046,16 @@ static int +@@ -3051,9 +3051,16 @@ static int mt753x_setup(struct dsa_switch *ds) { struct mt7530_priv *priv = ds->priv; @@ -100,7 +100,7 @@ Signed-off-by: Jakub Kicinski if (ret) return ret; -@@ -3060,13 +3067,6 @@ mt753x_setup(struct dsa_switch *ds) +@@ -3065,13 +3072,6 @@ mt753x_setup(struct dsa_switch *ds) if (ret && priv->irq) mt7530_free_irq_common(priv); diff --git a/target/linux/generic/backport-5.15/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch b/target/linux/generic/backport-5.15/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch index 2476cd361a8..e33908c13d6 100644 --- a/target/linux/generic/backport-5.15/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch +++ b/target/linux/generic/backport-5.15/705-11-v6.0-net-dsa-mt7530-rework-mt7530_hw_vlan_-add-del.patch @@ -26,7 +26,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -1537,11 +1537,11 @@ static void +@@ -1540,11 +1540,11 @@ static void mt7530_hw_vlan_add(struct mt7530_priv *priv, struct mt7530_hw_vlan_entry *entry) { @@ -40,7 +40,7 @@ Signed-off-by: Jakub Kicinski /* Validate the entry with independent learning, create egress tag per * VLAN and joining the port as one of the port members. -@@ -1552,22 +1552,20 @@ mt7530_hw_vlan_add(struct mt7530_priv *p +@@ -1555,22 +1555,20 @@ mt7530_hw_vlan_add(struct mt7530_priv *p /* Decide whether adding tag or not for those outgoing packets from the * port inside the VLAN. @@ -72,7 +72,7 @@ Signed-off-by: Jakub Kicinski } static void -@@ -1586,11 +1584,7 @@ mt7530_hw_vlan_del(struct mt7530_priv *p +@@ -1589,11 +1587,7 @@ mt7530_hw_vlan_del(struct mt7530_priv *p return; } diff --git a/target/linux/generic/backport-5.15/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch b/target/linux/generic/backport-5.15/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch index 6928c5960a0..a50fe30164b 100644 --- a/target/linux/generic/backport-5.15/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch +++ b/target/linux/generic/backport-5.15/705-12-v6.0-net-dsa-mt7530-rework-mt753-01-_setup.patch @@ -16,7 +16,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2102,11 +2102,12 @@ static int +@@ -2105,11 +2105,12 @@ static int mt7530_setup(struct dsa_switch *ds) { struct mt7530_priv *priv = ds->priv; @@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski u32 id, val; int ret, i; -@@ -2114,7 +2115,19 @@ mt7530_setup(struct dsa_switch *ds) +@@ -2117,7 +2118,19 @@ mt7530_setup(struct dsa_switch *ds) * controller also is the container for two GMACs nodes representing * as two netdev instances. */ @@ -51,7 +51,7 @@ Signed-off-by: Jakub Kicinski ds->assisted_learning_on_cpu_port = true; ds->mtu_enforcement_ingress = true; -@@ -2276,6 +2289,7 @@ mt7531_setup(struct dsa_switch *ds) +@@ -2281,6 +2294,7 @@ mt7531_setup(struct dsa_switch *ds) { struct mt7530_priv *priv = ds->priv; struct mt7530_dummy_poll p; @@ -59,7 +59,7 @@ Signed-off-by: Jakub Kicinski u32 val, id; int ret, i; -@@ -2350,8 +2364,11 @@ mt7531_setup(struct dsa_switch *ds) +@@ -2355,8 +2369,11 @@ mt7531_setup(struct dsa_switch *ds) CORE_PLL_GROUP4, val); /* BPDU to CPU port */ diff --git a/target/linux/generic/backport-5.15/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch b/target/linux/generic/backport-5.15/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch index 828c2b52745..8563e7e136a 100644 --- a/target/linux/generic/backport-5.15/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch +++ b/target/linux/generic/backport-5.15/705-13-v6.0-net-dsa-mt7530-get-cpu-port-via-dp-cpu_dp-instead-of.patch @@ -21,7 +21,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -1041,6 +1041,7 @@ static int +@@ -1044,6 +1044,7 @@ static int mt7530_port_enable(struct dsa_switch *ds, int port, struct phy_device *phy) { @@ -29,7 +29,7 @@ Signed-off-by: Jakub Kicinski struct mt7530_priv *priv = ds->priv; mutex_lock(&priv->reg_mutex); -@@ -1049,7 +1050,11 @@ mt7530_port_enable(struct dsa_switch *ds +@@ -1052,7 +1053,11 @@ mt7530_port_enable(struct dsa_switch *ds * restore the port matrix if the port is the member of a certain * bridge. */ @@ -42,7 +42,7 @@ Signed-off-by: Jakub Kicinski priv->ports[port].enable = true; mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, priv->ports[port].pm); -@@ -1197,7 +1202,8 @@ mt7530_port_bridge_join(struct dsa_switc +@@ -1200,7 +1205,8 @@ mt7530_port_bridge_join(struct dsa_switc struct net_device *bridge) { struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; @@ -52,7 +52,7 @@ Signed-off-by: Jakub Kicinski struct mt7530_priv *priv = ds->priv; mutex_lock(&priv->reg_mutex); -@@ -1274,9 +1280,12 @@ mt7530_port_set_vlan_unaware(struct dsa_ +@@ -1277,9 +1283,12 @@ mt7530_port_set_vlan_unaware(struct dsa_ * the CPU port get out of VLAN filtering mode. */ if (all_user_ports_removed) { @@ -67,7 +67,7 @@ Signed-off-by: Jakub Kicinski | PVC_EG_TAG(MT7530_VLAN_EG_CONSISTENT)); } } -@@ -1326,6 +1335,7 @@ mt7530_port_bridge_leave(struct dsa_swit +@@ -1329,6 +1338,7 @@ mt7530_port_bridge_leave(struct dsa_swit struct net_device *bridge) { struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; @@ -75,7 +75,7 @@ Signed-off-by: Jakub Kicinski struct mt7530_priv *priv = ds->priv; mutex_lock(&priv->reg_mutex); -@@ -1354,8 +1364,8 @@ mt7530_port_bridge_leave(struct dsa_swit +@@ -1357,8 +1367,8 @@ mt7530_port_bridge_leave(struct dsa_swit */ if (priv->ports[port].enable) mt7530_rmw(priv, MT7530_PCR_P(port), PCR_MATRIX_MASK, @@ -86,7 +86,7 @@ Signed-off-by: Jakub Kicinski /* When a port is removed from the bridge, the port would be set up * back to the default as is at initial boot which is a VLAN-unaware -@@ -1518,6 +1528,9 @@ static int +@@ -1521,6 +1531,9 @@ static int mt7530_port_vlan_filtering(struct dsa_switch *ds, int port, bool vlan_filtering, struct netlink_ext_ack *extack) { @@ -96,7 +96,7 @@ Signed-off-by: Jakub Kicinski if (vlan_filtering) { /* The port is being kept as VLAN-unaware port when bridge is * set up with vlan_filtering not being set, Otherwise, the -@@ -1525,7 +1538,7 @@ mt7530_port_vlan_filtering(struct dsa_sw +@@ -1528,7 +1541,7 @@ mt7530_port_vlan_filtering(struct dsa_sw * for becoming a VLAN-aware port. */ mt7530_port_set_vlan_aware(ds, port); diff --git a/target/linux/generic/backport-5.15/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch b/target/linux/generic/backport-5.15/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch index f9aa6fb67b4..c5501fb0402 100644 --- a/target/linux/generic/backport-5.15/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch +++ b/target/linux/generic/backport-5.15/706-00-v6.0-net-ethernet-mtk_eth_soc-rely-on-page_pool-for-singl.patch @@ -27,7 +27,7 @@ Signed-off-by: David S. Miller MediaTek SoC family. --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1388,6 +1388,68 @@ static void mtk_update_rx_cpu_idx(struct +@@ -1389,6 +1389,68 @@ static void mtk_update_rx_cpu_idx(struct } } @@ -96,7 +96,7 @@ Signed-off-by: David S. Miller static int mtk_poll_rx(struct napi_struct *napi, int budget, struct mtk_eth *eth) { -@@ -1401,9 +1463,9 @@ static int mtk_poll_rx(struct napi_struc +@@ -1402,9 +1464,9 @@ static int mtk_poll_rx(struct napi_struc while (done < budget) { unsigned int pktlen, *rxdcsum; @@ -107,7 +107,7 @@ Signed-off-by: David S. Miller int mac = 0; ring = mtk_get_rx_ring(eth); -@@ -1434,36 +1496,54 @@ static int mtk_poll_rx(struct napi_struc +@@ -1435,36 +1497,54 @@ static int mtk_poll_rx(struct napi_struc goto release_desc; /* alloc new buffer */ @@ -184,7 +184,7 @@ Signed-off-by: David S. Miller pktlen = RX_DMA_GET_PLEN0(trxd.rxd2); skb->dev = netdev; -@@ -1517,7 +1597,6 @@ static int mtk_poll_rx(struct napi_struc +@@ -1518,7 +1598,6 @@ static int mtk_poll_rx(struct napi_struc skip_rx: ring->data[idx] = new_data; rxd->rxd1 = (unsigned int)dma_addr; @@ -192,7 +192,7 @@ Signed-off-by: David S. Miller release_desc: if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) rxd->rxd2 = RX_DMA_LSO; -@@ -1525,7 +1604,6 @@ release_desc: +@@ -1526,7 +1605,6 @@ release_desc: rxd->rxd2 = RX_DMA_PREP_PLEN0(ring->buf_size); ring->calc_idx = idx; @@ -200,7 +200,7 @@ Signed-off-by: David S. Miller done++; } -@@ -1889,13 +1967,15 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1890,13 +1968,15 @@ static int mtk_rx_alloc(struct mtk_eth * if (!ring->data) return -ENOMEM; @@ -223,7 +223,7 @@ Signed-off-by: David S. Miller } ring->dma = dma_alloc_coherent(eth->dma_dev, -@@ -1906,16 +1986,33 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1907,16 +1987,33 @@ static int mtk_rx_alloc(struct mtk_eth * for (i = 0; i < rx_dma_size; i++) { struct mtk_rx_dma_v2 *rxd; @@ -264,7 +264,7 @@ Signed-off-by: David S. Miller if (MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) rxd->rxd2 = RX_DMA_LSO; -@@ -1931,6 +2028,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1932,6 +2029,7 @@ static int mtk_rx_alloc(struct mtk_eth * rxd->rxd8 = 0; } } @@ -272,7 +272,7 @@ Signed-off-by: David S. Miller ring->dma_size = rx_dma_size; ring->calc_idx_update = false; ring->calc_idx = rx_dma_size - 1; -@@ -1982,7 +2080,7 @@ static void mtk_rx_clean(struct mtk_eth +@@ -1983,7 +2081,7 @@ static void mtk_rx_clean(struct mtk_eth dma_unmap_single(eth->dma_dev, rxd->rxd1, ring->buf_size, DMA_FROM_DEVICE); @@ -281,7 +281,7 @@ Signed-off-by: David S. Miller } kfree(ring->data); ring->data = NULL; -@@ -1994,6 +2092,13 @@ static void mtk_rx_clean(struct mtk_eth +@@ -1995,6 +2093,13 @@ static void mtk_rx_clean(struct mtk_eth ring->dma, ring->phys); ring->dma = NULL; } @@ -318,7 +318,7 @@ Signed-off-by: David S. Miller #define MTK_QRX_OFFSET 0x10 #define MTK_MAX_RX_RING_NUM 4 -@@ -742,6 +749,9 @@ struct mtk_rx_ring { +@@ -743,6 +750,9 @@ struct mtk_rx_ring { bool calc_idx_update; u16 calc_idx; u32 crx_idx_reg; diff --git a/target/linux/generic/backport-5.15/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch b/target/linux/generic/backport-5.15/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch index f03f6a3a414..d94bdabd714 100644 --- a/target/linux/generic/backport-5.15/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch +++ b/target/linux/generic/backport-5.15/706-01-v6.0-net-ethernet-mtk_eth_soc-add-basic-XDP-support.patch @@ -18,7 +18,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1388,6 +1388,11 @@ static void mtk_update_rx_cpu_idx(struct +@@ -1389,6 +1389,11 @@ static void mtk_update_rx_cpu_idx(struct } } @@ -30,7 +30,7 @@ Signed-off-by: David S. Miller static struct page_pool *mtk_create_page_pool(struct mtk_eth *eth, struct xdp_rxq_info *xdp_q, int id, int size) -@@ -1450,11 +1455,52 @@ static void mtk_rx_put_buff(struct mtk_r +@@ -1451,11 +1456,52 @@ static void mtk_rx_put_buff(struct mtk_r skb_free_frag(data); } @@ -83,7 +83,7 @@ Signed-off-by: David S. Miller int idx; struct sk_buff *skb; u8 *data, *new_data; -@@ -1463,9 +1509,9 @@ static int mtk_poll_rx(struct napi_struc +@@ -1464,9 +1510,9 @@ static int mtk_poll_rx(struct napi_struc while (done < budget) { unsigned int pktlen, *rxdcsum; @@ -94,7 +94,7 @@ Signed-off-by: David S. Miller int mac = 0; ring = mtk_get_rx_ring(eth); -@@ -1495,8 +1541,14 @@ static int mtk_poll_rx(struct napi_struc +@@ -1496,8 +1542,14 @@ static int mtk_poll_rx(struct napi_struc if (unlikely(test_bit(MTK_RESETTING, ð->state))) goto release_desc; @@ -109,7 +109,7 @@ Signed-off-by: David S. Miller new_data = mtk_page_pool_get_buff(ring->page_pool, &dma_addr, GFP_ATOMIC); -@@ -1504,6 +1556,34 @@ static int mtk_poll_rx(struct napi_struc +@@ -1505,6 +1557,34 @@ static int mtk_poll_rx(struct napi_struc netdev->stats.rx_dropped++; goto release_desc; } @@ -144,7 +144,7 @@ Signed-off-by: David S. Miller } else { if (ring->frag_size <= PAGE_SIZE) new_data = napi_alloc_frag(ring->frag_size); -@@ -1527,27 +1607,20 @@ static int mtk_poll_rx(struct napi_struc +@@ -1528,27 +1608,20 @@ static int mtk_poll_rx(struct napi_struc dma_unmap_single(eth->dma_dev, trxd.rxd1, ring->buf_size, DMA_FROM_DEVICE); @@ -181,7 +181,7 @@ Signed-off-by: David S. Miller if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) rxdcsum = &trxd.rxd3; -@@ -1559,7 +1632,6 @@ static int mtk_poll_rx(struct napi_struc +@@ -1560,7 +1633,6 @@ static int mtk_poll_rx(struct napi_struc else skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, netdev); @@ -189,7 +189,7 @@ Signed-off-by: David S. Miller hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY; if (hash != MTK_RXD4_FOE_ENTRY) { -@@ -1622,6 +1694,9 @@ rx_done: +@@ -1623,6 +1695,9 @@ rx_done: &dim_sample); net_dim(ð->rx_dim, dim_sample); @@ -199,7 +199,7 @@ Signed-off-by: David S. Miller return done; } -@@ -1967,7 +2042,7 @@ static int mtk_rx_alloc(struct mtk_eth * +@@ -1968,7 +2043,7 @@ static int mtk_rx_alloc(struct mtk_eth * if (!ring->data) return -ENOMEM; @@ -208,7 +208,7 @@ Signed-off-by: David S. Miller struct page_pool *pp; pp = mtk_create_page_pool(eth, &ring->xdp_q, ring_no, -@@ -2709,6 +2784,48 @@ static int mtk_stop(struct net_device *d +@@ -2710,6 +2785,48 @@ static int mtk_stop(struct net_device *d return 0; } @@ -257,7 +257,7 @@ Signed-off-by: David S. Miller static void ethsys_reset(struct mtk_eth *eth, u32 reset_bits) { regmap_update_bits(eth->ethsys, ETHSYS_RSTCTRL, -@@ -3004,6 +3121,12 @@ static int mtk_change_mtu(struct net_dev +@@ -3005,6 +3122,12 @@ static int mtk_change_mtu(struct net_dev struct mtk_eth *eth = mac->hw; u32 mcr_cur, mcr_new; @@ -270,7 +270,7 @@ Signed-off-by: David S. Miller if (!MTK_HAS_CAPS(eth->soc->caps, MTK_SOC_MT7628)) { mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); mcr_new = mcr_cur & ~MAC_MCR_MAX_RX_MASK; -@@ -3331,6 +3454,7 @@ static const struct net_device_ops mtk_n +@@ -3332,6 +3455,7 @@ static const struct net_device_ops mtk_n .ndo_poll_controller = mtk_poll_controller, #endif .ndo_setup_tc = mtk_eth_setup_tc, @@ -280,7 +280,7 @@ Signed-off-by: David S. Miller static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1085,6 +1085,8 @@ struct mtk_eth { +@@ -1086,6 +1086,8 @@ struct mtk_eth { struct mtk_ppe *ppe; struct rhashtable flow_table; diff --git a/target/linux/generic/backport-5.15/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch b/target/linux/generic/backport-5.15/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch index b68f44aae2a..cf4d658684b 100644 --- a/target/linux/generic/backport-5.15/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch +++ b/target/linux/generic/backport-5.15/706-02-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-ethtool-count.patch @@ -39,7 +39,7 @@ Signed-off-by: David S. Miller }; static const char * const mtk_clks_source_name[] = { -@@ -1458,6 +1469,9 @@ static void mtk_rx_put_buff(struct mtk_r +@@ -1459,6 +1470,9 @@ static void mtk_rx_put_buff(struct mtk_r static u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring, struct xdp_buff *xdp, struct net_device *dev) { @@ -49,7 +49,7 @@ Signed-off-by: David S. Miller struct bpf_prog *prog; u32 act = XDP_PASS; -@@ -1470,13 +1484,16 @@ static u32 mtk_xdp_run(struct mtk_eth *e +@@ -1471,13 +1485,16 @@ static u32 mtk_xdp_run(struct mtk_eth *e act = bpf_prog_run_xdp(prog, xdp); switch (act) { case XDP_PASS: @@ -68,7 +68,7 @@ Signed-off-by: David S. Miller default: bpf_warn_invalid_xdp_action(act); fallthrough; -@@ -1489,6 +1506,11 @@ static u32 mtk_xdp_run(struct mtk_eth *e +@@ -1490,6 +1507,11 @@ static u32 mtk_xdp_run(struct mtk_eth *e page_pool_put_full_page(ring->page_pool, virt_to_head_page(xdp->data), true); @@ -82,7 +82,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -567,6 +567,16 @@ struct mtk_tx_dma_v2 { +@@ -568,6 +568,16 @@ struct mtk_tx_dma_v2 { struct mtk_eth; struct mtk_mac; @@ -99,7 +99,7 @@ Signed-off-by: David S. Miller /* struct mtk_hw_stats - the structure that holds the traffic statistics. * @stats_lock: make sure that stats operations are atomic * @reg_offset: the status register offset of the SoC -@@ -590,6 +600,8 @@ struct mtk_hw_stats { +@@ -591,6 +601,8 @@ struct mtk_hw_stats { u64 rx_checksum_errors; u64 rx_flow_control_packets; diff --git a/target/linux/generic/backport-5.15/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch b/target/linux/generic/backport-5.15/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch index 345c6bed01b..58b49f28a25 100644 --- a/target/linux/generic/backport-5.15/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch +++ b/target/linux/generic/backport-5.15/706-03-v6.0-net-ethernet-mtk_eth_soc-add-xmit-XDP-support.patch @@ -15,7 +15,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -987,15 +987,26 @@ static void mtk_tx_unmap(struct mtk_eth +@@ -988,15 +988,26 @@ static void mtk_tx_unmap(struct mtk_eth } } @@ -49,7 +49,7 @@ Signed-off-by: David S. Miller } static void setup_tx_buf(struct mtk_eth *eth, struct mtk_tx_buf *tx_buf, -@@ -1012,7 +1023,7 @@ static void setup_tx_buf(struct mtk_eth +@@ -1013,7 +1024,7 @@ static void setup_tx_buf(struct mtk_eth dma_unmap_addr_set(tx_buf, dma_addr1, mapped_addr); dma_unmap_len_set(tx_buf, dma_len1, size); } else { @@ -58,7 +58,7 @@ Signed-off-by: David S. Miller txd->txd1 = mapped_addr; txd->txd2 = TX_DMA_PLEN0(size); dma_unmap_addr_set(tx_buf, dma_addr0, mapped_addr); -@@ -1188,7 +1199,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1189,7 +1200,7 @@ static int mtk_tx_map(struct sk_buff *sk soc->txrx.txd_size); if (new_desc) memset(tx_buf, 0, sizeof(*tx_buf)); @@ -67,7 +67,7 @@ Signed-off-by: David S. Miller tx_buf->flags |= MTK_TX_FLAGS_PAGE0; tx_buf->flags |= (!mac->id) ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1; -@@ -1202,7 +1213,8 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1203,7 +1214,8 @@ static int mtk_tx_map(struct sk_buff *sk } /* store skb to cleanup */ @@ -77,7 +77,7 @@ Signed-off-by: David S. Miller if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { if (k & 0x1) -@@ -1414,13 +1426,14 @@ static struct page_pool *mtk_create_page +@@ -1415,13 +1427,14 @@ static struct page_pool *mtk_create_page .pool_size = size, .nid = NUMA_NO_NODE, .dev = eth->dma_dev, @@ -93,7 +93,7 @@ Signed-off-by: David S. Miller pp = page_pool_create(&pp_params); if (IS_ERR(pp)) return pp; -@@ -1466,6 +1479,122 @@ static void mtk_rx_put_buff(struct mtk_r +@@ -1467,6 +1480,122 @@ static void mtk_rx_put_buff(struct mtk_r skb_free_frag(data); } @@ -216,7 +216,7 @@ Signed-off-by: David S. Miller static u32 mtk_xdp_run(struct mtk_eth *eth, struct mtk_rx_ring *ring, struct xdp_buff *xdp, struct net_device *dev) { -@@ -1494,6 +1623,18 @@ static u32 mtk_xdp_run(struct mtk_eth *e +@@ -1495,6 +1624,18 @@ static u32 mtk_xdp_run(struct mtk_eth *e count = &hw_stats->xdp_stats.rx_xdp_redirect; goto update_stats; @@ -235,7 +235,7 @@ Signed-off-by: David S. Miller default: bpf_warn_invalid_xdp_action(act); fallthrough; -@@ -1727,9 +1868,8 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -1728,9 +1869,8 @@ static int mtk_poll_tx_qdma(struct mtk_e { const struct mtk_reg_map *reg_map = eth->soc->reg_map; struct mtk_tx_ring *ring = ð->tx_ring; @@ -246,7 +246,7 @@ Signed-off-by: David S. Miller u32 cpu, dma; cpu = ring->last_free_ptr; -@@ -1750,15 +1890,21 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -1751,15 +1891,21 @@ static int mtk_poll_tx_qdma(struct mtk_e if (tx_buf->flags & MTK_TX_FLAGS_FPORT1) mac = 1; @@ -271,7 +271,7 @@ Signed-off-by: David S. Miller mtk_tx_unmap(eth, tx_buf, true); ring->last_free = desc; -@@ -1777,9 +1923,8 @@ static int mtk_poll_tx_pdma(struct mtk_e +@@ -1778,9 +1924,8 @@ static int mtk_poll_tx_pdma(struct mtk_e unsigned int *done, unsigned int *bytes) { struct mtk_tx_ring *ring = ð->tx_ring; @@ -282,7 +282,7 @@ Signed-off-by: David S. Miller u32 cpu, dma; cpu = ring->cpu_idx; -@@ -1787,14 +1932,18 @@ static int mtk_poll_tx_pdma(struct mtk_e +@@ -1788,14 +1933,18 @@ static int mtk_poll_tx_pdma(struct mtk_e while ((cpu != dma) && budget) { tx_buf = &ring->buf[cpu]; @@ -304,7 +304,7 @@ Signed-off-by: David S. Miller } mtk_tx_unmap(eth, tx_buf, true); -@@ -3477,6 +3626,7 @@ static const struct net_device_ops mtk_n +@@ -3478,6 +3627,7 @@ static const struct net_device_ops mtk_n #endif .ndo_setup_tc = mtk_eth_setup_tc, .ndo_bpf = mtk_xdp, @@ -314,7 +314,7 @@ Signed-off-by: David S. Miller static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -693,6 +693,12 @@ enum mtk_dev_state { +@@ -694,6 +694,12 @@ enum mtk_dev_state { MTK_RESETTING }; @@ -327,7 +327,7 @@ Signed-off-by: David S. Miller /* struct mtk_tx_buf - This struct holds the pointers to the memory pointed at * by the TX descriptor s * @skb: The SKB pointer of the packet being sent -@@ -702,7 +708,9 @@ enum mtk_dev_state { +@@ -703,7 +709,9 @@ enum mtk_dev_state { * @dma_len1: The length of the second segment */ struct mtk_tx_buf { diff --git a/target/linux/generic/backport-5.15/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch b/target/linux/generic/backport-5.15/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch index 8302cb05c43..e93e0df544a 100644 --- a/target/linux/generic/backport-5.15/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch +++ b/target/linux/generic/backport-5.15/706-04-v6.0-net-ethernet-mtk_eth_soc-add-support-for-page_pool_g.patch @@ -26,7 +26,7 @@ Signed-off-by: David S. Miller MediaTek SoC family. --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3487,11 +3487,18 @@ static void mtk_get_strings(struct net_d +@@ -3488,11 +3488,18 @@ static void mtk_get_strings(struct net_d int i; switch (stringset) { @@ -46,7 +46,7 @@ Signed-off-by: David S. Miller break; } } -@@ -3499,13 +3506,35 @@ static void mtk_get_strings(struct net_d +@@ -3500,13 +3507,35 @@ static void mtk_get_strings(struct net_d static int mtk_get_sset_count(struct net_device *dev, int sset) { switch (sset) { @@ -84,7 +84,7 @@ Signed-off-by: David S. Miller static void mtk_get_ethtool_stats(struct net_device *dev, struct ethtool_stats *stats, u64 *data) { -@@ -3533,6 +3562,8 @@ static void mtk_get_ethtool_stats(struct +@@ -3534,6 +3563,8 @@ static void mtk_get_ethtool_stats(struct for (i = 0; i < ARRAY_SIZE(mtk_ethtool_stats); i++) *data_dst++ = *(data_src + mtk_ethtool_stats[i].offset); diff --git a/target/linux/generic/backport-5.15/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch b/target/linux/generic/backport-5.15/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch index 33a76166521..8e6895fe97f 100644 --- a/target/linux/generic/backport-5.15/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch +++ b/target/linux/generic/backport-5.15/706-05-v6.0-net-ethernet-mtk_eth_soc-introduce-mtk_xdp_frame_map.patch @@ -15,7 +15,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1479,6 +1479,41 @@ static void mtk_rx_put_buff(struct mtk_r +@@ -1480,6 +1480,41 @@ static void mtk_rx_put_buff(struct mtk_r skb_free_frag(data); } @@ -57,7 +57,7 @@ Signed-off-by: David S. Miller static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, struct net_device *dev, bool dma_map) { -@@ -1489,9 +1524,8 @@ static int mtk_xdp_submit_frame(struct m +@@ -1490,9 +1525,8 @@ static int mtk_xdp_submit_frame(struct m .first = true, .last = true, }; @@ -68,7 +68,7 @@ Signed-off-by: David S. Miller struct mtk_tx_buf *tx_buf; if (unlikely(test_bit(MTK_RESETTING, ð->state))) -@@ -1511,36 +1545,18 @@ static int mtk_xdp_submit_frame(struct m +@@ -1512,36 +1546,18 @@ static int mtk_xdp_submit_frame(struct m tx_buf = mtk_desc_to_tx_buf(ring, txd, soc->txrx.txd_size); memset(tx_buf, 0, sizeof(*tx_buf)); diff --git a/target/linux/generic/backport-5.15/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch b/target/linux/generic/backport-5.15/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch index e75861bc829..23e4a4dfcbf 100644 --- a/target/linux/generic/backport-5.15/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch +++ b/target/linux/generic/backport-5.15/706-06-v6.0-net-ethernet-mtk_eth_soc-introduce-xdp-multi-frag-su.patch @@ -14,7 +14,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -987,23 +987,22 @@ static void mtk_tx_unmap(struct mtk_eth +@@ -988,23 +988,22 @@ static void mtk_tx_unmap(struct mtk_eth } } @@ -47,7 +47,7 @@ Signed-off-by: David S. Miller } tx_buf->flags = 0; tx_buf->data = NULL; -@@ -1506,6 +1505,8 @@ static int mtk_xdp_frame_map(struct mtk_ +@@ -1507,6 +1506,8 @@ static int mtk_xdp_frame_map(struct mtk_ mtk_tx_set_dma_desc(dev, txd, txd_info); tx_buf->flags |= !mac->id ? MTK_TX_FLAGS_FPORT0 : MTK_TX_FLAGS_FPORT1; @@ -56,7 +56,7 @@ Signed-off-by: David S. Miller txd_pdma = qdma_to_pdma(ring, txd); setup_tx_buf(eth, tx_buf, txd_pdma, txd_info->addr, txd_info->size, -@@ -1517,43 +1518,69 @@ static int mtk_xdp_frame_map(struct mtk_ +@@ -1518,43 +1519,69 @@ static int mtk_xdp_frame_map(struct mtk_ static int mtk_xdp_submit_frame(struct mtk_eth *eth, struct xdp_frame *xdpf, struct net_device *dev, bool dma_map) { @@ -140,7 +140,7 @@ Signed-off-by: David S. Miller if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { txd_pdma = qdma_to_pdma(ring, txd); -@@ -1580,7 +1607,24 @@ static int mtk_xdp_submit_frame(struct m +@@ -1581,7 +1608,24 @@ static int mtk_xdp_submit_frame(struct m mtk_w32(eth, NEXT_DESP_IDX(idx, ring->dma_size), MT7628_TX_CTX_IDX0); } @@ -166,7 +166,7 @@ Signed-off-by: David S. Miller spin_unlock(ð->page_lock); return err; -@@ -1909,18 +1953,15 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -1910,18 +1954,15 @@ static int mtk_poll_tx_qdma(struct mtk_e if (!tx_buf->data) break; @@ -191,7 +191,7 @@ Signed-off-by: David S. Miller mtk_tx_unmap(eth, tx_buf, true); ring->last_free = desc; -@@ -1951,17 +1992,15 @@ static int mtk_poll_tx_pdma(struct mtk_e +@@ -1952,17 +1993,15 @@ static int mtk_poll_tx_pdma(struct mtk_e if (!tx_buf->data) break; diff --git a/target/linux/generic/backport-5.15/707-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch b/target/linux/generic/backport-5.15/707-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch new file mode 100644 index 00000000000..1f2a3ee140f --- /dev/null +++ b/target/linux/generic/backport-5.15/707-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch @@ -0,0 +1,394 @@ +From 4765a9722e09765866e131ec31f7b9cf4c1f4854 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 19 Mar 2023 12:57:50 +0000 +Subject: [PATCH] net: pcs: add driver for MediaTek SGMII PCS + +The SGMII core found in several MediaTek SoCs is identical to what can +also be found in MediaTek's MT7531 Ethernet switch IC. +As this has not always been clear, both drivers developed different +implementations to deal with the PCS. +Recently Alexander Couzens pointed out this fact which lead to the +development of this shared driver. + +Add a dedicated driver, mostly by copying the code now found in the +Ethernet driver. The now redundant code will be removed by a follow-up +commit. + +Suggested-by: Alexander Couzens +Suggested-by: Russell King (Oracle) +Signed-off-by: Daniel Golle +Tested-by: Frank Wunderlich +Reviewed-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + MAINTAINERS | 8 + + drivers/net/pcs/Kconfig | 7 + + drivers/net/pcs/Makefile | 1 + + drivers/net/pcs/pcs-mtk-lynxi.c | 305 ++++++++++++++++++++++++++++++ + include/linux/pcs/pcs-mtk-lynxi.h | 13 ++ + 5 files changed, 334 insertions(+) + create mode 100644 drivers/net/pcs/pcs-mtk-lynxi.c + create mode 100644 include/linux/pcs/pcs-mtk-lynxi.h + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -11790,6 +11790,14 @@ L: netdev@vger.kernel.org + S: Maintained + F: drivers/net/ethernet/mediatek/ + ++MEDIATEK ETHERNET PCS DRIVER ++M: Alexander Couzens ++M: Daniel Golle ++L: netdev@vger.kernel.org ++S: Maintained ++F: drivers/net/pcs/pcs-mtk-lynxi.c ++F: include/linux/pcs/pcs-mtk-lynxi.h ++ + MEDIATEK I2C CONTROLLER DRIVER + M: Qii Wang + L: linux-i2c@vger.kernel.org +--- a/drivers/net/pcs/Kconfig ++++ b/drivers/net/pcs/Kconfig +@@ -18,4 +18,11 @@ config PCS_LYNX + This module provides helpers to phylink for managing the Lynx PCS + which is part of the Layerscape and QorIQ Ethernet SERDES. + ++config PCS_MTK_LYNXI ++ tristate ++ select REGMAP ++ help ++ This module provides helpers to phylink for managing the LynxI PCS ++ which is part of MediaTek's SoC and Ethernet switch ICs. ++ + endmenu +--- a/drivers/net/pcs/Makefile ++++ b/drivers/net/pcs/Makefile +@@ -5,3 +5,4 @@ pcs_xpcs-$(CONFIG_PCS_XPCS) := pcs-xpcs. + + obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o + obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o ++obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o +--- /dev/null ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -0,0 +1,305 @@ ++// SPDX-License-Identifier: GPL-2.0 ++// Copyright (c) 2018-2019 MediaTek Inc. ++/* A library for MediaTek SGMII circuit ++ * ++ * Author: Sean Wang ++ * Author: Alexander Couzens ++ * Author: Daniel Golle ++ * ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++/* SGMII subsystem config registers */ ++/* BMCR (low 16) BMSR (high 16) */ ++#define SGMSYS_PCS_CONTROL_1 0x0 ++#define SGMII_BMCR GENMASK(15, 0) ++#define SGMII_BMSR GENMASK(31, 16) ++ ++#define SGMSYS_PCS_DEVICE_ID 0x4 ++#define SGMII_LYNXI_DEV_ID 0x4d544950 ++ ++#define SGMSYS_PCS_ADVERTISE 0x8 ++#define SGMII_ADVERTISE GENMASK(15, 0) ++#define SGMII_LPA GENMASK(31, 16) ++ ++#define SGMSYS_PCS_SCRATCH 0x14 ++#define SGMII_DEV_VERSION GENMASK(31, 16) ++ ++/* Register to programmable link timer, the unit in 2 * 8ns */ ++#define SGMSYS_PCS_LINK_TIMER 0x18 ++#define SGMII_LINK_TIMER_MASK GENMASK(19, 0) ++#define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \ ++ ((ns) / 2 / 8)) ++ ++/* Register to control remote fault */ ++#define SGMSYS_SGMII_MODE 0x20 ++#define SGMII_IF_MODE_SGMII BIT(0) ++#define SGMII_SPEED_DUPLEX_AN BIT(1) ++#define SGMII_SPEED_MASK GENMASK(3, 2) ++#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) ++#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1) ++#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2) ++#define SGMII_DUPLEX_HALF BIT(4) ++#define SGMII_REMOTE_FAULT_DIS BIT(8) ++ ++/* Register to reset SGMII design */ ++#define SGMSYS_RESERVED_0 0x34 ++#define SGMII_SW_RESET BIT(0) ++ ++/* Register to set SGMII speed, ANA RG_ Control Signals III */ ++#define SGMII_PHY_SPEED_MASK GENMASK(3, 2) ++#define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0) ++#define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1) ++ ++/* Register to power up QPHY */ ++#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 ++#define SGMII_PHYA_PWD BIT(4) ++ ++/* Register to QPHY wrapper control */ ++#define SGMSYS_QPHY_WRAP_CTRL 0xec ++#define SGMII_PN_SWAP_MASK GENMASK(1, 0) ++#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) ++ ++/* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated ++ * data ++ * @regmap: The register map pointing at the range used to setup ++ * SGMII modes ++ * @dev: Pointer to device owning the PCS ++ * @ana_rgc3: The offset of register ANA_RGC3 relative to regmap ++ * @interface: Currently configured interface mode ++ * @pcs: Phylink PCS structure ++ * @flags: Flags indicating hardware properties ++ */ ++struct mtk_pcs_lynxi { ++ struct regmap *regmap; ++ u32 ana_rgc3; ++ phy_interface_t interface; ++ struct phylink_pcs pcs; ++ u32 flags; ++}; ++ ++static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs) ++{ ++ return container_of(pcs, struct mtk_pcs_lynxi, pcs); ++} ++ ++static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ unsigned int bm, adv; ++ ++ /* Read the BMSR and LPA */ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); ++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ ++ phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), ++ FIELD_GET(SGMII_LPA, adv)); ++} ++ ++static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, ++ const unsigned long *advertising, ++ bool permit_pause_to_mac) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ bool mode_changed = false, changed, use_an; ++ unsigned int rgc3, sgm_mode, bmcr; ++ int advertise, link_timer; ++ ++ advertise = phylink_mii_c22_pcs_encode_advertisement(interface, ++ advertising); ++ if (advertise < 0) ++ return advertise; ++ ++ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and ++ * we assume that fixes it's speed at bitrate = line rate (in ++ * other words, 1000Mbps or 2500Mbps). ++ */ ++ if (interface == PHY_INTERFACE_MODE_SGMII) { ++ sgm_mode = SGMII_IF_MODE_SGMII; ++ if (phylink_autoneg_inband(mode)) { ++ sgm_mode |= SGMII_REMOTE_FAULT_DIS | ++ SGMII_SPEED_DUPLEX_AN; ++ use_an = true; ++ } else { ++ use_an = false; ++ } ++ } else if (phylink_autoneg_inband(mode)) { ++ /* 1000base-X or 2500base-X autoneg */ ++ sgm_mode = SGMII_REMOTE_FAULT_DIS; ++ use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ advertising); ++ } else { ++ /* 1000base-X or 2500base-X without autoneg */ ++ sgm_mode = 0; ++ use_an = false; ++ } ++ ++ if (use_an) ++ bmcr = BMCR_ANENABLE; ++ else ++ bmcr = 0; ++ ++ if (mpcs->interface != interface) { ++ link_timer = phylink_get_link_timer_ns(interface); ++ if (link_timer < 0) ++ return link_timer; ++ ++ /* PHYA power down */ ++ regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, ++ SGMII_PHYA_PWD); ++ ++ /* Reset SGMII PCS state */ ++ regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0, ++ SGMII_SW_RESET); ++ ++ if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP) ++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, ++ SGMII_PN_SWAP_MASK, ++ SGMII_PN_SWAP_TX_RX); ++ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) ++ rgc3 = SGMII_PHY_SPEED_3_125G; ++ else ++ rgc3 = SGMII_PHY_SPEED_1_25G; ++ ++ /* Configure the underlying interface speed */ ++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, ++ SGMII_PHY_SPEED_MASK, rgc3); ++ ++ /* Setup the link timer */ ++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, ++ SGMII_LINK_TIMER_VAL(link_timer)); ++ ++ mpcs->interface = interface; ++ mode_changed = true; ++ } ++ ++ /* Update the advertisement, noting whether it has changed */ ++ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, ++ SGMII_ADVERTISE, advertise, &changed); ++ ++ /* Update the sgmsys mode register */ ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | ++ SGMII_IF_MODE_SGMII, sgm_mode); ++ ++ /* Update the BMCR */ ++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, ++ BMCR_ANENABLE, bmcr); ++ ++ /* Release PHYA power down state ++ * Only removing bit SGMII_PHYA_PWD isn't enough. ++ * There are cases when the SGMII_PHYA_PWD register contains 0x9 which ++ * prevents SGMII from working. The SGMII still shows link but no traffic ++ * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was ++ * taken from a good working state of the SGMII interface. ++ * Unknown how much the QPHY needs but it is racy without a sleep. ++ * Tested on mt7622 & mt7986. ++ */ ++ usleep_range(50, 100); ++ regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); ++ ++ return changed || mode_changed; ++} ++ ++static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ ++ regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART); ++} ++ ++static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode, ++ phy_interface_t interface, int speed, ++ int duplex) ++{ ++ struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); ++ unsigned int sgm_mode; ++ ++ if (!phylink_autoneg_inband(mode)) { ++ /* Force the speed and duplex setting */ ++ if (speed == SPEED_10) ++ sgm_mode = SGMII_SPEED_10; ++ else if (speed == SPEED_100) ++ sgm_mode = SGMII_SPEED_100; ++ else ++ sgm_mode = SGMII_SPEED_1000; ++ ++ if (duplex != DUPLEX_FULL) ++ sgm_mode |= SGMII_DUPLEX_HALF; ++ ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK, ++ sgm_mode); ++ } ++} ++ ++static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = { ++ .pcs_get_state = mtk_pcs_lynxi_get_state, ++ .pcs_config = mtk_pcs_lynxi_config, ++ .pcs_an_restart = mtk_pcs_lynxi_restart_an, ++ .pcs_link_up = mtk_pcs_lynxi_link_up, ++}; ++ ++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, ++ struct regmap *regmap, u32 ana_rgc3, ++ u32 flags) ++{ ++ struct mtk_pcs_lynxi *mpcs; ++ u32 id, ver; ++ int ret; ++ ++ ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id); ++ if (ret < 0) ++ return NULL; ++ ++ if (id != SGMII_LYNXI_DEV_ID) { ++ dev_err(dev, "unknown PCS device id %08x\n", id); ++ return NULL; ++ } ++ ++ ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver); ++ if (ret < 0) ++ return NULL; ++ ++ ver = FIELD_GET(SGMII_DEV_VERSION, ver); ++ if (ver != 0x1) { ++ dev_err(dev, "unknown PCS device version %04x\n", ver); ++ return NULL; ++ } ++ ++ dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id, ++ ver); ++ ++ mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL); ++ if (!mpcs) ++ return NULL; ++ ++ mpcs->ana_rgc3 = ana_rgc3; ++ mpcs->regmap = regmap; ++ mpcs->flags = flags; ++ mpcs->pcs.ops = &mtk_pcs_lynxi_ops; ++ mpcs->pcs.poll = true; ++ mpcs->interface = PHY_INTERFACE_MODE_NA; ++ ++ return &mpcs->pcs; ++} ++EXPORT_SYMBOL(mtk_pcs_lynxi_create); ++ ++void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs) ++{ ++ if (!pcs) ++ return; ++ ++ kfree(pcs_to_mtk_pcs_lynxi(pcs)); ++} ++EXPORT_SYMBOL(mtk_pcs_lynxi_destroy); ++ ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/include/linux/pcs/pcs-mtk-lynxi.h +@@ -0,0 +1,13 @@ ++/* SPDX-License-Identifier: GPL-2.0 */ ++#ifndef __LINUX_PCS_MTK_LYNXI_H ++#define __LINUX_PCS_MTK_LYNXI_H ++ ++#include ++#include ++ ++#define MTK_SGMII_FLAG_PN_SWAP BIT(0) ++struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev, ++ struct regmap *regmap, ++ u32 ana_rgc3, u32 flags); ++void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs); ++#endif diff --git a/target/linux/generic/backport-5.15/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch b/target/linux/generic/backport-5.15/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch index a3842d35f56..817b3e10fdf 100644 --- a/target/linux/generic/backport-5.15/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch +++ b/target/linux/generic/backport-5.15/710-v6.0-net-ethernet-mtk_eth_soc-fix-hw-hash-reporting-for-M.patch @@ -18,7 +18,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1845,10 +1845,19 @@ static int mtk_poll_rx(struct napi_struc +@@ -1846,10 +1846,19 @@ static int mtk_poll_rx(struct napi_struc skb->dev = netdev; bytes += skb->len; @@ -40,7 +40,7 @@ Signed-off-by: Paolo Abeni if (*rxdcsum & eth->soc->txrx.rx_dma_l4_valid) skb->ip_summed = CHECKSUM_UNNECESSARY; -@@ -1856,16 +1865,9 @@ static int mtk_poll_rx(struct napi_struc +@@ -1857,16 +1866,9 @@ static int mtk_poll_rx(struct napi_struc skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, netdev); diff --git a/target/linux/generic/backport-5.15/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch b/target/linux/generic/backport-5.15/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch index f4eb030ef69..f6fc7340794 100644 --- a/target/linux/generic/backport-5.15/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch +++ b/target/linux/generic/backport-5.15/712-v6.0-net-ethernet-mtk_eth_soc-enable-XDP-support-just-for.patch @@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1412,7 +1412,7 @@ static void mtk_update_rx_cpu_idx(struct +@@ -1413,7 +1413,7 @@ static void mtk_update_rx_cpu_idx(struct static bool mtk_page_pool_enabled(struct mtk_eth *eth) { diff --git a/target/linux/generic/backport-5.15/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch b/target/linux/generic/backport-5.15/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch index ca2d161056d..f8bbea6c858 100644 --- a/target/linux/generic/backport-5.15/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch +++ b/target/linux/generic/backport-5.15/713-v6.0-net-ethernet-mtk_eth_soc-move-gdma_to_ppe-and-ppe_ba.patch @@ -57,7 +57,7 @@ Signed-off-by: Lorenzo Bianconi }; /* strings used by ethtool */ -@@ -2924,6 +2928,7 @@ static int mtk_open(struct net_device *d +@@ -2925,6 +2929,7 @@ static int mtk_open(struct net_device *d /* we run 2 netdevs on the same dma ring so we only bring it up once */ if (!refcount_read(ð->dma_refcnt)) { @@ -65,7 +65,7 @@ Signed-off-by: Lorenzo Bianconi u32 gdm_config = MTK_GDMA_TO_PDMA; int err; -@@ -2933,15 +2938,15 @@ static int mtk_open(struct net_device *d +@@ -2934,15 +2939,15 @@ static int mtk_open(struct net_device *d return err; } @@ -84,7 +84,7 @@ Signed-off-by: Lorenzo Bianconi refcount_set(ð->dma_refcnt, 1); } else -@@ -4047,7 +4052,9 @@ static int mtk_probe(struct platform_dev +@@ -4048,7 +4053,9 @@ static int mtk_probe(struct platform_dev } if (eth->soc->offload_version) { @@ -105,7 +105,7 @@ Signed-off-by: Lorenzo Bianconi #define MTK_GDMA_DROP_ALL 0x7777 /* Unicast Filter MAC Address Register - Low */ -@@ -952,6 +951,8 @@ struct mtk_reg_map { +@@ -953,6 +952,8 @@ struct mtk_reg_map { u32 fq_blen; /* fq free page buffer length */ } qdma; u32 gdm1_cnt; diff --git a/target/linux/generic/backport-5.15/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch b/target/linux/generic/backport-5.15/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch index 89a3aa98931..7bed2785c99 100644 --- a/target/linux/generic/backport-5.15/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch +++ b/target/linux/generic/backport-5.15/714-v6.0-net-ethernet-mtk_eth_soc-move-ppe-table-hash-offset-.patch @@ -44,7 +44,7 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4150,6 +4150,7 @@ static const struct mtk_soc_data mt7621_ +@@ -4151,6 +4151,7 @@ static const struct mtk_soc_data mt7621_ .required_clks = MT7621_CLKS_BITMAP, .required_pctl = false, .offload_version = 2, @@ -52,7 +52,7 @@ Signed-off-by: Lorenzo Bianconi .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4168,6 +4169,7 @@ static const struct mtk_soc_data mt7622_ +@@ -4169,6 +4170,7 @@ static const struct mtk_soc_data mt7622_ .required_clks = MT7622_CLKS_BITMAP, .required_pctl = false, .offload_version = 2, @@ -60,7 +60,7 @@ Signed-off-by: Lorenzo Bianconi .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4185,6 +4187,7 @@ static const struct mtk_soc_data mt7623_ +@@ -4186,6 +4188,7 @@ static const struct mtk_soc_data mt7623_ .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, .offload_version = 2, @@ -68,7 +68,7 @@ Signed-off-by: Lorenzo Bianconi .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4218,6 +4221,7 @@ static const struct mtk_soc_data mt7986_ +@@ -4219,6 +4222,7 @@ static const struct mtk_soc_data mt7986_ .caps = MT7986_CAPS, .required_clks = MT7986_CLKS_BITMAP, .required_pctl = false, @@ -78,7 +78,7 @@ Signed-off-by: Lorenzo Bianconi .rxd_size = sizeof(struct mtk_rx_dma_v2), --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -966,6 +966,7 @@ struct mtk_reg_map { +@@ -967,6 +967,7 @@ struct mtk_reg_map { * the target SoC * @required_pctl A bool value to show whether the SoC requires * the extra setup for those pins used by GMAC. @@ -86,7 +86,7 @@ Signed-off-by: Lorenzo Bianconi * @txd_size Tx DMA descriptor size. * @rxd_size Rx DMA descriptor size. * @rx_irq_done_mask Rx irq done register mask. -@@ -980,6 +981,7 @@ struct mtk_soc_data { +@@ -981,6 +982,7 @@ struct mtk_soc_data { u32 required_clks; bool required_pctl; u8 offload_version; diff --git a/target/linux/generic/backport-5.15/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch b/target/linux/generic/backport-5.15/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch index 9f4875eed3b..4d6dd4dcb44 100644 --- a/target/linux/generic/backport-5.15/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch +++ b/target/linux/generic/backport-5.15/715-v6.0-net-ethernet-mtk_eth_soc-add-the-capability-to-run-m.patch @@ -48,7 +48,7 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1871,7 +1871,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1872,7 +1872,7 @@ static int mtk_poll_rx(struct napi_struc reason = FIELD_GET(MTK_RXD4_PPE_CPU_REASON, trxd.rxd4); if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) @@ -57,7 +57,7 @@ Signed-off-by: Lorenzo Bianconi if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { -@@ -2929,7 +2929,8 @@ static int mtk_open(struct net_device *d +@@ -2930,7 +2930,8 @@ static int mtk_open(struct net_device *d /* we run 2 netdevs on the same dma ring so we only bring it up once */ if (!refcount_read(ð->dma_refcnt)) { const struct mtk_soc_data *soc = eth->soc; @@ -67,7 +67,7 @@ Signed-off-by: Lorenzo Bianconi int err; err = mtk_start_dma(eth); -@@ -2938,8 +2939,11 @@ static int mtk_open(struct net_device *d +@@ -2939,8 +2940,11 @@ static int mtk_open(struct net_device *d return err; } @@ -81,7 +81,7 @@ Signed-off-by: Lorenzo Bianconi mtk_gdm_config(eth, gdm_config); -@@ -2984,6 +2988,7 @@ static int mtk_stop(struct net_device *d +@@ -2985,6 +2989,7 @@ static int mtk_stop(struct net_device *d { struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; @@ -89,7 +89,7 @@ Signed-off-by: Lorenzo Bianconi phylink_stop(mac->phylink); -@@ -3011,8 +3016,8 @@ static int mtk_stop(struct net_device *d +@@ -3012,8 +3017,8 @@ static int mtk_stop(struct net_device *d mtk_dma_free(eth); @@ -100,7 +100,7 @@ Signed-off-by: Lorenzo Bianconi return 0; } -@@ -4052,12 +4057,19 @@ static int mtk_probe(struct platform_dev +@@ -4053,12 +4058,19 @@ static int mtk_probe(struct platform_dev } if (eth->soc->offload_version) { @@ -127,7 +127,7 @@ Signed-off-by: Lorenzo Bianconi err = mtk_eth_offload_init(eth); --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1111,7 +1111,7 @@ struct mtk_eth { +@@ -1112,7 +1112,7 @@ struct mtk_eth { int ip_align; diff --git a/target/linux/generic/backport-5.15/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch b/target/linux/generic/backport-5.15/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch index 9ac6875da14..a4b285632e1 100644 --- a/target/linux/generic/backport-5.15/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch +++ b/target/linux/generic/backport-5.15/716-v6.0-net-ethernet-mtk_eth_soc-move-wdma_base-definitions-.patch @@ -39,7 +39,7 @@ Signed-off-by: Lorenzo Bianconi }; /* strings used by ethtool */ -@@ -3969,16 +3977,12 @@ static int mtk_probe(struct platform_dev +@@ -3970,16 +3978,12 @@ static int mtk_probe(struct platform_dev for (i = 0;; i++) { struct device_node *np = of_parse_phandle(pdev->dev.of_node, "mediatek,wed", i); @@ -70,7 +70,7 @@ Signed-off-by: Lorenzo Bianconi /* QDMA descriptor txd4 */ #define TX_DMA_CHKSUM (0x7 << 29) #define TX_DMA_TSO BIT(28) -@@ -953,6 +950,7 @@ struct mtk_reg_map { +@@ -954,6 +951,7 @@ struct mtk_reg_map { u32 gdm1_cnt; u32 gdma_to_ppe0; u32 ppe_base; diff --git a/target/linux/generic/backport-5.15/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch b/target/linux/generic/backport-5.15/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch index 16bc8e29fc1..2bce63c4c01 100644 --- a/target/linux/generic/backport-5.15/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch +++ b/target/linux/generic/backport-5.15/717-v6.0-net-ethernet-mtk_eth_soc-add-foe_entry_size-to-mtk_e.patch @@ -21,7 +21,7 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4167,6 +4167,7 @@ static const struct mtk_soc_data mt7621_ +@@ -4168,6 +4168,7 @@ static const struct mtk_soc_data mt7621_ .required_pctl = false, .offload_version = 2, .hash_offset = 2, @@ -29,7 +29,7 @@ Signed-off-by: Lorenzo Bianconi .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4186,6 +4187,7 @@ static const struct mtk_soc_data mt7622_ +@@ -4187,6 +4188,7 @@ static const struct mtk_soc_data mt7622_ .required_pctl = false, .offload_version = 2, .hash_offset = 2, @@ -37,7 +37,7 @@ Signed-off-by: Lorenzo Bianconi .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4204,6 +4206,7 @@ static const struct mtk_soc_data mt7623_ +@@ -4205,6 +4207,7 @@ static const struct mtk_soc_data mt7623_ .required_pctl = true, .offload_version = 2, .hash_offset = 2, @@ -47,7 +47,7 @@ Signed-off-by: Lorenzo Bianconi .rxd_size = sizeof(struct mtk_rx_dma), --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -965,6 +965,7 @@ struct mtk_reg_map { +@@ -966,6 +966,7 @@ struct mtk_reg_map { * @required_pctl A bool value to show whether the SoC requires * the extra setup for those pins used by GMAC. * @hash_offset Flow table hash offset. @@ -55,7 +55,7 @@ Signed-off-by: Lorenzo Bianconi * @txd_size Tx DMA descriptor size. * @rxd_size Rx DMA descriptor size. * @rx_irq_done_mask Rx irq done register mask. -@@ -980,6 +981,7 @@ struct mtk_soc_data { +@@ -981,6 +982,7 @@ struct mtk_soc_data { bool required_pctl; u8 offload_version; u8 hash_offset; @@ -63,7 +63,7 @@ Signed-off-by: Lorenzo Bianconi netdev_features_t hw_features; struct { u32 txd_size; -@@ -1140,6 +1142,14 @@ struct mtk_mac { +@@ -1141,6 +1143,14 @@ struct mtk_mac { /* the struct describing the SoC. these are declared in the soc_xyz.c files */ extern const struct of_device_id of_mtk_match[]; diff --git a/target/linux/generic/backport-5.15/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch b/target/linux/generic/backport-5.15/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch index cd841d2dafc..bfca7b20e45 100644 --- a/target/linux/generic/backport-5.15/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch +++ b/target/linux/generic/backport-5.15/721-v6.0-net-ethernet-mtk_eth_wed-add-wed-support-for-mt7986-.patch @@ -26,7 +26,7 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3894,6 +3894,7 @@ void mtk_eth_set_dma_device(struct mtk_e +@@ -3895,6 +3895,7 @@ void mtk_eth_set_dma_device(struct mtk_e static int mtk_probe(struct platform_device *pdev) { @@ -34,7 +34,7 @@ Signed-off-by: Lorenzo Bianconi struct device_node *mac_np; struct mtk_eth *eth; int err, i; -@@ -3974,16 +3975,31 @@ static int mtk_probe(struct platform_dev +@@ -3975,16 +3976,31 @@ static int mtk_probe(struct platform_dev } } diff --git a/target/linux/generic/backport-5.15/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch b/target/linux/generic/backport-5.15/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch index 2fb0b1dfd67..fedcb6ccd89 100644 --- a/target/linux/generic/backport-5.15/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch +++ b/target/linux/generic/backport-5.15/723-v6.0-net-ethernet-mtk_eth_soc-introduce-flow-offloading-s.patch @@ -26,7 +26,7 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1858,12 +1858,14 @@ static int mtk_poll_rx(struct napi_struc +@@ -1859,12 +1859,14 @@ static int mtk_poll_rx(struct napi_struc bytes += skb->len; if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { @@ -41,7 +41,7 @@ Signed-off-by: Lorenzo Bianconi hash = trxd.rxd4 & MTK_RXD4_FOE_ENTRY; if (hash != MTK_RXD4_FOE_ENTRY) skb_set_hash(skb, jhash_1word(hash, 0), -@@ -1877,7 +1879,6 @@ static int mtk_poll_rx(struct napi_struc +@@ -1878,7 +1880,6 @@ static int mtk_poll_rx(struct napi_struc skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, netdev); @@ -49,7 +49,7 @@ Signed-off-by: Lorenzo Bianconi if (reason == MTK_PPE_CPU_REASON_HIT_UNBIND_RATE_REACHED) mtk_ppe_check_skb(eth->ppe[0], skb, hash); -@@ -4183,7 +4184,7 @@ static const struct mtk_soc_data mt7621_ +@@ -4184,7 +4185,7 @@ static const struct mtk_soc_data mt7621_ .required_pctl = false, .offload_version = 2, .hash_offset = 2, @@ -58,7 +58,7 @@ Signed-off-by: Lorenzo Bianconi .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4203,7 +4204,7 @@ static const struct mtk_soc_data mt7622_ +@@ -4204,7 +4205,7 @@ static const struct mtk_soc_data mt7622_ .required_pctl = false, .offload_version = 2, .hash_offset = 2, @@ -67,7 +67,7 @@ Signed-off-by: Lorenzo Bianconi .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4222,7 +4223,7 @@ static const struct mtk_soc_data mt7623_ +@@ -4223,7 +4224,7 @@ static const struct mtk_soc_data mt7623_ .required_pctl = true, .offload_version = 2, .hash_offset = 2, @@ -76,7 +76,7 @@ Signed-off-by: Lorenzo Bianconi .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4254,9 +4255,11 @@ static const struct mtk_soc_data mt7986_ +@@ -4255,9 +4256,11 @@ static const struct mtk_soc_data mt7986_ .reg_map = &mt7986_reg_map, .ana_rgc3 = 0x128, .caps = MT7986_CAPS, @@ -90,7 +90,7 @@ Signed-off-by: Lorenzo Bianconi .rxd_size = sizeof(struct mtk_rx_dma_v2), --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1150,6 +1150,78 @@ mtk_foe_get_entry(struct mtk_ppe *ppe, u +@@ -1151,6 +1151,78 @@ mtk_foe_get_entry(struct mtk_ppe *ppe, u return ppe->foe_table + hash * soc->foe_entry_size; } diff --git a/target/linux/generic/backport-5.15/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch b/target/linux/generic/backport-5.15/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch index 5b94c3ad3a0..e8bb85ac940 100644 --- a/target/linux/generic/backport-5.15/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch +++ b/target/linux/generic/backport-5.15/724-v6.0-net-ethernet-mtk_eth_soc-enable-flow-offloading-supp.patch @@ -16,7 +16,7 @@ Signed-off-by: Lorenzo Bianconi --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4258,6 +4258,7 @@ static const struct mtk_soc_data mt7986_ +@@ -4259,6 +4259,7 @@ static const struct mtk_soc_data mt7986_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7986_CLKS_BITMAP, .required_pctl = false, diff --git a/target/linux/generic/backport-5.15/728-v6.1-01-net-ethernet-mtk_eth_soc-fix-possible-memory-leak-in.patch b/target/linux/generic/backport-5.15/728-v6.1-01-net-ethernet-mtk_eth_soc-fix-possible-memory-leak-in.patch index 08ec7e1ab94..b41318afd76 100644 --- a/target/linux/generic/backport-5.15/728-v6.1-01-net-ethernet-mtk_eth_soc-fix-possible-memory-leak-in.patch +++ b/target/linux/generic/backport-5.15/728-v6.1-01-net-ethernet-mtk_eth_soc-fix-possible-memory-leak-in.patch @@ -17,7 +17,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4010,19 +4010,23 @@ static int mtk_probe(struct platform_dev +@@ -4011,19 +4011,23 @@ static int mtk_probe(struct platform_dev eth->irq[i] = platform_get_irq(pdev, i); if (eth->irq[i] < 0) { dev_err(&pdev->dev, "no IRQ%d resource found\n", i); @@ -45,7 +45,7 @@ Signed-off-by: David S. Miller } eth->clks[i] = NULL; } -@@ -4033,7 +4037,7 @@ static int mtk_probe(struct platform_dev +@@ -4034,7 +4038,7 @@ static int mtk_probe(struct platform_dev err = mtk_hw_init(eth); if (err) @@ -54,7 +54,7 @@ Signed-off-by: David S. Miller eth->hwlro = MTK_HAS_CAPS(eth->soc->caps, MTK_HWLRO); -@@ -4131,6 +4135,8 @@ err_free_dev: +@@ -4132,6 +4136,8 @@ err_free_dev: mtk_free_dev(eth); err_deinit_hw: mtk_hw_deinit(eth); @@ -63,7 +63,7 @@ Signed-off-by: David S. Miller return err; } -@@ -4150,6 +4156,7 @@ static int mtk_remove(struct platform_de +@@ -4151,6 +4157,7 @@ static int mtk_remove(struct platform_de phylink_disconnect_phy(mac->phylink); } diff --git a/target/linux/generic/backport-5.15/729-06-v6.1-net-ethernet-mtk_eth_soc-do-not-overwrite-mtu-config.patch b/target/linux/generic/backport-5.15/729-06-v6.1-net-ethernet-mtk_eth_soc-do-not-overwrite-mtu-config.patch index f9e7caa27cb..003ca9bae69 100644 --- a/target/linux/generic/backport-5.15/729-06-v6.1-net-ethernet-mtk_eth_soc-do-not-overwrite-mtu-config.patch +++ b/target/linux/generic/backport-5.15/729-06-v6.1-net-ethernet-mtk_eth_soc-do-not-overwrite-mtu-config.patch @@ -14,7 +14,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3175,6 +3175,30 @@ static void mtk_dim_tx(struct work_struc +@@ -3176,6 +3176,30 @@ static void mtk_dim_tx(struct work_struc dim->state = DIM_START_MEASURE; } @@ -45,7 +45,7 @@ Signed-off-by: David S. Miller static int mtk_hw_init(struct mtk_eth *eth) { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | -@@ -3249,8 +3273,16 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3250,8 +3274,16 @@ static int mtk_hw_init(struct mtk_eth *e * up with the more appropriate value when mtk_mac_config call is being * invoked. */ @@ -63,7 +63,7 @@ Signed-off-by: David S. Miller /* Indicates CDM to parse the MTK special tag from CPU * which also is working out for untag packets. -@@ -3366,7 +3398,6 @@ static int mtk_change_mtu(struct net_dev +@@ -3367,7 +3399,6 @@ static int mtk_change_mtu(struct net_dev int length = new_mtu + MTK_RX_ETH_HLEN; struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; @@ -71,7 +71,7 @@ Signed-off-by: David S. Miller if (rcu_access_pointer(eth->prog) && length > MTK_PP_MAX_BUF_SIZE) { -@@ -3374,23 +3405,7 @@ static int mtk_change_mtu(struct net_dev +@@ -3375,23 +3406,7 @@ static int mtk_change_mtu(struct net_dev return -EINVAL; } diff --git a/target/linux/generic/backport-5.15/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch b/target/linux/generic/backport-5.15/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch index e51dd7b97a9..342ffb99d2f 100644 --- a/target/linux/generic/backport-5.15/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch +++ b/target/linux/generic/backport-5.15/729-07-v6.1-net-ethernet-mtk_eth_soc-remove-cpu_relax-in-mtk_pen.patch @@ -12,7 +12,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3436,11 +3436,8 @@ static void mtk_pending_work(struct work +@@ -3437,11 +3437,8 @@ static void mtk_pending_work(struct work rtnl_lock(); dev_dbg(eth->dev, "[%s][%d] reset\n", __func__, __LINE__); @@ -25,7 +25,7 @@ Signed-off-by: David S. Miller /* stop all devices to make sure that dma is properly shut down */ for (i = 0; i < MTK_MAC_COUNT; i++) { if (!eth->netdev[i]) -@@ -3474,7 +3471,7 @@ static void mtk_pending_work(struct work +@@ -3475,7 +3472,7 @@ static void mtk_pending_work(struct work dev_dbg(eth->dev, "[%s][%d] reset done\n", __func__, __LINE__); diff --git a/target/linux/generic/backport-5.15/729-08-v6.2-net-ethernet-mtk_eth_soc-fix-RSTCTRL_PPE-0-1-definit.patch b/target/linux/generic/backport-5.15/729-08-v6.2-net-ethernet-mtk_eth_soc-fix-RSTCTRL_PPE-0-1-definit.patch index 69df58eed1c..12aa3ebf6a7 100644 --- a/target/linux/generic/backport-5.15/729-08-v6.2-net-ethernet-mtk_eth_soc-fix-RSTCTRL_PPE-0-1-definit.patch +++ b/target/linux/generic/backport-5.15/729-08-v6.2-net-ethernet-mtk_eth_soc-fix-RSTCTRL_PPE-0-1-definit.patch @@ -13,7 +13,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3238,16 +3238,17 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3239,16 +3239,17 @@ static int mtk_hw_init(struct mtk_eth *e return 0; } @@ -39,7 +39,7 @@ Signed-off-by: David S. Miller regmap_write(eth->ethsys, ETHSYS_FE_RST_CHK_IDLE_EN, --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -444,18 +444,14 @@ +@@ -445,18 +445,14 @@ /* ethernet reset control register */ #define ETHSYS_RSTCTRL 0x34 #define RSTCTRL_FE BIT(6) diff --git a/target/linux/generic/backport-5.15/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch b/target/linux/generic/backport-5.15/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch index f5e9dddd42f..45b85c06cb5 100644 --- a/target/linux/generic/backport-5.15/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch +++ b/target/linux/generic/backport-5.15/729-18-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_reset-util.patch @@ -16,7 +16,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3199,6 +3199,27 @@ static void mtk_set_mcr_max_rx(struct mt +@@ -3200,6 +3200,27 @@ static void mtk_set_mcr_max_rx(struct mt mtk_w32(mac->hw, mcr_new, MTK_MAC_MCR(mac->id)); } @@ -44,7 +44,7 @@ Signed-off-by: Paolo Abeni static int mtk_hw_init(struct mtk_eth *eth) { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | -@@ -3238,22 +3259,9 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3239,22 +3260,9 @@ static int mtk_hw_init(struct mtk_eth *e return 0; } diff --git a/target/linux/generic/backport-5.15/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch b/target/linux/generic/backport-5.15/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch index f2c9a7e53dc..c34e30c773e 100644 --- a/target/linux/generic/backport-5.15/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch +++ b/target/linux/generic/backport-5.15/729-19-v6.3-net-ethernet-mtk_eth_soc-introduce-mtk_hw_warm_reset.patch @@ -17,7 +17,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3220,7 +3220,54 @@ static void mtk_hw_reset(struct mtk_eth +@@ -3221,7 +3221,54 @@ static void mtk_hw_reset(struct mtk_eth 0x3ffffff); } @@ -73,7 +73,7 @@ Signed-off-by: Paolo Abeni { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | ETHSYS_DMA_AG_MAP_PPE; -@@ -3259,7 +3306,12 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3260,7 +3307,12 @@ static int mtk_hw_init(struct mtk_eth *e return 0; } @@ -87,7 +87,7 @@ Signed-off-by: Paolo Abeni if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { /* Set FE to PDMAv2 if necessary */ -@@ -3464,7 +3516,7 @@ static void mtk_pending_work(struct work +@@ -3465,7 +3517,7 @@ static void mtk_pending_work(struct work if (eth->dev->pins) pinctrl_select_state(eth->dev->pins->p, eth->dev->pins->default_state); @@ -96,7 +96,7 @@ Signed-off-by: Paolo Abeni /* restart DMA and enable IRQs */ for (i = 0; i < MTK_MAC_COUNT; i++) { -@@ -4056,7 +4108,7 @@ static int mtk_probe(struct platform_dev +@@ -4057,7 +4109,7 @@ static int mtk_probe(struct platform_dev eth->msg_enable = netif_msg_init(mtk_msg_level, MTK_DEFAULT_MSG_ENABLE); INIT_WORK(ð->pending_work, mtk_pending_work); diff --git a/target/linux/generic/backport-5.15/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch b/target/linux/generic/backport-5.15/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch index 02e34bb7cd4..29a5b627390 100644 --- a/target/linux/generic/backport-5.15/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch +++ b/target/linux/generic/backport-5.15/729-20-v6.3-net-ethernet-mtk_eth_soc-align-reset-procedure-to-ve.patch @@ -16,7 +16,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2785,14 +2785,29 @@ static void mtk_dma_free(struct mtk_eth +@@ -2786,14 +2786,29 @@ static void mtk_dma_free(struct mtk_eth kfree(eth->scratch_head); } @@ -48,7 +48,7 @@ Signed-off-by: Paolo Abeni schedule_work(ð->pending_work); } -@@ -3274,15 +3289,17 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3275,15 +3290,17 @@ static int mtk_hw_init(struct mtk_eth *e const struct mtk_reg_map *reg_map = eth->soc->reg_map; int i, val, ret; @@ -72,7 +72,7 @@ Signed-off-by: Paolo Abeni if (eth->ethsys) regmap_update_bits(eth->ethsys, ETHSYS_DMA_AG_MAP, dma_mask, -@@ -3408,8 +3425,10 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3409,8 +3426,10 @@ static int mtk_hw_init(struct mtk_eth *e return 0; err_disable_pm: @@ -85,7 +85,7 @@ Signed-off-by: Paolo Abeni return ret; } -@@ -3488,30 +3507,53 @@ static int mtk_do_ioctl(struct net_devic +@@ -3489,30 +3508,53 @@ static int mtk_do_ioctl(struct net_devic return -EOPNOTSUPP; } @@ -148,7 +148,7 @@ Signed-off-by: Paolo Abeni if (eth->dev->pins) pinctrl_select_state(eth->dev->pins->p, -@@ -3522,15 +3564,19 @@ static void mtk_pending_work(struct work +@@ -3523,15 +3565,19 @@ static void mtk_pending_work(struct work for (i = 0; i < MTK_MAC_COUNT; i++) { if (!test_bit(i, &restart)) continue; diff --git a/target/linux/generic/backport-5.15/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch b/target/linux/generic/backport-5.15/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch index 240e7897c6f..cebb750bf64 100644 --- a/target/linux/generic/backport-5.15/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch +++ b/target/linux/generic/backport-5.15/729-21-v6.3-net-ethernet-mtk_eth_soc-add-dma-checks-to-mtk_hw_re.patch @@ -49,7 +49,7 @@ Signed-off-by: Paolo Abeni }; /* strings used by ethtool */ -@@ -3282,6 +3288,102 @@ static void mtk_hw_warm_reset(struct mtk +@@ -3283,6 +3289,102 @@ static void mtk_hw_warm_reset(struct mtk val, rst_mask); } @@ -152,7 +152,7 @@ Signed-off-by: Paolo Abeni static int mtk_hw_init(struct mtk_eth *eth, bool reset) { u32 dma_mask = ETHSYS_DMA_AG_MAP_PDMA | ETHSYS_DMA_AG_MAP_QDMA | -@@ -3614,6 +3716,7 @@ static int mtk_cleanup(struct mtk_eth *e +@@ -3615,6 +3717,7 @@ static int mtk_cleanup(struct mtk_eth *e mtk_unreg_dev(eth); mtk_free_dev(eth); cancel_work_sync(ð->pending_work); @@ -160,7 +160,7 @@ Signed-off-by: Paolo Abeni return 0; } -@@ -4041,6 +4144,7 @@ static int mtk_probe(struct platform_dev +@@ -4042,6 +4145,7 @@ static int mtk_probe(struct platform_dev eth->rx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; INIT_WORK(ð->rx_dim.work, mtk_dim_rx); @@ -168,7 +168,7 @@ Signed-off-by: Paolo Abeni eth->tx_dim.mode = DIM_CQ_PERIOD_MODE_START_FROM_EQE; INIT_WORK(ð->tx_dim.work, mtk_dim_tx); -@@ -4245,6 +4349,8 @@ static int mtk_probe(struct platform_dev +@@ -4246,6 +4350,8 @@ static int mtk_probe(struct platform_dev NAPI_POLL_WEIGHT); platform_set_drvdata(pdev, eth); @@ -188,7 +188,7 @@ Signed-off-by: Paolo Abeni /* QDMA Interrupt grouping registers */ #define MTK_RLS_DONE_INT BIT(0) -@@ -537,6 +539,17 @@ +@@ -538,6 +540,17 @@ #define MT7628_SDM_RBCNT (MT7628_SDM_OFFSET + 0x10c) #define MT7628_SDM_CS_ERR (MT7628_SDM_OFFSET + 0x110) @@ -206,7 +206,7 @@ Signed-off-by: Paolo Abeni struct mtk_rx_dma { unsigned int rxd1; unsigned int rxd2; -@@ -933,6 +946,7 @@ struct mtk_reg_map { +@@ -934,6 +947,7 @@ struct mtk_reg_map { u32 delay_irq; /* delay interrupt */ u32 irq_status; /* interrupt status */ u32 irq_mask; /* interrupt mask */ @@ -214,7 +214,7 @@ Signed-off-by: Paolo Abeni u32 int_grp; } pdma; struct { -@@ -959,6 +973,8 @@ struct mtk_reg_map { +@@ -960,6 +974,8 @@ struct mtk_reg_map { u32 gdma_to_ppe0; u32 ppe_base; u32 wdma_base[2]; @@ -223,7 +223,7 @@ Signed-off-by: Paolo Abeni }; /* struct mtk_eth_data - This is the structure holding all differences -@@ -1001,6 +1017,8 @@ struct mtk_soc_data { +@@ -1002,6 +1018,8 @@ struct mtk_soc_data { } txrx; }; @@ -232,7 +232,7 @@ Signed-off-by: Paolo Abeni /* currently no SoC has more than 2 macs */ #define MTK_MAX_DEVS 2 -@@ -1123,6 +1141,14 @@ struct mtk_eth { +@@ -1124,6 +1142,14 @@ struct mtk_eth { struct rhashtable flow_table; struct bpf_prog __rcu *prog; diff --git a/target/linux/generic/backport-5.15/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch b/target/linux/generic/backport-5.15/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch index 9f67e97e6f0..2eca2686708 100644 --- a/target/linux/generic/backport-5.15/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch +++ b/target/linux/generic/backport-5.15/729-22-v6.3-net-ethernet-mtk_wed-add-reset-reset_complete-callba.patch @@ -14,7 +14,7 @@ Signed-off-by: Paolo Abeni --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3645,6 +3645,11 @@ static void mtk_pending_work(struct work +@@ -3646,6 +3646,11 @@ static void mtk_pending_work(struct work set_bit(MTK_RESETTING, ð->state); mtk_prepare_for_reset(eth); @@ -26,7 +26,7 @@ Signed-off-by: Paolo Abeni /* stop all devices to make sure that dma is properly shut down */ for (i = 0; i < MTK_MAC_COUNT; i++) { -@@ -3682,6 +3687,8 @@ static void mtk_pending_work(struct work +@@ -3683,6 +3688,8 @@ static void mtk_pending_work(struct work clear_bit(MTK_RESETTING, ð->state); diff --git a/target/linux/generic/backport-5.15/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch b/target/linux/generic/backport-5.15/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch index 85c29e77de3..b3412df9e0c 100644 --- a/target/linux/generic/backport-5.15/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch +++ b/target/linux/generic/backport-5.15/730-02-v6.3-net-ethernet-mtk_eth_soc-increase-tx-ring-side-for-Q.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -900,7 +900,7 @@ static int mtk_init_fq_dma(struct mtk_et +@@ -901,7 +901,7 @@ static int mtk_init_fq_dma(struct mtk_et { const struct mtk_soc_data *soc = eth->soc; dma_addr_t phy_ring_tail; @@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau dma_addr_t dma_addr; int i; -@@ -2154,19 +2154,25 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2155,19 +2155,25 @@ static int mtk_tx_alloc(struct mtk_eth * struct mtk_tx_ring *ring = ð->tx_ring; int i, sz = soc->txrx.txd_size; struct mtk_tx_dma_v2 *txd; @@ -51,7 +51,7 @@ Signed-off-by: Felix Fietkau u32 next_ptr = ring->phys + next * sz; txd = ring->dma + i * sz; -@@ -2186,22 +2192,22 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2187,22 +2193,22 @@ static int mtk_tx_alloc(struct mtk_eth * * descriptors in ring->dma_pdma. */ if (!MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { @@ -79,7 +79,7 @@ Signed-off-by: Felix Fietkau ring->thresh = MAX_SKB_FRAGS; /* make sure that all changes to the dma ring are flushed before we -@@ -2213,14 +2219,14 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2214,14 +2220,14 @@ static int mtk_tx_alloc(struct mtk_eth * mtk_w32(eth, ring->phys, soc->reg_map->qdma.ctx_ptr); mtk_w32(eth, ring->phys, soc->reg_map->qdma.dtx_ptr); mtk_w32(eth, @@ -96,7 +96,7 @@ Signed-off-by: Felix Fietkau mtk_w32(eth, 0, MT7628_TX_CTX_IDX0); mtk_w32(eth, MT7628_PST_DTX_IDX0, soc->reg_map->pdma.rst_idx); } -@@ -2238,7 +2244,7 @@ static void mtk_tx_clean(struct mtk_eth +@@ -2239,7 +2245,7 @@ static void mtk_tx_clean(struct mtk_eth int i; if (ring->buf) { @@ -105,7 +105,7 @@ Signed-off-by: Felix Fietkau mtk_tx_unmap(eth, &ring->buf[i], false); kfree(ring->buf); ring->buf = NULL; -@@ -2246,14 +2252,14 @@ static void mtk_tx_clean(struct mtk_eth +@@ -2247,14 +2253,14 @@ static void mtk_tx_clean(struct mtk_eth if (ring->dma) { dma_free_coherent(eth->dma_dev, @@ -122,7 +122,7 @@ Signed-off-by: Felix Fietkau ring->dma_pdma, ring->phys_pdma); ring->dma_pdma = NULL; } -@@ -2773,7 +2779,7 @@ static void mtk_dma_free(struct mtk_eth +@@ -2774,7 +2780,7 @@ static void mtk_dma_free(struct mtk_eth netdev_reset_queue(eth->netdev[i]); if (eth->scratch_ring) { dma_free_coherent(eth->dma_dev, diff --git a/target/linux/generic/backport-5.15/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch b/target/linux/generic/backport-5.15/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch index 67b389490b2..32dc5c37832 100644 --- a/target/linux/generic/backport-5.15/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch +++ b/target/linux/generic/backport-5.15/730-03-v6.3-net-ethernet-mtk_eth_soc-avoid-port_mg-assignment-on.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4427,7 +4427,7 @@ static const struct mtk_soc_data mt7621_ +@@ -4428,7 +4428,7 @@ static const struct mtk_soc_data mt7621_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7621_CLKS_BITMAP, .required_pctl = false, @@ -21,7 +21,7 @@ Signed-off-by: Felix Fietkau .hash_offset = 2, .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { -@@ -4466,7 +4466,7 @@ static const struct mtk_soc_data mt7623_ +@@ -4467,7 +4467,7 @@ static const struct mtk_soc_data mt7623_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7623_CLKS_BITMAP, .required_pctl = true, diff --git a/target/linux/generic/backport-5.15/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch b/target/linux/generic/backport-5.15/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch index 79f2d0a38ab..cfbd510c8f2 100644 --- a/target/linux/generic/backport-5.15/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch +++ b/target/linux/generic/backport-5.15/730-04-v6.3-net-ethernet-mtk_eth_soc-implement-multi-queue-suppo.patch @@ -54,7 +54,7 @@ Signed-off-by: Felix Fietkau }, .gdm1_cnt = 0x1c00, .gdma_to_ppe0 = 0x3333, -@@ -576,6 +580,75 @@ static void mtk_mac_link_down(struct phy +@@ -577,6 +581,75 @@ static void mtk_mac_link_down(struct phy mtk_w32(mac->hw, mcr, MTK_MAC_MCR(mac->id)); } @@ -130,7 +130,7 @@ Signed-off-by: Felix Fietkau static void mtk_mac_link_up(struct phylink_config *config, struct phy_device *phy, unsigned int mode, phy_interface_t interface, -@@ -601,6 +674,8 @@ static void mtk_mac_link_up(struct phyli +@@ -602,6 +675,8 @@ static void mtk_mac_link_up(struct phyli break; } @@ -139,7 +139,7 @@ Signed-off-by: Felix Fietkau /* Configure duplex */ if (duplex == DUPLEX_FULL) mcr |= MAC_MCR_FORCE_DPX; -@@ -1059,7 +1134,8 @@ static void mtk_tx_set_dma_desc_v1(struc +@@ -1060,7 +1135,8 @@ static void mtk_tx_set_dma_desc_v1(struc WRITE_ONCE(desc->txd1, info->addr); @@ -149,7 +149,7 @@ Signed-off-by: Felix Fietkau if (info->last) data |= TX_DMA_LS0; WRITE_ONCE(desc->txd3, data); -@@ -1093,9 +1169,6 @@ static void mtk_tx_set_dma_desc_v2(struc +@@ -1094,9 +1170,6 @@ static void mtk_tx_set_dma_desc_v2(struc data |= TX_DMA_LS0; WRITE_ONCE(desc->txd3, data); @@ -159,7 +159,7 @@ Signed-off-by: Felix Fietkau data = (mac->id + 1) << TX_DMA_FPORT_SHIFT_V2; /* forward port */ data |= TX_DMA_SWC_V2 | QID_BITS_V2(info->qid); WRITE_ONCE(desc->txd4, data); -@@ -1139,11 +1212,12 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1140,11 +1213,12 @@ static int mtk_tx_map(struct sk_buff *sk .gso = gso, .csum = skb->ip_summed == CHECKSUM_PARTIAL, .vlan = skb_vlan_tag_present(skb), @@ -173,7 +173,7 @@ Signed-off-by: Felix Fietkau struct mtk_mac *mac = netdev_priv(dev); struct mtk_eth *eth = mac->hw; const struct mtk_soc_data *soc = eth->soc; -@@ -1151,8 +1225,10 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1152,8 +1226,10 @@ static int mtk_tx_map(struct sk_buff *sk struct mtk_tx_dma *itxd_pdma, *txd_pdma; struct mtk_tx_buf *itx_buf, *tx_buf; int i, n_desc = 1; @@ -184,7 +184,7 @@ Signed-off-by: Felix Fietkau itxd = ring->next_free; itxd_pdma = qdma_to_pdma(ring, itxd); if (itxd == ring->last_free) -@@ -1201,7 +1277,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1202,7 +1278,7 @@ static int mtk_tx_map(struct sk_buff *sk memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); txd_info.size = min_t(unsigned int, frag_size, soc->txrx.dma_max_len); @@ -193,7 +193,7 @@ Signed-off-by: Felix Fietkau txd_info.last = i == skb_shinfo(skb)->nr_frags - 1 && !(frag_size - txd_info.size); txd_info.addr = skb_frag_dma_map(eth->dma_dev, frag, -@@ -1240,7 +1316,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1241,7 +1317,7 @@ static int mtk_tx_map(struct sk_buff *sk txd_pdma->txd2 |= TX_DMA_LS1; } @@ -202,7 +202,7 @@ Signed-off-by: Felix Fietkau skb_tx_timestamp(skb); ring->next_free = mtk_qdma_phys_to_virt(ring, txd->txd2); -@@ -1252,8 +1328,7 @@ static int mtk_tx_map(struct sk_buff *sk +@@ -1253,8 +1329,7 @@ static int mtk_tx_map(struct sk_buff *sk wmb(); if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) { @@ -212,7 +212,7 @@ Signed-off-by: Felix Fietkau mtk_w32(eth, txd->txd2, soc->reg_map->qdma.ctx_ptr); } else { int next_idx; -@@ -1322,7 +1397,7 @@ static void mtk_wake_queue(struct mtk_et +@@ -1323,7 +1398,7 @@ static void mtk_wake_queue(struct mtk_et for (i = 0; i < MTK_MAC_COUNT; i++) { if (!eth->netdev[i]) continue; @@ -221,7 +221,7 @@ Signed-off-by: Felix Fietkau } } -@@ -1346,7 +1421,7 @@ static netdev_tx_t mtk_start_xmit(struct +@@ -1347,7 +1422,7 @@ static netdev_tx_t mtk_start_xmit(struct tx_num = mtk_cal_txd_req(eth, skb); if (unlikely(atomic_read(&ring->free_count) <= tx_num)) { @@ -230,7 +230,7 @@ Signed-off-by: Felix Fietkau netif_err(eth, tx_queued, dev, "Tx Ring full when queue awake!\n"); spin_unlock(ð->page_lock); -@@ -1372,7 +1447,7 @@ static netdev_tx_t mtk_start_xmit(struct +@@ -1373,7 +1448,7 @@ static netdev_tx_t mtk_start_xmit(struct goto drop; if (unlikely(atomic_read(&ring->free_count) <= ring->thresh)) @@ -239,7 +239,7 @@ Signed-off-by: Felix Fietkau spin_unlock(ð->page_lock); -@@ -1539,10 +1614,12 @@ static int mtk_xdp_submit_frame(struct m +@@ -1540,10 +1615,12 @@ static int mtk_xdp_submit_frame(struct m struct skb_shared_info *sinfo = xdp_get_shared_info_from_frame(xdpf); const struct mtk_soc_data *soc = eth->soc; struct mtk_tx_ring *ring = ð->tx_ring; @@ -252,7 +252,7 @@ Signed-off-by: Felix Fietkau }; int err, index = 0, n_desc = 1, nr_frags; struct mtk_tx_dma *htxd, *txd, *txd_pdma; -@@ -1593,6 +1670,7 @@ static int mtk_xdp_submit_frame(struct m +@@ -1594,6 +1671,7 @@ static int mtk_xdp_submit_frame(struct m memset(&txd_info, 0, sizeof(struct mtk_tx_dma_desc_info)); txd_info.size = skb_frag_size(&sinfo->frags[index]); txd_info.last = index + 1 == nr_frags; @@ -260,7 +260,7 @@ Signed-off-by: Felix Fietkau data = skb_frag_address(&sinfo->frags[index]); index++; -@@ -1944,8 +2022,46 @@ rx_done: +@@ -1945,8 +2023,46 @@ rx_done: return done; } @@ -308,7 +308,7 @@ Signed-off-by: Felix Fietkau { const struct mtk_reg_map *reg_map = eth->soc->reg_map; struct mtk_tx_ring *ring = ð->tx_ring; -@@ -1975,12 +2091,9 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -1976,12 +2092,9 @@ static int mtk_poll_tx_qdma(struct mtk_e break; if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { @@ -323,7 +323,7 @@ Signed-off-by: Felix Fietkau budget--; } mtk_tx_unmap(eth, tx_buf, true); -@@ -1998,7 +2111,7 @@ static int mtk_poll_tx_qdma(struct mtk_e +@@ -1999,7 +2112,7 @@ static int mtk_poll_tx_qdma(struct mtk_e } static int mtk_poll_tx_pdma(struct mtk_eth *eth, int budget, @@ -332,7 +332,7 @@ Signed-off-by: Felix Fietkau { struct mtk_tx_ring *ring = ð->tx_ring; struct mtk_tx_buf *tx_buf; -@@ -2014,12 +2127,8 @@ static int mtk_poll_tx_pdma(struct mtk_e +@@ -2015,12 +2128,8 @@ static int mtk_poll_tx_pdma(struct mtk_e break; if (tx_buf->data != (void *)MTK_DMA_DUMMY_DESC) { @@ -347,7 +347,7 @@ Signed-off-by: Felix Fietkau budget--; } mtk_tx_unmap(eth, tx_buf, true); -@@ -2040,26 +2149,15 @@ static int mtk_poll_tx(struct mtk_eth *e +@@ -2041,26 +2150,15 @@ static int mtk_poll_tx(struct mtk_eth *e { struct mtk_tx_ring *ring = ð->tx_ring; struct dim_sample dim_sample = {}; @@ -379,7 +379,7 @@ Signed-off-by: Felix Fietkau dim_update_sample(eth->tx_events, eth->tx_packets, eth->tx_bytes, &dim_sample); -@@ -2069,7 +2167,7 @@ static int mtk_poll_tx(struct mtk_eth *e +@@ -2070,7 +2168,7 @@ static int mtk_poll_tx(struct mtk_eth *e (atomic_read(&ring->free_count) > ring->thresh)) mtk_wake_queue(eth); @@ -388,7 +388,7 @@ Signed-off-by: Felix Fietkau } static void mtk_handle_status_irq(struct mtk_eth *eth) -@@ -2155,6 +2253,7 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2156,6 +2254,7 @@ static int mtk_tx_alloc(struct mtk_eth * int i, sz = soc->txrx.txd_size; struct mtk_tx_dma_v2 *txd; int ring_size; @@ -396,7 +396,7 @@ Signed-off-by: Felix Fietkau if (MTK_HAS_CAPS(soc->caps, MTK_QDMA)) ring_size = MTK_QDMA_RING_SIZE; -@@ -2222,8 +2321,25 @@ static int mtk_tx_alloc(struct mtk_eth * +@@ -2223,8 +2322,25 @@ static int mtk_tx_alloc(struct mtk_eth * ring->phys + ((ring_size - 1) * sz), soc->reg_map->qdma.crx_ptr); mtk_w32(eth, ring->last_free_ptr, soc->reg_map->qdma.drx_ptr); @@ -424,7 +424,7 @@ Signed-off-by: Felix Fietkau } else { mtk_w32(eth, ring->phys_pdma, MT7628_TX_BASE_PTR0); mtk_w32(eth, ring_size, MT7628_TX_MAX_CNT0); -@@ -2903,7 +3019,7 @@ static int mtk_start_dma(struct mtk_eth +@@ -2904,7 +3020,7 @@ static int mtk_start_dma(struct mtk_eth if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) val |= MTK_MUTLI_CNT | MTK_RESV_BUF | MTK_WCOMP_EN | MTK_DMAD_WR_WDONE | @@ -433,7 +433,7 @@ Signed-off-by: Felix Fietkau else val |= MTK_RX_BT_32DWORDS; mtk_w32(eth, val, reg_map->qdma.glo_cfg); -@@ -2949,6 +3065,45 @@ static void mtk_gdm_config(struct mtk_et +@@ -2950,6 +3066,45 @@ static void mtk_gdm_config(struct mtk_et mtk_w32(eth, 0, MTK_RST_GL); } @@ -479,7 +479,7 @@ Signed-off-by: Felix Fietkau static int mtk_open(struct net_device *dev) { struct mtk_mac *mac = netdev_priv(dev); -@@ -2993,7 +3148,8 @@ static int mtk_open(struct net_device *d +@@ -2994,7 +3149,8 @@ static int mtk_open(struct net_device *d refcount_inc(ð->dma_refcnt); phylink_start(mac->phylink); @@ -489,7 +489,7 @@ Signed-off-by: Felix Fietkau return 0; } -@@ -3716,8 +3872,12 @@ static int mtk_unreg_dev(struct mtk_eth +@@ -3717,8 +3873,12 @@ static int mtk_unreg_dev(struct mtk_eth int i; for (i = 0; i < MTK_MAC_COUNT; i++) { @@ -502,7 +502,7 @@ Signed-off-by: Felix Fietkau unregister_netdev(eth->netdev[i]); } -@@ -3934,6 +4094,23 @@ static int mtk_set_rxnfc(struct net_devi +@@ -3935,6 +4095,23 @@ static int mtk_set_rxnfc(struct net_devi return ret; } @@ -526,7 +526,7 @@ Signed-off-by: Felix Fietkau static const struct ethtool_ops mtk_ethtool_ops = { .get_link_ksettings = mtk_get_link_ksettings, .set_link_ksettings = mtk_set_link_ksettings, -@@ -3969,6 +4146,7 @@ static const struct net_device_ops mtk_n +@@ -3970,6 +4147,7 @@ static const struct net_device_ops mtk_n .ndo_setup_tc = mtk_eth_setup_tc, .ndo_bpf = mtk_xdp, .ndo_xdp_xmit = mtk_xdp_xmit, @@ -534,7 +534,7 @@ Signed-off-by: Felix Fietkau }; static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) -@@ -3978,6 +4156,7 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -3979,6 +4157,7 @@ static int mtk_add_mac(struct mtk_eth *e struct phylink *phylink; struct mtk_mac *mac; int id, err; @@ -542,7 +542,7 @@ Signed-off-by: Felix Fietkau if (!_id) { dev_err(eth->dev, "missing mac id\n"); -@@ -3995,7 +4174,10 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -3996,7 +4175,10 @@ static int mtk_add_mac(struct mtk_eth *e return -EINVAL; } @@ -554,7 +554,7 @@ Signed-off-by: Felix Fietkau if (!eth->netdev[id]) { dev_err(eth->dev, "alloc_etherdev failed\n"); return -ENOMEM; -@@ -4092,6 +4274,11 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4093,6 +4275,11 @@ static int mtk_add_mac(struct mtk_eth *e else eth->netdev[id]->max_mtu = MTK_MAX_RX_LENGTH_2K - MTK_RX_ETH_HLEN; @@ -628,7 +628,7 @@ Signed-off-by: Felix Fietkau /* PDMA on MT7628 */ #define TX_DMA_DONE BIT(31) -@@ -952,6 +971,7 @@ struct mtk_reg_map { +@@ -953,6 +972,7 @@ struct mtk_reg_map { } pdma; struct { u32 qtx_cfg; /* tx queue configuration */ @@ -636,7 +636,7 @@ Signed-off-by: Felix Fietkau u32 rx_ptr; /* rx base pointer */ u32 rx_cnt_cfg; /* rx max count configuration */ u32 qcrx_ptr; /* rx cpu pointer */ -@@ -969,6 +989,7 @@ struct mtk_reg_map { +@@ -970,6 +990,7 @@ struct mtk_reg_map { u32 fq_tail; /* fq tail pointer */ u32 fq_count; /* fq free page count */ u32 fq_blen; /* fq free page buffer length */ @@ -644,7 +644,7 @@ Signed-off-by: Felix Fietkau } qdma; u32 gdm1_cnt; u32 gdma_to_ppe0; -@@ -1172,6 +1193,7 @@ struct mtk_mac { +@@ -1173,6 +1194,7 @@ struct mtk_mac { __be32 hwlro_ip[MTK_MAX_LRO_IP_CNT]; int hwlro_ip_cnt; unsigned int syscfg0; diff --git a/target/linux/generic/backport-5.15/730-07-v6.3-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch b/target/linux/generic/backport-5.15/730-07-v6.3-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch index d7e7492aa27..44af9128b75 100644 --- a/target/linux/generic/backport-5.15/730-07-v6.3-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch +++ b/target/linux/generic/backport-5.15/730-07-v6.3-net-ethernet-mtk_eth_soc-compile-out-netsys-v2-code-.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -916,7 +916,13 @@ enum mkt_eth_capabilities { +@@ -917,7 +917,13 @@ enum mkt_eth_capabilities { #define MTK_MUX_GMAC12_TO_GEPHY_SGMII \ (MTK_ETH_MUX_GMAC12_TO_GEPHY_SGMII | MTK_MUX) diff --git a/target/linux/generic/backport-5.15/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch b/target/linux/generic/backport-5.15/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch index a87a8101b76..beba07238fd 100644 --- a/target/linux/generic/backport-5.15/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch +++ b/target/linux/generic/backport-5.15/730-09-v6.3-net-ethernet-mtk_eth_soc-fix-VLAN-rx-hardware-accele.patch @@ -22,7 +22,7 @@ Signed-off-by: Felix Fietkau #include "mtk_eth_soc.h" #include "mtk_wed.h" -@@ -1973,16 +1974,22 @@ static int mtk_poll_rx(struct napi_struc +@@ -1974,16 +1975,22 @@ static int mtk_poll_rx(struct napi_struc htons(RX_DMA_VPID(trxd.rxd4)), RX_DMA_VID(trxd.rxd4)); } else if (trxd.rxd2 & RX_DMA_VTAG) { @@ -52,7 +52,7 @@ Signed-off-by: Felix Fietkau } skb_record_rx_queue(skb, 0); -@@ -2799,15 +2806,30 @@ static netdev_features_t mtk_fix_feature +@@ -2800,15 +2807,30 @@ static netdev_features_t mtk_fix_feature static int mtk_set_features(struct net_device *dev, netdev_features_t features) { @@ -88,7 +88,7 @@ Signed-off-by: Felix Fietkau } /* wait for DMA to finish whatever it is doing before we start using it again */ -@@ -3104,11 +3126,45 @@ found: +@@ -3105,11 +3127,45 @@ found: return NOTIFY_DONE; } @@ -135,7 +135,7 @@ Signed-off-by: Felix Fietkau err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); if (err) { -@@ -3631,6 +3687,10 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3632,6 +3688,10 @@ static int mtk_hw_init(struct mtk_eth *e */ val = mtk_r32(eth, MTK_CDMQ_IG_CTRL); mtk_w32(eth, val | MTK_CDMQ_STAG_EN, MTK_CDMQ_IG_CTRL); @@ -146,7 +146,7 @@ Signed-off-by: Felix Fietkau /* Enable RX VLan Offloading */ mtk_w32(eth, 1, MTK_CDMP_EG_CTRL); -@@ -3864,6 +3924,12 @@ static int mtk_free_dev(struct mtk_eth * +@@ -3865,6 +3925,12 @@ static int mtk_free_dev(struct mtk_eth * free_netdev(eth->netdev[i]); } @@ -181,7 +181,7 @@ Signed-off-by: Felix Fietkau /* CDMP Ingress Control Register */ #define MTK_CDMP_IG_CTRL 0x400 #define MTK_CDMP_STAG_EN BIT(0) -@@ -1165,6 +1171,8 @@ struct mtk_eth { +@@ -1166,6 +1172,8 @@ struct mtk_eth { int ip_align; diff --git a/target/linux/generic/backport-5.15/730-10-v6.3-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch b/target/linux/generic/backport-5.15/730-10-v6.3-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch index d6b3c28ccc5..0bf48b07800 100644 --- a/target/linux/generic/backport-5.15/730-10-v6.3-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch +++ b/target/linux/generic/backport-5.15/730-10-v6.3-net-ethernet-mtk_eth_soc-drop-packets-to-WDMA-if-the.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3711,9 +3711,12 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3712,9 +3712,12 @@ static int mtk_hw_init(struct mtk_eth *e mtk_w32(eth, 0x21021000, MTK_FE_INT_GRP); if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { diff --git a/target/linux/generic/backport-5.15/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch b/target/linux/generic/backport-5.15/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch index caffe890038..08bf608bc12 100644 --- a/target/linux/generic/backport-5.15/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch +++ b/target/linux/generic/backport-5.15/730-12-v6.3-net-ethernet-mtk_eth_soc-disable-hardware-DSA-untagg.patch @@ -20,7 +20,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3142,7 +3142,8 @@ static int mtk_open(struct net_device *d +@@ -3143,7 +3143,8 @@ static int mtk_open(struct net_device *d struct mtk_eth *eth = mac->hw; int i, err; @@ -30,7 +30,7 @@ Signed-off-by: Jakub Kicinski for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) { struct metadata_dst *md_dst = eth->dsa_meta[i]; -@@ -3159,7 +3160,8 @@ static int mtk_open(struct net_device *d +@@ -3160,7 +3161,8 @@ static int mtk_open(struct net_device *d } } else { /* Hardware special tag parsing needs to be disabled if at least diff --git a/target/linux/generic/backport-5.15/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch b/target/linux/generic/backport-5.15/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch index 14abdcd8763..efab99ada00 100644 --- a/target/linux/generic/backport-5.15/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch +++ b/target/linux/generic/backport-5.15/730-13-v6.3-net-ethernet-mtk_eth_soc-enable-special-tag-when-any.patch @@ -23,7 +23,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -3077,7 +3077,7 @@ static void mtk_gdm_config(struct mtk_et +@@ -3078,7 +3078,7 @@ static void mtk_gdm_config(struct mtk_et val |= config; @@ -32,7 +32,7 @@ Signed-off-by: David S. Miller val |= MTK_GDMA_SPECIAL_TAG; mtk_w32(eth, val, MTK_GDMA_FWD_CFG(i)); -@@ -3142,8 +3142,7 @@ static int mtk_open(struct net_device *d +@@ -3143,8 +3143,7 @@ static int mtk_open(struct net_device *d struct mtk_eth *eth = mac->hw; int i, err; @@ -42,7 +42,7 @@ Signed-off-by: David S. Miller for (i = 0; i < ARRAY_SIZE(eth->dsa_meta); i++) { struct metadata_dst *md_dst = eth->dsa_meta[i]; -@@ -3160,8 +3159,7 @@ static int mtk_open(struct net_device *d +@@ -3161,8 +3160,7 @@ static int mtk_open(struct net_device *d } } else { /* Hardware special tag parsing needs to be disabled if at least diff --git a/target/linux/generic/backport-5.15/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch b/target/linux/generic/backport-5.15/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch index e75459696b6..c392429b887 100644 --- a/target/linux/generic/backport-5.15/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch +++ b/target/linux/generic/backport-5.15/730-14-v6.3-net-ethernet-mtk_eth_soc-fix-DSA-TX-tag-hwaccel-for-.patch @@ -77,7 +77,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1829,7 +1829,9 @@ static int mtk_poll_rx(struct napi_struc +@@ -1830,7 +1830,9 @@ static int mtk_poll_rx(struct napi_struc while (done < budget) { unsigned int pktlen, *rxdcsum; @@ -87,7 +87,7 @@ Signed-off-by: David S. Miller dma_addr_t dma_addr; u32 hash, reason; int mac = 0; -@@ -1969,27 +1971,29 @@ static int mtk_poll_rx(struct napi_struc +@@ -1970,27 +1972,29 @@ static int mtk_poll_rx(struct napi_struc if (netdev->features & NETIF_F_HW_VLAN_CTAG_RX) { if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { diff --git a/target/linux/generic/backport-5.15/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch b/target/linux/generic/backport-5.15/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch new file mode 100644 index 00000000000..68f3659367a --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-02-net-mtk_eth_soc-add-definitions-for-PCS.patch @@ -0,0 +1,55 @@ +From b6a709cb51f7bdc55c01cec886098a9753ce8c28 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:10:42 +0100 +Subject: [PATCH 01/10] net: mtk_eth_soc: add definitions for PCS + +As a result of help from Frank Wunderlich to investigate and test, we +know a bit more about the PCS on the Mediatek platforms. Update the +definitions from this investigation. + +This PCS appears similar, but not identical to the Lynx PCS. + +Although not included in this patch, but for future reference, the PHY +ID registers at offset 4 read as 0x4d544950 'MTIP'. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 13 ++++++++++--- + 1 file changed, 10 insertions(+), 3 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -501,8 +501,10 @@ + #define ETHSYS_DMA_AG_MAP_PPE BIT(2) + + /* SGMII subsystem config registers */ +-/* Register to auto-negotiation restart */ ++/* BMCR (low 16) BMSR (high 16) */ + #define SGMSYS_PCS_CONTROL_1 0x0 ++#define SGMII_BMCR GENMASK(15, 0) ++#define SGMII_BMSR GENMASK(31, 16) + #define SGMII_AN_RESTART BIT(9) + #define SGMII_ISOLATE BIT(10) + #define SGMII_AN_ENABLE BIT(12) +@@ -512,13 +514,18 @@ + #define SGMII_PCS_FAULT BIT(23) + #define SGMII_AN_EXPANSION_CLR BIT(30) + ++#define SGMSYS_PCS_ADVERTISE 0x8 ++#define SGMII_ADVERTISE GENMASK(15, 0) ++#define SGMII_LPA GENMASK(31, 16) ++ + /* Register to programmable link timer, the unit in 2 * 8ns */ + #define SGMSYS_PCS_LINK_TIMER 0x18 +-#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & GENMASK(19, 0)) ++#define SGMII_LINK_TIMER_MASK GENMASK(19, 0) ++#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK) + + /* Register to control remote fault */ + #define SGMSYS_SGMII_MODE 0x20 +-#define SGMII_IF_MODE_BIT0 BIT(0) ++#define SGMII_IF_MODE_SGMII BIT(0) + #define SGMII_SPEED_DUPLEX_AN BIT(1) + #define SGMII_SPEED_MASK GENMASK(3, 2) + #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) diff --git a/target/linux/generic/backport-5.15/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch b/target/linux/generic/backport-5.15/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch new file mode 100644 index 00000000000..4ea428c9d6b --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-03-net-mtk_eth_soc-eliminate-unnecessary-error-handling.patch @@ -0,0 +1,74 @@ +From 5cf7797526ee81bea0f627bccaa3d887f48f53e0 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:10:47 +0100 +Subject: [PATCH 02/10] net: mtk_eth_soc: eliminate unnecessary error handling + +The functions called by the pcs_config() method always return zero, so +there is no point trying to handle an error from these functions. Make +these functions void, eliminate the "err" variable and simply return +zero from the pcs_config() function itself. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 ++++++------------ + 1 file changed, 6 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -20,7 +20,7 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st + } + + /* For SGMII interface mode */ +-static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) ++static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) + { + unsigned int val; + +@@ -39,16 +39,13 @@ static int mtk_pcs_setup_mode_an(struct + regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val &= ~SGMII_PHYA_PWD; + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); +- +- return 0; +- + } + + /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a + * fixed speed. + */ +-static int mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, +- phy_interface_t interface) ++static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, ++ phy_interface_t interface) + { + unsigned int val; + +@@ -73,8 +70,6 @@ static int mtk_pcs_setup_mode_force(stru + regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); + val &= ~SGMII_PHYA_PWD; + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); +- +- return 0; + } + + static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, +@@ -83,15 +78,14 @@ static int mtk_pcs_config(struct phylink + bool permit_pause_to_mac) + { + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); +- int err = 0; + + /* Setup SGMIISYS with the determined property */ + if (interface != PHY_INTERFACE_MODE_SGMII) +- err = mtk_pcs_setup_mode_force(mpcs, interface); ++ mtk_pcs_setup_mode_force(mpcs, interface); + else if (phylink_autoneg_inband(mode)) +- err = mtk_pcs_setup_mode_an(mpcs); ++ mtk_pcs_setup_mode_an(mpcs); + +- return err; ++ return 0; + } + + static void mtk_pcs_restart_an(struct phylink_pcs *pcs) diff --git a/target/linux/generic/backport-5.15/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch b/target/linux/generic/backport-5.15/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch new file mode 100644 index 00000000000..64a4a72fa6a --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-04-net-mtk_eth_soc-add-pcs_get_state-implementation.patch @@ -0,0 +1,46 @@ +From c000dca098002da193b98099df051c9ead0cacb4 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:10:52 +0100 +Subject: [PATCH 03/10] net: mtk_eth_soc: add pcs_get_state() implementation + +Add a pcs_get_state() implementation which uses the advertisements +to compute the resulting link modes, and BMSR contents to determine +negotiation and link status. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 15 +++++++++++++++ + 1 file changed, 15 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -19,6 +19,20 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st + return container_of(pcs, struct mtk_pcs, pcs); + } + ++static void mtk_pcs_get_state(struct phylink_pcs *pcs, ++ struct phylink_link_state *state) ++{ ++ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); ++ unsigned int bm, adv; ++ ++ /* Read the BMSR and LPA */ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); ++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ ++ phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), ++ FIELD_GET(SGMII_LPA, adv)); ++} ++ + /* For SGMII interface mode */ + static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) + { +@@ -117,6 +131,7 @@ static void mtk_pcs_link_up(struct phyli + } + + static const struct phylink_pcs_ops mtk_pcs_ops = { ++ .pcs_get_state = mtk_pcs_get_state, + .pcs_config = mtk_pcs_config, + .pcs_an_restart = mtk_pcs_restart_an, + .pcs_link_up = mtk_pcs_link_up, diff --git a/target/linux/generic/backport-5.15/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch b/target/linux/generic/backport-5.15/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch new file mode 100644 index 00000000000..24610fe11e1 --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-05-net-mtk_eth_soc-convert-mtk_sgmii-to-use-regmap_upda.patch @@ -0,0 +1,130 @@ +From 0d2351dc2768061689abd4de1529fa206bbd574e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:10:58 +0100 +Subject: [PATCH 04/10] net: mtk_eth_soc: convert mtk_sgmii to use + regmap_update_bits() + +mtk_sgmii does a lot of read-modify-write operations, for which there +is a specific regmap function. Use this function instead of open-coding +the operations. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 61 ++++++++++------------- + 1 file changed, 26 insertions(+), 35 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -36,23 +36,18 @@ static void mtk_pcs_get_state(struct phy + /* For SGMII interface mode */ + static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) + { +- unsigned int val; +- + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, + SGMII_LINK_TIMER_DEFAULT); + +- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); +- val |= SGMII_REMOTE_FAULT_DIS; +- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); +- +- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); +- val |= SGMII_AN_RESTART; +- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); +- +- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); +- val &= ~SGMII_PHYA_PWD; +- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS); ++ ++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, ++ SGMII_AN_RESTART, SGMII_AN_RESTART); ++ ++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, ++ SGMII_PHYA_PWD, 0); + } + + /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a +@@ -61,29 +56,26 @@ static void mtk_pcs_setup_mode_an(struct + static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, + phy_interface_t interface) + { +- unsigned int val; ++ unsigned int rgc3; + +- regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); +- val &= ~RG_PHY_SPEED_MASK; + if (interface == PHY_INTERFACE_MODE_2500BASEX) +- val |= RG_PHY_SPEED_3_125G; +- regmap_write(mpcs->regmap, mpcs->ana_rgc3, val); ++ rgc3 = RG_PHY_SPEED_3_125G; ++ ++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, ++ RG_PHY_SPEED_3_125G, rgc3); + + /* Disable SGMII AN */ +- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); +- val &= ~SGMII_AN_ENABLE; +- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); ++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, ++ SGMII_AN_ENABLE, 0); + + /* Set the speed etc but leave the duplex unchanged */ +- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); +- val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK; +- val |= SGMII_SPEED_1000; +- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL, ++ SGMII_SPEED_1000); + + /* Release PHYA power down state */ +- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); +- val &= ~SGMII_PHYA_PWD; +- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); ++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, ++ SGMII_PHYA_PWD, 0); + } + + static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, +@@ -105,29 +97,28 @@ static int mtk_pcs_config(struct phylink + static void mtk_pcs_restart_an(struct phylink_pcs *pcs) + { + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); +- unsigned int val; + +- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); +- val |= SGMII_AN_RESTART; +- regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); ++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, ++ SGMII_AN_RESTART, SGMII_AN_RESTART); + } + + static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex) + { + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); +- unsigned int val; ++ unsigned int sgm_mode; + + if (!phy_interface_mode_is_8023z(interface)) + return; + + /* SGMII force duplex setting */ +- regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); +- val &= ~SGMII_DUPLEX_FULL; + if (duplex == DUPLEX_FULL) +- val |= SGMII_DUPLEX_FULL; ++ sgm_mode = SGMII_DUPLEX_FULL; ++ else ++ sgm_mode = 0; + +- regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_DUPLEX_FULL, sgm_mode); + } + + static const struct phylink_pcs_ops mtk_pcs_ops = { diff --git a/target/linux/generic/backport-5.15/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch b/target/linux/generic/backport-5.15/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch new file mode 100644 index 00000000000..ba76ca40ffa --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-06-net-mtk_eth_soc-add-out-of-band-forcing-of-speed-and.patch @@ -0,0 +1,52 @@ +From 12198c3a410fe69843e335c1bbf6d4c2a4d48e4e Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:11:03 +0100 +Subject: [PATCH 05/10] net: mtk_eth_soc: add out of band forcing of speed and + duplex in pcs_link_up + +Add support for forcing the link speed and duplex setting in the +pcs_link_up() method for out of band modes, which will be useful when +we finish converting the pcs_config() method. Until then, we still have +to force duplex for 802.3z modes to work correctly. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 28 ++++++++++++++--------- + 1 file changed, 17 insertions(+), 11 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -108,17 +108,23 @@ static void mtk_pcs_link_up(struct phyli + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int sgm_mode; + +- if (!phy_interface_mode_is_8023z(interface)) +- return; ++ if (!phylink_autoneg_inband(mode) || ++ phy_interface_mode_is_8023z(interface)) { ++ /* Force the speed and duplex setting */ ++ if (speed == SPEED_10) ++ sgm_mode = SGMII_SPEED_10; ++ else if (speed == SPEED_100) ++ sgm_mode = SGMII_SPEED_100; ++ else ++ sgm_mode = SGMII_SPEED_1000; + +- /* SGMII force duplex setting */ +- if (duplex == DUPLEX_FULL) +- sgm_mode = SGMII_DUPLEX_FULL; +- else +- sgm_mode = 0; ++ if (duplex == DUPLEX_FULL) ++ sgm_mode |= SGMII_DUPLEX_FULL; + +- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, +- SGMII_DUPLEX_FULL, sgm_mode); ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_DUPLEX_FULL | SGMII_SPEED_MASK, ++ sgm_mode); ++ } + } + + static const struct phylink_pcs_ops mtk_pcs_ops = { diff --git a/target/linux/generic/backport-5.15/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch b/target/linux/generic/backport-5.15/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch new file mode 100644 index 00000000000..b76e1592750 --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-07-net-mtk_eth_soc-move-PHY-power-up.patch @@ -0,0 +1,48 @@ +From 6f38fffe2179dd29612aea2c67c46ed6682b4e46 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:11:08 +0100 +Subject: [PATCH 06/10] net: mtk_eth_soc: move PHY power up + +The PHY power up is common to both configuration paths, so move it into +the parent function. We need to do this for all serdes modes. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 11 ++++------- + 1 file changed, 4 insertions(+), 7 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -45,9 +45,6 @@ static void mtk_pcs_setup_mode_an(struct + + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + SGMII_AN_RESTART, SGMII_AN_RESTART); +- +- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, +- SGMII_PHYA_PWD, 0); + } + + /* For 1000BASE-X and 2500BASE-X interface modes, which operate at a +@@ -72,10 +69,6 @@ static void mtk_pcs_setup_mode_force(str + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL, + SGMII_SPEED_1000); +- +- /* Release PHYA power down state */ +- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, +- SGMII_PHYA_PWD, 0); + } + + static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, +@@ -91,6 +84,10 @@ static int mtk_pcs_config(struct phylink + else if (phylink_autoneg_inband(mode)) + mtk_pcs_setup_mode_an(mpcs); + ++ /* Release PHYA power down state */ ++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, ++ SGMII_PHYA_PWD, 0); ++ + return 0; + } + diff --git a/target/linux/generic/backport-5.15/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch b/target/linux/generic/backport-5.15/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch new file mode 100644 index 00000000000..cd9f0699b3e --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-08-net-mtk_eth_soc-move-interface-speed-selection.patch @@ -0,0 +1,48 @@ +From f752c0df13dfeb721c11d3debb79f08cf437344f Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:11:13 +0100 +Subject: [PATCH 07/10] net: mtk_eth_soc: move interface speed selection + +Move the selection of the underlying interface speed to the pcs_config +function, so we always program the interface speed. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 18 ++++++++++-------- + 1 file changed, 10 insertions(+), 8 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -53,14 +53,6 @@ static void mtk_pcs_setup_mode_an(struct + static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, + phy_interface_t interface) + { +- unsigned int rgc3; +- +- if (interface == PHY_INTERFACE_MODE_2500BASEX) +- rgc3 = RG_PHY_SPEED_3_125G; +- +- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, +- RG_PHY_SPEED_3_125G, rgc3); +- + /* Disable SGMII AN */ + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + SGMII_AN_ENABLE, 0); +@@ -77,6 +69,16 @@ static int mtk_pcs_config(struct phylink + bool permit_pause_to_mac) + { + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); ++ unsigned int rgc3; ++ ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) ++ rgc3 = RG_PHY_SPEED_3_125G; ++ else ++ rgc3 = 0; ++ ++ /* Configure the underlying interface speed */ ++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, ++ RG_PHY_SPEED_3_125G, rgc3); + + /* Setup SGMIISYS with the determined property */ + if (interface != PHY_INTERFACE_MODE_SGMII) diff --git a/target/linux/generic/backport-5.15/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch b/target/linux/generic/backport-5.15/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch new file mode 100644 index 00000000000..f08358e963d --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-09-net-mtk_eth_soc-add-advertisement-programming.patch @@ -0,0 +1,52 @@ +From c125c66ea71b9377ae2478c4f1b87b180cc5c6ef Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:11:18 +0100 +Subject: [PATCH 08/10] net: mtk_eth_soc: add advertisement programming + +Program the advertisement into the mtk PCS block. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -70,16 +70,27 @@ static int mtk_pcs_config(struct phylink + { + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int rgc3; ++ int advertise; ++ bool changed; + + if (interface == PHY_INTERFACE_MODE_2500BASEX) + rgc3 = RG_PHY_SPEED_3_125G; + else + rgc3 = 0; + ++ advertise = phylink_mii_c22_pcs_encode_advertisement(interface, ++ advertising); ++ if (advertise < 0) ++ return advertise; ++ + /* Configure the underlying interface speed */ + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, + RG_PHY_SPEED_3_125G, rgc3); + ++ /* Update the advertisement, noting whether it has changed */ ++ regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, ++ SGMII_ADVERTISE, advertise, &changed); ++ + /* Setup SGMIISYS with the determined property */ + if (interface != PHY_INTERFACE_MODE_SGMII) + mtk_pcs_setup_mode_force(mpcs, interface); +@@ -90,7 +101,7 @@ static int mtk_pcs_config(struct phylink + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD, 0); + +- return 0; ++ return changed; + } + + static void mtk_pcs_restart_an(struct phylink_pcs *pcs) diff --git a/target/linux/generic/backport-5.15/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch b/target/linux/generic/backport-5.15/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch new file mode 100644 index 00000000000..602d52c6f46 --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-10-net-mtk_eth_soc-move-and-correct-link-timer-programm.patch @@ -0,0 +1,63 @@ +From 3027d89f87707e7f3e5b683e0d37a32afb5bde96 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:11:23 +0100 +Subject: [PATCH 09/10] net: mtk_eth_soc: move and correct link timer + programming + +Program the link timer appropriately for the interface mode being +used, using the newly introduced phylink helper that provides the +nanosecond link timer interval. + +The intervals are 1.6ms for SGMII based protocols and 10ms for +802.3z based protocols. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -36,10 +36,6 @@ static void mtk_pcs_get_state(struct phy + /* For SGMII interface mode */ + static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) + { +- /* Setup the link timer and QPHY power up inside SGMIISYS */ +- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, +- SGMII_LINK_TIMER_DEFAULT); +- + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS); + +@@ -69,8 +65,8 @@ static int mtk_pcs_config(struct phylink + bool permit_pause_to_mac) + { + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); ++ int advertise, link_timer; + unsigned int rgc3; +- int advertise; + bool changed; + + if (interface == PHY_INTERFACE_MODE_2500BASEX) +@@ -83,6 +79,10 @@ static int mtk_pcs_config(struct phylink + if (advertise < 0) + return advertise; + ++ link_timer = phylink_get_link_timer_ns(interface); ++ if (link_timer < 0) ++ return link_timer; ++ + /* Configure the underlying interface speed */ + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, + RG_PHY_SPEED_3_125G, rgc3); +@@ -91,6 +91,9 @@ static int mtk_pcs_config(struct phylink + regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, + SGMII_ADVERTISE, advertise, &changed); + ++ /* Setup the link timer and QPHY power up inside SGMIISYS */ ++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); ++ + /* Setup SGMIISYS with the determined property */ + if (interface != PHY_INTERFACE_MODE_SGMII) + mtk_pcs_setup_mode_force(mpcs, interface); diff --git a/target/linux/generic/backport-5.15/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch b/target/linux/generic/backport-5.15/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch new file mode 100644 index 00000000000..0e9a0535a7b --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.2-11-net-mtk_eth_soc-add-support-for-in-band-802.3z-negot.patch @@ -0,0 +1,132 @@ +From 81b0f12a2a8a1699a7d49c3995e5f71e4ec018e6 Mon Sep 17 00:00:00 2001 +From: "Russell King (Oracle)" +Date: Thu, 27 Oct 2022 14:11:28 +0100 +Subject: [PATCH 10/10] net: mtk_eth_soc: add support for in-band 802.3z + negotiation + +As a result of help from Frank Wunderlich to investigate and test, we +now know how to program this PCS for in-band 802.3z negotiation. Add +support for this by moving the contents of the two functions into the +common mtk_pcs_config() function and adding the register settings for +802.3z negotiation. + +Signed-off-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 77 ++++++++++++----------- + 1 file changed, 42 insertions(+), 35 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -33,41 +33,15 @@ static void mtk_pcs_get_state(struct phy + FIELD_GET(SGMII_LPA, adv)); + } + +-/* For SGMII interface mode */ +-static void mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) +-{ +- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, +- SGMII_REMOTE_FAULT_DIS, SGMII_REMOTE_FAULT_DIS); +- +- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, +- SGMII_AN_RESTART, SGMII_AN_RESTART); +-} +- +-/* For 1000BASE-X and 2500BASE-X interface modes, which operate at a +- * fixed speed. +- */ +-static void mtk_pcs_setup_mode_force(struct mtk_pcs *mpcs, +- phy_interface_t interface) +-{ +- /* Disable SGMII AN */ +- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, +- SGMII_AN_ENABLE, 0); +- +- /* Set the speed etc but leave the duplex unchanged */ +- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, +- SGMII_IF_MODE_MASK & ~SGMII_DUPLEX_FULL, +- SGMII_SPEED_1000); +-} +- + static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) + { + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); ++ unsigned int rgc3, sgm_mode, bmcr; + int advertise, link_timer; +- unsigned int rgc3; +- bool changed; ++ bool changed, use_an; + + if (interface == PHY_INTERFACE_MODE_2500BASEX) + rgc3 = RG_PHY_SPEED_3_125G; +@@ -83,6 +57,37 @@ static int mtk_pcs_config(struct phylink + if (link_timer < 0) + return link_timer; + ++ /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and ++ * we assume that fixes it's speed at bitrate = line rate (in ++ * other words, 1000Mbps or 2500Mbps). ++ */ ++ if (interface == PHY_INTERFACE_MODE_SGMII) { ++ sgm_mode = SGMII_IF_MODE_SGMII; ++ if (phylink_autoneg_inband(mode)) { ++ sgm_mode |= SGMII_REMOTE_FAULT_DIS | ++ SGMII_SPEED_DUPLEX_AN; ++ use_an = true; ++ } else { ++ use_an = false; ++ } ++ } else if (phylink_autoneg_inband(mode)) { ++ /* 1000base-X or 2500base-X autoneg */ ++ sgm_mode = SGMII_REMOTE_FAULT_DIS; ++ use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, ++ advertising); ++ } else { ++ /* 1000base-X or 2500base-X without autoneg */ ++ sgm_mode = 0; ++ use_an = false; ++ } ++ ++ if (use_an) { ++ /* FIXME: Do we need to set AN_RESTART here? */ ++ bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE; ++ } else { ++ bmcr = 0; ++ } ++ + /* Configure the underlying interface speed */ + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, + RG_PHY_SPEED_3_125G, rgc3); +@@ -94,11 +99,14 @@ static int mtk_pcs_config(struct phylink + /* Setup the link timer and QPHY power up inside SGMIISYS */ + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); + +- /* Setup SGMIISYS with the determined property */ +- if (interface != PHY_INTERFACE_MODE_SGMII) +- mtk_pcs_setup_mode_force(mpcs, interface); +- else if (phylink_autoneg_inband(mode)) +- mtk_pcs_setup_mode_an(mpcs); ++ /* Update the sgmsys mode register */ ++ regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, ++ SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | ++ SGMII_IF_MODE_SGMII, sgm_mode); ++ ++ /* Update the BMCR */ ++ regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, ++ SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr); + + /* Release PHYA power down state */ + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, +@@ -121,8 +129,7 @@ static void mtk_pcs_link_up(struct phyli + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int sgm_mode; + +- if (!phylink_autoneg_inband(mode) || +- phy_interface_mode_is_8023z(interface)) { ++ if (!phylink_autoneg_inband(mode)) { + /* Force the speed and duplex setting */ + if (speed == SPEED_10) + sgm_mode = SGMII_SPEED_10; diff --git a/target/linux/generic/backport-5.15/733-v6.2-02-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch b/target/linux/generic/backport-5.15/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch similarity index 66% rename from target/linux/generic/backport-5.15/733-v6.2-02-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch rename to target/linux/generic/backport-5.15/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch index 49ec12293ad..8d2991f4500 100644 --- a/target/linux/generic/backport-5.15/733-v6.2-02-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch +++ b/target/linux/generic/backport-5.15/733-v6.2-12-net-mediatek-sgmii-ensure-the-SGMII-PHY-is-powered-d.patch @@ -1,8 +1,8 @@ From 7ff82416de8295c61423ef6fd75f052d3837d2f7 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Wed, 1 Feb 2023 19:23:29 +0100 -Subject: [PATCH] net: mediatek: sgmii: ensure the SGMII PHY is powered down on - configuration +Subject: [PATCH 11/13] net: mediatek: sgmii: ensure the SGMII PHY is powered + down on configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -34,7 +34,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1062,11 +1062,13 @@ struct mtk_soc_data { +@@ -1070,11 +1070,13 @@ struct mtk_soc_data { * @regmap: The register map pointing at the range used to setup * SGMII modes * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap @@ -50,57 +50,51 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c -@@ -24,6 +24,10 @@ static int mtk_pcs_setup_mode_an(struct - { - unsigned int val; +@@ -43,11 +43,6 @@ static int mtk_pcs_config(struct phylink + int advertise, link_timer; + bool changed, use_an; -+ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); -+ val &= ~RG_PHY_SPEED_MASK; -+ regmap_write(mpcs->regmap, mpcs->ana_rgc3, val); -+ - /* Setup the link timer and QPHY power up inside SGMIISYS */ - regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, - SGMII_LINK_TIMER_DEFAULT); -@@ -36,9 +40,6 @@ static int mtk_pcs_setup_mode_an(struct - val |= SGMII_AN_RESTART; - regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); - -- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); -- val &= ~SGMII_PHYA_PWD; -- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); - - return 0; - -@@ -69,11 +70,6 @@ static int mtk_pcs_setup_mode_force(stru - val |= SGMII_SPEED_1000; - regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); - -- /* Release PHYA power down state */ -- regmap_read(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, &val); -- val &= ~SGMII_PHYA_PWD; -- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, val); +- if (interface == PHY_INTERFACE_MODE_2500BASEX) +- rgc3 = RG_PHY_SPEED_3_125G; +- else +- rgc3 = 0; - - return 0; - } - -@@ -85,12 +81,32 @@ static int mtk_pcs_config(struct phylink - struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); - int err = 0; + advertise = phylink_mii_c22_pcs_encode_advertisement(interface, + advertising); + if (advertise < 0) +@@ -88,9 +83,22 @@ static int mtk_pcs_config(struct phylink + bmcr = 0; + } +- /* Configure the underlying interface speed */ +- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, +- RG_PHY_SPEED_3_125G, rgc3); + if (mpcs->interface != interface) { + /* PHYA power down */ + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD, SGMII_PHYA_PWD); + ++ if (interface == PHY_INTERFACE_MODE_2500BASEX) ++ rgc3 = RG_PHY_SPEED_3_125G; ++ else ++ rgc3 = 0; ++ ++ /* Configure the underlying interface speed */ ++ regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, ++ RG_PHY_SPEED_3_125G, rgc3); ++ + mpcs->interface = interface; + } -+ - /* Setup SGMIISYS with the determined property */ - if (interface != PHY_INTERFACE_MODE_SGMII) - err = mtk_pcs_setup_mode_force(mpcs, interface); - else if (phylink_autoneg_inband(mode)) - err = mtk_pcs_setup_mode_an(mpcs); + /* Update the advertisement, noting whether it has changed */ + regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, +@@ -108,9 +116,17 @@ static int mtk_pcs_config(struct phylink + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, + SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr); + +- /* Release PHYA power down state */ +- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, +- SGMII_PHYA_PWD, 0); + /* Release PHYA power down state + * Only removing bit SGMII_PHYA_PWD isn't enough. + * There are cases when the SGMII_PHYA_PWD register contains 0x9 which @@ -112,11 +106,10 @@ Signed-off-by: Jakub Kicinski + */ + usleep_range(50, 100); + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); -+ - return err; - } -@@ -145,6 +161,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + return changed; + } +@@ -171,6 +187,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, return PTR_ERR(ss->pcs[i].regmap); ss->pcs[i].pcs.ops = &mtk_pcs_ops; diff --git a/target/linux/generic/backport-5.15/733-v6.2-03-net-mediatek-sgmii-fix-duplex-configuration.patch b/target/linux/generic/backport-5.15/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch similarity index 63% rename from target/linux/generic/backport-5.15/733-v6.2-03-net-mediatek-sgmii-fix-duplex-configuration.patch rename to target/linux/generic/backport-5.15/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch index 9fac86da72e..79e5ad147ce 100644 --- a/target/linux/generic/backport-5.15/733-v6.2-03-net-mediatek-sgmii-fix-duplex-configuration.patch +++ b/target/linux/generic/backport-5.15/733-v6.2-13-net-mediatek-sgmii-fix-duplex-configuration.patch @@ -1,7 +1,7 @@ From 9d32637122de88f1ef614c29703f0e050cad342e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Mork?= Date: Wed, 1 Feb 2023 19:23:30 +0100 -Subject: [PATCH] net: mediatek: sgmii: fix duplex configuration +Subject: [PATCH 12/13] net: mediatek: sgmii: fix duplex configuration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -24,7 +24,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -523,7 +523,7 @@ +@@ -531,7 +531,7 @@ #define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) #define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1) #define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2) @@ -35,26 +35,18 @@ Signed-off-by: Jakub Kicinski #define SGMII_CODE_SYNC_SET_VAL BIT(9) --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c -@@ -66,7 +66,7 @@ static int mtk_pcs_setup_mode_force(stru +@@ -154,11 +154,11 @@ static void mtk_pcs_link_up(struct phyli + else + sgm_mode = SGMII_SPEED_1000; - /* Set the speed etc but leave the duplex unchanged */ - regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); -- val &= SGMII_DUPLEX_FULL | ~SGMII_IF_MODE_MASK; -+ val &= SGMII_DUPLEX_HALF | ~SGMII_IF_MODE_MASK; - val |= SGMII_SPEED_1000; - regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); +- if (duplex == DUPLEX_FULL) +- sgm_mode |= SGMII_DUPLEX_FULL; ++ if (duplex != DUPLEX_FULL) ++ sgm_mode |= SGMII_DUPLEX_HALF; -@@ -131,9 +131,10 @@ static void mtk_pcs_link_up(struct phyli - - /* SGMII force duplex setting */ - regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); -- val &= ~SGMII_DUPLEX_FULL; -- if (duplex == DUPLEX_FULL) -- val |= SGMII_DUPLEX_FULL; -+ -+ val &= ~SGMII_DUPLEX_HALF; -+ if (duplex != DUPLEX_FULL) -+ val |= SGMII_DUPLEX_HALF; - - regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, +- SGMII_DUPLEX_FULL | SGMII_SPEED_MASK, ++ SGMII_DUPLEX_HALF | SGMII_SPEED_MASK, + sgm_mode); + } } diff --git a/target/linux/generic/backport-5.15/733-v6.2-04-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch b/target/linux/generic/backport-5.15/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch similarity index 89% rename from target/linux/generic/backport-5.15/733-v6.2-04-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch rename to target/linux/generic/backport-5.15/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch index ace8aa075b3..56d7a1348fb 100644 --- a/target/linux/generic/backport-5.15/733-v6.2-04-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch +++ b/target/linux/generic/backport-5.15/733-v6.2-14-mtk_sgmii-enable-PCS-polling-to-allow-SFP-work.patch @@ -1,7 +1,7 @@ From 3337a6e04ddf2923a1bdcf3d31b3b52412bf82dd Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Wed, 1 Feb 2023 19:23:31 +0100 -Subject: [PATCH] mtk_sgmii: enable PCS polling to allow SFP work +Subject: [PATCH 13/13] mtk_sgmii: enable PCS polling to allow SFP work MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @@ -23,7 +23,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c -@@ -162,6 +162,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, +@@ -187,6 +187,7 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, return PTR_ERR(ss->pcs[i].regmap); ss->pcs[i].pcs.ops = &mtk_pcs_ops; diff --git a/target/linux/generic/pending-5.15/733-01-net-ethernet-mtk_eth_soc-reset-PCS-state.patch b/target/linux/generic/backport-5.15/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch similarity index 65% rename from target/linux/generic/pending-5.15/733-01-net-ethernet-mtk_eth_soc-reset-PCS-state.patch rename to target/linux/generic/backport-5.15/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch index 67c0974417a..a63b110914b 100644 --- a/target/linux/generic/pending-5.15/733-01-net-ethernet-mtk_eth_soc-reset-PCS-state.patch +++ b/target/linux/generic/backport-5.15/733-v6.3-15-net-ethernet-mtk_eth_soc-reset-PCS-state.patch @@ -1,16 +1,20 @@ -From b66105968b8c37c26a75b9da9281cbc1c8f73594 Mon Sep 17 00:00:00 2001 +From 611e2dabb4b3243d176739fd6a5a34d007fa3f86 Mon Sep 17 00:00:00 2001 From: Daniel Golle -Date: Sun, 22 Jan 2023 23:58:36 +0000 -Subject: [PATCH] net: ethernet: mtk_eth_soc: reset PCS state +Date: Tue, 14 Mar 2023 00:34:26 +0000 +Subject: [PATCH 1/2] net: ethernet: mtk_eth_soc: reset PCS state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit -Reset PCS state when changing interface mode. +Reset the internal PCS state machine when changing interface mode. +This prevents confusing the state machine when changing interface +modes, e.g. from SGMII to 2500Base-X or vice-versa. +Fixes: 7e538372694b ("net: ethernet: mediatek: Re-add support SGMII") Reviewed-by: Russell King (Oracle) Tested-by: Bjørn Mork Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller --- drivers/net/ethernet/mediatek/mtk_eth_soc.h | 4 ++++ drivers/net/ethernet/mediatek/mtk_sgmii.c | 4 ++++ @@ -18,7 +22,7 @@ Signed-off-by: Daniel Golle --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -529,6 +529,10 @@ +@@ -539,6 +539,10 @@ #define SGMII_SEND_AN_ERROR_EN BIT(11) #define SGMII_IF_MODE_MASK GENMASK(5, 1) @@ -31,7 +35,7 @@ Signed-off-by: Daniel Golle #define RG_PHY_SPEED_MASK (BIT(2) | BIT(3)) --- a/drivers/net/ethernet/mediatek/mtk_sgmii.c +++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c -@@ -90,6 +90,10 @@ static int mtk_pcs_config(struct phylink +@@ -88,6 +88,10 @@ static int mtk_pcs_config(struct phylink regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD, SGMII_PHYA_PWD); @@ -39,6 +43,6 @@ Signed-off-by: Daniel Golle + regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0, + SGMII_SW_RESET, SGMII_SW_RESET); + - mpcs->interface = interface; - } - + if (interface == PHY_INTERFACE_MODE_2500BASEX) + rgc3 = RG_PHY_SPEED_3_125G; + else diff --git a/target/linux/generic/backport-5.15/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch b/target/linux/generic/backport-5.15/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch new file mode 100644 index 00000000000..0fabeea20c0 --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.3-16-net-ethernet-mtk_eth_soc-only-write-values-if-needed.patch @@ -0,0 +1,103 @@ +From 6e933a804c7db8be64f367f33e63cd7dcc302ebb Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 14 Mar 2023 00:34:45 +0000 +Subject: [PATCH 2/2] net: ethernet: mtk_eth_soc: only write values if needed +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Only restart auto-negotiation and write link timer if actually +necessary. This prevents losing the link in case of minor +changes. + +Fixes: 7e538372694b ("net: ethernet: mediatek: Re-add support SGMII") +Reviewed-by: Russell King (Oracle) +Tested-by: Bjørn Mork +Signed-off-by: Daniel Golle +Signed-off-by: David S. Miller +--- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 24 +++++++++++------------ + 1 file changed, 12 insertions(+), 12 deletions(-) + +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -38,20 +38,16 @@ static int mtk_pcs_config(struct phylink + const unsigned long *advertising, + bool permit_pause_to_mac) + { ++ bool mode_changed = false, changed, use_an; + struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); + unsigned int rgc3, sgm_mode, bmcr; + int advertise, link_timer; +- bool changed, use_an; + + advertise = phylink_mii_c22_pcs_encode_advertisement(interface, + advertising); + if (advertise < 0) + return advertise; + +- link_timer = phylink_get_link_timer_ns(interface); +- if (link_timer < 0) +- return link_timer; +- + /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and + * we assume that fixes it's speed at bitrate = line rate (in + * other words, 1000Mbps or 2500Mbps). +@@ -77,13 +73,16 @@ static int mtk_pcs_config(struct phylink + } + + if (use_an) { +- /* FIXME: Do we need to set AN_RESTART here? */ +- bmcr = SGMII_AN_RESTART | SGMII_AN_ENABLE; ++ bmcr = SGMII_AN_ENABLE; + } else { + bmcr = 0; + } + + if (mpcs->interface != interface) { ++ link_timer = phylink_get_link_timer_ns(interface); ++ if (link_timer < 0) ++ return link_timer; ++ + /* PHYA power down */ + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD, SGMII_PHYA_PWD); +@@ -101,16 +100,17 @@ static int mtk_pcs_config(struct phylink + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, + RG_PHY_SPEED_3_125G, rgc3); + ++ /* Setup the link timer */ ++ regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); ++ + mpcs->interface = interface; ++ mode_changed = true; + } + + /* Update the advertisement, noting whether it has changed */ + regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, + SGMII_ADVERTISE, advertise, &changed); + +- /* Setup the link timer and QPHY power up inside SGMIISYS */ +- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); +- + /* Update the sgmsys mode register */ + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, + SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | +@@ -118,7 +118,7 @@ static int mtk_pcs_config(struct phylink + + /* Update the BMCR */ + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, +- SGMII_AN_RESTART | SGMII_AN_ENABLE, bmcr); ++ SGMII_AN_ENABLE, bmcr); + + /* Release PHYA power down state + * Only removing bit SGMII_PHYA_PWD isn't enough. +@@ -132,7 +132,7 @@ static int mtk_pcs_config(struct phylink + usleep_range(50, 100); + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); + +- return changed; ++ return changed || mode_changed; + } + + static void mtk_pcs_restart_an(struct phylink_pcs *pcs) diff --git a/target/linux/generic/backport-5.15/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch b/target/linux/generic/backport-5.15/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch new file mode 100644 index 00000000000..bfa0df30534 --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.3-18-net-ethernet-mtk_eth_soc-add-support-for-MT7981.patch @@ -0,0 +1,183 @@ +--- a/drivers/net/ethernet/mediatek/mtk_eth_path.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_path.c +@@ -96,12 +96,20 @@ static int set_mux_gmac2_gmac0_to_gephy( + + static int set_mux_u3_gmac2_to_qphy(struct mtk_eth *eth, int path) + { +- unsigned int val = 0; ++ unsigned int val = 0, mask = 0, reg = 0; + bool updated = true; + + switch (path) { + case MTK_ETH_PATH_GMAC2_SGMII: +- val = CO_QPHY_SEL; ++ if (MTK_HAS_CAPS(eth->soc->caps, MTK_U3_COPHY_V2)) { ++ reg = USB_PHY_SWITCH_REG; ++ val = SGMII_QPHY_SEL; ++ mask = QPHY_SEL_MASK; ++ } else { ++ reg = INFRA_MISC2; ++ val = CO_QPHY_SEL; ++ mask = val; ++ } + break; + default: + updated = false; +@@ -109,7 +117,7 @@ static int set_mux_u3_gmac2_to_qphy(stru + } + + if (updated) +- regmap_update_bits(eth->infra, INFRA_MISC2, CO_QPHY_SEL, val); ++ regmap_update_bits(eth->infra, reg, mask, val); + + dev_dbg(eth->dev, "path %s in %s updated = %d\n", + mtk_eth_path_name(path), __func__, updated); +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -4757,6 +4757,26 @@ static const struct mtk_soc_data mt7629_ + }, + }; + ++static const struct mtk_soc_data mt7981_data = { ++ .reg_map = &mt7986_reg_map, ++ .ana_rgc3 = 0x128, ++ .caps = MT7981_CAPS, ++ .hw_features = MTK_HW_FEATURES, ++ .required_clks = MT7981_CLKS_BITMAP, ++ .required_pctl = false, ++ .offload_version = 2, ++ .hash_offset = 4, ++ .foe_entry_size = sizeof(struct mtk_foe_entry), ++ .txrx = { ++ .txd_size = sizeof(struct mtk_tx_dma_v2), ++ .rxd_size = sizeof(struct mtk_rx_dma_v2), ++ .rx_irq_done_mask = MTK_RX_DONE_INT_V2, ++ .rx_dma_l4_valid = RX_DMA_L4_VALID_V2, ++ .dma_max_len = MTK_TX_DMA_BUF_LEN_V2, ++ .dma_len_offset = 8, ++ }, ++}; ++ + static const struct mtk_soc_data mt7986_data = { + .reg_map = &mt7986_reg_map, + .ana_rgc3 = 0x128, +@@ -4799,6 +4819,7 @@ const struct of_device_id of_mtk_match[] + { .compatible = "mediatek,mt7622-eth", .data = &mt7622_data}, + { .compatible = "mediatek,mt7623-eth", .data = &mt7623_data}, + { .compatible = "mediatek,mt7629-eth", .data = &mt7629_data}, ++ { .compatible = "mediatek,mt7981-eth", .data = &mt7981_data}, + { .compatible = "mediatek,mt7986-eth", .data = &mt7986_data}, + { .compatible = "ralink,rt5350-eth", .data = &rt5350_data}, + {}, +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -553,11 +553,22 @@ + #define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 + #define SGMII_PHYA_PWD BIT(4) + ++/* Register to QPHY wrapper control */ ++#define SGMSYS_QPHY_WRAP_CTRL 0xec ++#define SGMII_PN_SWAP_MASK GENMASK(1, 0) ++#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) ++#define MTK_SGMII_FLAG_PN_SWAP BIT(0) ++ + /* Infrasys subsystem config registers */ + #define INFRA_MISC2 0x70c + #define CO_QPHY_SEL BIT(0) + #define GEPHY_MAC_SEL BIT(1) + ++/* Top misc registers */ ++#define USB_PHY_SWITCH_REG 0x218 ++#define QPHY_SEL_MASK GENMASK(1, 0) ++#define SGMII_QPHY_SEL 0x2 ++ + /* MT7628/88 specific stuff */ + #define MT7628_PDMA_OFFSET 0x0800 + #define MT7628_SDM_OFFSET 0x0c00 +@@ -738,6 +749,17 @@ enum mtk_clks_map { + BIT(MTK_CLK_SGMII2_CDR_FB) | \ + BIT(MTK_CLK_SGMII_CK) | \ + BIT(MTK_CLK_ETH2PLL) | BIT(MTK_CLK_SGMIITOP)) ++#define MT7981_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \ ++ BIT(MTK_CLK_WOCPU0) | \ ++ BIT(MTK_CLK_SGMII_TX_250M) | \ ++ BIT(MTK_CLK_SGMII_RX_250M) | \ ++ BIT(MTK_CLK_SGMII_CDR_REF) | \ ++ BIT(MTK_CLK_SGMII_CDR_FB) | \ ++ BIT(MTK_CLK_SGMII2_TX_250M) | \ ++ BIT(MTK_CLK_SGMII2_RX_250M) | \ ++ BIT(MTK_CLK_SGMII2_CDR_REF) | \ ++ BIT(MTK_CLK_SGMII2_CDR_FB) | \ ++ BIT(MTK_CLK_SGMII_CK)) + #define MT7986_CLKS_BITMAP (BIT(MTK_CLK_FE) | BIT(MTK_CLK_GP2) | BIT(MTK_CLK_GP1) | \ + BIT(MTK_CLK_WOCPU1) | BIT(MTK_CLK_WOCPU0) | \ + BIT(MTK_CLK_SGMII_TX_250M) | \ +@@ -851,6 +873,7 @@ enum mkt_eth_capabilities { + MTK_NETSYS_V2_BIT, + MTK_SOC_MT7628_BIT, + MTK_RSTCTRL_PPE1_BIT, ++ MTK_U3_COPHY_V2_BIT, + + /* MUX BITS*/ + MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT, +@@ -885,6 +908,7 @@ enum mkt_eth_capabilities { + #define MTK_NETSYS_V2 BIT(MTK_NETSYS_V2_BIT) + #define MTK_SOC_MT7628 BIT(MTK_SOC_MT7628_BIT) + #define MTK_RSTCTRL_PPE1 BIT(MTK_RSTCTRL_PPE1_BIT) ++#define MTK_U3_COPHY_V2 BIT(MTK_U3_COPHY_V2_BIT) + + #define MTK_ETH_MUX_GDM1_TO_GMAC1_ESW \ + BIT(MTK_ETH_MUX_GDM1_TO_GMAC1_ESW_BIT) +@@ -963,6 +987,11 @@ enum mkt_eth_capabilities { + MTK_MUX_U3_GMAC2_TO_QPHY | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA) + ++#define MT7981_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | MTK_GMAC2_GEPHY | \ ++ MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ ++ MTK_MUX_U3_GMAC2_TO_QPHY | MTK_U3_COPHY_V2 | \ ++ MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1) ++ + #define MT7986_CAPS (MTK_GMAC1_SGMII | MTK_GMAC2_SGMII | \ + MTK_MUX_GMAC12_TO_GEPHY_SGMII | MTK_QDMA | \ + MTK_NETSYS_V2 | MTK_RSTCTRL_PPE1) +@@ -1076,12 +1105,14 @@ struct mtk_soc_data { + * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap + * @interface: Currently configured interface mode + * @pcs: Phylink PCS structure ++ * @flags: Flags indicating hardware properties + */ + struct mtk_pcs { + struct regmap *regmap; + u32 ana_rgc3; + phy_interface_t interface; + struct phylink_pcs pcs; ++ u32 flags; + }; + + /* struct mtk_sgmii - This is the structure holding sgmii regmap and its +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c +@@ -87,6 +87,11 @@ static int mtk_pcs_config(struct phylink + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, + SGMII_PHYA_PWD, SGMII_PHYA_PWD); + ++ if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP) ++ regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, ++ SGMII_PN_SWAP_MASK, ++ SGMII_PN_SWAP_TX_RX); ++ + /* Reset SGMII PCS state */ + regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0, + SGMII_SW_RESET, SGMII_SW_RESET); +@@ -186,6 +191,11 @@ int mtk_sgmii_init(struct mtk_sgmii *ss, + + ss->pcs[i].ana_rgc3 = ana_rgc3; + ss->pcs[i].regmap = syscon_node_to_regmap(np); ++ ++ ss->pcs[i].flags = 0; ++ if (of_property_read_bool(np, "mediatek,pnswap")) ++ ss->pcs[i].flags |= MTK_SGMII_FLAG_PN_SWAP; ++ + of_node_put(np); + if (IS_ERR(ss->pcs[i].regmap)) + return PTR_ERR(ss->pcs[i].regmap); diff --git a/target/linux/generic/backport-5.15/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch b/target/linux/generic/backport-5.15/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch new file mode 100644 index 00000000000..df7ee081f79 --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.3-19-net-ethernet-mtk_eth_soc-set-MDIO-bus-clock-frequenc.patch @@ -0,0 +1,76 @@ +From c0a440031d4314d1023c1b87f43a4233634eebdb Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 19 Mar 2023 12:57:15 +0000 +Subject: [PATCH] net: ethernet: mtk_eth_soc: set MDIO bus clock frequency +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Set MDIO bus clock frequency and allow setting a custom maximum +frequency from device tree. + +Reviewed-by: Andrew Lunn +Reviewed-by: Florian Fainelli +Tested-by: Bjørn Mork +Signed-off-by: Daniel Golle +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 21 +++++++++++++++++++++ + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 7 +++++++ + 2 files changed, 28 insertions(+) + +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -704,8 +704,10 @@ static const struct phylink_mac_ops mtk_ + + static int mtk_mdio_init(struct mtk_eth *eth) + { ++ unsigned int max_clk = 2500000, divider; + struct device_node *mii_np; + int ret; ++ u32 val; + + mii_np = of_get_child_by_name(eth->dev->of_node, "mdio-bus"); + if (!mii_np) { +@@ -731,6 +733,25 @@ static int mtk_mdio_init(struct mtk_eth + eth->mii_bus->parent = eth->dev; + + snprintf(eth->mii_bus->id, MII_BUS_ID_SIZE, "%pOFn", mii_np); ++ ++ if (!of_property_read_u32(mii_np, "clock-frequency", &val)) { ++ if (val > MDC_MAX_FREQ || val < MDC_MAX_FREQ / MDC_MAX_DIVIDER) { ++ dev_err(eth->dev, "MDIO clock frequency out of range"); ++ ret = -EINVAL; ++ goto err_put_node; ++ } ++ max_clk = val; ++ } ++ divider = min_t(unsigned int, DIV_ROUND_UP(MDC_MAX_FREQ, max_clk), 63); ++ ++ /* Configure MDC Divider */ ++ val = mtk_r32(eth, MTK_PPSC); ++ val &= ~PPSC_MDC_CFG; ++ val |= FIELD_PREP(PPSC_MDC_CFG, divider) | PPSC_MDC_TURBO; ++ mtk_w32(eth, val, MTK_PPSC); ++ ++ dev_dbg(eth->dev, "MDC is running on %d Hz\n", MDC_MAX_FREQ / divider); ++ + ret = of_mdiobus_register(eth->mii_bus, mii_np); + + err_put_node: +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -363,6 +363,13 @@ + #define RX_DMA_VTAG_V2 BIT(0) + #define RX_DMA_L4_VALID_V2 BIT(2) + ++/* PHY Polling and SMI Master Control registers */ ++#define MTK_PPSC 0x10000 ++#define PPSC_MDC_CFG GENMASK(29, 24) ++#define PPSC_MDC_TURBO BIT(20) ++#define MDC_MAX_FREQ 25000000 ++#define MDC_MAX_DIVIDER 63 ++ + /* PHY Indirect Access Control registers */ + #define MTK_PHY_IAC 0x10004 + #define PHY_IAC_ACCESS BIT(31) diff --git a/target/linux/generic/backport-5.15/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch b/target/linux/generic/backport-5.15/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch new file mode 100644 index 00000000000..1eca7dfeaf5 --- /dev/null +++ b/target/linux/generic/backport-5.15/733-v6.3-20-net-ethernet-mtk_eth_soc-switch-to-external-PCS-driv.patch @@ -0,0 +1,512 @@ +From 2a3ec7ae313310c1092e4256208cc04d1958e469 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Sun, 19 Mar 2023 12:58:02 +0000 +Subject: [PATCH] net: ethernet: mtk_eth_soc: switch to external PCS driver + +Now that we got a PCS driver, use it and remove the now redundant +PCS code and it's header macros from the Ethernet driver. + +Signed-off-by: Daniel Golle +Tested-by: Frank Wunderlich +Reviewed-by: Russell King (Oracle) +Signed-off-by: Jakub Kicinski +--- + drivers/net/ethernet/mediatek/Kconfig | 2 + + drivers/net/ethernet/mediatek/Makefile | 2 +- + drivers/net/ethernet/mediatek/mtk_eth_soc.c | 61 +++++- + drivers/net/ethernet/mediatek/mtk_eth_soc.h | 93 +-------- + drivers/net/ethernet/mediatek/mtk_sgmii.c | 217 -------------------- + 5 files changed, 56 insertions(+), 319 deletions(-) + delete mode 100644 drivers/net/ethernet/mediatek/mtk_sgmii.c + +--- a/drivers/net/ethernet/mediatek/Kconfig ++++ b/drivers/net/ethernet/mediatek/Kconfig +@@ -18,6 +18,8 @@ config NET_MEDIATEK_SOC + select DIMLIB + select PAGE_POOL + select PAGE_POOL_STATS ++ select PCS_MTK_LYNXI ++ select REGMAP_MMIO + help + This driver supports the gigabit ethernet MACs in the + MediaTek SoC family. +--- a/drivers/net/ethernet/mediatek/Makefile ++++ b/drivers/net/ethernet/mediatek/Makefile +@@ -4,7 +4,7 @@ + # + + obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o +-mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o ++mtk_eth-y := mtk_eth_soc.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o + mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o mtk_wed_wo.o + ifdef CONFIG_DEBUG_FS + mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -357,7 +358,7 @@ static struct phylink_pcs *mtk_mac_selec + sid = (MTK_HAS_CAPS(eth->soc->caps, MTK_SHARED_SGMII)) ? + 0 : mac->id; + +- return mtk_sgmii_select_pcs(eth->sgmii, sid); ++ return eth->sgmii_pcs[sid]; + } + + return NULL; +@@ -3979,8 +3980,17 @@ static int mtk_unreg_dev(struct mtk_eth + return 0; + } + ++static void mtk_sgmii_destroy(struct mtk_eth *eth) ++{ ++ int i; ++ ++ for (i = 0; i < MTK_MAX_DEVS; i++) ++ mtk_pcs_lynxi_destroy(eth->sgmii_pcs[i]); ++} ++ + static int mtk_cleanup(struct mtk_eth *eth) + { ++ mtk_sgmii_destroy(eth); + mtk_unreg_dev(eth); + mtk_free_dev(eth); + cancel_work_sync(ð->pending_work); +@@ -4410,6 +4420,36 @@ void mtk_eth_set_dma_device(struct mtk_e + rtnl_unlock(); + } + ++static int mtk_sgmii_init(struct mtk_eth *eth) ++{ ++ struct device_node *np; ++ struct regmap *regmap; ++ u32 flags; ++ int i; ++ ++ for (i = 0; i < MTK_MAX_DEVS; i++) { ++ np = of_parse_phandle(eth->dev->of_node, "mediatek,sgmiisys", i); ++ if (!np) ++ break; ++ ++ regmap = syscon_node_to_regmap(np); ++ flags = 0; ++ if (of_property_read_bool(np, "mediatek,pnswap")) ++ flags |= MTK_SGMII_FLAG_PN_SWAP; ++ ++ of_node_put(np); ++ ++ if (IS_ERR(regmap)) ++ return PTR_ERR(regmap); ++ ++ eth->sgmii_pcs[i] = mtk_pcs_lynxi_create(eth->dev, regmap, ++ eth->soc->ana_rgc3, ++ flags); ++ } ++ ++ return 0; ++} ++ + static int mtk_probe(struct platform_device *pdev) + { + struct resource *res = NULL; +@@ -4473,13 +4513,7 @@ static int mtk_probe(struct platform_dev + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_SGMII)) { +- eth->sgmii = devm_kzalloc(eth->dev, sizeof(*eth->sgmii), +- GFP_KERNEL); +- if (!eth->sgmii) +- return -ENOMEM; +- +- err = mtk_sgmii_init(eth->sgmii, pdev->dev.of_node, +- eth->soc->ana_rgc3); ++ err = mtk_sgmii_init(eth); + + if (err) + return err; +@@ -4490,14 +4524,17 @@ static int mtk_probe(struct platform_dev + "mediatek,pctl"); + if (IS_ERR(eth->pctl)) { + dev_err(&pdev->dev, "no pctl regmap found\n"); +- return PTR_ERR(eth->pctl); ++ err = PTR_ERR(eth->pctl); ++ goto err_destroy_sgmii; + } + } + + if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); +- if (!res) +- return -EINVAL; ++ if (!res) { ++ err = -EINVAL; ++ goto err_destroy_sgmii; ++ } + } + + if (eth->soc->offload_version) { +@@ -4657,6 +4694,8 @@ err_deinit_hw: + mtk_hw_deinit(eth); + err_wed_exit: + mtk_wed_exit(); ++err_destroy_sgmii: ++ mtk_sgmii_destroy(eth); + + return err; + } +--- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h ++++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h +@@ -507,65 +507,6 @@ + #define ETHSYS_DMA_AG_MAP_QDMA BIT(1) + #define ETHSYS_DMA_AG_MAP_PPE BIT(2) + +-/* SGMII subsystem config registers */ +-/* BMCR (low 16) BMSR (high 16) */ +-#define SGMSYS_PCS_CONTROL_1 0x0 +-#define SGMII_BMCR GENMASK(15, 0) +-#define SGMII_BMSR GENMASK(31, 16) +-#define SGMII_AN_RESTART BIT(9) +-#define SGMII_ISOLATE BIT(10) +-#define SGMII_AN_ENABLE BIT(12) +-#define SGMII_LINK_STATYS BIT(18) +-#define SGMII_AN_ABILITY BIT(19) +-#define SGMII_AN_COMPLETE BIT(21) +-#define SGMII_PCS_FAULT BIT(23) +-#define SGMII_AN_EXPANSION_CLR BIT(30) +- +-#define SGMSYS_PCS_ADVERTISE 0x8 +-#define SGMII_ADVERTISE GENMASK(15, 0) +-#define SGMII_LPA GENMASK(31, 16) +- +-/* Register to programmable link timer, the unit in 2 * 8ns */ +-#define SGMSYS_PCS_LINK_TIMER 0x18 +-#define SGMII_LINK_TIMER_MASK GENMASK(19, 0) +-#define SGMII_LINK_TIMER_DEFAULT (0x186a0 & SGMII_LINK_TIMER_MASK) +- +-/* Register to control remote fault */ +-#define SGMSYS_SGMII_MODE 0x20 +-#define SGMII_IF_MODE_SGMII BIT(0) +-#define SGMII_SPEED_DUPLEX_AN BIT(1) +-#define SGMII_SPEED_MASK GENMASK(3, 2) +-#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0) +-#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1) +-#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2) +-#define SGMII_DUPLEX_HALF BIT(4) +-#define SGMII_IF_MODE_BIT5 BIT(5) +-#define SGMII_REMOTE_FAULT_DIS BIT(8) +-#define SGMII_CODE_SYNC_SET_VAL BIT(9) +-#define SGMII_CODE_SYNC_SET_EN BIT(10) +-#define SGMII_SEND_AN_ERROR_EN BIT(11) +-#define SGMII_IF_MODE_MASK GENMASK(5, 1) +- +-/* Register to reset SGMII design */ +-#define SGMII_RESERVED_0 0x34 +-#define SGMII_SW_RESET BIT(0) +- +-/* Register to set SGMII speed, ANA RG_ Control Signals III*/ +-#define SGMSYS_ANA_RG_CS3 0x2028 +-#define RG_PHY_SPEED_MASK (BIT(2) | BIT(3)) +-#define RG_PHY_SPEED_1_25G 0x0 +-#define RG_PHY_SPEED_3_125G BIT(2) +- +-/* Register to power up QPHY */ +-#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8 +-#define SGMII_PHYA_PWD BIT(4) +- +-/* Register to QPHY wrapper control */ +-#define SGMSYS_QPHY_WRAP_CTRL 0xec +-#define SGMII_PN_SWAP_MASK GENMASK(1, 0) +-#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1)) +-#define MTK_SGMII_FLAG_PN_SWAP BIT(0) +- + /* Infrasys subsystem config registers */ + #define INFRA_MISC2 0x70c + #define CO_QPHY_SEL BIT(0) +@@ -1105,31 +1046,6 @@ struct mtk_soc_data { + /* currently no SoC has more than 2 macs */ + #define MTK_MAX_DEVS 2 + +-/* struct mtk_pcs - This structure holds each sgmii regmap and associated +- * data +- * @regmap: The register map pointing at the range used to setup +- * SGMII modes +- * @ana_rgc3: The offset refers to register ANA_RGC3 related to regmap +- * @interface: Currently configured interface mode +- * @pcs: Phylink PCS structure +- * @flags: Flags indicating hardware properties +- */ +-struct mtk_pcs { +- struct regmap *regmap; +- u32 ana_rgc3; +- phy_interface_t interface; +- struct phylink_pcs pcs; +- u32 flags; +-}; +- +-/* struct mtk_sgmii - This is the structure holding sgmii regmap and its +- * characteristics +- * @pcs Array of individual PCS structures +- */ +-struct mtk_sgmii { +- struct mtk_pcs pcs[MTK_MAX_DEVS]; +-}; +- + /* struct mtk_eth - This is the main datasructure for holding the state + * of the driver + * @dev: The device pointer +@@ -1149,6 +1065,7 @@ struct mtk_sgmii { + * MII modes + * @infra: The register map pointing at the range used to setup + * SGMII and GePHY path ++ * @sgmii_pcs: Pointers to mtk-pcs-lynxi phylink_pcs instances + * @pctl: The register map pointing at the range used to setup + * GMAC port drive/slew values + * @dma_refcnt: track how many netdevs are using the DMA engine +@@ -1189,8 +1106,8 @@ struct mtk_eth { + u32 msg_enable; + unsigned long sysclk; + struct regmap *ethsys; +- struct regmap *infra; +- struct mtk_sgmii *sgmii; ++ struct regmap *infra; ++ struct phylink_pcs *sgmii_pcs[MTK_MAX_DEVS]; + struct regmap *pctl; + bool hwlro; + refcount_t dma_refcnt; +@@ -1352,10 +1269,6 @@ void mtk_stats_update_mac(struct mtk_mac + void mtk_w32(struct mtk_eth *eth, u32 val, unsigned reg); + u32 mtk_r32(struct mtk_eth *eth, unsigned reg); + +-struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id); +-int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *np, +- u32 ana_rgc3); +- + int mtk_gmac_sgmii_path_setup(struct mtk_eth *eth, int mac_id); + int mtk_gmac_gephy_path_setup(struct mtk_eth *eth, int mac_id); + int mtk_gmac_rgmii_path_setup(struct mtk_eth *eth, int mac_id); +--- a/drivers/net/ethernet/mediatek/mtk_sgmii.c ++++ /dev/null +@@ -1,217 +0,0 @@ +-// SPDX-License-Identifier: GPL-2.0 +-// Copyright (c) 2018-2019 MediaTek Inc. +- +-/* A library for MediaTek SGMII circuit +- * +- * Author: Sean Wang +- * +- */ +- +-#include +-#include +-#include +-#include +- +-#include "mtk_eth_soc.h" +- +-static struct mtk_pcs *pcs_to_mtk_pcs(struct phylink_pcs *pcs) +-{ +- return container_of(pcs, struct mtk_pcs, pcs); +-} +- +-static void mtk_pcs_get_state(struct phylink_pcs *pcs, +- struct phylink_link_state *state) +-{ +- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); +- unsigned int bm, adv; +- +- /* Read the BMSR and LPA */ +- regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); +- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); +- +- phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), +- FIELD_GET(SGMII_LPA, adv)); +-} +- +-static int mtk_pcs_config(struct phylink_pcs *pcs, unsigned int mode, +- phy_interface_t interface, +- const unsigned long *advertising, +- bool permit_pause_to_mac) +-{ +- bool mode_changed = false, changed, use_an; +- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); +- unsigned int rgc3, sgm_mode, bmcr; +- int advertise, link_timer; +- +- advertise = phylink_mii_c22_pcs_encode_advertisement(interface, +- advertising); +- if (advertise < 0) +- return advertise; +- +- /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and +- * we assume that fixes it's speed at bitrate = line rate (in +- * other words, 1000Mbps or 2500Mbps). +- */ +- if (interface == PHY_INTERFACE_MODE_SGMII) { +- sgm_mode = SGMII_IF_MODE_SGMII; +- if (phylink_autoneg_inband(mode)) { +- sgm_mode |= SGMII_REMOTE_FAULT_DIS | +- SGMII_SPEED_DUPLEX_AN; +- use_an = true; +- } else { +- use_an = false; +- } +- } else if (phylink_autoneg_inband(mode)) { +- /* 1000base-X or 2500base-X autoneg */ +- sgm_mode = SGMII_REMOTE_FAULT_DIS; +- use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, +- advertising); +- } else { +- /* 1000base-X or 2500base-X without autoneg */ +- sgm_mode = 0; +- use_an = false; +- } +- +- if (use_an) { +- bmcr = SGMII_AN_ENABLE; +- } else { +- bmcr = 0; +- } +- +- if (mpcs->interface != interface) { +- link_timer = phylink_get_link_timer_ns(interface); +- if (link_timer < 0) +- return link_timer; +- +- /* PHYA power down */ +- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, +- SGMII_PHYA_PWD, SGMII_PHYA_PWD); +- +- if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP) +- regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL, +- SGMII_PN_SWAP_MASK, +- SGMII_PN_SWAP_TX_RX); +- +- /* Reset SGMII PCS state */ +- regmap_update_bits(mpcs->regmap, SGMII_RESERVED_0, +- SGMII_SW_RESET, SGMII_SW_RESET); +- +- if (interface == PHY_INTERFACE_MODE_2500BASEX) +- rgc3 = RG_PHY_SPEED_3_125G; +- else +- rgc3 = 0; +- +- /* Configure the underlying interface speed */ +- regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3, +- RG_PHY_SPEED_3_125G, rgc3); +- +- /* Setup the link timer */ +- regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, link_timer / 2 / 8); +- +- mpcs->interface = interface; +- mode_changed = true; +- } +- +- /* Update the advertisement, noting whether it has changed */ +- regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE, +- SGMII_ADVERTISE, advertise, &changed); +- +- /* Update the sgmsys mode register */ +- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, +- SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN | +- SGMII_IF_MODE_SGMII, sgm_mode); +- +- /* Update the BMCR */ +- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, +- SGMII_AN_ENABLE, bmcr); +- +- /* Release PHYA power down state +- * Only removing bit SGMII_PHYA_PWD isn't enough. +- * There are cases when the SGMII_PHYA_PWD register contains 0x9 which +- * prevents SGMII from working. The SGMII still shows link but no traffic +- * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was +- * taken from a good working state of the SGMII interface. +- * Unknown how much the QPHY needs but it is racy without a sleep. +- * Tested on mt7622 & mt7986. +- */ +- usleep_range(50, 100); +- regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0); +- +- return changed || mode_changed; +-} +- +-static void mtk_pcs_restart_an(struct phylink_pcs *pcs) +-{ +- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); +- +- regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, +- SGMII_AN_RESTART, SGMII_AN_RESTART); +-} +- +-static void mtk_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, +- phy_interface_t interface, int speed, int duplex) +-{ +- struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); +- unsigned int sgm_mode; +- +- if (!phylink_autoneg_inband(mode)) { +- /* Force the speed and duplex setting */ +- if (speed == SPEED_10) +- sgm_mode = SGMII_SPEED_10; +- else if (speed == SPEED_100) +- sgm_mode = SGMII_SPEED_100; +- else +- sgm_mode = SGMII_SPEED_1000; +- +- if (duplex != DUPLEX_FULL) +- sgm_mode |= SGMII_DUPLEX_HALF; +- +- regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE, +- SGMII_DUPLEX_HALF | SGMII_SPEED_MASK, +- sgm_mode); +- } +-} +- +-static const struct phylink_pcs_ops mtk_pcs_ops = { +- .pcs_get_state = mtk_pcs_get_state, +- .pcs_config = mtk_pcs_config, +- .pcs_an_restart = mtk_pcs_restart_an, +- .pcs_link_up = mtk_pcs_link_up, +-}; +- +-int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) +-{ +- struct device_node *np; +- int i; +- +- for (i = 0; i < MTK_MAX_DEVS; i++) { +- np = of_parse_phandle(r, "mediatek,sgmiisys", i); +- if (!np) +- break; +- +- ss->pcs[i].ana_rgc3 = ana_rgc3; +- ss->pcs[i].regmap = syscon_node_to_regmap(np); +- +- ss->pcs[i].flags = 0; +- if (of_property_read_bool(np, "mediatek,pnswap")) +- ss->pcs[i].flags |= MTK_SGMII_FLAG_PN_SWAP; +- +- of_node_put(np); +- if (IS_ERR(ss->pcs[i].regmap)) +- return PTR_ERR(ss->pcs[i].regmap); +- +- ss->pcs[i].pcs.ops = &mtk_pcs_ops; +- ss->pcs[i].pcs.poll = true; +- ss->pcs[i].interface = PHY_INTERFACE_MODE_NA; +- } +- +- return 0; +-} +- +-struct phylink_pcs *mtk_sgmii_select_pcs(struct mtk_sgmii *ss, int id) +-{ +- if (!ss->pcs[id].regmap) +- return NULL; +- +- return &ss->pcs[id].pcs; +-} diff --git a/target/linux/generic/backport-5.15/743-v6.3-0005-net-dsa-b53-mmap-fix-device-tree-support.patch b/target/linux/generic/backport-5.15/743-v6.3-0005-net-dsa-b53-mmap-fix-device-tree-support.patch new file mode 100644 index 00000000000..8c277e6a3ef --- /dev/null +++ b/target/linux/generic/backport-5.15/743-v6.3-0005-net-dsa-b53-mmap-fix-device-tree-support.patch @@ -0,0 +1,30 @@ +From 30796d0dcb6e41c6558a07950f2ce60c209da867 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Thu, 16 Mar 2023 18:28:07 +0100 +Subject: [PATCH] net: dsa: b53: mmap: fix device tree support +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +CPU port should also be enabled in order to get a working switch. + +Fixes: a5538a777b73 ("net: dsa: b53: mmap: Add device tree support") +Signed-off-by: Álvaro Fernández Rojas +Acked-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230316172807.460146-1-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + drivers/net/dsa/b53/b53_mmap.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/dsa/b53/b53_mmap.c ++++ b/drivers/net/dsa/b53/b53_mmap.c +@@ -263,7 +263,7 @@ static int b53_mmap_probe_of(struct plat + if (of_property_read_u32(of_port, "reg", ®)) + continue; + +- if (reg < B53_CPU_PORT) ++ if (reg < B53_N_PORTS) + pdata->enabled_ports |= BIT(reg); + } + diff --git a/target/linux/generic/backport-5.15/743-v6.3-0006-net-dsa-tag_brcm-legacy-fix-daisy-chained-switches.patch b/target/linux/generic/backport-5.15/743-v6.3-0006-net-dsa-tag_brcm-legacy-fix-daisy-chained-switches.patch new file mode 100644 index 00000000000..fdefa9ffb69 --- /dev/null +++ b/target/linux/generic/backport-5.15/743-v6.3-0006-net-dsa-tag_brcm-legacy-fix-daisy-chained-switches.patch @@ -0,0 +1,65 @@ +From 032a954061afd4b7426c3eb6bfd2952ef1e9a384 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= +Date: Sun, 19 Mar 2023 10:55:40 +0100 +Subject: [PATCH] net: dsa: tag_brcm: legacy: fix daisy-chained switches +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +When BCM63xx internal switches are connected to switches with a 4-byte +Broadcom tag, it does not identify the packet as VLAN tagged, so it adds one +based on its PVID (which is likely 0). +Right now, the packet is received by the BCM63xx internal switch and the 6-byte +tag is properly processed. The next step would to decode the corresponding +4-byte tag. However, the internal switch adds an invalid VLAN tag after the +6-byte tag and the 4-byte tag handling fails. +In order to fix this we need to remove the invalid VLAN tag after the 6-byte +tag before passing it to the 4-byte tag decoding. + +Fixes: 964dbf186eaa ("net: dsa: tag_brcm: add support for legacy tags") +Signed-off-by: Álvaro Fernández Rojas +Reviewed-by: Michal Swiatkowski +Reviewed-by: Florian Fainelli +Link: https://lore.kernel.org/r/20230319095540.239064-1-noltari@gmail.com +Signed-off-by: Jakub Kicinski +--- + net/dsa/tag_brcm.c | 10 ++++++++-- + 1 file changed, 8 insertions(+), 2 deletions(-) + +--- a/net/dsa/tag_brcm.c ++++ b/net/dsa/tag_brcm.c +@@ -7,6 +7,7 @@ + + #include + #include ++#include + #include + #include + +@@ -248,6 +249,7 @@ static struct sk_buff *brcm_leg_tag_xmit + static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, + struct net_device *dev) + { ++ int len = BRCM_LEG_TAG_LEN; + int source_port; + u8 *brcm_tag; + +@@ -262,12 +264,16 @@ static struct sk_buff *brcm_leg_tag_rcv( + if (!skb->dev) + return NULL; + ++ /* VLAN tag is added by BCM63xx internal switch */ ++ if (netdev_uses_dsa(skb->dev)) ++ len += VLAN_HLEN; ++ + /* Remove Broadcom tag and update checksum */ +- skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN); ++ skb_pull_rcsum(skb, len); + + dsa_default_offload_fwd_mark(skb); + +- dsa_strip_etype_header(skb, BRCM_LEG_TAG_LEN); ++ dsa_strip_etype_header(skb, len); + + return skb; + } diff --git a/target/linux/generic/backport-5.15/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch b/target/linux/generic/backport-5.15/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch index 3df9bb62108..c8a3c96f376 100644 --- a/target/linux/generic/backport-5.15/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch +++ b/target/linux/generic/backport-5.15/775-v6.0-01-net-ethernet-stmicro-stmmac-move-queue-reset-to-dedi.patch @@ -27,7 +27,7 @@ Signed-off-by: Jakub Kicinski static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue); static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue); -@@ -1711,9 +1714,6 @@ static int __init_dma_rx_desc_rings(stru +@@ -1712,9 +1715,6 @@ static int __init_dma_rx_desc_rings(stru return -ENOMEM; } @@ -37,7 +37,7 @@ Signed-off-by: Jakub Kicinski /* Setup the chained descriptor addresses */ if (priv->mode == STMMAC_CHAIN_MODE) { if (priv->extend_desc) -@@ -1819,12 +1819,6 @@ static int __init_dma_tx_desc_rings(stru +@@ -1820,12 +1820,6 @@ static int __init_dma_tx_desc_rings(stru tx_q->tx_skbuff[i] = NULL; } @@ -50,7 +50,7 @@ Signed-off-by: Jakub Kicinski return 0; } -@@ -2693,10 +2687,7 @@ static void stmmac_tx_err(struct stmmac_ +@@ -2694,10 +2688,7 @@ static void stmmac_tx_err(struct stmmac_ stmmac_stop_tx_dma(priv, chan); dma_free_tx_skbufs(priv, chan); stmmac_clear_tx_descriptors(priv, chan); @@ -62,7 +62,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, chan); stmmac_start_tx_dma(priv, chan); -@@ -3780,6 +3771,8 @@ static int stmmac_open(struct net_device +@@ -3781,6 +3772,8 @@ static int stmmac_open(struct net_device } } @@ -71,7 +71,7 @@ Signed-off-by: Jakub Kicinski ret = stmmac_hw_setup(dev, true); if (ret < 0) { netdev_err(priv->dev, "%s: Hw setup failed\n", __func__); -@@ -6423,6 +6416,7 @@ void stmmac_enable_rx_queue(struct stmma +@@ -6424,6 +6417,7 @@ void stmmac_enable_rx_queue(struct stmma return; } @@ -79,7 +79,7 @@ Signed-off-by: Jakub Kicinski stmmac_clear_rx_descriptors(priv, queue); stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, -@@ -6484,6 +6478,7 @@ void stmmac_enable_tx_queue(struct stmma +@@ -6485,6 +6479,7 @@ void stmmac_enable_tx_queue(struct stmma return; } @@ -87,7 +87,7 @@ Signed-off-by: Jakub Kicinski stmmac_clear_tx_descriptors(priv, queue); stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, -@@ -7406,6 +7401,25 @@ int stmmac_suspend(struct device *dev) +@@ -7407,6 +7402,25 @@ int stmmac_suspend(struct device *dev) } EXPORT_SYMBOL_GPL(stmmac_suspend); @@ -113,7 +113,7 @@ Signed-off-by: Jakub Kicinski /** * stmmac_reset_queues_param - reset queue parameters * @priv: device pointer -@@ -7416,22 +7430,11 @@ static void stmmac_reset_queues_param(st +@@ -7417,22 +7431,11 @@ static void stmmac_reset_queues_param(st u32 tx_cnt = priv->plat->tx_queues_to_use; u32 queue; diff --git a/target/linux/generic/backport-5.15/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch b/target/linux/generic/backport-5.15/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch index 5c033e9d524..8eca92a5c54 100644 --- a/target/linux/generic/backport-5.15/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch +++ b/target/linux/generic/backport-5.15/775-v6.0-02-net-ethernet-stmicro-stmmac-first-disable-all-queues.patch @@ -17,7 +17,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -3832,8 +3832,6 @@ static int stmmac_release(struct net_dev +@@ -3833,8 +3833,6 @@ static int stmmac_release(struct net_dev struct stmmac_priv *priv = netdev_priv(dev); u32 chan; @@ -26,7 +26,7 @@ Signed-off-by: Jakub Kicinski if (device_may_wakeup(priv->device)) phylink_speed_down(priv->phylink, false); /* Stop and disconnect the PHY */ -@@ -3845,6 +3843,8 @@ static int stmmac_release(struct net_dev +@@ -3846,6 +3844,8 @@ static int stmmac_release(struct net_dev for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) hrtimer_cancel(&priv->tx_queue[chan].txtimer); diff --git a/target/linux/generic/backport-5.15/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch b/target/linux/generic/backport-5.15/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch index a717688125c..de1add41a50 100644 --- a/target/linux/generic/backport-5.15/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch +++ b/target/linux/generic/backport-5.15/775-v6.0-03-net-ethernet-stmicro-stmmac-move-dma-conf-to-dedicat.patch @@ -189,7 +189,7 @@ Signed-off-by: Jakub Kicinski if (tx_q->dirty_tx != tx_q->cur_tx) return -EBUSY; /* still unfinished work */ -@@ -1308,7 +1308,7 @@ static void stmmac_display_rx_rings(stru +@@ -1309,7 +1309,7 @@ static void stmmac_display_rx_rings(stru /* Display RX rings */ for (queue = 0; queue < rx_cnt; queue++) { @@ -198,7 +198,7 @@ Signed-off-by: Jakub Kicinski pr_info("\tRX Queue %u rings\n", queue); -@@ -1321,7 +1321,7 @@ static void stmmac_display_rx_rings(stru +@@ -1322,7 +1322,7 @@ static void stmmac_display_rx_rings(stru } /* Display RX ring */ @@ -207,7 +207,7 @@ Signed-off-by: Jakub Kicinski rx_q->dma_rx_phy, desc_size); } } -@@ -1335,7 +1335,7 @@ static void stmmac_display_tx_rings(stru +@@ -1336,7 +1336,7 @@ static void stmmac_display_tx_rings(stru /* Display TX rings */ for (queue = 0; queue < tx_cnt; queue++) { @@ -216,7 +216,7 @@ Signed-off-by: Jakub Kicinski pr_info("\tTX Queue %d rings\n", queue); -@@ -1350,7 +1350,7 @@ static void stmmac_display_tx_rings(stru +@@ -1351,7 +1351,7 @@ static void stmmac_display_tx_rings(stru desc_size = sizeof(struct dma_desc); } @@ -225,7 +225,7 @@ Signed-off-by: Jakub Kicinski tx_q->dma_tx_phy, desc_size); } } -@@ -1391,21 +1391,21 @@ static int stmmac_set_bfsize(int mtu, in +@@ -1392,21 +1392,21 @@ static int stmmac_set_bfsize(int mtu, in */ static void stmmac_clear_rx_descriptors(struct stmmac_priv *priv, u32 queue) { @@ -253,7 +253,7 @@ Signed-off-by: Jakub Kicinski } /** -@@ -1417,12 +1417,12 @@ static void stmmac_clear_rx_descriptors( +@@ -1418,12 +1418,12 @@ static void stmmac_clear_rx_descriptors( */ static void stmmac_clear_tx_descriptors(struct stmmac_priv *priv, u32 queue) { @@ -269,7 +269,7 @@ Signed-off-by: Jakub Kicinski struct dma_desc *p; if (priv->extend_desc) -@@ -1470,7 +1470,7 @@ static void stmmac_clear_descriptors(str +@@ -1471,7 +1471,7 @@ static void stmmac_clear_descriptors(str static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p, int i, gfp_t flags, u32 queue) { @@ -278,7 +278,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; if (!buf->page) { -@@ -1495,7 +1495,7 @@ static int stmmac_init_rx_buffers(struct +@@ -1496,7 +1496,7 @@ static int stmmac_init_rx_buffers(struct buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; stmmac_set_desc_addr(priv, p, buf->addr); @@ -287,7 +287,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_desc3(priv, p); return 0; -@@ -1509,7 +1509,7 @@ static int stmmac_init_rx_buffers(struct +@@ -1510,7 +1510,7 @@ static int stmmac_init_rx_buffers(struct */ static void stmmac_free_rx_buffer(struct stmmac_priv *priv, u32 queue, int i) { @@ -296,7 +296,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; if (buf->page) -@@ -1529,7 +1529,7 @@ static void stmmac_free_rx_buffer(struct +@@ -1530,7 +1530,7 @@ static void stmmac_free_rx_buffer(struct */ static void stmmac_free_tx_buffer(struct stmmac_priv *priv, u32 queue, int i) { @@ -305,7 +305,7 @@ Signed-off-by: Jakub Kicinski if (tx_q->tx_skbuff_dma[i].buf && tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) { -@@ -1574,17 +1574,17 @@ static void dma_free_rx_skbufs(struct st +@@ -1575,17 +1575,17 @@ static void dma_free_rx_skbufs(struct st { int i; @@ -326,7 +326,7 @@ Signed-off-by: Jakub Kicinski struct dma_desc *p; int ret; -@@ -1611,10 +1611,10 @@ static int stmmac_alloc_rx_buffers(struc +@@ -1612,10 +1612,10 @@ static int stmmac_alloc_rx_buffers(struc */ static void dma_free_rx_xskbufs(struct stmmac_priv *priv, u32 queue) { @@ -339,7 +339,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; if (!buf->xdp) -@@ -1627,10 +1627,10 @@ static void dma_free_rx_xskbufs(struct s +@@ -1628,10 +1628,10 @@ static void dma_free_rx_xskbufs(struct s static int stmmac_alloc_rx_buffers_zc(struct stmmac_priv *priv, u32 queue) { @@ -352,7 +352,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_rx_buffer *buf; dma_addr_t dma_addr; struct dma_desc *p; -@@ -1673,7 +1673,7 @@ static struct xsk_buff_pool *stmmac_get_ +@@ -1674,7 +1674,7 @@ static struct xsk_buff_pool *stmmac_get_ */ static int __init_dma_rx_desc_rings(struct stmmac_priv *priv, u32 queue, gfp_t flags) { @@ -361,7 +361,7 @@ Signed-off-by: Jakub Kicinski int ret; netif_dbg(priv, probe, priv->dev, -@@ -1719,11 +1719,11 @@ static int __init_dma_rx_desc_rings(stru +@@ -1720,11 +1720,11 @@ static int __init_dma_rx_desc_rings(stru if (priv->extend_desc) stmmac_mode_init(priv, rx_q->dma_erx, rx_q->dma_rx_phy, @@ -375,7 +375,7 @@ Signed-off-by: Jakub Kicinski } return 0; -@@ -1750,7 +1750,7 @@ static int init_dma_rx_desc_rings(struct +@@ -1751,7 +1751,7 @@ static int init_dma_rx_desc_rings(struct err_init_rx_buffers: while (queue >= 0) { @@ -384,7 +384,7 @@ Signed-off-by: Jakub Kicinski if (rx_q->xsk_pool) dma_free_rx_xskbufs(priv, queue); -@@ -1779,7 +1779,7 @@ err_init_rx_buffers: +@@ -1780,7 +1780,7 @@ err_init_rx_buffers: */ static int __init_dma_tx_desc_rings(struct stmmac_priv *priv, u32 queue) { @@ -393,7 +393,7 @@ Signed-off-by: Jakub Kicinski int i; netif_dbg(priv, probe, priv->dev, -@@ -1791,16 +1791,16 @@ static int __init_dma_tx_desc_rings(stru +@@ -1792,16 +1792,16 @@ static int __init_dma_tx_desc_rings(stru if (priv->extend_desc) stmmac_mode_init(priv, tx_q->dma_etx, tx_q->dma_tx_phy, @@ -413,7 +413,7 @@ Signed-off-by: Jakub Kicinski struct dma_desc *p; if (priv->extend_desc) -@@ -1870,12 +1870,12 @@ static int init_dma_desc_rings(struct ne +@@ -1871,12 +1871,12 @@ static int init_dma_desc_rings(struct ne */ static void dma_free_tx_skbufs(struct stmmac_priv *priv, u32 queue) { @@ -428,7 +428,7 @@ Signed-off-by: Jakub Kicinski stmmac_free_tx_buffer(priv, queue, i); if (tx_q->xsk_pool && tx_q->xsk_frames_done) { -@@ -1905,7 +1905,7 @@ static void stmmac_free_tx_skbufs(struct +@@ -1906,7 +1906,7 @@ static void stmmac_free_tx_skbufs(struct */ static void __free_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) { @@ -437,7 +437,7 @@ Signed-off-by: Jakub Kicinski /* Release the DMA RX socket buffers */ if (rx_q->xsk_pool) -@@ -1918,11 +1918,11 @@ static void __free_dma_rx_desc_resources +@@ -1919,11 +1919,11 @@ static void __free_dma_rx_desc_resources /* Free DMA regions of consistent memory previously allocated */ if (!priv->extend_desc) @@ -451,7 +451,7 @@ Signed-off-by: Jakub Kicinski sizeof(struct dma_extended_desc), rx_q->dma_erx, rx_q->dma_rx_phy); -@@ -1951,7 +1951,7 @@ static void free_dma_rx_desc_resources(s +@@ -1952,7 +1952,7 @@ static void free_dma_rx_desc_resources(s */ static void __free_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) { @@ -460,7 +460,7 @@ Signed-off-by: Jakub Kicinski size_t size; void *addr; -@@ -1969,7 +1969,7 @@ static void __free_dma_tx_desc_resources +@@ -1970,7 +1970,7 @@ static void __free_dma_tx_desc_resources addr = tx_q->dma_tx; } @@ -469,7 +469,7 @@ Signed-off-by: Jakub Kicinski dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); -@@ -1998,7 +1998,7 @@ static void free_dma_tx_desc_resources(s +@@ -1999,7 +1999,7 @@ static void free_dma_tx_desc_resources(s */ static int __alloc_dma_rx_desc_resources(struct stmmac_priv *priv, u32 queue) { @@ -478,7 +478,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_channel *ch = &priv->channel[queue]; bool xdp_prog = stmmac_xdp_is_enabled(priv); struct page_pool_params pp_params = { 0 }; -@@ -2010,8 +2010,8 @@ static int __alloc_dma_rx_desc_resources +@@ -2011,8 +2011,8 @@ static int __alloc_dma_rx_desc_resources rx_q->priv_data = priv; pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; @@ -489,7 +489,7 @@ Signed-off-by: Jakub Kicinski pp_params.order = ilog2(num_pages); pp_params.nid = dev_to_node(priv->device); pp_params.dev = priv->device; -@@ -2026,7 +2026,7 @@ static int __alloc_dma_rx_desc_resources +@@ -2027,7 +2027,7 @@ static int __alloc_dma_rx_desc_resources return ret; } @@ -498,7 +498,7 @@ Signed-off-by: Jakub Kicinski sizeof(*rx_q->buf_pool), GFP_KERNEL); if (!rx_q->buf_pool) -@@ -2034,7 +2034,7 @@ static int __alloc_dma_rx_desc_resources +@@ -2035,7 +2035,7 @@ static int __alloc_dma_rx_desc_resources if (priv->extend_desc) { rx_q->dma_erx = dma_alloc_coherent(priv->device, @@ -507,7 +507,7 @@ Signed-off-by: Jakub Kicinski sizeof(struct dma_extended_desc), &rx_q->dma_rx_phy, GFP_KERNEL); -@@ -2043,7 +2043,7 @@ static int __alloc_dma_rx_desc_resources +@@ -2044,7 +2044,7 @@ static int __alloc_dma_rx_desc_resources } else { rx_q->dma_rx = dma_alloc_coherent(priv->device, @@ -516,7 +516,7 @@ Signed-off-by: Jakub Kicinski sizeof(struct dma_desc), &rx_q->dma_rx_phy, GFP_KERNEL); -@@ -2100,20 +2100,20 @@ err_dma: +@@ -2101,20 +2101,20 @@ err_dma: */ static int __alloc_dma_tx_desc_resources(struct stmmac_priv *priv, u32 queue) { @@ -540,7 +540,7 @@ Signed-off-by: Jakub Kicinski sizeof(struct sk_buff *), GFP_KERNEL); if (!tx_q->tx_skbuff) -@@ -2126,7 +2126,7 @@ static int __alloc_dma_tx_desc_resources +@@ -2127,7 +2127,7 @@ static int __alloc_dma_tx_desc_resources else size = sizeof(struct dma_desc); @@ -549,7 +549,7 @@ Signed-off-by: Jakub Kicinski addr = dma_alloc_coherent(priv->device, size, &tx_q->dma_tx_phy, GFP_KERNEL); -@@ -2370,7 +2370,7 @@ static void stmmac_dma_operation_mode(st +@@ -2371,7 +2371,7 @@ static void stmmac_dma_operation_mode(st /* configure all channels */ for (chan = 0; chan < rx_channels_count; chan++) { @@ -558,7 +558,7 @@ Signed-off-by: Jakub Kicinski u32 buf_size; qmode = priv->plat->rx_queues_cfg[chan].mode_to_use; -@@ -2385,7 +2385,7 @@ static void stmmac_dma_operation_mode(st +@@ -2386,7 +2386,7 @@ static void stmmac_dma_operation_mode(st chan); } else { stmmac_set_dma_bfsize(priv, priv->ioaddr, @@ -567,7 +567,7 @@ Signed-off-by: Jakub Kicinski chan); } } -@@ -2401,7 +2401,7 @@ static void stmmac_dma_operation_mode(st +@@ -2402,7 +2402,7 @@ static void stmmac_dma_operation_mode(st static bool stmmac_xdp_xmit_zc(struct stmmac_priv *priv, u32 queue, u32 budget) { struct netdev_queue *nq = netdev_get_tx_queue(priv->dev, queue); @@ -576,7 +576,7 @@ Signed-off-by: Jakub Kicinski struct xsk_buff_pool *pool = tx_q->xsk_pool; unsigned int entry = tx_q->cur_tx; struct dma_desc *tx_desc = NULL; -@@ -2476,7 +2476,7 @@ static bool stmmac_xdp_xmit_zc(struct st +@@ -2477,7 +2477,7 @@ static bool stmmac_xdp_xmit_zc(struct st stmmac_enable_dma_transmission(priv, priv->ioaddr); @@ -585,7 +585,7 @@ Signed-off-by: Jakub Kicinski entry = tx_q->cur_tx; } -@@ -2502,7 +2502,7 @@ static bool stmmac_xdp_xmit_zc(struct st +@@ -2503,7 +2503,7 @@ static bool stmmac_xdp_xmit_zc(struct st */ static int stmmac_tx_clean(struct stmmac_priv *priv, int budget, u32 queue) { @@ -594,7 +594,7 @@ Signed-off-by: Jakub Kicinski unsigned int bytes_compl = 0, pkts_compl = 0; unsigned int entry, xmits = 0, count = 0; -@@ -2515,7 +2515,7 @@ static int stmmac_tx_clean(struct stmmac +@@ -2516,7 +2516,7 @@ static int stmmac_tx_clean(struct stmmac entry = tx_q->dirty_tx; /* Try to clean all TX complete frame in 1 shot */ @@ -603,7 +603,7 @@ Signed-off-by: Jakub Kicinski struct xdp_frame *xdpf; struct sk_buff *skb; struct dma_desc *p; -@@ -2615,7 +2615,7 @@ static int stmmac_tx_clean(struct stmmac +@@ -2616,7 +2616,7 @@ static int stmmac_tx_clean(struct stmmac stmmac_release_tx_desc(priv, p, priv->mode); @@ -612,7 +612,7 @@ Signed-off-by: Jakub Kicinski } tx_q->dirty_tx = entry; -@@ -2680,7 +2680,7 @@ static int stmmac_tx_clean(struct stmmac +@@ -2681,7 +2681,7 @@ static int stmmac_tx_clean(struct stmmac */ static void stmmac_tx_err(struct stmmac_priv *priv, u32 chan) { @@ -621,7 +621,7 @@ Signed-off-by: Jakub Kicinski netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); -@@ -2747,8 +2747,8 @@ static int stmmac_napi_check(struct stmm +@@ -2748,8 +2748,8 @@ static int stmmac_napi_check(struct stmm { int status = stmmac_dma_interrupt_status(priv, priv->ioaddr, &priv->xstats, chan, dir); @@ -632,7 +632,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_channel *ch = &priv->channel[chan]; struct napi_struct *rx_napi; struct napi_struct *tx_napi; -@@ -2924,7 +2924,7 @@ static int stmmac_init_dma_engine(struct +@@ -2925,7 +2925,7 @@ static int stmmac_init_dma_engine(struct /* DMA RX Channel Configuration */ for (chan = 0; chan < rx_channels_count; chan++) { @@ -641,7 +641,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, rx_q->dma_rx_phy, chan); -@@ -2938,7 +2938,7 @@ static int stmmac_init_dma_engine(struct +@@ -2939,7 +2939,7 @@ static int stmmac_init_dma_engine(struct /* DMA TX Channel Configuration */ for (chan = 0; chan < tx_channels_count; chan++) { @@ -650,7 +650,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, chan); -@@ -2953,7 +2953,7 @@ static int stmmac_init_dma_engine(struct +@@ -2954,7 +2954,7 @@ static int stmmac_init_dma_engine(struct static void stmmac_tx_timer_arm(struct stmmac_priv *priv, u32 queue) { @@ -659,7 +659,7 @@ Signed-off-by: Jakub Kicinski hrtimer_start(&tx_q->txtimer, STMMAC_COAL_TIMER(priv->tx_coal_timer[queue]), -@@ -3003,7 +3003,7 @@ static void stmmac_init_coalesce(struct +@@ -3004,7 +3004,7 @@ static void stmmac_init_coalesce(struct u32 chan; for (chan = 0; chan < tx_channel_count; chan++) { @@ -668,7 +668,7 @@ Signed-off-by: Jakub Kicinski priv->tx_coal_frames[chan] = STMMAC_TX_FRAMES; priv->tx_coal_timer[chan] = STMMAC_COAL_TX_TIMER; -@@ -3025,12 +3025,12 @@ static void stmmac_set_rings_length(stru +@@ -3026,12 +3026,12 @@ static void stmmac_set_rings_length(stru /* set TX ring length */ for (chan = 0; chan < tx_channels_count; chan++) stmmac_set_tx_ring_len(priv, priv->ioaddr, @@ -683,7 +683,7 @@ Signed-off-by: Jakub Kicinski } /** -@@ -3365,7 +3365,7 @@ static int stmmac_hw_setup(struct net_de +@@ -3366,7 +3366,7 @@ static int stmmac_hw_setup(struct net_de /* Enable TSO */ if (priv->tso) { for (chan = 0; chan < tx_cnt; chan++) { @@ -692,7 +692,7 @@ Signed-off-by: Jakub Kicinski /* TSO and TBS cannot co-exist */ if (tx_q->tbs & STMMAC_TBS_AVAIL) -@@ -3387,7 +3387,7 @@ static int stmmac_hw_setup(struct net_de +@@ -3388,7 +3388,7 @@ static int stmmac_hw_setup(struct net_de /* TBS */ for (chan = 0; chan < tx_cnt; chan++) { @@ -701,7 +701,7 @@ Signed-off-by: Jakub Kicinski int enable = tx_q->tbs & STMMAC_TBS_AVAIL; stmmac_enable_tbs(priv, priv->ioaddr, enable, chan); -@@ -3431,7 +3431,7 @@ static void stmmac_free_irq(struct net_d +@@ -3432,7 +3432,7 @@ static void stmmac_free_irq(struct net_d for (j = irq_idx - 1; j >= 0; j--) { if (priv->tx_irq[j] > 0) { irq_set_affinity_hint(priv->tx_irq[j], NULL); @@ -710,7 +710,7 @@ Signed-off-by: Jakub Kicinski } } irq_idx = priv->plat->rx_queues_to_use; -@@ -3440,7 +3440,7 @@ static void stmmac_free_irq(struct net_d +@@ -3441,7 +3441,7 @@ static void stmmac_free_irq(struct net_d for (j = irq_idx - 1; j >= 0; j--) { if (priv->rx_irq[j] > 0) { irq_set_affinity_hint(priv->rx_irq[j], NULL); @@ -719,7 +719,7 @@ Signed-off-by: Jakub Kicinski } } -@@ -3573,7 +3573,7 @@ static int stmmac_request_irq_multi_msi( +@@ -3574,7 +3574,7 @@ static int stmmac_request_irq_multi_msi( sprintf(int_name, "%s:%s-%d", dev->name, "rx", i); ret = request_irq(priv->rx_irq[i], stmmac_msi_intr_rx, @@ -728,7 +728,7 @@ Signed-off-by: Jakub Kicinski if (unlikely(ret < 0)) { netdev_err(priv->dev, "%s: alloc rx-%d MSI %d (error: %d)\n", -@@ -3596,7 +3596,7 @@ static int stmmac_request_irq_multi_msi( +@@ -3597,7 +3597,7 @@ static int stmmac_request_irq_multi_msi( sprintf(int_name, "%s:%s-%d", dev->name, "tx", i); ret = request_irq(priv->tx_irq[i], stmmac_msi_intr_tx, @@ -737,7 +737,7 @@ Signed-off-by: Jakub Kicinski if (unlikely(ret < 0)) { netdev_err(priv->dev, "%s: alloc tx-%d MSI %d (error: %d)\n", -@@ -3727,21 +3727,21 @@ static int stmmac_open(struct net_device +@@ -3728,21 +3728,21 @@ static int stmmac_open(struct net_device bfsize = 0; if (bfsize < BUF_SIZE_16KiB) @@ -766,7 +766,7 @@ Signed-off-by: Jakub Kicinski int tbs_en = priv->plat->tx_queues_cfg[chan].tbs_en; /* Setup per-TXQ tbs flag before TX descriptor alloc */ -@@ -3799,7 +3799,7 @@ irq_error: +@@ -3800,7 +3800,7 @@ irq_error: phylink_stop(priv->phylink); for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) @@ -775,7 +775,7 @@ Signed-off-by: Jakub Kicinski stmmac_hw_teardown(dev); init_error: -@@ -3841,7 +3841,7 @@ static int stmmac_release(struct net_dev +@@ -3842,7 +3842,7 @@ static int stmmac_release(struct net_dev stmmac_disable_all_queues(priv); for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) @@ -784,7 +784,7 @@ Signed-off-by: Jakub Kicinski netif_tx_disable(dev); -@@ -3905,7 +3905,7 @@ static bool stmmac_vlan_insert(struct st +@@ -3906,7 +3906,7 @@ static bool stmmac_vlan_insert(struct st return false; stmmac_set_tx_owner(priv, p); @@ -793,7 +793,7 @@ Signed-off-by: Jakub Kicinski return true; } -@@ -3923,7 +3923,7 @@ static bool stmmac_vlan_insert(struct st +@@ -3924,7 +3924,7 @@ static bool stmmac_vlan_insert(struct st static void stmmac_tso_allocator(struct stmmac_priv *priv, dma_addr_t des, int total_len, bool last_segment, u32 queue) { @@ -802,7 +802,7 @@ Signed-off-by: Jakub Kicinski struct dma_desc *desc; u32 buff_size; int tmp_len; -@@ -3934,7 +3934,7 @@ static void stmmac_tso_allocator(struct +@@ -3935,7 +3935,7 @@ static void stmmac_tso_allocator(struct dma_addr_t curr_addr; tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, @@ -811,7 +811,7 @@ Signed-off-by: Jakub Kicinski WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]); if (tx_q->tbs & STMMAC_TBS_AVAIL) -@@ -3962,7 +3962,7 @@ static void stmmac_tso_allocator(struct +@@ -3963,7 +3963,7 @@ static void stmmac_tso_allocator(struct static void stmmac_flush_tx_descriptors(struct stmmac_priv *priv, int queue) { @@ -820,7 +820,7 @@ Signed-off-by: Jakub Kicinski int desc_size; if (likely(priv->extend_desc)) -@@ -4024,7 +4024,7 @@ static netdev_tx_t stmmac_tso_xmit(struc +@@ -4025,7 +4025,7 @@ static netdev_tx_t stmmac_tso_xmit(struc dma_addr_t des; int i; @@ -829,7 +829,7 @@ Signed-off-by: Jakub Kicinski first_tx = tx_q->cur_tx; /* Compute header lengths */ -@@ -4064,7 +4064,7 @@ static netdev_tx_t stmmac_tso_xmit(struc +@@ -4065,7 +4065,7 @@ static netdev_tx_t stmmac_tso_xmit(struc stmmac_set_mss(priv, mss_desc, mss); tx_q->mss = mss; tx_q->cur_tx = STMMAC_GET_ENTRY(tx_q->cur_tx, @@ -838,7 +838,7 @@ Signed-off-by: Jakub Kicinski WARN_ON(tx_q->tx_skbuff[tx_q->cur_tx]); } -@@ -4176,7 +4176,7 @@ static netdev_tx_t stmmac_tso_xmit(struc +@@ -4177,7 +4177,7 @@ static netdev_tx_t stmmac_tso_xmit(struc * ndo_start_xmit will fill this descriptor the next time it's * called and stmmac_tx_clean may clean up to this descriptor. */ @@ -847,7 +847,7 @@ Signed-off-by: Jakub Kicinski if (unlikely(stmmac_tx_avail(priv, queue) <= (MAX_SKB_FRAGS + 1))) { netif_dbg(priv, hw, priv->dev, "%s: stop transmitted packets\n", -@@ -4264,7 +4264,7 @@ static netdev_tx_t stmmac_xmit(struct sk +@@ -4265,7 +4265,7 @@ static netdev_tx_t stmmac_xmit(struct sk int entry, first_tx; dma_addr_t des; @@ -856,7 +856,7 @@ Signed-off-by: Jakub Kicinski first_tx = tx_q->cur_tx; if (priv->tx_path_in_lpi_mode && priv->eee_sw_timer_en) -@@ -4327,7 +4327,7 @@ static netdev_tx_t stmmac_xmit(struct sk +@@ -4328,7 +4328,7 @@ static netdev_tx_t stmmac_xmit(struct sk int len = skb_frag_size(frag); bool last_segment = (i == (nfrags - 1)); @@ -865,7 +865,7 @@ Signed-off-by: Jakub Kicinski WARN_ON(tx_q->tx_skbuff[entry]); if (likely(priv->extend_desc)) -@@ -4398,7 +4398,7 @@ static netdev_tx_t stmmac_xmit(struct sk +@@ -4399,7 +4399,7 @@ static netdev_tx_t stmmac_xmit(struct sk * ndo_start_xmit will fill this descriptor the next time it's * called and stmmac_tx_clean may clean up to this descriptor. */ @@ -874,7 +874,7 @@ Signed-off-by: Jakub Kicinski tx_q->cur_tx = entry; if (netif_msg_pktdata(priv)) { -@@ -4513,7 +4513,7 @@ static void stmmac_rx_vlan(struct net_de +@@ -4514,7 +4514,7 @@ static void stmmac_rx_vlan(struct net_de */ static inline void stmmac_rx_refill(struct stmmac_priv *priv, u32 queue) { @@ -883,7 +883,7 @@ Signed-off-by: Jakub Kicinski int dirty = stmmac_rx_dirty(priv, queue); unsigned int entry = rx_q->dirty_rx; -@@ -4563,7 +4563,7 @@ static inline void stmmac_rx_refill(stru +@@ -4564,7 +4564,7 @@ static inline void stmmac_rx_refill(stru dma_wmb(); stmmac_set_rx_owner(priv, p, use_rx_wd); @@ -892,7 +892,7 @@ Signed-off-by: Jakub Kicinski } rx_q->dirty_rx = entry; rx_q->rx_tail_addr = rx_q->dma_rx_phy + -@@ -4591,12 +4591,12 @@ static unsigned int stmmac_rx_buf1_len(s +@@ -4592,12 +4592,12 @@ static unsigned int stmmac_rx_buf1_len(s /* First descriptor, not last descriptor and not split header */ if (status & rx_not_ls) @@ -907,7 +907,7 @@ Signed-off-by: Jakub Kicinski } static unsigned int stmmac_rx_buf2_len(struct stmmac_priv *priv, -@@ -4612,7 +4612,7 @@ static unsigned int stmmac_rx_buf2_len(s +@@ -4613,7 +4613,7 @@ static unsigned int stmmac_rx_buf2_len(s /* Not last descriptor */ if (status & rx_not_ls) @@ -916,7 +916,7 @@ Signed-off-by: Jakub Kicinski plen = stmmac_get_rx_frame_len(priv, p, coe); -@@ -4623,7 +4623,7 @@ static unsigned int stmmac_rx_buf2_len(s +@@ -4624,7 +4624,7 @@ static unsigned int stmmac_rx_buf2_len(s static int stmmac_xdp_xmit_xdpf(struct stmmac_priv *priv, int queue, struct xdp_frame *xdpf, bool dma_map) { @@ -925,7 +925,7 @@ Signed-off-by: Jakub Kicinski unsigned int entry = tx_q->cur_tx; struct dma_desc *tx_desc; dma_addr_t dma_addr; -@@ -4686,7 +4686,7 @@ static int stmmac_xdp_xmit_xdpf(struct s +@@ -4687,7 +4687,7 @@ static int stmmac_xdp_xmit_xdpf(struct s stmmac_enable_dma_transmission(priv, priv->ioaddr); @@ -934,7 +934,7 @@ Signed-off-by: Jakub Kicinski tx_q->cur_tx = entry; return STMMAC_XDP_TX; -@@ -4860,7 +4860,7 @@ static void stmmac_dispatch_skb_zc(struc +@@ -4861,7 +4861,7 @@ static void stmmac_dispatch_skb_zc(struc static bool stmmac_rx_refill_zc(struct stmmac_priv *priv, u32 queue, u32 budget) { @@ -943,7 +943,7 @@ Signed-off-by: Jakub Kicinski unsigned int entry = rx_q->dirty_rx; struct dma_desc *rx_desc = NULL; bool ret = true; -@@ -4903,7 +4903,7 @@ static bool stmmac_rx_refill_zc(struct s +@@ -4904,7 +4904,7 @@ static bool stmmac_rx_refill_zc(struct s dma_wmb(); stmmac_set_rx_owner(priv, rx_desc, use_rx_wd); @@ -952,7 +952,7 @@ Signed-off-by: Jakub Kicinski } if (rx_desc) { -@@ -4918,7 +4918,7 @@ static bool stmmac_rx_refill_zc(struct s +@@ -4919,7 +4919,7 @@ static bool stmmac_rx_refill_zc(struct s static int stmmac_rx_zc(struct stmmac_priv *priv, int limit, u32 queue) { @@ -961,7 +961,7 @@ Signed-off-by: Jakub Kicinski unsigned int count = 0, error = 0, len = 0; int dirty = stmmac_rx_dirty(priv, queue); unsigned int next_entry = rx_q->cur_rx; -@@ -4940,7 +4940,7 @@ static int stmmac_rx_zc(struct stmmac_pr +@@ -4941,7 +4941,7 @@ static int stmmac_rx_zc(struct stmmac_pr desc_size = sizeof(struct dma_desc); } @@ -970,7 +970,7 @@ Signed-off-by: Jakub Kicinski rx_q->dma_rx_phy, desc_size); } while (count < limit) { -@@ -4987,7 +4987,7 @@ read_again: +@@ -4988,7 +4988,7 @@ read_again: /* Prefetch the next RX descriptor */ rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, @@ -979,7 +979,7 @@ Signed-off-by: Jakub Kicinski next_entry = rx_q->cur_rx; if (priv->extend_desc) -@@ -5108,7 +5108,7 @@ read_again: +@@ -5109,7 +5109,7 @@ read_again: */ static int stmmac_rx(struct stmmac_priv *priv, int limit, u32 queue) { @@ -988,7 +988,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_channel *ch = &priv->channel[queue]; unsigned int count = 0, error = 0, len = 0; int status = 0, coe = priv->hw->rx_csum; -@@ -5121,7 +5121,7 @@ static int stmmac_rx(struct stmmac_priv +@@ -5122,7 +5122,7 @@ static int stmmac_rx(struct stmmac_priv int buf_sz; dma_dir = page_pool_get_dma_dir(rx_q->page_pool); @@ -997,7 +997,7 @@ Signed-off-by: Jakub Kicinski if (netif_msg_rx_status(priv)) { void *rx_head; -@@ -5135,7 +5135,7 @@ static int stmmac_rx(struct stmmac_priv +@@ -5136,7 +5136,7 @@ static int stmmac_rx(struct stmmac_priv desc_size = sizeof(struct dma_desc); } @@ -1006,7 +1006,7 @@ Signed-off-by: Jakub Kicinski rx_q->dma_rx_phy, desc_size); } while (count < limit) { -@@ -5179,7 +5179,7 @@ read_again: +@@ -5180,7 +5180,7 @@ read_again: break; rx_q->cur_rx = STMMAC_GET_ENTRY(rx_q->cur_rx, @@ -1015,7 +1015,7 @@ Signed-off-by: Jakub Kicinski next_entry = rx_q->cur_rx; if (priv->extend_desc) -@@ -5313,7 +5313,7 @@ read_again: +@@ -5314,7 +5314,7 @@ read_again: buf1_len, dma_dir); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, buf->page, buf->page_offset, buf1_len, @@ -1024,7 +1024,7 @@ Signed-off-by: Jakub Kicinski /* Data payload appended into SKB */ page_pool_release_page(rx_q->page_pool, buf->page); -@@ -5325,7 +5325,7 @@ read_again: +@@ -5326,7 +5326,7 @@ read_again: buf2_len, dma_dir); skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, buf->sec_page, 0, buf2_len, @@ -1033,7 +1033,7 @@ Signed-off-by: Jakub Kicinski /* Data payload appended into SKB */ page_pool_release_page(rx_q->page_pool, buf->sec_page); -@@ -5767,11 +5767,13 @@ static irqreturn_t stmmac_safety_interru +@@ -5768,11 +5768,13 @@ static irqreturn_t stmmac_safety_interru static irqreturn_t stmmac_msi_intr_tx(int irq, void *data) { struct stmmac_tx_queue *tx_q = (struct stmmac_tx_queue *)data; @@ -1048,7 +1048,7 @@ Signed-off-by: Jakub Kicinski if (unlikely(!data)) { netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__); -@@ -5811,10 +5813,12 @@ static irqreturn_t stmmac_msi_intr_tx(in +@@ -5812,10 +5814,12 @@ static irqreturn_t stmmac_msi_intr_tx(in static irqreturn_t stmmac_msi_intr_rx(int irq, void *data) { struct stmmac_rx_queue *rx_q = (struct stmmac_rx_queue *)data; @@ -1062,7 +1062,7 @@ Signed-off-by: Jakub Kicinski if (unlikely(!data)) { netdev_err(priv->dev, "%s: invalid dev pointer\n", __func__); -@@ -5845,10 +5849,10 @@ static void stmmac_poll_controller(struc +@@ -5846,10 +5850,10 @@ static void stmmac_poll_controller(struc if (priv->plat->multi_msi_en) { for (i = 0; i < priv->plat->rx_queues_to_use; i++) @@ -1075,7 +1075,7 @@ Signed-off-by: Jakub Kicinski } else { disable_irq(dev->irq); stmmac_interrupt(dev->irq, dev); -@@ -6029,34 +6033,34 @@ static int stmmac_rings_status_show(stru +@@ -6030,34 +6034,34 @@ static int stmmac_rings_status_show(stru return 0; for (queue = 0; queue < rx_count; queue++) { @@ -1116,7 +1116,7 @@ Signed-off-by: Jakub Kicinski } } -@@ -6397,7 +6401,7 @@ void stmmac_disable_rx_queue(struct stmm +@@ -6398,7 +6402,7 @@ void stmmac_disable_rx_queue(struct stmm void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue) { @@ -1125,7 +1125,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_channel *ch = &priv->channel[queue]; unsigned long flags; u32 buf_size; -@@ -6434,7 +6438,7 @@ void stmmac_enable_rx_queue(struct stmma +@@ -6435,7 +6439,7 @@ void stmmac_enable_rx_queue(struct stmma rx_q->queue_index); } else { stmmac_set_dma_bfsize(priv, priv->ioaddr, @@ -1134,7 +1134,7 @@ Signed-off-by: Jakub Kicinski rx_q->queue_index); } -@@ -6460,7 +6464,7 @@ void stmmac_disable_tx_queue(struct stmm +@@ -6461,7 +6465,7 @@ void stmmac_disable_tx_queue(struct stmm void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) { @@ -1143,7 +1143,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_channel *ch = &priv->channel[queue]; unsigned long flags; int ret; -@@ -6510,7 +6514,7 @@ void stmmac_xdp_release(struct net_devic +@@ -6511,7 +6515,7 @@ void stmmac_xdp_release(struct net_devic stmmac_disable_all_queues(priv); for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) @@ -1152,7 +1152,7 @@ Signed-off-by: Jakub Kicinski /* Free the IRQ lines */ stmmac_free_irq(dev, REQ_IRQ_ERR_ALL, 0); -@@ -6569,7 +6573,7 @@ int stmmac_xdp_open(struct net_device *d +@@ -6570,7 +6574,7 @@ int stmmac_xdp_open(struct net_device *d /* DMA RX Channel Configuration */ for (chan = 0; chan < rx_cnt; chan++) { @@ -1161,7 +1161,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, rx_q->dma_rx_phy, chan); -@@ -6587,7 +6591,7 @@ int stmmac_xdp_open(struct net_device *d +@@ -6588,7 +6592,7 @@ int stmmac_xdp_open(struct net_device *d rx_q->queue_index); } else { stmmac_set_dma_bfsize(priv, priv->ioaddr, @@ -1170,7 +1170,7 @@ Signed-off-by: Jakub Kicinski rx_q->queue_index); } -@@ -6596,7 +6600,7 @@ int stmmac_xdp_open(struct net_device *d +@@ -6597,7 +6601,7 @@ int stmmac_xdp_open(struct net_device *d /* DMA TX Channel Configuration */ for (chan = 0; chan < tx_cnt; chan++) { @@ -1179,7 +1179,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, chan); -@@ -6629,7 +6633,7 @@ int stmmac_xdp_open(struct net_device *d +@@ -6630,7 +6634,7 @@ int stmmac_xdp_open(struct net_device *d irq_error: for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) @@ -1188,7 +1188,7 @@ Signed-off-by: Jakub Kicinski stmmac_hw_teardown(dev); init_error: -@@ -6656,8 +6660,8 @@ int stmmac_xsk_wakeup(struct net_device +@@ -6657,8 +6661,8 @@ int stmmac_xsk_wakeup(struct net_device queue >= priv->plat->tx_queues_to_use) return -EINVAL; @@ -1199,7 +1199,7 @@ Signed-off-by: Jakub Kicinski ch = &priv->channel[queue]; if (!rx_q->xsk_pool && !tx_q->xsk_pool) -@@ -6913,8 +6917,8 @@ int stmmac_reinit_ringparam(struct net_d +@@ -6914,8 +6918,8 @@ int stmmac_reinit_ringparam(struct net_d if (netif_running(dev)) stmmac_release(dev); @@ -1210,7 +1210,7 @@ Signed-off-by: Jakub Kicinski if (netif_running(dev)) ret = stmmac_open(dev); -@@ -7352,7 +7356,7 @@ int stmmac_suspend(struct device *dev) +@@ -7353,7 +7357,7 @@ int stmmac_suspend(struct device *dev) stmmac_disable_all_queues(priv); for (chan = 0; chan < priv->plat->tx_queues_to_use; chan++) @@ -1219,7 +1219,7 @@ Signed-off-by: Jakub Kicinski if (priv->eee_enabled) { priv->tx_path_in_lpi_mode = false; -@@ -7403,7 +7407,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend); +@@ -7404,7 +7408,7 @@ EXPORT_SYMBOL_GPL(stmmac_suspend); static void stmmac_reset_rx_queue(struct stmmac_priv *priv, u32 queue) { @@ -1228,7 +1228,7 @@ Signed-off-by: Jakub Kicinski rx_q->cur_rx = 0; rx_q->dirty_rx = 0; -@@ -7411,7 +7415,7 @@ static void stmmac_reset_rx_queue(struct +@@ -7412,7 +7416,7 @@ static void stmmac_reset_rx_queue(struct static void stmmac_reset_tx_queue(struct stmmac_priv *priv, u32 queue) { diff --git a/target/linux/generic/backport-5.15/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch b/target/linux/generic/backport-5.15/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch index 8ad6f208cbd..a8f7bdb7d15 100644 --- a/target/linux/generic/backport-5.15/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch +++ b/target/linux/generic/backport-5.15/775-v6.0-04-net-ethernet-stmicro-stmmac-generate-stmmac-dma-conf.patch @@ -17,7 +17,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -1299,7 +1299,8 @@ static int stmmac_phy_setup(struct stmma +@@ -1300,7 +1300,8 @@ static int stmmac_phy_setup(struct stmma return 0; } @@ -27,7 +27,7 @@ Signed-off-by: Jakub Kicinski { u32 rx_cnt = priv->plat->rx_queues_to_use; unsigned int desc_size; -@@ -1308,7 +1309,7 @@ static void stmmac_display_rx_rings(stru +@@ -1309,7 +1310,7 @@ static void stmmac_display_rx_rings(stru /* Display RX rings */ for (queue = 0; queue < rx_cnt; queue++) { @@ -36,7 +36,7 @@ Signed-off-by: Jakub Kicinski pr_info("\tRX Queue %u rings\n", queue); -@@ -1321,12 +1322,13 @@ static void stmmac_display_rx_rings(stru +@@ -1322,12 +1323,13 @@ static void stmmac_display_rx_rings(stru } /* Display RX ring */ @@ -52,7 +52,7 @@ Signed-off-by: Jakub Kicinski { u32 tx_cnt = priv->plat->tx_queues_to_use; unsigned int desc_size; -@@ -1335,7 +1337,7 @@ static void stmmac_display_tx_rings(stru +@@ -1336,7 +1338,7 @@ static void stmmac_display_tx_rings(stru /* Display TX rings */ for (queue = 0; queue < tx_cnt; queue++) { @@ -61,7 +61,7 @@ Signed-off-by: Jakub Kicinski pr_info("\tTX Queue %d rings\n", queue); -@@ -1350,18 +1352,19 @@ static void stmmac_display_tx_rings(stru +@@ -1351,18 +1353,19 @@ static void stmmac_display_tx_rings(stru desc_size = sizeof(struct dma_desc); } @@ -85,7 +85,7 @@ Signed-off-by: Jakub Kicinski } static int stmmac_set_bfsize(int mtu, int bufsize) -@@ -1385,44 +1388,50 @@ static int stmmac_set_bfsize(int mtu, in +@@ -1386,44 +1389,50 @@ static int stmmac_set_bfsize(int mtu, in /** * stmmac_clear_rx_descriptors - clear RX descriptors * @priv: driver private structure @@ -147,7 +147,7 @@ Signed-off-by: Jakub Kicinski struct dma_desc *p; if (priv->extend_desc) -@@ -1439,10 +1448,12 @@ static void stmmac_clear_tx_descriptors( +@@ -1440,10 +1449,12 @@ static void stmmac_clear_tx_descriptors( /** * stmmac_clear_descriptors - clear descriptors * @priv: driver private structure @@ -161,7 +161,7 @@ Signed-off-by: Jakub Kicinski { u32 rx_queue_cnt = priv->plat->rx_queues_to_use; u32 tx_queue_cnt = priv->plat->tx_queues_to_use; -@@ -1450,16 +1461,17 @@ static void stmmac_clear_descriptors(str +@@ -1451,16 +1462,17 @@ static void stmmac_clear_descriptors(str /* Clear the RX descriptors */ for (queue = 0; queue < rx_queue_cnt; queue++) @@ -181,7 +181,7 @@ Signed-off-by: Jakub Kicinski * @p: descriptor pointer * @i: descriptor index * @flags: gfp flag -@@ -1467,10 +1479,12 @@ static void stmmac_clear_descriptors(str +@@ -1468,10 +1480,12 @@ static void stmmac_clear_descriptors(str * Description: this function is called to allocate a receive buffer, perform * the DMA mapping and init the descriptor. */ @@ -196,7 +196,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; if (!buf->page) { -@@ -1495,7 +1509,7 @@ static int stmmac_init_rx_buffers(struct +@@ -1496,7 +1510,7 @@ static int stmmac_init_rx_buffers(struct buf->addr = page_pool_get_dma_addr(buf->page) + buf->page_offset; stmmac_set_desc_addr(priv, p, buf->addr); @@ -205,7 +205,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_desc3(priv, p); return 0; -@@ -1504,12 +1518,13 @@ static int stmmac_init_rx_buffers(struct +@@ -1505,12 +1519,13 @@ static int stmmac_init_rx_buffers(struct /** * stmmac_free_rx_buffer - free RX dma buffers * @priv: private structure @@ -222,7 +222,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; if (buf->page) -@@ -1524,12 +1539,15 @@ static void stmmac_free_rx_buffer(struct +@@ -1525,12 +1540,15 @@ static void stmmac_free_rx_buffer(struct /** * stmmac_free_tx_buffer - free RX dma buffers * @priv: private structure @@ -240,7 +240,7 @@ Signed-off-by: Jakub Kicinski if (tx_q->tx_skbuff_dma[i].buf && tx_q->tx_skbuff_dma[i].buf_type != STMMAC_TXBUF_T_XDP_TX) { -@@ -1568,23 +1586,28 @@ static void stmmac_free_tx_buffer(struct +@@ -1569,23 +1587,28 @@ static void stmmac_free_tx_buffer(struct /** * dma_free_rx_skbufs - free RX dma buffers * @priv: private structure @@ -276,7 +276,7 @@ Signed-off-by: Jakub Kicinski struct dma_desc *p; int ret; -@@ -1593,7 +1616,7 @@ static int stmmac_alloc_rx_buffers(struc +@@ -1594,7 +1617,7 @@ static int stmmac_alloc_rx_buffers(struc else p = rx_q->dma_rx + i; @@ -285,7 +285,7 @@ Signed-off-by: Jakub Kicinski queue); if (ret) return ret; -@@ -1607,14 +1630,17 @@ static int stmmac_alloc_rx_buffers(struc +@@ -1608,14 +1631,17 @@ static int stmmac_alloc_rx_buffers(struc /** * dma_free_rx_xskbufs - free RX dma buffers from XSK pool * @priv: private structure @@ -306,7 +306,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_rx_buffer *buf = &rx_q->buf_pool[i]; if (!buf->xdp) -@@ -1625,12 +1651,14 @@ static void dma_free_rx_xskbufs(struct s +@@ -1626,12 +1652,14 @@ static void dma_free_rx_xskbufs(struct s } } @@ -324,7 +324,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_rx_buffer *buf; dma_addr_t dma_addr; struct dma_desc *p; -@@ -1665,22 +1693,25 @@ static struct xsk_buff_pool *stmmac_get_ +@@ -1666,22 +1694,25 @@ static struct xsk_buff_pool *stmmac_get_ /** * __init_dma_rx_desc_rings - init the RX descriptor ring (per queue) * @priv: driver private structure @@ -353,7 +353,7 @@ Signed-off-by: Jakub Kicinski xdp_rxq_info_unreg_mem_model(&rx_q->xdp_rxq); -@@ -1707,9 +1738,9 @@ static int __init_dma_rx_desc_rings(stru +@@ -1708,9 +1739,9 @@ static int __init_dma_rx_desc_rings(stru /* RX XDP ZC buffer pool may not be populated, e.g. * xdpsock TX-only. */ @@ -365,7 +365,7 @@ Signed-off-by: Jakub Kicinski if (ret < 0) return -ENOMEM; } -@@ -1719,17 +1750,19 @@ static int __init_dma_rx_desc_rings(stru +@@ -1720,17 +1751,19 @@ static int __init_dma_rx_desc_rings(stru if (priv->extend_desc) stmmac_mode_init(priv, rx_q->dma_erx, rx_q->dma_rx_phy, @@ -388,7 +388,7 @@ Signed-off-by: Jakub Kicinski { struct stmmac_priv *priv = netdev_priv(dev); u32 rx_count = priv->plat->rx_queues_to_use; -@@ -1741,7 +1774,7 @@ static int init_dma_rx_desc_rings(struct +@@ -1742,7 +1775,7 @@ static int init_dma_rx_desc_rings(struct "SKB addresses:\nskb\t\tskb data\tdma data\n"); for (queue = 0; queue < rx_count; queue++) { @@ -397,7 +397,7 @@ Signed-off-by: Jakub Kicinski if (ret) goto err_init_rx_buffers; } -@@ -1750,12 +1783,12 @@ static int init_dma_rx_desc_rings(struct +@@ -1751,12 +1784,12 @@ static int init_dma_rx_desc_rings(struct err_init_rx_buffers: while (queue >= 0) { @@ -413,7 +413,7 @@ Signed-off-by: Jakub Kicinski rx_q->buf_alloc_num = 0; rx_q->xsk_pool = NULL; -@@ -1772,14 +1805,17 @@ err_init_rx_buffers: +@@ -1773,14 +1806,17 @@ err_init_rx_buffers: /** * __init_dma_tx_desc_rings - init the TX descriptor ring (per queue) * @priv: driver private structure @@ -434,7 +434,7 @@ Signed-off-by: Jakub Kicinski int i; netif_dbg(priv, probe, priv->dev, -@@ -1791,16 +1827,16 @@ static int __init_dma_tx_desc_rings(stru +@@ -1792,16 +1828,16 @@ static int __init_dma_tx_desc_rings(stru if (priv->extend_desc) stmmac_mode_init(priv, tx_q->dma_etx, tx_q->dma_tx_phy, @@ -454,7 +454,7 @@ Signed-off-by: Jakub Kicinski struct dma_desc *p; if (priv->extend_desc) -@@ -1822,7 +1858,8 @@ static int __init_dma_tx_desc_rings(stru +@@ -1823,7 +1859,8 @@ static int __init_dma_tx_desc_rings(stru return 0; } @@ -464,7 +464,7 @@ Signed-off-by: Jakub Kicinski { struct stmmac_priv *priv = netdev_priv(dev); u32 tx_queue_cnt; -@@ -1831,7 +1868,7 @@ static int init_dma_tx_desc_rings(struct +@@ -1832,7 +1869,7 @@ static int init_dma_tx_desc_rings(struct tx_queue_cnt = priv->plat->tx_queues_to_use; for (queue = 0; queue < tx_queue_cnt; queue++) @@ -473,7 +473,7 @@ Signed-off-by: Jakub Kicinski return 0; } -@@ -1839,26 +1876,29 @@ static int init_dma_tx_desc_rings(struct +@@ -1840,26 +1877,29 @@ static int init_dma_tx_desc_rings(struct /** * init_dma_desc_rings - init the RX/TX descriptor rings * @dev: net device structure @@ -508,7 +508,7 @@ Signed-off-by: Jakub Kicinski return ret; } -@@ -1866,17 +1906,20 @@ static int init_dma_desc_rings(struct ne +@@ -1867,17 +1907,20 @@ static int init_dma_desc_rings(struct ne /** * dma_free_tx_skbufs - free TX dma buffers * @priv: private structure @@ -533,7 +533,7 @@ Signed-off-by: Jakub Kicinski if (tx_q->xsk_pool && tx_q->xsk_frames_done) { xsk_tx_completed(tx_q->xsk_pool, tx_q->xsk_frames_done); -@@ -1895,34 +1938,37 @@ static void stmmac_free_tx_skbufs(struct +@@ -1896,34 +1939,37 @@ static void stmmac_free_tx_skbufs(struct u32 queue; for (queue = 0; queue < tx_queue_cnt; queue++) @@ -578,7 +578,7 @@ Signed-off-by: Jakub Kicinski sizeof(struct dma_extended_desc), rx_q->dma_erx, rx_q->dma_rx_phy); -@@ -1934,29 +1980,33 @@ static void __free_dma_rx_desc_resources +@@ -1935,29 +1981,33 @@ static void __free_dma_rx_desc_resources page_pool_destroy(rx_q->page_pool); } @@ -617,7 +617,7 @@ Signed-off-by: Jakub Kicinski if (priv->extend_desc) { size = sizeof(struct dma_extended_desc); -@@ -1969,7 +2019,7 @@ static void __free_dma_tx_desc_resources +@@ -1970,7 +2020,7 @@ static void __free_dma_tx_desc_resources addr = tx_q->dma_tx; } @@ -626,7 +626,7 @@ Signed-off-by: Jakub Kicinski dma_free_coherent(priv->device, size, addr, tx_q->dma_tx_phy); -@@ -1977,28 +2027,32 @@ static void __free_dma_tx_desc_resources +@@ -1978,28 +2028,32 @@ static void __free_dma_tx_desc_resources kfree(tx_q->tx_skbuff); } @@ -663,7 +663,7 @@ Signed-off-by: Jakub Kicinski struct stmmac_channel *ch = &priv->channel[queue]; bool xdp_prog = stmmac_xdp_is_enabled(priv); struct page_pool_params pp_params = { 0 }; -@@ -2010,8 +2064,8 @@ static int __alloc_dma_rx_desc_resources +@@ -2011,8 +2065,8 @@ static int __alloc_dma_rx_desc_resources rx_q->priv_data = priv; pp_params.flags = PP_FLAG_DMA_MAP | PP_FLAG_DMA_SYNC_DEV; @@ -674,7 +674,7 @@ Signed-off-by: Jakub Kicinski pp_params.order = ilog2(num_pages); pp_params.nid = dev_to_node(priv->device); pp_params.dev = priv->device; -@@ -2026,7 +2080,7 @@ static int __alloc_dma_rx_desc_resources +@@ -2027,7 +2081,7 @@ static int __alloc_dma_rx_desc_resources return ret; } @@ -683,7 +683,7 @@ Signed-off-by: Jakub Kicinski sizeof(*rx_q->buf_pool), GFP_KERNEL); if (!rx_q->buf_pool) -@@ -2034,7 +2088,7 @@ static int __alloc_dma_rx_desc_resources +@@ -2035,7 +2089,7 @@ static int __alloc_dma_rx_desc_resources if (priv->extend_desc) { rx_q->dma_erx = dma_alloc_coherent(priv->device, @@ -692,7 +692,7 @@ Signed-off-by: Jakub Kicinski sizeof(struct dma_extended_desc), &rx_q->dma_rx_phy, GFP_KERNEL); -@@ -2043,7 +2097,7 @@ static int __alloc_dma_rx_desc_resources +@@ -2044,7 +2098,7 @@ static int __alloc_dma_rx_desc_resources } else { rx_q->dma_rx = dma_alloc_coherent(priv->device, @@ -701,7 +701,7 @@ Signed-off-by: Jakub Kicinski sizeof(struct dma_desc), &rx_q->dma_rx_phy, GFP_KERNEL); -@@ -2068,7 +2122,8 @@ static int __alloc_dma_rx_desc_resources +@@ -2069,7 +2123,8 @@ static int __alloc_dma_rx_desc_resources return 0; } @@ -711,7 +711,7 @@ Signed-off-by: Jakub Kicinski { u32 rx_count = priv->plat->rx_queues_to_use; u32 queue; -@@ -2076,7 +2131,7 @@ static int alloc_dma_rx_desc_resources(s +@@ -2077,7 +2132,7 @@ static int alloc_dma_rx_desc_resources(s /* RX queues buffers and DMA */ for (queue = 0; queue < rx_count; queue++) { @@ -720,7 +720,7 @@ Signed-off-by: Jakub Kicinski if (ret) goto err_dma; } -@@ -2084,7 +2139,7 @@ static int alloc_dma_rx_desc_resources(s +@@ -2085,7 +2140,7 @@ static int alloc_dma_rx_desc_resources(s return 0; err_dma: @@ -729,7 +729,7 @@ Signed-off-by: Jakub Kicinski return ret; } -@@ -2092,28 +2147,31 @@ err_dma: +@@ -2093,28 +2148,31 @@ err_dma: /** * __alloc_dma_tx_desc_resources - alloc TX resources (per queue). * @priv: private structure @@ -765,7 +765,7 @@ Signed-off-by: Jakub Kicinski sizeof(struct sk_buff *), GFP_KERNEL); if (!tx_q->tx_skbuff) -@@ -2126,7 +2184,7 @@ static int __alloc_dma_tx_desc_resources +@@ -2127,7 +2185,7 @@ static int __alloc_dma_tx_desc_resources else size = sizeof(struct dma_desc); @@ -774,7 +774,7 @@ Signed-off-by: Jakub Kicinski addr = dma_alloc_coherent(priv->device, size, &tx_q->dma_tx_phy, GFP_KERNEL); -@@ -2143,7 +2201,8 @@ static int __alloc_dma_tx_desc_resources +@@ -2144,7 +2202,8 @@ static int __alloc_dma_tx_desc_resources return 0; } @@ -784,7 +784,7 @@ Signed-off-by: Jakub Kicinski { u32 tx_count = priv->plat->tx_queues_to_use; u32 queue; -@@ -2151,7 +2210,7 @@ static int alloc_dma_tx_desc_resources(s +@@ -2152,7 +2211,7 @@ static int alloc_dma_tx_desc_resources(s /* TX queues buffers and DMA */ for (queue = 0; queue < tx_count; queue++) { @@ -793,7 +793,7 @@ Signed-off-by: Jakub Kicinski if (ret) goto err_dma; } -@@ -2159,27 +2218,29 @@ static int alloc_dma_tx_desc_resources(s +@@ -2160,27 +2219,29 @@ static int alloc_dma_tx_desc_resources(s return 0; err_dma: @@ -827,7 +827,7 @@ Signed-off-by: Jakub Kicinski return ret; } -@@ -2187,16 +2248,18 @@ static int alloc_dma_desc_resources(stru +@@ -2188,16 +2249,18 @@ static int alloc_dma_desc_resources(stru /** * free_dma_desc_resources - free dma desc resources * @priv: private structure @@ -849,7 +849,7 @@ Signed-off-by: Jakub Kicinski } /** -@@ -2685,8 +2748,8 @@ static void stmmac_tx_err(struct stmmac_ +@@ -2686,8 +2749,8 @@ static void stmmac_tx_err(struct stmmac_ netif_tx_stop_queue(netdev_get_tx_queue(priv->dev, chan)); stmmac_stop_tx_dma(priv, chan); @@ -860,7 +860,7 @@ Signed-off-by: Jakub Kicinski stmmac_reset_tx_queue(priv, chan); stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, chan); -@@ -3683,19 +3746,93 @@ static int stmmac_request_irq(struct net +@@ -3684,19 +3747,93 @@ static int stmmac_request_irq(struct net } /** @@ -957,7 +957,7 @@ Signed-off-by: Jakub Kicinski u32 chan; int ret; -@@ -3722,45 +3859,10 @@ static int stmmac_open(struct net_device +@@ -3723,45 +3860,10 @@ static int stmmac_open(struct net_device memset(&priv->xstats, 0, sizeof(struct stmmac_extra_stats)); priv->xstats.threshold = tc; @@ -1005,7 +1005,7 @@ Signed-off-by: Jakub Kicinski if (priv->plat->serdes_powerup) { ret = priv->plat->serdes_powerup(dev, priv->plat->bsp_priv); -@@ -3803,14 +3905,28 @@ irq_error: +@@ -3804,14 +3906,28 @@ irq_error: stmmac_hw_teardown(dev); init_error: @@ -1036,7 +1036,7 @@ Signed-off-by: Jakub Kicinski static void stmmac_fpe_stop_wq(struct stmmac_priv *priv) { set_bit(__FPE_REMOVING, &priv->fpe_task_state); -@@ -3857,7 +3973,7 @@ static int stmmac_release(struct net_dev +@@ -3858,7 +3974,7 @@ static int stmmac_release(struct net_dev stmmac_stop_all_dma(priv); /* Release and free the Rx/Tx resources */ @@ -1045,7 +1045,7 @@ Signed-off-by: Jakub Kicinski /* Disable the MAC Rx/Tx */ stmmac_mac_set(priv, priv->ioaddr, false); -@@ -6396,7 +6512,7 @@ void stmmac_disable_rx_queue(struct stmm +@@ -6397,7 +6513,7 @@ void stmmac_disable_rx_queue(struct stmm spin_unlock_irqrestore(&ch->lock, flags); stmmac_stop_rx_dma(priv, queue); @@ -1054,7 +1054,7 @@ Signed-off-by: Jakub Kicinski } void stmmac_enable_rx_queue(struct stmmac_priv *priv, u32 queue) -@@ -6407,21 +6523,21 @@ void stmmac_enable_rx_queue(struct stmma +@@ -6408,21 +6524,21 @@ void stmmac_enable_rx_queue(struct stmma u32 buf_size; int ret; @@ -1080,7 +1080,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_rx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, rx_q->dma_rx_phy, rx_q->queue_index); -@@ -6459,7 +6575,7 @@ void stmmac_disable_tx_queue(struct stmm +@@ -6460,7 +6576,7 @@ void stmmac_disable_tx_queue(struct stmm spin_unlock_irqrestore(&ch->lock, flags); stmmac_stop_tx_dma(priv, queue); @@ -1089,7 +1089,7 @@ Signed-off-by: Jakub Kicinski } void stmmac_enable_tx_queue(struct stmmac_priv *priv, u32 queue) -@@ -6469,21 +6585,21 @@ void stmmac_enable_tx_queue(struct stmma +@@ -6470,21 +6586,21 @@ void stmmac_enable_tx_queue(struct stmma unsigned long flags; int ret; @@ -1115,7 +1115,7 @@ Signed-off-by: Jakub Kicinski stmmac_init_tx_chan(priv, priv->ioaddr, priv->plat->dma_cfg, tx_q->dma_tx_phy, tx_q->queue_index); -@@ -6523,7 +6639,7 @@ void stmmac_xdp_release(struct net_devic +@@ -6524,7 +6640,7 @@ void stmmac_xdp_release(struct net_devic stmmac_stop_all_dma(priv); /* Release and free the Rx/Tx resources */ @@ -1124,7 +1124,7 @@ Signed-off-by: Jakub Kicinski /* Disable the MAC Rx/Tx */ stmmac_mac_set(priv, priv->ioaddr, false); -@@ -6548,14 +6664,14 @@ int stmmac_xdp_open(struct net_device *d +@@ -6549,14 +6665,14 @@ int stmmac_xdp_open(struct net_device *d u32 chan; int ret; @@ -1141,7 +1141,7 @@ Signed-off-by: Jakub Kicinski if (ret < 0) { netdev_err(dev, "%s: DMA descriptors initialization failed\n", __func__); -@@ -6637,7 +6753,7 @@ irq_error: +@@ -6638,7 +6754,7 @@ irq_error: stmmac_hw_teardown(dev); init_error: @@ -1150,7 +1150,7 @@ Signed-off-by: Jakub Kicinski dma_desc_error: return ret; } -@@ -7498,7 +7614,7 @@ int stmmac_resume(struct device *dev) +@@ -7499,7 +7615,7 @@ int stmmac_resume(struct device *dev) stmmac_reset_queues_param(priv); stmmac_free_tx_skbufs(priv); diff --git a/target/linux/generic/backport-5.15/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch b/target/linux/generic/backport-5.15/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch index 2576df45224..8fccc716597 100644 --- a/target/linux/generic/backport-5.15/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch +++ b/target/linux/generic/backport-5.15/775-v6.0-05-net-ethernet-stmicro-stmmac-permit-MTU-change-with-i.patch @@ -19,7 +19,7 @@ Signed-off-by: Jakub Kicinski --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c -@@ -5625,18 +5625,15 @@ static int stmmac_change_mtu(struct net_ +@@ -5626,18 +5626,15 @@ static int stmmac_change_mtu(struct net_ { struct stmmac_priv *priv = netdev_priv(dev); int txfifosz = priv->plat->tx_fifo_size; @@ -40,7 +40,7 @@ Signed-off-by: Jakub Kicinski if (stmmac_xdp_is_enabled(priv) && new_mtu > ETH_DATA_LEN) { netdev_dbg(priv->dev, "Jumbo frames not supported for XDP\n"); return -EINVAL; -@@ -5648,8 +5645,29 @@ static int stmmac_change_mtu(struct net_ +@@ -5649,8 +5646,29 @@ static int stmmac_change_mtu(struct net_ if ((txfifosz < new_mtu) || (new_mtu > BUF_SIZE_16KiB)) return -EINVAL; diff --git a/target/linux/generic/backport-5.15/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch b/target/linux/generic/backport-5.15/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch index 988dfa317de..4a7e6ef7ad2 100644 --- a/target/linux/generic/backport-5.15/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch +++ b/target/linux/generic/backport-5.15/782-v6.1-net-dsa-mt7530-add-support-for-in-band-link-status.patch @@ -19,7 +19,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2711,9 +2711,6 @@ mt7531_mac_config(struct dsa_switch *ds, +@@ -2716,9 +2716,6 @@ mt7531_mac_config(struct dsa_switch *ds, case PHY_INTERFACE_MODE_NA: case PHY_INTERFACE_MODE_1000BASEX: case PHY_INTERFACE_MODE_2500BASEX: @@ -29,7 +29,7 @@ Signed-off-by: David S. Miller return mt7531_sgmii_setup_mode_force(priv, port, interface); default: return -EINVAL; -@@ -2789,13 +2786,6 @@ unsupported: +@@ -2794,13 +2791,6 @@ unsupported: return; } @@ -43,7 +43,7 @@ Signed-off-by: David S. Miller mcr_cur = mt7530_read(priv, MT7530_PMCR_P(port)); mcr_new = mcr_cur; mcr_new &= ~PMCR_LINK_SETTINGS_MASK; -@@ -2932,6 +2922,9 @@ static void mt753x_phylink_get_caps(stru +@@ -2937,6 +2927,9 @@ static void mt753x_phylink_get_caps(stru config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | MAC_100 | MAC_1000FD; @@ -53,7 +53,7 @@ Signed-off-by: David S. Miller /* This driver does not make use of the speed, duplex, pause or the * advertisement in its mac_config, so it is safe to mark this driver * as non-legacy. -@@ -2997,6 +2990,7 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 +@@ -3002,6 +2995,7 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); state->link = !!(status & MT7531_SGMII_LINK_STATUS); @@ -61,7 +61,7 @@ Signed-off-by: David S. Miller if (state->interface == PHY_INTERFACE_MODE_SGMII && (status & MT7531_SGMII_AN_ENABLE)) { val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port)); -@@ -3027,16 +3021,44 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 +@@ -3032,16 +3026,44 @@ mt7531_sgmii_pcs_get_state_an(struct mt7 return 0; } @@ -109,7 +109,7 @@ Signed-off-by: David S. Miller } static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode, -@@ -3077,6 +3099,8 @@ mt753x_setup(struct dsa_switch *ds) +@@ -3082,6 +3104,8 @@ mt753x_setup(struct dsa_switch *ds) priv->pcs[i].pcs.ops = priv->info->pcs_ops; priv->pcs[i].priv = priv; priv->pcs[i].port = i; diff --git a/target/linux/generic/backport-5.15/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch b/target/linux/generic/backport-5.15/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch new file mode 100644 index 00000000000..f55b7aa8ea8 --- /dev/null +++ b/target/linux/generic/backport-5.15/788-v6.3-net-dsa-mt7530-use-external-PCS-driver.patch @@ -0,0 +1,514 @@ +From patchwork Thu Mar 9 10:57:44 2023 +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 8bit +X-Patchwork-Submitter: Daniel Golle +X-Patchwork-Id: 13167235 +X-Patchwork-Delegate: kuba@kernel.org +Return-Path: +Date: Thu, 9 Mar 2023 10:57:44 +0000 +From: Daniel Golle +To: netdev@vger.kernel.org, linux-mediatek@lists.infradead.org, + linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, + Russell King , + Heiner Kallweit , + Lorenzo Bianconi , + Mark Lee , + John Crispin , Felix Fietkau , + AngeloGioacchino Del Regno + , + Matthias Brugger , + DENG Qingfang , + Landen Chao , + Sean Wang , + Paolo Abeni , + Jakub Kicinski , + Eric Dumazet , + "David S. Miller" , + Vladimir Oltean , + Florian Fainelli , + Andrew Lunn , + Vladimir Oltean +Cc: =?iso-8859-1?q?Bj=F8rn?= Mork , + Frank Wunderlich , + Alexander Couzens +Subject: [PATCH net-next v13 11/16] net: dsa: mt7530: use external PCS driver +Message-ID: + <2ac2ee40d3b0e705461b50613fda6a7edfdbc4b3.1678357225.git.daniel@makrotopia.org> +References: +MIME-Version: 1.0 +Content-Disposition: inline +In-Reply-To: +Precedence: bulk +List-ID: +X-Mailing-List: netdev@vger.kernel.org +X-Patchwork-Delegate: kuba@kernel.org + +Implement regmap access wrappers, for now only to be used by the +pcs-mtk driver. +Make use of external PCS driver and drop the reduntant implementation +in mt7530.c. +As a nice side effect the SGMII registers can now also more easily be +inspected for debugging via /sys/kernel/debug/regmap. + +Reviewed-by: Russell King (Oracle) +Tested-by: Bjørn Mork +Signed-off-by: Daniel Golle +Tested-by: Frank Wunderlich +--- + drivers/net/dsa/Kconfig | 1 + + drivers/net/dsa/mt7530.c | 277 ++++++++++----------------------------- + drivers/net/dsa/mt7530.h | 47 +------ + 3 files changed, 71 insertions(+), 254 deletions(-) + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -37,6 +37,7 @@ config NET_DSA_MT7530 + tristate "MediaTek MT753x and MT7621 Ethernet switch support" + select NET_DSA_TAG_MTK + select MEDIATEK_GE_PHY ++ select PCS_MTK_LYNXI + help + This enables support for the MediaTek MT7530, MT7531, and MT7621 + Ethernet switch chips. +--- a/drivers/net/dsa/mt7530.c ++++ b/drivers/net/dsa/mt7530.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -2568,128 +2569,11 @@ static int mt7531_rgmii_setup(struct mt7 + return 0; + } + +-static void mt7531_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode, +- phy_interface_t interface, int speed, int duplex) +-{ +- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; +- int port = pcs_to_mt753x_pcs(pcs)->port; +- unsigned int val; +- +- /* For adjusting speed and duplex of SGMII force mode. */ +- if (interface != PHY_INTERFACE_MODE_SGMII || +- phylink_autoneg_inband(mode)) +- return; +- +- /* SGMII force mode setting */ +- val = mt7530_read(priv, MT7531_SGMII_MODE(port)); +- val &= ~MT7531_SGMII_IF_MODE_MASK; +- +- switch (speed) { +- case SPEED_10: +- val |= MT7531_SGMII_FORCE_SPEED_10; +- break; +- case SPEED_100: +- val |= MT7531_SGMII_FORCE_SPEED_100; +- break; +- case SPEED_1000: +- val |= MT7531_SGMII_FORCE_SPEED_1000; +- break; +- } +- +- /* MT7531 SGMII 1G force mode can only work in full duplex mode, +- * no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. +- * +- * The speed check is unnecessary as the MAC capabilities apply +- * this restriction. --rmk +- */ +- if ((speed == SPEED_10 || speed == SPEED_100) && +- duplex != DUPLEX_FULL) +- val |= MT7531_SGMII_FORCE_HALF_DUPLEX; +- +- mt7530_write(priv, MT7531_SGMII_MODE(port), val); +-} +- + static bool mt753x_is_mac_port(u32 port) + { + return (port == 5 || port == 6); + } + +-static int mt7531_sgmii_setup_mode_force(struct mt7530_priv *priv, u32 port, +- phy_interface_t interface) +-{ +- u32 val; +- +- if (!mt753x_is_mac_port(port)) +- return -EINVAL; +- +- mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port), +- MT7531_SGMII_PHYA_PWD); +- +- val = mt7530_read(priv, MT7531_PHYA_CTRL_SIGNAL3(port)); +- val &= ~MT7531_RG_TPHY_SPEED_MASK; +- /* Setup 2.5 times faster clock for 2.5Gbps data speeds with 10B/8B +- * encoding. +- */ +- val |= (interface == PHY_INTERFACE_MODE_2500BASEX) ? +- MT7531_RG_TPHY_SPEED_3_125G : MT7531_RG_TPHY_SPEED_1_25G; +- mt7530_write(priv, MT7531_PHYA_CTRL_SIGNAL3(port), val); +- +- mt7530_clear(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE); +- +- /* MT7531 SGMII 1G and 2.5G force mode can only work in full duplex +- * mode, no matter MT7531_SGMII_FORCE_HALF_DUPLEX is set or not. +- */ +- mt7530_rmw(priv, MT7531_SGMII_MODE(port), +- MT7531_SGMII_IF_MODE_MASK | MT7531_SGMII_REMOTE_FAULT_DIS, +- MT7531_SGMII_FORCE_SPEED_1000); +- +- mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0); +- +- return 0; +-} +- +-static int mt7531_sgmii_setup_mode_an(struct mt7530_priv *priv, int port, +- phy_interface_t interface) +-{ +- if (!mt753x_is_mac_port(port)) +- return -EINVAL; +- +- mt7530_set(priv, MT7531_QPHY_PWR_STATE_CTRL(port), +- MT7531_SGMII_PHYA_PWD); +- +- mt7530_rmw(priv, MT7531_PHYA_CTRL_SIGNAL3(port), +- MT7531_RG_TPHY_SPEED_MASK, MT7531_RG_TPHY_SPEED_1_25G); +- +- mt7530_set(priv, MT7531_SGMII_MODE(port), +- MT7531_SGMII_REMOTE_FAULT_DIS | +- MT7531_SGMII_SPEED_DUPLEX_AN); +- +- mt7530_rmw(priv, MT7531_PCS_SPEED_ABILITY(port), +- MT7531_SGMII_TX_CONFIG_MASK, 1); +- +- mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_ENABLE); +- +- mt7530_set(priv, MT7531_PCS_CONTROL_1(port), MT7531_SGMII_AN_RESTART); +- +- mt7530_write(priv, MT7531_QPHY_PWR_STATE_CTRL(port), 0); +- +- return 0; +-} +- +-static void mt7531_pcs_an_restart(struct phylink_pcs *pcs) +-{ +- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; +- int port = pcs_to_mt753x_pcs(pcs)->port; +- u32 val; +- +- /* Only restart AN when AN is enabled */ +- val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); +- if (val & MT7531_SGMII_AN_ENABLE) { +- val |= MT7531_SGMII_AN_RESTART; +- mt7530_write(priv, MT7531_PCS_CONTROL_1(port), val); +- } +-} +- + static int + mt7531_mac_config(struct dsa_switch *ds, int port, unsigned int mode, + phy_interface_t interface) +@@ -2712,11 +2596,11 @@ mt7531_mac_config(struct dsa_switch *ds, + phydev = dp->slave->phydev; + return mt7531_rgmii_setup(priv, port, interface, phydev); + case PHY_INTERFACE_MODE_SGMII: +- return mt7531_sgmii_setup_mode_an(priv, port, interface); + case PHY_INTERFACE_MODE_NA: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: +- return mt7531_sgmii_setup_mode_force(priv, port, interface); ++ /* handled in SGMII PCS driver */ ++ return 0; + default: + return -EINVAL; + } +@@ -2741,11 +2625,11 @@ mt753x_phylink_mac_select_pcs(struct dsa + + switch (interface) { + case PHY_INTERFACE_MODE_TRGMII: ++ return &priv->pcs[port].pcs; + case PHY_INTERFACE_MODE_SGMII: + case PHY_INTERFACE_MODE_1000BASEX: + case PHY_INTERFACE_MODE_2500BASEX: +- return &priv->pcs[port].pcs; +- ++ return priv->ports[port].sgmii_pcs; + default: + return NULL; + } +@@ -2986,86 +2870,6 @@ static void mt7530_pcs_get_state(struct + state->pause |= MLO_PAUSE_TX; + } + +-static int +-mt7531_sgmii_pcs_get_state_an(struct mt7530_priv *priv, int port, +- struct phylink_link_state *state) +-{ +- u32 status, val; +- u16 config_reg; +- +- status = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); +- state->link = !!(status & MT7531_SGMII_LINK_STATUS); +- state->an_complete = !!(status & MT7531_SGMII_AN_COMPLETE); +- if (state->interface == PHY_INTERFACE_MODE_SGMII && +- (status & MT7531_SGMII_AN_ENABLE)) { +- val = mt7530_read(priv, MT7531_PCS_SPEED_ABILITY(port)); +- config_reg = val >> 16; +- +- switch (config_reg & LPA_SGMII_SPD_MASK) { +- case LPA_SGMII_1000: +- state->speed = SPEED_1000; +- break; +- case LPA_SGMII_100: +- state->speed = SPEED_100; +- break; +- case LPA_SGMII_10: +- state->speed = SPEED_10; +- break; +- default: +- dev_err(priv->dev, "invalid sgmii PHY speed\n"); +- state->link = false; +- return -EINVAL; +- } +- +- if (config_reg & LPA_SGMII_FULL_DUPLEX) +- state->duplex = DUPLEX_FULL; +- else +- state->duplex = DUPLEX_HALF; +- } +- +- return 0; +-} +- +-static void +-mt7531_sgmii_pcs_get_state_inband(struct mt7530_priv *priv, int port, +- struct phylink_link_state *state) +-{ +- unsigned int val; +- +- val = mt7530_read(priv, MT7531_PCS_CONTROL_1(port)); +- state->link = !!(val & MT7531_SGMII_LINK_STATUS); +- if (!state->link) +- return; +- +- state->an_complete = state->link; +- +- if (state->interface == PHY_INTERFACE_MODE_2500BASEX) +- state->speed = SPEED_2500; +- else +- state->speed = SPEED_1000; +- +- state->duplex = DUPLEX_FULL; +- state->pause = MLO_PAUSE_NONE; +-} +- +-static void mt7531_pcs_get_state(struct phylink_pcs *pcs, +- struct phylink_link_state *state) +-{ +- struct mt7530_priv *priv = pcs_to_mt753x_pcs(pcs)->priv; +- int port = pcs_to_mt753x_pcs(pcs)->port; +- +- if (state->interface == PHY_INTERFACE_MODE_SGMII) { +- mt7531_sgmii_pcs_get_state_an(priv, port, state); +- return; +- } else if ((state->interface == PHY_INTERFACE_MODE_1000BASEX) || +- (state->interface == PHY_INTERFACE_MODE_2500BASEX)) { +- mt7531_sgmii_pcs_get_state_inband(priv, port, state); +- return; +- } +- +- state->link = false; +-} +- + static int mt753x_pcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, +@@ -3085,18 +2889,57 @@ static const struct phylink_pcs_ops mt75 + .pcs_an_restart = mt7530_pcs_an_restart, + }; + +-static const struct phylink_pcs_ops mt7531_pcs_ops = { +- .pcs_validate = mt753x_pcs_validate, +- .pcs_get_state = mt7531_pcs_get_state, +- .pcs_config = mt753x_pcs_config, +- .pcs_an_restart = mt7531_pcs_an_restart, +- .pcs_link_up = mt7531_pcs_link_up, ++static int mt7530_regmap_read(void *context, unsigned int reg, unsigned int *val) ++{ ++ struct mt7530_priv *priv = context; ++ ++ *val = mt7530_read(priv, reg); ++ return 0; ++}; ++ ++static int mt7530_regmap_write(void *context, unsigned int reg, unsigned int val) ++{ ++ struct mt7530_priv *priv = context; ++ ++ mt7530_write(priv, reg, val); ++ return 0; ++}; ++ ++static int mt7530_regmap_update_bits(void *context, unsigned int reg, ++ unsigned int mask, unsigned int val) ++{ ++ struct mt7530_priv *priv = context; ++ ++ mt7530_rmw(priv, reg, mask, val); ++ return 0; ++}; ++ ++static const struct regmap_bus mt7531_regmap_bus = { ++ .reg_write = mt7530_regmap_write, ++ .reg_read = mt7530_regmap_read, ++ .reg_update_bits = mt7530_regmap_update_bits, ++}; ++ ++#define MT7531_PCS_REGMAP_CONFIG(_name, _reg_base) \ ++ { \ ++ .name = _name, \ ++ .reg_bits = 16, \ ++ .val_bits = 32, \ ++ .reg_stride = 4, \ ++ .reg_base = _reg_base, \ ++ .max_register = 0x17c, \ ++ } ++ ++static const struct regmap_config mt7531_pcs_config[] = { ++ MT7531_PCS_REGMAP_CONFIG("port5", MT7531_SGMII_REG_BASE(5)), ++ MT7531_PCS_REGMAP_CONFIG("port6", MT7531_SGMII_REG_BASE(6)), + }; + + static int + mt753x_setup(struct dsa_switch *ds) + { + struct mt7530_priv *priv = ds->priv; ++ struct regmap *regmap; + int i, ret; + + /* Initialise the PCS devices */ +@@ -3104,8 +2947,6 @@ mt753x_setup(struct dsa_switch *ds) + priv->pcs[i].pcs.ops = priv->info->pcs_ops; + priv->pcs[i].priv = priv; + priv->pcs[i].port = i; +- if (mt753x_is_mac_port(i)) +- priv->pcs[i].pcs.poll = 1; + } + + ret = priv->info->sw_setup(ds); +@@ -3120,6 +2961,16 @@ mt753x_setup(struct dsa_switch *ds) + if (ret && priv->irq) + mt7530_free_irq_common(priv); + ++ if (priv->id == ID_MT7531) ++ for (i = 0; i < 2; i++) { ++ regmap = devm_regmap_init(ds->dev, ++ &mt7531_regmap_bus, priv, ++ &mt7531_pcs_config[i]); ++ priv->ports[5 + i].sgmii_pcs = ++ mtk_pcs_lynxi_create(ds->dev, regmap, ++ MT7531_PHYA_CTRL_SIGNAL3, 0); ++ } ++ + return ret; + } + +@@ -3211,7 +3062,7 @@ static const struct mt753x_info mt753x_t + }, + [ID_MT7531] = { + .id = ID_MT7531, +- .pcs_ops = &mt7531_pcs_ops, ++ .pcs_ops = &mt7530_pcs_ops, + .sw_setup = mt7531_setup, + .phy_read = mt7531_ind_phy_read, + .phy_write = mt7531_ind_phy_write, +@@ -3319,7 +3170,7 @@ static void + mt7530_remove(struct mdio_device *mdiodev) + { + struct mt7530_priv *priv = dev_get_drvdata(&mdiodev->dev); +- int ret = 0; ++ int ret = 0, i; + + if (!priv) + return; +@@ -3338,6 +3189,10 @@ mt7530_remove(struct mdio_device *mdiode + mt7530_free_irq(priv); + + dsa_unregister_switch(priv->ds); ++ ++ for (i = 0; i < 2; ++i) ++ mtk_pcs_lynxi_destroy(priv->ports[5 + i].sgmii_pcs); ++ + mutex_destroy(&priv->reg_mutex); + + dev_set_drvdata(&mdiodev->dev, NULL); +--- a/drivers/net/dsa/mt7530.h ++++ b/drivers/net/dsa/mt7530.h +@@ -364,47 +364,8 @@ enum mt7530_vlan_port_acc_frm { + CCR_TX_OCT_CNT_BAD) + + /* MT7531 SGMII register group */ +-#define MT7531_SGMII_REG_BASE 0x5000 +-#define MT7531_SGMII_REG(p, r) (MT7531_SGMII_REG_BASE + \ +- ((p) - 5) * 0x1000 + (r)) +- +-/* Register forSGMII PCS_CONTROL_1 */ +-#define MT7531_PCS_CONTROL_1(p) MT7531_SGMII_REG(p, 0x00) +-#define MT7531_SGMII_LINK_STATUS BIT(18) +-#define MT7531_SGMII_AN_ENABLE BIT(12) +-#define MT7531_SGMII_AN_RESTART BIT(9) +-#define MT7531_SGMII_AN_COMPLETE BIT(21) +- +-/* Register for SGMII PCS_SPPED_ABILITY */ +-#define MT7531_PCS_SPEED_ABILITY(p) MT7531_SGMII_REG(p, 0x08) +-#define MT7531_SGMII_TX_CONFIG_MASK GENMASK(15, 0) +-#define MT7531_SGMII_TX_CONFIG BIT(0) +- +-/* Register for SGMII_MODE */ +-#define MT7531_SGMII_MODE(p) MT7531_SGMII_REG(p, 0x20) +-#define MT7531_SGMII_REMOTE_FAULT_DIS BIT(8) +-#define MT7531_SGMII_IF_MODE_MASK GENMASK(5, 1) +-#define MT7531_SGMII_FORCE_DUPLEX BIT(4) +-#define MT7531_SGMII_FORCE_SPEED_MASK GENMASK(3, 2) +-#define MT7531_SGMII_FORCE_SPEED_1000 BIT(3) +-#define MT7531_SGMII_FORCE_SPEED_100 BIT(2) +-#define MT7531_SGMII_FORCE_SPEED_10 0 +-#define MT7531_SGMII_SPEED_DUPLEX_AN BIT(1) +- +-enum mt7531_sgmii_force_duplex { +- MT7531_SGMII_FORCE_FULL_DUPLEX = 0, +- MT7531_SGMII_FORCE_HALF_DUPLEX = 0x10, +-}; +- +-/* Fields of QPHY_PWR_STATE_CTRL */ +-#define MT7531_QPHY_PWR_STATE_CTRL(p) MT7531_SGMII_REG(p, 0xe8) +-#define MT7531_SGMII_PHYA_PWD BIT(4) +- +-/* Values of SGMII SPEED */ +-#define MT7531_PHYA_CTRL_SIGNAL3(p) MT7531_SGMII_REG(p, 0x128) +-#define MT7531_RG_TPHY_SPEED_MASK (BIT(2) | BIT(3)) +-#define MT7531_RG_TPHY_SPEED_1_25G 0x0 +-#define MT7531_RG_TPHY_SPEED_3_125G BIT(2) ++#define MT7531_SGMII_REG_BASE(p) (0x5000 + ((p) - 5) * 0x1000) ++#define MT7531_PHYA_CTRL_SIGNAL3 0x128 + + /* Register for system reset */ + #define MT7530_SYS_CTRL 0x7000 +@@ -703,13 +664,13 @@ struct mt7530_fdb { + * @pm: The matrix used to show all connections with the port. + * @pvid: The VLAN specified is to be considered a PVID at ingress. Any + * untagged frames will be assigned to the related VLAN. +- * @vlan_filtering: The flags indicating whether the port that can recognize +- * VLAN-tagged frames. ++ * @sgmii_pcs: Pointer to PCS instance for SerDes ports + */ + struct mt7530_port { + bool enable; + u32 pm; + u16 pvid; ++ struct phylink_pcs *sgmii_pcs; + }; + + /* Port 5 interface select definitions */ diff --git a/target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch b/target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch index a4c1b66c9e3..2a9dc749478 100644 --- a/target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch +++ b/target/linux/generic/backport-5.15/804-v5.18-0009-nvmem-Add-driver-for-OCOTP-in-Sunplus-SP7021.patch @@ -19,7 +19,7 @@ Signed-off-by: Greg Kroah-Hartman --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -17954,6 +17954,11 @@ L: netdev@vger.kernel.org +@@ -17962,6 +17962,11 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/dlink/sundance.c diff --git a/target/linux/generic/backport-5.15/806-v6.0-0001-nvmem-microchip-otpc-add-support.patch b/target/linux/generic/backport-5.15/806-v6.0-0001-nvmem-microchip-otpc-add-support.patch index 6503c7aa3b1..cec66ec383e 100644 --- a/target/linux/generic/backport-5.15/806-v6.0-0001-nvmem-microchip-otpc-add-support.patch +++ b/target/linux/generic/backport-5.15/806-v6.0-0001-nvmem-microchip-otpc-add-support.patch @@ -57,7 +57,7 @@ Signed-off-by: Greg Kroah-Hartman --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -12354,6 +12354,14 @@ S: Supported +@@ -12362,6 +12362,14 @@ S: Supported F: Documentation/devicetree/bindings/mtd/atmel-nand.txt F: drivers/mtd/nand/raw/atmel/* diff --git a/target/linux/generic/backport-5.15/890-v6.1-mtd-spinand-winbond-fix-flash-detection.patch b/target/linux/generic/backport-5.15/890-v6.1-mtd-spinand-winbond-fix-flash-detection.patch new file mode 100644 index 00000000000..38fbc3a3d73 --- /dev/null +++ b/target/linux/generic/backport-5.15/890-v6.1-mtd-spinand-winbond-fix-flash-detection.patch @@ -0,0 +1,40 @@ +From dbf70fc204d2fbb0d8ad8f42038a60846502efda Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Mon, 10 Oct 2022 13:51:09 +0300 +Subject: [PATCH] mtd: spinand: winbond: fix flash identification + +Winbond uses 3 bytes to identify flash: vendor_id, dev_id_0, dev_id_1, +but current driver uses only first 2 bytes of it for devices +identification. As result Winbond W25N02KV flash (id_bytes: EF, AA, 22) +is identified as W25N01GV (id_bytes: EF, AA, 21). + +Fix this by adding missed identification bytes. + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: Frieder Schrempf +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20221010105110.446674-1-mikhail.kshevetskiy@iopsys.eu +--- + drivers/mtd/nand/spi/winbond.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +--- a/drivers/mtd/nand/spi/winbond.c ++++ b/drivers/mtd/nand/spi/winbond.c +@@ -76,7 +76,7 @@ static int w25m02gv_select_target(struct + + static const struct spinand_info winbond_spinand_table[] = { + SPINAND_INFO("W25M02GV", +- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab), ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 2), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, +@@ -86,7 +86,7 @@ static const struct spinand_info winbond + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL), + SPINAND_SELECT_TARGET(w25m02gv_select_target)), + SPINAND_INFO("W25N01GV", +- SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa), ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x21), + NAND_MEMORG(1, 2048, 64, 64, 1024, 20, 1, 1, 1), + NAND_ECCREQ(1, 512), + SPINAND_INFO_OP_VARIANTS(&read_cache_variants, diff --git a/target/linux/generic/backport-5.15/891-v6.1-mtd-spinand-winbond-add-W25N02KV.patch b/target/linux/generic/backport-5.15/891-v6.1-mtd-spinand-winbond-add-W25N02KV.patch new file mode 100644 index 00000000000..d75a1acc57c --- /dev/null +++ b/target/linux/generic/backport-5.15/891-v6.1-mtd-spinand-winbond-add-W25N02KV.patch @@ -0,0 +1,106 @@ +From 6154c7a583483d7b69f53bea868efdc369edd563 Mon Sep 17 00:00:00 2001 +From: Mikhail Kshevetskiy +Date: Mon, 10 Oct 2022 13:51:10 +0300 +Subject: [PATCH] mtd: spinand: winbond: add Winbond W25N02KV flash support + +Add support of Winbond W25N02KV flash + +Signed-off-by: Mikhail Kshevetskiy +Reviewed-by: Frieder Schrempf +Signed-off-by: Miquel Raynal +Link: https://lore.kernel.org/linux-mtd/20221010105110.446674-2-mikhail.kshevetskiy@iopsys.eu +--- + drivers/mtd/nand/spi/winbond.c | 75 ++++++++++++++++++++++++++++++++++ + 1 file changed, 75 insertions(+) + +--- a/drivers/mtd/nand/spi/winbond.c ++++ b/drivers/mtd/nand/spi/winbond.c +@@ -74,6 +74,72 @@ static int w25m02gv_select_target(struct + return spi_mem_exec_op(spinand->spimem, &op); + } + ++static int w25n02kv_ooblayout_ecc(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = 64 + (16 * section); ++ region->length = 13; ++ ++ return 0; ++} ++ ++static int w25n02kv_ooblayout_free(struct mtd_info *mtd, int section, ++ struct mtd_oob_region *region) ++{ ++ if (section > 3) ++ return -ERANGE; ++ ++ region->offset = (16 * section) + 2; ++ region->length = 14; ++ ++ return 0; ++} ++ ++static const struct mtd_ooblayout_ops w25n02kv_ooblayout = { ++ .ecc = w25n02kv_ooblayout_ecc, ++ .free = w25n02kv_ooblayout_free, ++}; ++ ++static int w25n02kv_ecc_get_status(struct spinand_device *spinand, ++ u8 status) ++{ ++ struct nand_device *nand = spinand_to_nand(spinand); ++ u8 mbf = 0; ++ struct spi_mem_op op = SPINAND_GET_FEATURE_OP(0x30, &mbf); ++ ++ switch (status & STATUS_ECC_MASK) { ++ case STATUS_ECC_NO_BITFLIPS: ++ return 0; ++ ++ case STATUS_ECC_UNCOR_ERROR: ++ return -EBADMSG; ++ ++ case STATUS_ECC_HAS_BITFLIPS: ++ /* ++ * Let's try to retrieve the real maximum number of bitflips ++ * in order to avoid forcing the wear-leveling layer to move ++ * data around if it's not necessary. ++ */ ++ if (spi_mem_exec_op(spinand->spimem, &op)) ++ return nanddev_get_ecc_conf(nand)->strength; ++ ++ mbf >>= 4; ++ ++ if (WARN_ON(mbf > nanddev_get_ecc_conf(nand)->strength || !mbf)) ++ return nanddev_get_ecc_conf(nand)->strength; ++ ++ return mbf; ++ ++ default: ++ break; ++ } ++ ++ return -EINVAL; ++} ++ + static const struct spinand_info winbond_spinand_table[] = { + SPINAND_INFO("W25M02GV", + SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xab, 0x21), +@@ -94,6 +160,15 @@ static const struct spinand_info winbond + &update_cache_variants), + 0, + SPINAND_ECCINFO(&w25m02gv_ooblayout, NULL)), ++ SPINAND_INFO("W25N02KV", ++ SPINAND_ID(SPINAND_READID_METHOD_OPCODE_DUMMY, 0xaa, 0x22), ++ NAND_MEMORG(1, 2048, 128, 64, 2048, 40, 1, 1, 1), ++ NAND_ECCREQ(8, 512), ++ SPINAND_INFO_OP_VARIANTS(&read_cache_variants, ++ &write_cache_variants, ++ &update_cache_variants), ++ 0, ++ SPINAND_ECCINFO(&w25n02kv_ooblayout, w25n02kv_ecc_get_status)), + }; + + static int winbond_spinand_init(struct spinand_device *spinand) diff --git a/target/linux/generic/config-5.15 b/target/linux/generic/config-5.15 index b41de9cc288..c9581ba5d4a 100644 --- a/target/linux/generic/config-5.15 +++ b/target/linux/generic/config-5.15 @@ -4425,7 +4425,6 @@ CONFIG_NMI_LOG_BUF_SHIFT=13 # CONFIG_NO_HZ is not set # CONFIG_NO_HZ_FULL is not set # CONFIG_NO_HZ_IDLE is not set -CONFIG_NR_LRU_GENS=7 # CONFIG_NS83820 is not set # CONFIG_NTB is not set # CONFIG_NTFS3_64BIT_CLUSTER is not set @@ -6529,7 +6528,6 @@ CONFIG_THIN_ARCHIVES=y # CONFIG_THUNDER_NIC_VF is not set # CONFIG_TICK_CPU_ACCOUNTING is not set CONFIG_TICK_ONESHOT=y -CONFIG_TIERS_PER_GEN=4 # CONFIG_TIFM_CORE is not set # CONFIG_TIGON3 is not set # CONFIG_TIMB_DMA is not set diff --git a/target/linux/generic/hack-5.10/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-5.10/721-net-add-packet-mangeling.patch index df58bf4bd7f..a80ce4baae8 100644 --- a/target/linux/generic/hack-5.10/721-net-add-packet-mangeling.patch +++ b/target/linux/generic/hack-5.10/721-net-add-packet-mangeling.patch @@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -1646,6 +1646,7 @@ enum netdev_priv_flags { +@@ -1648,6 +1648,7 @@ enum netdev_priv_flags { IFF_FAILOVER_SLAVE = 1<<28, IFF_L3MDEV_RX_HANDLER = 1<<29, IFF_LIVE_RENAME_OK = 1<<30, @@ -27,7 +27,7 @@ Signed-off-by: Felix Fietkau }; #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN -@@ -1678,6 +1679,7 @@ enum netdev_priv_flags { +@@ -1680,6 +1681,7 @@ enum netdev_priv_flags { #define IFF_FAILOVER_SLAVE IFF_FAILOVER_SLAVE #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER #define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK @@ -35,7 +35,7 @@ Signed-off-by: Felix Fietkau /* Specifies the type of the struct net_device::ml_priv pointer */ enum netdev_ml_priv_type { -@@ -2018,6 +2020,11 @@ struct net_device { +@@ -2020,6 +2022,11 @@ struct net_device { const struct tlsdev_ops *tlsdev_ops; #endif @@ -47,7 +47,7 @@ Signed-off-by: Felix Fietkau const struct header_ops *header_ops; unsigned int flags; -@@ -2108,6 +2115,10 @@ struct net_device { +@@ -2110,6 +2117,10 @@ struct net_device { struct mpls_dev __rcu *mpls_ptr; #endif diff --git a/target/linux/generic/hack-5.15/221-module_exports.patch b/target/linux/generic/hack-5.15/221-module_exports.patch index 405dc5f78ce..87f541b46f2 100644 --- a/target/linux/generic/hack-5.15/221-module_exports.patch +++ b/target/linux/generic/hack-5.15/221-module_exports.patch @@ -56,7 +56,7 @@ Signed-off-by: Felix Fietkau } \ \ /* __*init sections */ \ -@@ -1017,6 +1027,8 @@ +@@ -1022,6 +1032,8 @@ #define COMMON_DISCARDS \ SANITIZER_DISCARDS \ diff --git a/target/linux/generic/hack-5.15/259-regmap_dynamic.patch b/target/linux/generic/hack-5.15/259-regmap_dynamic.patch index 6be5875177c..76a5ace6f31 100644 --- a/target/linux/generic/hack-5.15/259-regmap_dynamic.patch +++ b/target/linux/generic/hack-5.15/259-regmap_dynamic.patch @@ -125,7 +125,7 @@ Signed-off-by: Felix Fietkau #include #include #include -@@ -3341,3 +3342,5 @@ static int __init regmap_initcall(void) +@@ -3358,3 +3359,5 @@ static int __init regmap_initcall(void) return 0; } postcore_initcall(regmap_initcall); diff --git a/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch b/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch index 9dc86303a7a..16bb4855ff3 100644 --- a/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch +++ b/target/linux/generic/hack-5.15/721-net-add-packet-mangeling.patch @@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -1676,6 +1676,10 @@ enum netdev_priv_flags { +@@ -1679,6 +1679,10 @@ enum netdev_priv_flags { IFF_TX_SKB_NO_LINEAR = BIT_ULL(31), }; @@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau #define IFF_802_1Q_VLAN IFF_802_1Q_VLAN #define IFF_EBRIDGE IFF_EBRIDGE #define IFF_BONDING IFF_BONDING -@@ -1708,6 +1712,7 @@ enum netdev_priv_flags { +@@ -1711,6 +1715,7 @@ enum netdev_priv_flags { #define IFF_L3MDEV_RX_HANDLER IFF_L3MDEV_RX_HANDLER #define IFF_LIVE_RENAME_OK IFF_LIVE_RENAME_OK #define IFF_TX_SKB_NO_LINEAR IFF_TX_SKB_NO_LINEAR @@ -38,7 +38,7 @@ Signed-off-by: Felix Fietkau /* Specifies the type of the struct net_device::ml_priv pointer */ enum netdev_ml_priv_type { -@@ -2009,6 +2014,7 @@ struct net_device { +@@ -2012,6 +2017,7 @@ struct net_device { /* Read-mostly cache-line for fast-path access */ unsigned int flags; unsigned int priv_flags; @@ -46,7 +46,7 @@ Signed-off-by: Felix Fietkau const struct net_device_ops *netdev_ops; int ifindex; unsigned short gflags; -@@ -2069,6 +2075,11 @@ struct net_device { +@@ -2072,6 +2078,11 @@ struct net_device { const struct tlsdev_ops *tlsdev_ops; #endif @@ -58,7 +58,7 @@ Signed-off-by: Felix Fietkau const struct header_ops *header_ops; unsigned char operstate; -@@ -2143,6 +2154,10 @@ struct net_device { +@@ -2146,6 +2157,10 @@ struct net_device { struct mctp_dev __rcu *mctp_ptr; #endif diff --git a/target/linux/generic/hack-5.15/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch b/target/linux/generic/hack-5.15/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch new file mode 100644 index 00000000000..b03f0feab27 --- /dev/null +++ b/target/linux/generic/hack-5.15/750-net-pcs-mtk-lynxi-workaround-2500BaseX-no-an.patch @@ -0,0 +1,40 @@ +--- a/drivers/net/pcs/pcs-mtk-lynxi.c ++++ b/drivers/net/pcs/pcs-mtk-lynxi.c +@@ -92,14 +92,23 @@ static void mtk_pcs_lynxi_get_state(stru + struct phylink_link_state *state) + { + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs); +- unsigned int bm, adv; ++ unsigned int bm, bmsr, adv; + + /* Read the BMSR and LPA */ + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm); +- regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ bmsr = FIELD_GET(SGMII_BMSR, bm); ++ ++ if (state->interface == PHY_INTERFACE_MODE_2500BASEX) { ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ state->speed = SPEED_2500; ++ state->duplex = DUPLEX_FULL; + +- phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm), +- FIELD_GET(SGMII_LPA, adv)); ++ return; ++ } ++ ++ regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv); ++ phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv)); + } + + static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode, +@@ -134,7 +143,8 @@ static int mtk_pcs_lynxi_config(struct p + /* 1000base-X or 2500base-X autoneg */ + sgm_mode = SGMII_REMOTE_FAULT_DIS; + use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT, +- advertising); ++ advertising) && ++ !(interface == PHY_INTERFACE_MODE_2500BASEX); + } else { + /* 1000base-X or 2500base-X without autoneg */ + sgm_mode = 0; diff --git a/target/linux/generic/hack-5.15/765-mxl-gpy-control-LED-reg-from-DT.patch b/target/linux/generic/hack-5.15/765-mxl-gpy-control-LED-reg-from-DT.patch new file mode 100644 index 00000000000..8de3668def2 --- /dev/null +++ b/target/linux/generic/hack-5.15/765-mxl-gpy-control-LED-reg-from-DT.patch @@ -0,0 +1,101 @@ +From 94b90966095f3fa625897e8f53d215882f6e19b3 Mon Sep 17 00:00:00 2001 +From: David Bauer +Date: Sat, 11 Mar 2023 17:00:01 +0100 +Subject: [PATCH] mxl-gpy: control LED reg from DT + +Add dynamic configuration for the LED control registers on MXL PHYs. + +This patch has been tested with MaxLinear GPY211C. It is unlikely to be +accepted upstream, as upstream plans on integrating their own framework +for handling these LEDs. + +For the time being, use this hack to configure PHY driven device-LEDs to +show the correct state. + +A possible alternative might be to expose the LEDs using the kernel LED +framework and bind it to the netdevice. This might also be upstreamable, +although it is a considerable extra amount of work. + +Signed-off-by: David Bauer +--- + drivers/net/phy/mxl-gpy.c | 37 ++++++++++++++++++++++++++++++++++++- + 1 file changed, 36 insertions(+), 1 deletion(-) + +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #include + #include + +@@ -30,6 +31,7 @@ + #define PHY_MIISTAT 0x18 /* MII state */ + #define PHY_IMASK 0x19 /* interrupt mask */ + #define PHY_ISTAT 0x1A /* interrupt status */ ++#define PHY_LED 0x1B /* LED control */ + #define PHY_FWV 0x1E /* firmware version */ + + #define PHY_MIISTAT_SPD_MASK GENMASK(2, 0) +@@ -53,10 +55,15 @@ + PHY_IMASK_ADSC | \ + PHY_IMASK_ANC) + ++#define PHY_LED_NUM_LEDS 4 ++ + #define PHY_FWV_REL_MASK BIT(15) + #define PHY_FWV_TYPE_MASK GENMASK(11, 8) + #define PHY_FWV_MINOR_MASK GENMASK(7, 0) + ++/* LED */ ++#define VSPEC1_LED(x) (0x1 + x) ++ + /* SGMII */ + #define VSPEC1_SGMII_CTRL 0x08 + #define VSPEC1_SGMII_CTRL_ANEN BIT(12) /* Aneg enable */ +@@ -80,6 +87,31 @@ static const struct { + {9, 0x73}, + }; + ++static int gpy_led_write(struct phy_device *phydev) ++{ ++ struct device_node *node = phydev->mdio.dev.of_node; ++ u32 led_regs[PHY_LED_NUM_LEDS]; ++ int i, ret; ++ ++ if (!IS_ENABLED(CONFIG_OF_MDIO)) ++ return 0; ++ ++ if (of_property_read_u32_array(node, "mxl,led-config", led_regs, PHY_LED_NUM_LEDS)) ++ return 0; ++ ++ /* Enable LED function handling on all ports*/ ++ phy_write(phydev, PHY_LED, 0xFF00); ++ ++ /* Write LED register values */ ++ for (i = 0; i < PHY_LED_NUM_LEDS; i++) { ++ ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_LED(i), (u16)led_regs[i]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ + static int gpy_config_init(struct phy_device *phydev) + { + int ret; +@@ -91,7 +123,10 @@ static int gpy_config_init(struct phy_de + + /* Clear all pending interrupts */ + ret = phy_read(phydev, PHY_ISTAT); +- return ret < 0 ? ret : 0; ++ if (ret < 0) ++ return ret; ++ ++ return gpy_led_write(phydev); + } + + static int gpy_probe(struct phy_device *phydev) diff --git a/target/linux/generic/hack-5.15/773-bgmac-add-srab-switch.patch b/target/linux/generic/hack-5.15/773-bgmac-add-srab-switch.patch index 1e4fc446ce4..7127aa136c6 100644 --- a/target/linux/generic/hack-5.15/773-bgmac-add-srab-switch.patch +++ b/target/linux/generic/hack-5.15/773-bgmac-add-srab-switch.patch @@ -50,9 +50,9 @@ Signed-off-by: Hauke Mehrtens /************************************************** * MII **************************************************/ -@@ -1542,6 +1554,14 @@ int bgmac_enet_probe(struct bgmac *bgmac - /* Omit FCS from max MTU size */ - net_dev->max_mtu = BGMAC_RX_MAX_FRAME_SIZE - ETH_FCS_LEN; +@@ -1546,6 +1558,14 @@ int bgmac_enet_probe(struct bgmac *bgmac + + bgmac->in_init = false; + if ((bgmac->feature_flags & BGMAC_FEAT_SRAB) && !bgmac_b53_pdata.regs) { + bgmac_b53_pdata.regs = ioremap(0x18007000, 0x1000); @@ -65,7 +65,7 @@ Signed-off-by: Hauke Mehrtens err = register_netdev(bgmac->net_dev); if (err) { dev_err(bgmac->dev, "Cannot register net device\n"); -@@ -1564,6 +1584,10 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe); +@@ -1568,6 +1588,10 @@ EXPORT_SYMBOL_GPL(bgmac_enet_probe); void bgmac_enet_remove(struct bgmac *bgmac) { @@ -86,7 +86,7 @@ Signed-off-by: Hauke Mehrtens struct bgmac_slot_info { union { -@@ -495,6 +496,9 @@ struct bgmac { +@@ -497,6 +498,9 @@ struct bgmac { void (*cmn_maskset32)(struct bgmac *bgmac, u16 offset, u32 mask, u32 set); int (*phy_connect)(struct bgmac *bgmac); diff --git a/target/linux/generic/hack-5.15/795-backport-phylink_pcs-helpers.patch b/target/linux/generic/hack-5.15/795-backport-phylink_pcs-helpers.patch new file mode 100644 index 00000000000..2ccdc905a5c --- /dev/null +++ b/target/linux/generic/hack-5.15/795-backport-phylink_pcs-helpers.patch @@ -0,0 +1,150 @@ +--- a/include/linux/phylink.h ++++ b/include/linux/phylink.h +@@ -584,10 +584,37 @@ int phylink_speed_up(struct phylink *pl) + #define phylink_test(bm, mode) __phylink_do_bit(test_bit, bm, mode) + + void phylink_set_port_modes(unsigned long *bits); ++ ++/** ++ * phylink_get_link_timer_ns - return the PCS link timer value ++ * @interface: link &typedef phy_interface_t mode ++ * ++ * Return the PCS link timer setting in nanoseconds for the PHY @interface ++ * mode, or -EINVAL if not appropriate. ++ */ ++static inline int phylink_get_link_timer_ns(phy_interface_t interface) ++{ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_SGMII: ++ return 1600000; ++ ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ return 10000000; ++ ++ default: ++ return -EINVAL; ++ } ++} ++ + void phylink_helper_basex_speed(struct phylink_link_state *state); + ++void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, ++ u16 bmsr, u16 lpa); + void phylink_mii_c22_pcs_get_state(struct mdio_device *pcs, + struct phylink_link_state *state); ++int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface, ++ const unsigned long *advertising); + int phylink_mii_c22_pcs_set_advertisement(struct mdio_device *pcs, + phy_interface_t interface, + const unsigned long *advertising); +--- a/drivers/net/phy/phylink.c ++++ b/drivers/net/phy/phylink.c +@@ -885,7 +885,6 @@ static int phylink_change_inband_advert( + + return 0; + } +- + static void phylink_mac_pcs_get_state(struct phylink *pl, + struct phylink_link_state *state) + { +@@ -2966,6 +2965,52 @@ void phylink_mii_c22_pcs_get_state(struc + EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_get_state); + + /** ++ * phylink_mii_c22_pcs_decode_state() - Decode MAC PCS state from MII registers ++ * @state: a pointer to a &struct phylink_link_state. ++ * @bmsr: The value of the %MII_BMSR register ++ * @lpa: The value of the %MII_LPA register ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation and/or SGMII control. ++ * ++ * Parse the Clause 37 or Cisco SGMII link partner negotiation word into ++ * the phylink @state structure. This is suitable to be used for implementing ++ * the mac_pcs_get_state() member of the struct phylink_mac_ops structure if ++ * accessing @bmsr and @lpa cannot be done with MDIO directly. ++ */ ++void phylink_mii_c22_pcs_decode_state(struct phylink_link_state *state, ++ u16 bmsr, u16 lpa) ++{ ++ state->link = !!(bmsr & BMSR_LSTATUS); ++ state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE); ++ /* If there is no link or autonegotiation is disabled, the LP advertisement ++ * data is not meaningful, so don't go any further. ++ */ ++ if (!state->link || !state->an_enabled) ++ return; ++ ++ switch (state->interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ phylink_decode_c37_word(state, lpa, SPEED_1000); ++ break; ++ ++ case PHY_INTERFACE_MODE_2500BASEX: ++ phylink_decode_c37_word(state, lpa, SPEED_2500); ++ break; ++ ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ phylink_decode_sgmii_word(state, lpa); ++ break; ++ ++ default: ++ state->link = false; ++ break; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_decode_state); ++ ++/** + * phylink_mii_c22_pcs_set_advertisement() - configure the clause 37 PCS + * advertisement + * @pcs: a pointer to a &struct mdio_device. +@@ -3037,6 +3082,46 @@ int phylink_mii_c22_pcs_set_advertisemen + EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_set_advertisement); + + /** ++ * phylink_mii_c22_pcs_encode_advertisement() - configure the clause 37 PCS ++ * advertisement ++ * @interface: the PHY interface mode being configured ++ * @advertising: the ethtool advertisement mask ++ * ++ * Helper for MAC PCS supporting the 802.3 clause 22 register set for ++ * clause 37 negotiation and/or SGMII control. ++ * ++ * Encode the clause 37 PCS advertisement as specified by @interface and ++ * @advertising. ++ * ++ * Return: The new value for @adv, or ``-EINVAL`` if it should not be changed. ++ */ ++int phylink_mii_c22_pcs_encode_advertisement(phy_interface_t interface, ++ const unsigned long *advertising) ++{ ++ u16 adv; ++ ++ switch (interface) { ++ case PHY_INTERFACE_MODE_1000BASEX: ++ case PHY_INTERFACE_MODE_2500BASEX: ++ adv = ADVERTISE_1000XFULL; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, ++ advertising)) ++ adv |= ADVERTISE_1000XPAUSE; ++ if (linkmode_test_bit(ETHTOOL_LINK_MODE_Asym_Pause_BIT, ++ advertising)) ++ adv |= ADVERTISE_1000XPSE_ASYM; ++ return adv; ++ case PHY_INTERFACE_MODE_SGMII: ++ case PHY_INTERFACE_MODE_QSGMII: ++ return 0x0001; ++ default: ++ /* Nothing to do for other modes */ ++ return -EINVAL; ++ } ++} ++EXPORT_SYMBOL_GPL(phylink_mii_c22_pcs_encode_advertisement); ++ ++/** + * phylink_mii_c22_pcs_config() - configure clause 22 PCS + * @pcs: a pointer to a &struct mdio_device. + * @mode: link autonegotiation mode diff --git a/target/linux/generic/hack-5.15/902-debloat_proc.patch b/target/linux/generic/hack-5.15/902-debloat_proc.patch index ef2fe47ae9a..a7a73cc885f 100644 --- a/target/linux/generic/hack-5.15/902-debloat_proc.patch +++ b/target/linux/generic/hack-5.15/902-debloat_proc.patch @@ -29,7 +29,7 @@ Signed-off-by: Felix Fietkau --- a/fs/locks.c +++ b/fs/locks.c -@@ -2952,6 +2952,8 @@ static const struct seq_operations locks +@@ -2953,6 +2953,8 @@ static const struct seq_operations locks static int __init proc_locks_init(void) { diff --git a/target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch index d990cee90b0..12a7fb5a7d6 100644 --- a/target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch +++ b/target/linux/generic/pending-5.10/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -2057,6 +2057,8 @@ struct net_device { +@@ -2059,6 +2059,8 @@ struct net_device { struct netdev_hw_addr_list mc; struct netdev_hw_addr_list dev_addrs; diff --git a/target/linux/generic/pending-5.10/701-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch b/target/linux/generic/pending-5.10/701-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch index a31929d58a8..b9c6d4378a1 100644 --- a/target/linux/generic/pending-5.10/701-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch +++ b/target/linux/generic/pending-5.10/701-03-net-ethernet-mtk_eth_soc-implement-flow-offloading-t.patch @@ -233,7 +233,7 @@ Signed-off-by: Felix Fietkau static inline void --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -854,6 +854,7 @@ enum net_device_path_type { +@@ -856,6 +856,7 @@ enum net_device_path_type { DEV_PATH_BRIDGE, DEV_PATH_PPPOE, DEV_PATH_DSA, @@ -241,7 +241,7 @@ Signed-off-by: Felix Fietkau }; struct net_device_path { -@@ -879,6 +880,12 @@ struct net_device_path { +@@ -881,6 +882,12 @@ struct net_device_path { int port; u16 proto; } dsa; diff --git a/target/linux/generic/pending-5.10/704-02-net-fix-dev_fill_forward_path-with-pppoe-bridge.patch b/target/linux/generic/pending-5.10/704-02-net-fix-dev_fill_forward_path-with-pppoe-bridge.patch index 1a1f6ab412f..c0660e50c4e 100644 --- a/target/linux/generic/pending-5.10/704-02-net-fix-dev_fill_forward_path-with-pppoe-bridge.patch +++ b/target/linux/generic/pending-5.10/704-02-net-fix-dev_fill_forward_path-with-pppoe-bridge.patch @@ -40,7 +40,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -899,7 +899,7 @@ struct net_device_path_stack { +@@ -901,7 +901,7 @@ struct net_device_path_stack { struct net_device_path_ctx { const struct net_device *dev; diff --git a/target/linux/generic/pending-5.10/760-net-dsa-mv88e6xxx-fix-vlan-setup.patch b/target/linux/generic/pending-5.10/760-net-dsa-mv88e6xxx-fix-vlan-setup.patch index fff817dd8c1..a9c2f7ed129 100644 --- a/target/linux/generic/pending-5.10/760-net-dsa-mv88e6xxx-fix-vlan-setup.patch +++ b/target/linux/generic/pending-5.10/760-net-dsa-mv88e6xxx-fix-vlan-setup.patch @@ -17,7 +17,7 @@ Signed-off-by: DENG Qingfang --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c -@@ -2921,6 +2921,7 @@ static int mv88e6xxx_setup(struct dsa_sw +@@ -2929,6 +2929,7 @@ static int mv88e6xxx_setup(struct dsa_sw chip->ds = ds; ds->slave_mii_bus = mv88e6xxx_default_mdio_bus(chip); diff --git a/target/linux/generic/pending-5.10/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-5.10/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch index 80e2b481bf5..10781da152c 100644 --- a/target/linux/generic/pending-5.10/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch +++ b/target/linux/generic/pending-5.10/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch @@ -17,7 +17,7 @@ Signed-off-by: Tobias Waldekranz --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c -@@ -5490,6 +5490,7 @@ static int mv88e6xxx_register_switch(str +@@ -5498,6 +5498,7 @@ static int mv88e6xxx_register_switch(str ds->ops = &mv88e6xxx_switch_ops; ds->ageing_time_min = chip->info->age_time_coeff; ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; diff --git a/target/linux/generic/pending-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch b/target/linux/generic/pending-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch index 4865659c04b..c1313c82f4d 100644 --- a/target/linux/generic/pending-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch +++ b/target/linux/generic/pending-5.15/120-Fix-alloc_node_mem_map-with-ARCH_PFN_OFFSET-calcu.patch @@ -71,7 +71,7 @@ Signed-off-by: Tobias Wolf --- a/mm/page_alloc.c +++ b/mm/page_alloc.c -@@ -7605,7 +7605,7 @@ static void __init alloc_node_mem_map(st +@@ -7604,7 +7604,7 @@ static void __init alloc_node_mem_map(st if (pgdat == NODE_DATA(0)) { mem_map = NODE_DATA(0)->node_mem_map; if (page_to_pfn(mem_map) != pgdat->node_start_pfn) diff --git a/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch b/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch index 2d3efb73d00..e9705fae461 100644 --- a/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch +++ b/target/linux/generic/pending-5.15/680-NET-skip-GRO-for-foreign-MAC-addresses.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h -@@ -2096,6 +2096,8 @@ struct net_device { +@@ -2098,6 +2098,8 @@ struct net_device { struct netdev_hw_addr_list mc; struct netdev_hw_addr_list dev_addrs; diff --git a/target/linux/generic/pending-5.15/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch b/target/linux/generic/pending-5.15/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch index 99cfa04db0f..5dcbc6c46a2 100644 --- a/target/linux/generic/pending-5.15/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch +++ b/target/linux/generic/pending-5.15/702-net-ethernet-mtk_eth_soc-enable-threaded-NAPI.patch @@ -10,7 +10,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -2971,8 +2971,8 @@ static irqreturn_t mtk_handle_irq_rx(int +@@ -2994,8 +2994,8 @@ static irqreturn_t mtk_handle_irq_rx(int eth->rx_events++; if (likely(napi_schedule_prep(ð->rx_napi))) { @@ -20,7 +20,7 @@ Signed-off-by: Felix Fietkau } return IRQ_HANDLED; -@@ -2984,8 +2984,8 @@ static irqreturn_t mtk_handle_irq_tx(int +@@ -3007,8 +3007,8 @@ static irqreturn_t mtk_handle_irq_tx(int eth->tx_events++; if (likely(napi_schedule_prep(ð->tx_napi))) { @@ -30,7 +30,7 @@ Signed-off-by: Felix Fietkau } return IRQ_HANDLED; -@@ -4616,6 +4616,8 @@ static int mtk_probe(struct platform_dev +@@ -4675,6 +4675,8 @@ static int mtk_probe(struct platform_dev * for NAPI to work */ init_dummy_netdev(ð->dummy_dev); diff --git a/target/linux/generic/pending-5.15/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch b/target/linux/generic/pending-5.15/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch index 12a3044d057..0226cef8c3f 100644 --- a/target/linux/generic/pending-5.15/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch +++ b/target/linux/generic/pending-5.15/723-net-mt7531-ensure-all-MACs-are-powered-down-before-r.patch @@ -15,7 +15,7 @@ Signed-off-by: Alexander Couzens --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c -@@ -2336,6 +2336,10 @@ mt7531_setup(struct dsa_switch *ds) +@@ -2342,6 +2342,10 @@ mt7531_setup(struct dsa_switch *ds) return -ENODEV; } diff --git a/target/linux/generic/pending-5.15/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch b/target/linux/generic/pending-5.15/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch deleted file mode 100644 index 9a6b228a272..00000000000 --- a/target/linux/generic/pending-5.15/724-net-mtk_sgmii-implement-mtk_pcs_ops.patch +++ /dev/null @@ -1,46 +0,0 @@ -From cbfed00575d15eafd85efd9619b7ecc0836a4aa7 Mon Sep 17 00:00:00 2001 -From: Alexander Couzens -Date: Sat, 13 Aug 2022 14:42:12 +0200 -Subject: [PATCH 04/10] net: mtk_sgmii: implement mtk_pcs_ops - -Implement mtk_pcs_ops for the SGMII pcs to read the current state -of the hardware. - -Signed-off-by: Alexander Couzens -[added DUPLEX_FULL] -Signed-off-by: Daniel Golle ---- - drivers/net/ethernet/mediatek/mtk_sgmii.c | 15 +++++++++++++++ - 1 file changed, 15 insertions(+) - ---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c -+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c -@@ -139,10 +139,28 @@ static void mtk_pcs_link_up(struct phyli - regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); - } - -+static void mtk_pcs_get_state(struct phylink_pcs *pcs, struct phylink_link_state *state) -+{ -+ struct mtk_pcs *mpcs = pcs_to_mtk_pcs(pcs); -+ unsigned int val; -+ -+ regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); -+ state->an_complete = !!(val & SGMII_AN_COMPLETE); -+ state->link = !!(val & SGMII_LINK_STATYS); -+ if (!state->link) -+ return; -+ -+ regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); -+ state->speed = val & RG_PHY_SPEED_3_125G ? SPEED_2500 : SPEED_1000; -+ state->duplex = DUPLEX_FULL; -+ state->pause = 0; -+} -+ - static const struct phylink_pcs_ops mtk_pcs_ops = { - .pcs_config = mtk_pcs_config, - .pcs_an_restart = mtk_pcs_restart_an, - .pcs_link_up = mtk_pcs_link_up, -+ .pcs_get_state = mtk_pcs_get_state, - }; - - int mtk_sgmii_init(struct mtk_sgmii *ss, struct device_node *r, u32 ana_rgc3) diff --git a/target/linux/generic/pending-5.15/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch b/target/linux/generic/pending-5.15/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch deleted file mode 100644 index fd68ce22f69..00000000000 --- a/target/linux/generic/pending-5.15/727-net-mtk_sgmii-mtk_pcs_setup_mode_an-don-t-rely-on-re.patch +++ /dev/null @@ -1,31 +0,0 @@ -From e4dca7affb8c03438b63bdb5fddefd6ad2431cfd Mon Sep 17 00:00:00 2001 -From: Alexander Couzens -Date: Mon, 15 Aug 2022 14:59:29 +0200 -Subject: [PATCH 07/10] net: mtk_sgmii: mtk_pcs_setup_mode_an: don't rely on - register defaults - -Ensure autonegotiation is enabled. - -Signed-off-by: Alexander Couzens ---- - drivers/net/ethernet/mediatek/mtk_sgmii.c | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - ---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c -+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c -@@ -32,12 +32,13 @@ static int mtk_pcs_setup_mode_an(struct - regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER, - SGMII_LINK_TIMER_DEFAULT); - -+ /* disable remote fault & enable auto neg */ - regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); -- val |= SGMII_REMOTE_FAULT_DIS; -+ val |= SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN; - regmap_write(mpcs->regmap, SGMSYS_SGMII_MODE, val); - - regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &val); -- val |= SGMII_AN_RESTART; -+ val |= SGMII_AN_RESTART | SGMII_AN_ENABLE; - regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); - - diff --git a/target/linux/generic/pending-5.15/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch b/target/linux/generic/pending-5.15/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch deleted file mode 100644 index 1649a64551d..00000000000 --- a/target/linux/generic/pending-5.15/728-net-mtk_sgmii-set-the-speed-according-to-the-phy-int.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 952b64575613d26163a5afa5ff8bfdb57840091b Mon Sep 17 00:00:00 2001 -From: Alexander Couzens -Date: Mon, 15 Aug 2022 15:00:14 +0200 -Subject: [PATCH 08/10] net: mtk_sgmii: set the speed according to the phy - interface in AN - -The non auto-negotioting code path is setting the correct speed for the -interface. Ensure auto-negotiation code path is doing it as well. - -Signed-off-by: Alexander Couzens ---- - drivers/net/ethernet/mediatek/mtk_sgmii.c | 11 +++++++++-- - 1 file changed, 9 insertions(+), 2 deletions(-) - ---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c -+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c -@@ -20,12 +20,14 @@ static struct mtk_pcs *pcs_to_mtk_pcs(st - } - - /* For SGMII interface mode */ --static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs) -+static int mtk_pcs_setup_mode_an(struct mtk_pcs *mpcs, phy_interface_t interface) - { - unsigned int val; - - regmap_read(mpcs->regmap, mpcs->ana_rgc3, &val); - val &= ~RG_PHY_SPEED_MASK; -+ if (interface == PHY_INTERFACE_MODE_2500BASEX) -+ val |= RG_PHY_SPEED_3_125G; - regmap_write(mpcs->regmap, mpcs->ana_rgc3, val); - - /* Setup the link timer and QPHY power up inside SGMIISYS */ -@@ -94,7 +96,7 @@ static int mtk_pcs_config(struct phylink - if (interface != PHY_INTERFACE_MODE_SGMII) - err = mtk_pcs_setup_mode_force(mpcs, interface); - else if (phylink_autoneg_inband(mode)) -- err = mtk_pcs_setup_mode_an(mpcs); -+ err = mtk_pcs_setup_mode_an(mpcs, interface); - - /* Release PHYA power down state - * Only removing bit SGMII_PHYA_PWD isn't enough. diff --git a/target/linux/generic/pending-5.15/729-net-mtk_eth_soc-improve-comment.patch b/target/linux/generic/pending-5.15/729-net-mtk_eth_soc-improve-comment.patch deleted file mode 100644 index cd64c6147ad..00000000000 --- a/target/linux/generic/pending-5.15/729-net-mtk_eth_soc-improve-comment.patch +++ /dev/null @@ -1,22 +0,0 @@ -From 06773f19cffd6c9d34dcbc8320169afef5ab60ba Mon Sep 17 00:00:00 2001 -From: Alexander Couzens -Date: Mon, 15 Aug 2022 13:58:07 +0200 -Subject: [PATCH 09/10] net: mtk_eth_soc: improve comment - -Signed-off-by: Alexander Couzens ---- - drivers/net/ethernet/mediatek/mtk_sgmii.c | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - ---- a/drivers/net/ethernet/mediatek/mtk_sgmii.c -+++ b/drivers/net/ethernet/mediatek/mtk_sgmii.c -@@ -67,7 +67,8 @@ static int mtk_pcs_setup_mode_force(stru - val &= ~SGMII_AN_ENABLE; - regmap_write(mpcs->regmap, SGMSYS_PCS_CONTROL_1, val); - -- /* Set the speed etc but leave the duplex unchanged */ -+ /* Set the speed etc but leave the duplex unchanged. -+ * The SGMII mode for 2.5gbit is the same as for 1gbit, expect the speed in ANA_RGC3 */ - regmap_read(mpcs->regmap, SGMSYS_SGMII_MODE, &val); - val &= SGMII_DUPLEX_HALF | ~SGMII_IF_MODE_MASK; - val |= SGMII_SPEED_1000; diff --git a/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch b/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch index d3aca7f849d..c7e1d3f6f08 100644 --- a/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch +++ b/target/linux/generic/pending-5.15/731-net-ethernet-mediatek-ppe-add-support-for-flow-accou.patch @@ -53,7 +53,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4585,8 +4585,8 @@ static int mtk_probe(struct platform_dev +@@ -4644,8 +4644,8 @@ static int mtk_probe(struct platform_dev for (i = 0; i < num_ppe; i++) { u32 ppe_addr = eth->soc->reg_map->ppe_base + i * 0x400; @@ -64,7 +64,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov if (!eth->ppe[i]) { err = -ENOMEM; goto err_free_dev; -@@ -4711,6 +4711,7 @@ static const struct mtk_soc_data mt7622_ +@@ -4772,6 +4772,7 @@ static const struct mtk_soc_data mt7622_ .required_pctl = false, .offload_version = 2, .hash_offset = 2, @@ -72,7 +72,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov .foe_entry_size = sizeof(struct mtk_foe_entry) - 16, .txrx = { .txd_size = sizeof(struct mtk_tx_dma), -@@ -4748,6 +4749,7 @@ static const struct mtk_soc_data mt7629_ +@@ -4809,6 +4810,7 @@ static const struct mtk_soc_data mt7629_ .hw_features = MTK_HW_FEATURES, .required_clks = MT7629_CLKS_BITMAP, .required_pctl = false, @@ -80,7 +80,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov .txrx = { .txd_size = sizeof(struct mtk_tx_dma), .rxd_size = sizeof(struct mtk_rx_dma), -@@ -4768,6 +4770,7 @@ static const struct mtk_soc_data mt7986_ +@@ -4829,6 +4831,7 @@ static const struct mtk_soc_data mt7981_ .offload_version = 2, .hash_offset = 4, .foe_entry_size = sizeof(struct mtk_foe_entry), @@ -90,7 +90,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov .rxd_size = sizeof(struct mtk_rx_dma_v2), --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1025,6 +1025,8 @@ struct mtk_reg_map { +@@ -1014,6 +1014,8 @@ struct mtk_reg_map { * the extra setup for those pins used by GMAC. * @hash_offset Flow table hash offset. * @foe_entry_size Foe table entry size. @@ -99,7 +99,7 @@ v2: fix wrong variable name in return value check spotted by Denis Kirjanov * @txd_size Tx DMA descriptor size. * @rxd_size Rx DMA descriptor size. * @rx_irq_done_mask Rx irq done register mask. -@@ -1042,6 +1044,7 @@ struct mtk_soc_data { +@@ -1031,6 +1033,7 @@ struct mtk_soc_data { u8 hash_offset; u16 foe_entry_size; netdev_features_t hw_features; diff --git a/target/linux/generic/pending-5.15/732-00-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch b/target/linux/generic/pending-5.15/732-00-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch index cb20b97c285..b5e118f9fab 100644 --- a/target/linux/generic/pending-5.15/732-00-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch +++ b/target/linux/generic/pending-5.15/732-00-net-ethernet-mtk_eth_soc-drop-generic-vlan-rx-offloa.patch @@ -17,7 +17,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1829,9 +1829,7 @@ static int mtk_poll_rx(struct napi_struc +@@ -1852,9 +1852,7 @@ static int mtk_poll_rx(struct napi_struc while (done < budget) { unsigned int pktlen, *rxdcsum; @@ -27,7 +27,7 @@ Signed-off-by: Felix Fietkau dma_addr_t dma_addr; u32 hash, reason; int mac = 0; -@@ -1966,36 +1964,21 @@ static int mtk_poll_rx(struct napi_struc +@@ -1989,36 +1987,21 @@ static int mtk_poll_rx(struct napi_struc skb_checksum_none_assert(skb); skb->protocol = eth_type_trans(skb, netdev); @@ -70,7 +70,7 @@ Signed-off-by: Felix Fietkau skb_record_rx_queue(skb, 0); napi_gro_receive(napi, skb); -@@ -2810,29 +2793,11 @@ static netdev_features_t mtk_fix_feature +@@ -2833,29 +2816,11 @@ static netdev_features_t mtk_fix_feature static int mtk_set_features(struct net_device *dev, netdev_features_t features) { @@ -100,7 +100,7 @@ Signed-off-by: Felix Fietkau return 0; } -@@ -3146,30 +3111,6 @@ static int mtk_open(struct net_device *d +@@ -3169,30 +3134,6 @@ static int mtk_open(struct net_device *d struct mtk_eth *eth = mac->hw; int i, err; @@ -131,7 +131,7 @@ Signed-off-by: Felix Fietkau err = phylink_of_phy_connect(mac->phylink, mac->of_node, 0); if (err) { netdev_err(dev, "%s: could not attach PHY: %d\n", __func__, -@@ -3210,6 +3151,35 @@ static int mtk_open(struct net_device *d +@@ -3233,6 +3174,35 @@ static int mtk_open(struct net_device *d phylink_start(mac->phylink); netif_tx_start_all_queues(dev); @@ -167,7 +167,7 @@ Signed-off-by: Felix Fietkau return 0; } -@@ -3694,10 +3664,9 @@ static int mtk_hw_init(struct mtk_eth *e +@@ -3717,10 +3687,9 @@ static int mtk_hw_init(struct mtk_eth *e if (!MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { val = mtk_r32(eth, MTK_CDMP_IG_CTRL); mtk_w32(eth, val | MTK_CDMP_STAG_EN, MTK_CDMP_IG_CTRL); @@ -180,7 +180,7 @@ Signed-off-by: Felix Fietkau /* set interrupt delays based on current Net DIM sample */ mtk_dim_rx(ð->rx_dim.work); -@@ -4335,7 +4304,7 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4367,7 +4336,7 @@ static int mtk_add_mac(struct mtk_eth *e eth->netdev[id]->hw_features |= NETIF_F_LRO; eth->netdev[id]->vlan_features = eth->soc->hw_features & diff --git a/target/linux/generic/pending-5.15/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch b/target/linux/generic/pending-5.15/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch index 777e6f0ed13..72bad79c138 100644 --- a/target/linux/generic/pending-5.15/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch +++ b/target/linux/generic/pending-5.15/732-01-net-ethernet-mtk_eth_soc-work-around-issue-with-send.patch @@ -16,7 +16,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -1402,12 +1402,28 @@ static void mtk_wake_queue(struct mtk_et +@@ -1425,12 +1425,28 @@ static void mtk_wake_queue(struct mtk_et } } @@ -45,7 +45,7 @@ Signed-off-by: Felix Fietkau bool gso = false; int tx_num; -@@ -1429,6 +1445,18 @@ static netdev_tx_t mtk_start_xmit(struct +@@ -1452,6 +1468,18 @@ static netdev_tx_t mtk_start_xmit(struct return NETDEV_TX_BUSY; } @@ -64,7 +64,7 @@ Signed-off-by: Felix Fietkau /* TSO: fill MSS info in tcp checksum field */ if (skb_is_gso(skb)) { if (skb_cow_head(skb, 0)) { -@@ -1444,8 +1472,14 @@ static netdev_tx_t mtk_start_xmit(struct +@@ -1467,8 +1495,14 @@ static netdev_tx_t mtk_start_xmit(struct } } diff --git a/target/linux/generic/pending-5.15/733-02-net-ethernet-mtk_eth_soc-fix-RX-data-corruption-issu.patch b/target/linux/generic/pending-5.15/733-02-net-ethernet-mtk_eth_soc-fix-RX-data-corruption-issu.patch deleted file mode 100644 index 2d95c402dc4..00000000000 --- a/target/linux/generic/pending-5.15/733-02-net-ethernet-mtk_eth_soc-fix-RX-data-corruption-issu.patch +++ /dev/null @@ -1,46 +0,0 @@ -From e0eb504b1c9f973427a33d7ffef29ddecdb464b9 Mon Sep 17 00:00:00 2001 -From: Daniel Golle -Date: Mon, 23 Jan 2023 00:56:02 +0000 -Subject: [PATCH] net: ethernet: mtk_eth_soc: fix RX data corruption issue -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Also set bit 12 when setting up MAC MCR, as MediaTek SDK did the same -change stating: -"If without this patch, kernel might receive invalid packets that are -corrupted by GMAC."[1] -This fixes issues with <= 1G speed where we could previously observe -about 30% packet loss while the bad packet counter was increasing. -Unfortunately the meaning of bit 12 is not documented anywhere in SDK -code or datasheets. - -[1]: https://git01.mediatek.com/plugins/gitiles/openwrt/feeds/mtk-openwrt-feeds/+/d8a2975939a12686c4a95c40db21efdc3f821f63 -Tested-by: Bjørn Mork -Signed-off-by: Daniel Golle ---- - drivers/net/ethernet/mediatek/mtk_eth_soc.c | 2 +- - drivers/net/ethernet/mediatek/mtk_eth_soc.h | 1 + - 2 files changed, 2 insertions(+), 1 deletion(-) - ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -528,7 +528,7 @@ static int mtk_mac_finish(struct phylink - /* Setup gmac */ - mcr_cur = mtk_r32(mac->hw, MTK_MAC_MCR(mac->id)); - mcr_new = mcr_cur; -- mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_FORCE_MODE | -+ mcr_new |= MAC_MCR_IPG_CFG | MAC_MCR_BIT_12 | MAC_MCR_FORCE_MODE | - MAC_MCR_BACKOFF_EN | MAC_MCR_BACKPR_EN | MAC_MCR_FORCE_LINK; - - /* Only update control register when needed! */ ---- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h -+++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -392,6 +392,7 @@ - #define MAC_MCR_FORCE_MODE BIT(15) - #define MAC_MCR_TX_EN BIT(14) - #define MAC_MCR_RX_EN BIT(13) -+#define MAC_MCR_BIT_12 BIT(12) - #define MAC_MCR_BACKOFF_EN BIT(9) - #define MAC_MCR_BACKPR_EN BIT(8) - #define MAC_MCR_FORCE_RX_FC BIT(5) diff --git a/target/linux/generic/pending-5.15/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch b/target/linux/generic/pending-5.15/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch index 0496752f780..4f765c5c145 100644 --- a/target/linux/generic/pending-5.15/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch +++ b/target/linux/generic/pending-5.15/736-01-net-ethernet-mtk_eth_soc-add-code-for-offloading-flo.patch @@ -14,7 +14,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -1319,6 +1319,9 @@ int mtk_gmac_rgmii_path_setup(struct mtk +@@ -1277,6 +1277,9 @@ int mtk_gmac_rgmii_path_setup(struct mtk int mtk_eth_offload_init(struct mtk_eth *eth); int mtk_eth_setup_tc(struct net_device *dev, enum tc_setup_type type, void *type_data); diff --git a/target/linux/generic/pending-5.15/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch b/target/linux/generic/pending-5.15/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch index f43d15d1149..29d6e0b0991 100644 --- a/target/linux/generic/pending-5.15/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch +++ b/target/linux/generic/pending-5.15/736-03-net-ethernet-mtk_eth_soc-improve-keeping-track-of-of.patch @@ -12,7 +12,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c -@@ -466,26 +466,30 @@ int mtk_foe_entry_set_queue(struct mtk_e +@@ -466,42 +466,43 @@ int mtk_foe_entry_set_queue(struct mtk_e return 0; } @@ -51,15 +51,17 @@ Signed-off-by: Felix Fietkau +__mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry, + bool set_state) { - struct hlist_head *head; +- struct hlist_head *head; struct hlist_node *tmp; -@@ -495,13 +499,12 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp + + if (entry->type == MTK_FLOW_TYPE_L2) { + rhashtable_remove_fast(&ppe->l2_flows, &entry->l2_node, mtk_flow_l2_ht_params); - head = &entry->l2_flows; +- head = &entry->l2_flows; - hlist_for_each_entry_safe(entry, tmp, head, l2_data.list) - __mtk_foe_entry_clear(ppe, entry); -+ hlist_for_each_entry_safe(entry, tmp, head, list) ++ hlist_for_each_entry_safe(entry, tmp, &entry->l2_flows, l2_list) + __mtk_foe_entry_clear(ppe, entry, set_state); return; } @@ -70,16 +72,17 @@ Signed-off-by: Felix Fietkau struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, entry->hash); hwe->ib1 &= ~MTK_FOE_IB1_STATE; -@@ -520,7 +523,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp +@@ -520,7 +521,8 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp if (entry->type != MTK_FLOW_TYPE_L2_SUBFLOW) return; - hlist_del_init(&entry->l2_data.list); ++ hlist_del_init(&entry->l2_list); + hlist_del_init(&entry->list); kfree(entry); } -@@ -536,66 +539,55 @@ static int __mtk_foe_entry_idle_time(str +@@ -536,66 +538,55 @@ static int __mtk_foe_entry_idle_time(str return now - timestamp; } @@ -118,7 +121,7 @@ Signed-off-by: Felix Fietkau idle = __mtk_foe_entry_idle_time(ppe, entry->data.ib1); - hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_data.list) { -+ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, list) { ++ hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { int cur_idle; - u32 ib1; - @@ -175,7 +178,7 @@ Signed-off-by: Felix Fietkau } static void -@@ -632,7 +624,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p +@@ -632,7 +623,8 @@ __mtk_foe_entry_commit(struct mtk_ppe *p void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) { spin_lock_bh(&ppe_lock); @@ -185,7 +188,7 @@ Signed-off-by: Felix Fietkau spin_unlock_bh(&ppe_lock); } -@@ -679,8 +672,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ +@@ -679,8 +671,8 @@ mtk_foe_entry_commit_subflow(struct mtk_ { const struct mtk_soc_data *soc = ppe->eth->soc; struct mtk_flow_entry *flow_info; @@ -195,7 +198,7 @@ Signed-off-by: Felix Fietkau u32 ib1_mask = mtk_get_ib1_pkt_type_mask(ppe->eth) | MTK_FOE_IB1_UDP; int type; -@@ -688,30 +681,30 @@ mtk_foe_entry_commit_subflow(struct mtk_ +@@ -688,30 +680,30 @@ mtk_foe_entry_commit_subflow(struct mtk_ if (!flow_info) return; @@ -205,7 +208,7 @@ Signed-off-by: Felix Fietkau hlist_add_head(&flow_info->list, &ppe->foe_flow[hash / soc->hash_offset]); - hlist_add_head(&flow_info->l2_data.list, &entry->l2_flows); -+ hlist_add_head(&flow_info->list, &entry->l2_flows); ++ hlist_add_head(&flow_info->l2_list, &entry->l2_flows); hwe = mtk_foe_get_entry(ppe, hash); - memcpy(&foe, hwe, soc->foe_entry_size); @@ -236,7 +239,7 @@ Signed-off-by: Felix Fietkau } void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash) -@@ -721,9 +714,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe +@@ -721,9 +713,11 @@ void __mtk_ppe_check_skb(struct mtk_ppe struct mtk_foe_entry *hwe = mtk_foe_get_entry(ppe, hash); struct mtk_flow_entry *entry; struct mtk_foe_bridge key = {}; @@ -248,7 +251,7 @@ Signed-off-by: Felix Fietkau u8 *tag; spin_lock_bh(&ppe_lock); -@@ -731,20 +726,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe +@@ -731,20 +725,14 @@ void __mtk_ppe_check_skb(struct mtk_ppe if (FIELD_GET(MTK_FOE_IB1_STATE, hwe->ib1) == MTK_FOE_STATE_BIND) goto out; @@ -275,7 +278,7 @@ Signed-off-by: Felix Fietkau continue; } -@@ -795,9 +784,17 @@ out: +@@ -795,9 +783,17 @@ out: int mtk_foe_entry_idle_time(struct mtk_ppe *ppe, struct mtk_flow_entry *entry) { @@ -297,7 +300,21 @@ Signed-off-by: Felix Fietkau int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) --- a/drivers/net/ethernet/mediatek/mtk_ppe.h +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h -@@ -275,13 +275,7 @@ struct mtk_flow_entry { +@@ -265,7 +265,12 @@ enum { + + struct mtk_flow_entry { + union { +- struct hlist_node list; ++ /* regular flows + L2 subflows */ ++ struct { ++ struct hlist_node list; ++ struct hlist_node l2_list; ++ }; ++ /* L2 flows */ + struct { + struct rhash_head l2_node; + struct hlist_head l2_flows; +@@ -275,13 +280,7 @@ struct mtk_flow_entry { s8 wed_index; u8 ppe_index; u16 hash; diff --git a/target/linux/generic/pending-5.15/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch b/target/linux/generic/pending-5.15/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch index 43be09102e6..fdc3d520c11 100644 --- a/target/linux/generic/pending-5.15/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch +++ b/target/linux/generic/pending-5.15/736-04-net-ethernet-mediatek-fix-ppe-flow-accounting-for-L2.patch @@ -77,7 +77,7 @@ Signed-off-by: Felix Fietkau } static void mtk_ppe_cache_clear(struct mtk_ppe *ppe) -@@ -510,13 +525,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp +@@ -508,13 +523,6 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp hwe->ib1 &= ~MTK_FOE_IB1_STATE; hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); dma_wmb(); @@ -91,7 +91,7 @@ Signed-off-by: Felix Fietkau } entry->hash = 0xffff; -@@ -540,11 +548,14 @@ static int __mtk_foe_entry_idle_time(str +@@ -539,11 +547,14 @@ static int __mtk_foe_entry_idle_time(str } static bool @@ -107,7 +107,7 @@ Signed-off-by: Felix Fietkau int len; if (hash == 0xffff) -@@ -555,18 +566,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp +@@ -554,18 +565,35 @@ mtk_flow_entry_update(struct mtk_ppe *pp memcpy(&foe, hwe, len); if (!mtk_flow_entry_match(ppe->eth, entry, &foe, len) || @@ -146,8 +146,8 @@ Signed-off-by: Felix Fietkau struct mtk_flow_entry *cur; struct hlist_node *tmp; int idle; -@@ -575,7 +603,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe - hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, list) { +@@ -574,7 +602,9 @@ mtk_flow_entry_update_l2(struct mtk_ppe + hlist_for_each_entry_safe(cur, tmp, &entry->l2_flows, l2_list) { int cur_idle; - if (!mtk_flow_entry_update(ppe, cur)) { @@ -157,7 +157,7 @@ Signed-off-by: Felix Fietkau __mtk_foe_entry_clear(ppe, entry, false); continue; } -@@ -590,10 +620,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe +@@ -589,10 +619,29 @@ mtk_flow_entry_update_l2(struct mtk_ppe } } @@ -187,7 +187,7 @@ Signed-off-by: Felix Fietkau struct mtk_eth *eth = ppe->eth; u16 timestamp = mtk_eth_timestamp(eth); struct mtk_foe_entry *hwe; -@@ -618,6 +667,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p +@@ -617,6 +666,12 @@ __mtk_foe_entry_commit(struct mtk_ppe *p dma_wmb(); @@ -200,7 +200,7 @@ Signed-off-by: Felix Fietkau mtk_ppe_cache_clear(ppe); } -@@ -782,21 +837,6 @@ out: +@@ -781,21 +836,6 @@ out: spin_unlock_bh(&ppe_lock); } @@ -222,7 +222,7 @@ Signed-off-by: Felix Fietkau int mtk_ppe_prepare_reset(struct mtk_ppe *ppe) { if (!ppe) -@@ -824,32 +864,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe +@@ -823,32 +863,6 @@ int mtk_ppe_prepare_reset(struct mtk_ppe return mtk_ppe_wait_busy(ppe); } @@ -257,7 +257,7 @@ Signed-off-by: Felix Fietkau bool accounting = eth->soc->has_accounting; --- a/drivers/net/ethernet/mediatek/mtk_ppe.h +++ b/drivers/net/ethernet/mediatek/mtk_ppe.h -@@ -278,6 +278,8 @@ struct mtk_flow_entry { +@@ -283,6 +283,8 @@ struct mtk_flow_entry { struct mtk_foe_entry data; struct rhash_head node; unsigned long cookie; @@ -266,7 +266,7 @@ Signed-off-by: Felix Fietkau }; struct mtk_mib_entry { -@@ -320,6 +322,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ +@@ -325,6 +327,7 @@ struct mtk_ppe *mtk_ppe_init(struct mtk_ void mtk_ppe_start(struct mtk_ppe *ppe); int mtk_ppe_stop(struct mtk_ppe *ppe); int mtk_ppe_prepare_reset(struct mtk_ppe *ppe); @@ -274,7 +274,7 @@ Signed-off-by: Felix Fietkau void __mtk_ppe_check_skb(struct mtk_ppe *ppe, struct sk_buff *skb, u16 hash); -@@ -368,9 +371,8 @@ int mtk_foe_entry_set_queue(struct mtk_e +@@ -373,9 +376,8 @@ int mtk_foe_entry_set_queue(struct mtk_e unsigned int queue); int mtk_foe_entry_commit(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); void mtk_foe_entry_clear(struct mtk_ppe *ppe, struct mtk_flow_entry *entry); diff --git a/target/linux/generic/pending-5.15/736-05-net-ethernet-mtk_eth_soc-add-missing-ppe-cache-flush.patch b/target/linux/generic/pending-5.15/736-05-net-ethernet-mtk_eth_soc-add-missing-ppe-cache-flush.patch index 0b876acacf7..30839319c7d 100644 --- a/target/linux/generic/pending-5.15/736-05-net-ethernet-mtk_eth_soc-add-missing-ppe-cache-flush.patch +++ b/target/linux/generic/pending-5.15/736-05-net-ethernet-mtk_eth_soc-add-missing-ppe-cache-flush.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c -@@ -525,6 +525,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp +@@ -523,6 +523,7 @@ __mtk_foe_entry_clear(struct mtk_ppe *pp hwe->ib1 &= ~MTK_FOE_IB1_STATE; hwe->ib1 |= FIELD_PREP(MTK_FOE_IB1_STATE, MTK_FOE_STATE_INVALID); dma_wmb(); diff --git a/target/linux/generic/pending-5.15/736-06-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch b/target/linux/generic/pending-5.15/736-06-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch index c3d6dd4bbbf..3dfa193e707 100644 --- a/target/linux/generic/pending-5.15/736-06-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch +++ b/target/linux/generic/pending-5.15/736-06-net-ethernet-mediatek-fix-ppe-flow-accounting-for-v1.patch @@ -11,7 +11,7 @@ Signed-off-by: Felix Fietkau --- a/drivers/net/ethernet/mediatek/mtk_ppe.c +++ b/drivers/net/ethernet/mediatek/mtk_ppe.c -@@ -647,6 +647,7 @@ __mtk_foe_entry_commit(struct mtk_ppe *p +@@ -646,6 +646,7 @@ __mtk_foe_entry_commit(struct mtk_ppe *p struct mtk_eth *eth = ppe->eth; u16 timestamp = mtk_eth_timestamp(eth); struct mtk_foe_entry *hwe; @@ -19,7 +19,7 @@ Signed-off-by: Felix Fietkau if (MTK_HAS_CAPS(eth->soc->caps, MTK_NETSYS_V2)) { entry->ib1 &= ~MTK_FOE_IB1_BIND_TIMESTAMP_V2; -@@ -663,8 +664,13 @@ __mtk_foe_entry_commit(struct mtk_ppe *p +@@ -662,8 +663,13 @@ __mtk_foe_entry_commit(struct mtk_ppe *p wmb(); hwe->ib1 = entry->ib1; diff --git a/target/linux/generic/pending-5.15/760-net-core-add-optional-threading-for-backlog-processi.patch b/target/linux/generic/pending-5.15/760-net-core-add-optional-threading-for-backlog-processi.patch new file mode 100644 index 00000000000..c26491abdbe --- /dev/null +++ b/target/linux/generic/pending-5.15/760-net-core-add-optional-threading-for-backlog-processi.patch @@ -0,0 +1,224 @@ +From: Felix Fietkau +Date: Thu, 16 Feb 2023 18:39:04 +0100 +Subject: [PATCH] net/core: add optional threading for backlog processing + +When dealing with few flows or an imbalance on CPU utilization, static RPS +CPU assignment can be too inflexible. Add support for enabling threaded NAPI +for backlog processing in order to allow the scheduler to better balance +processing. This helps better spread the load across idle CPUs. + +Signed-off-by: Felix Fietkau +--- + +--- a/include/linux/netdevice.h ++++ b/include/linux/netdevice.h +@@ -502,6 +502,7 @@ static inline bool napi_complete(struct + } + + int dev_set_threaded(struct net_device *dev, bool threaded); ++int backlog_set_threaded(bool threaded); + + /** + * napi_disable - prevent NAPI from scheduling +@@ -3365,6 +3366,7 @@ struct softnet_data { + unsigned int processed; + unsigned int time_squeeze; + unsigned int received_rps; ++ unsigned int process_queue_empty; + #ifdef CONFIG_RPS + struct softnet_data *rps_ipi_list; + #endif +--- a/net/core/dev.c ++++ b/net/core/dev.c +@@ -4574,7 +4574,7 @@ static int rps_ipi_queued(struct softnet + #ifdef CONFIG_RPS + struct softnet_data *mysd = this_cpu_ptr(&softnet_data); + +- if (sd != mysd) { ++ if (sd != mysd && !test_bit(NAPI_STATE_THREADED, &sd->backlog.state)) { + sd->rps_ipi_next = mysd->rps_ipi_list; + mysd->rps_ipi_list = sd; + +@@ -5755,6 +5755,8 @@ static DEFINE_PER_CPU(struct work_struct + /* Network device is going away, flush any packets still pending */ + static void flush_backlog(struct work_struct *work) + { ++ unsigned int process_queue_empty; ++ bool threaded, flush_processq; + struct sk_buff *skb, *tmp; + struct softnet_data *sd; + +@@ -5770,9 +5772,18 @@ static void flush_backlog(struct work_st + input_queue_head_incr(sd); + } + } ++ ++ threaded = test_bit(NAPI_STATE_THREADED, &sd->backlog.state); ++ flush_processq = threaded && ++ !skb_queue_empty_lockless(&sd->process_queue); ++ if (flush_processq) ++ process_queue_empty = sd->process_queue_empty; + rps_unlock(sd); + local_irq_enable(); + ++ if (threaded) ++ goto out; ++ + skb_queue_walk_safe(&sd->process_queue, skb, tmp) { + if (skb->dev->reg_state == NETREG_UNREGISTERING) { + __skb_unlink(skb, &sd->process_queue); +@@ -5780,7 +5791,18 @@ static void flush_backlog(struct work_st + input_queue_head_incr(sd); + } + } ++ ++out: + local_bh_enable(); ++ ++ while (flush_processq) { ++ msleep(1); ++ local_irq_disable(); ++ rps_lock(sd); ++ flush_processq = process_queue_empty == sd->process_queue_empty; ++ rps_unlock(sd); ++ local_irq_enable(); ++ } + } + + static bool flush_required(int cpu) +@@ -6463,6 +6485,7 @@ static int process_backlog(struct napi_s + + local_irq_disable(); + rps_lock(sd); ++ sd->process_queue_empty++; + if (skb_queue_empty(&sd->input_pkt_queue)) { + /* + * Inline a custom version of __napi_complete(). +@@ -6472,7 +6495,8 @@ static int process_backlog(struct napi_s + * We can use a plain write instead of clear_bit(), + * and we dont need an smp_mb() memory barrier. + */ +- napi->state = 0; ++ napi->state &= ~(NAPIF_STATE_SCHED | ++ NAPIF_STATE_SCHED_THREADED); + again = false; + } else { + skb_queue_splice_tail_init(&sd->input_pkt_queue, +@@ -6889,6 +6913,57 @@ int dev_set_threaded(struct net_device * + } + EXPORT_SYMBOL(dev_set_threaded); + ++int backlog_set_threaded(bool threaded) ++{ ++ static bool backlog_threaded; ++ int err = 0; ++ int i; ++ ++ if (backlog_threaded == threaded) ++ return 0; ++ ++ for_each_possible_cpu(i) { ++ struct softnet_data *sd = &per_cpu(softnet_data, i); ++ struct napi_struct *n = &sd->backlog; ++ ++ if (n->thread) ++ continue; ++ n->thread = kthread_run(napi_threaded_poll, n, "napi/backlog-%d", i); ++ if (IS_ERR(n->thread)) { ++ err = PTR_ERR(n->thread); ++ pr_err("kthread_run failed with err %d\n", err); ++ n->thread = NULL; ++ threaded = false; ++ break; ++ } ++ ++ } ++ ++ backlog_threaded = threaded; ++ ++ /* Make sure kthread is created before THREADED bit ++ * is set. ++ */ ++ smp_mb__before_atomic(); ++ ++ for_each_possible_cpu(i) { ++ struct softnet_data *sd = &per_cpu(softnet_data, i); ++ struct napi_struct *n = &sd->backlog; ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ rps_lock(sd); ++ if (threaded) ++ n->state |= NAPIF_STATE_THREADED; ++ else ++ n->state &= ~NAPIF_STATE_THREADED; ++ rps_unlock(sd); ++ local_irq_restore(flags); ++ } ++ ++ return err; ++} ++ + void netif_napi_add(struct net_device *dev, struct napi_struct *napi, + int (*poll)(struct napi_struct *, int), int weight) + { +@@ -11367,6 +11442,9 @@ static int dev_cpu_dead(unsigned int old + raise_softirq_irqoff(NET_TX_SOFTIRQ); + local_irq_enable(); + ++ if (test_bit(NAPI_STATE_THREADED, &oldsd->backlog.state)) ++ return 0; ++ + #ifdef CONFIG_RPS + remsd = oldsd->rps_ipi_list; + oldsd->rps_ipi_list = NULL; +--- a/net/core/sysctl_net_core.c ++++ b/net/core/sysctl_net_core.c +@@ -28,6 +28,7 @@ static int int_3600 = 3600; + static int min_sndbuf = SOCK_MIN_SNDBUF; + static int min_rcvbuf = SOCK_MIN_RCVBUF; + static int max_skb_frags = MAX_SKB_FRAGS; ++static int backlog_threaded; + static long long_one __maybe_unused = 1; + static long long_max __maybe_unused = LONG_MAX; + +@@ -114,6 +115,23 @@ static int rps_sock_flow_sysctl(struct c + } + #endif /* CONFIG_RPS */ + ++static int backlog_threaded_sysctl(struct ctl_table *table, int write, ++ void *buffer, size_t *lenp, loff_t *ppos) ++{ ++ static DEFINE_MUTEX(backlog_threaded_mutex); ++ int ret; ++ ++ mutex_lock(&backlog_threaded_mutex); ++ ++ ret = proc_dointvec_minmax(table, write, buffer, lenp, ppos); ++ if (write && !ret) ++ ret = backlog_set_threaded(backlog_threaded); ++ ++ mutex_unlock(&backlog_threaded_mutex); ++ ++ return ret; ++} ++ + #ifdef CONFIG_NET_FLOW_LIMIT + static DEFINE_MUTEX(flow_limit_update_mutex); + +@@ -470,6 +488,15 @@ static struct ctl_table net_core_table[] + .proc_handler = rps_sock_flow_sysctl + }, + #endif ++ { ++ .procname = "backlog_threaded", ++ .data = &backlog_threaded, ++ .maxlen = sizeof(unsigned int), ++ .mode = 0644, ++ .proc_handler = backlog_threaded_sysctl, ++ .extra1 = SYSCTL_ZERO, ++ .extra2 = SYSCTL_ONE ++ }, + #ifdef CONFIG_NET_FLOW_LIMIT + { + .procname = "flow_limit_cpu_bitmap", diff --git a/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch b/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch index 8a718a02f21..335a2b04ed9 100644 --- a/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch +++ b/target/linux/generic/pending-5.15/768-net-dsa-mv88e6xxx-Request-assisted-learning-on-CPU-port.patch @@ -17,7 +17,7 @@ Signed-off-by: Tobias Waldekranz --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c -@@ -6319,6 +6319,7 @@ static int mv88e6xxx_register_switch(str +@@ -6327,6 +6327,7 @@ static int mv88e6xxx_register_switch(str ds->ops = &mv88e6xxx_switch_ops; ds->ageing_time_min = chip->info->age_time_coeff; ds->ageing_time_max = chip->info->age_time_coeff * U8_MAX; diff --git a/target/linux/generic/pending-5.15/770-net-dsa-b53-mmap-fix-device-tree-support.patch b/target/linux/generic/pending-5.15/770-net-dsa-b53-mmap-fix-device-tree-support.patch deleted file mode 100644 index 50db56af274..00000000000 --- a/target/linux/generic/pending-5.15/770-net-dsa-b53-mmap-fix-device-tree-support.patch +++ /dev/null @@ -1,104 +0,0 @@ -From patchwork Thu Mar 16 17:28:07 2023 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= - -X-Patchwork-Id: 13178135 -X-Patchwork-Delegate: kuba@kernel.org -Return-Path: -X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on - aws-us-west-2-korg-lkml-1.web.codeaurora.org -Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) - by smtp.lore.kernel.org (Postfix) with ESMTP id 9F385C6FD19 - for ; Thu, 16 Mar 2023 17:29:18 +0000 (UTC) -Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand - id S229918AbjCPR3Q (ORCPT ); - Thu, 16 Mar 2023 13:29:16 -0400 -Received: from lindbergh.monkeyblade.net ([23.128.96.19]:44474 "EHLO - lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org - with ESMTP id S229902AbjCPR3G (ORCPT - ); Thu, 16 Mar 2023 13:29:06 -0400 -Received: from mail-wr1-x433.google.com (mail-wr1-x433.google.com - [IPv6:2a00:1450:4864:20::433]) - by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DB3CC1D929; - Thu, 16 Mar 2023 10:29:03 -0700 (PDT) -Received: by mail-wr1-x433.google.com with SMTP id y14so2246984wrq.4; - Thu, 16 Mar 2023 10:29:03 -0700 (PDT) -DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=gmail.com; s=20210112; t=1678987742; - h=content-transfer-encoding:mime-version:references:in-reply-to - :message-id:date:subject:cc:to:from:from:to:cc:subject:date - :message-id:reply-to; - bh=n/Cr1XNCJNfBa+dPDtalOZXIIV6S3ucGcQ6qFqXXuSc=; - b=Ka0GgVg9azCPEBkuB0FcSt9tMYqZVblZJl1uT4KWVRAEgVcgqi44vSYpvlzc3TfFMI - Ulqrk15TYN022WhQxI63v/NQum/s+yKg2G7wZYj2wHxsGAaoaLtFw8lsjoqTx8NZjsXO - OIlTQqkyvDBE4t4FkO9w47A9Q2ruNzohH7Mng3Kraup/9Wyb3PGdybj/sC9/Pvnw6aPz - 4Ep0ufMhgNjCk8X2buy346Tcow1zyQVQeGAZrxQzPbWcwa5rFbWGv6iD4wYbHKYm/DZV - ah4lc+p0anRvAsNDlqj9fa+VY96nD4X/R7q5M3u7jTEL5YPvrOH5a8y5P5c92mWXiI7i - RABA== -X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=1e100.net; s=20210112; t=1678987742; - h=content-transfer-encoding:mime-version:references:in-reply-to - :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc - :subject:date:message-id:reply-to; - bh=n/Cr1XNCJNfBa+dPDtalOZXIIV6S3ucGcQ6qFqXXuSc=; - b=RnTP/w7AloNUBWmU5C26i+lQQMLIbND8o8gY/M87Dw22DuDdCIQQxRQypN9pzW4fmS - VfNg/kh5Hydzm2bSw7AXMtT0hrbTfBZr2sB6BEKlojw3txmRVCoDGvlZLhTCRtPYGECX - WqK1d37sgZ+bBEtjsPfCoVxdLpMCAjxlzLTCnwIclXsHLJg6Tsup33wu6kCFaQz1GYi1 - hDlc0KC9TZBXr0+3kzzXVyp0X3P30TlAamlojgoi44oa8246DcMBMtsbhiRPLUsdcIfs - k+UDK2IemrCvgh5hfN3tX/DtwzoOxPjmB2FZjdxi7+6+c/abS5IYjQ8I1b30JOzDZeMs - E6nA== -X-Gm-Message-State: AO0yUKXFx+XUMUlLCdoCi82z77K5Ax1VNa78esJmOz5Jv6sQIYmKTgvD - VrU+Kl3YdJgxxLbxpm1e8RI= -X-Google-Smtp-Source: - AK7set8fFjLrpZ1Ll4TW9iTex7qD+OJccAtDrfibvtBWYkVr0jRxWbwBsrVExeDl051bEIdDmJrszQ== -X-Received: by 2002:a5d:61c7:0:b0:2ce:a697:75c7 with SMTP id - q7-20020a5d61c7000000b002cea69775c7mr4660631wrv.33.1678987742168; - Thu, 16 Mar 2023 10:29:02 -0700 (PDT) -Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. - [79.146.124.255]) - by smtp.gmail.com with ESMTPSA id - n7-20020a5d4847000000b002c5d3f0f737sm7719124wrs.30.2023.03.16.10.29.01 - (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); - Thu, 16 Mar 2023 10:29:01 -0700 (PDT) -From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= -To: f.fainelli@gmail.com, jonas.gorski@gmail.com, andrew@lunn.ch, - olteanv@gmail.com, davem@davemloft.net, edumazet@google.com, - kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org, - linux-kernel@vger.kernel.org -Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= -Subject: [PATCH v2] net: dsa: b53: mmap: fix device tree support -Date: Thu, 16 Mar 2023 18:28:07 +0100 -Message-Id: <20230316172807.460146-1-noltari@gmail.com> -X-Mailer: git-send-email 2.30.2 -In-Reply-To: <20230310121059.4498-1-noltari@gmail.com> -References: <20230310121059.4498-1-noltari@gmail.com> -MIME-Version: 1.0 -Precedence: bulk -List-ID: -X-Mailing-List: netdev@vger.kernel.org -X-Patchwork-Delegate: kuba@kernel.org - -CPU port should also be enabled in order to get a working switch. - -Fixes: a5538a777b73 ("net: dsa: b53: mmap: Add device tree support") -Signed-off-by: Álvaro Fernández Rojas -Acked-by: Florian Fainelli ---- - v2: switch to B53_N_PORTS - - drivers/net/dsa/b53/b53_mmap.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - ---- a/drivers/net/dsa/b53/b53_mmap.c -+++ b/drivers/net/dsa/b53/b53_mmap.c -@@ -263,7 +263,7 @@ static int b53_mmap_probe_of(struct plat - if (of_property_read_u32(of_port, "reg", ®)) - continue; - -- if (reg < B53_CPU_PORT) -+ if (reg < B53_N_PORTS) - pdata->enabled_ports |= BIT(reg); - } - diff --git a/target/linux/generic/pending-5.15/771-net-dsa-tag_brcm-legacy-fix-daisy-chained-switches.patch b/target/linux/generic/pending-5.15/771-net-dsa-tag_brcm-legacy-fix-daisy-chained-switches.patch deleted file mode 100644 index 6c79bffdf41..00000000000 --- a/target/linux/generic/pending-5.15/771-net-dsa-tag_brcm-legacy-fix-daisy-chained-switches.patch +++ /dev/null @@ -1,140 +0,0 @@ -From patchwork Sun Mar 19 09:55:40 2023 -Content-Type: text/plain; charset="utf-8" -MIME-Version: 1.0 -Content-Transfer-Encoding: 8bit -X-Patchwork-Submitter: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= - -X-Patchwork-Id: 13180301 -X-Patchwork-Delegate: kuba@kernel.org -Return-Path: -X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on - aws-us-west-2-korg-lkml-1.web.codeaurora.org -Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) - by smtp.lore.kernel.org (Postfix) with ESMTP id 59CF8C7618A - for ; Sun, 19 Mar 2023 09:55:59 +0000 (UTC) -Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand - id S229723AbjCSJzz (ORCPT ); - Sun, 19 Mar 2023 05:55:55 -0400 -Received: from lindbergh.monkeyblade.net ([23.128.96.19]:33746 "EHLO - lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org - with ESMTP id S229468AbjCSJzv (ORCPT - ); Sun, 19 Mar 2023 05:55:51 -0400 -Received: from mail-wm1-x333.google.com (mail-wm1-x333.google.com - [IPv6:2a00:1450:4864:20::333]) - by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 83BCD23A6E; - Sun, 19 Mar 2023 02:55:49 -0700 (PDT) -Received: by mail-wm1-x333.google.com with SMTP id - l15-20020a05600c4f0f00b003ed58a9a15eso5776403wmq.5; - Sun, 19 Mar 2023 02:55:49 -0700 (PDT) -DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=gmail.com; s=20210112; t=1679219748; - h=content-transfer-encoding:mime-version:references:in-reply-to - :message-id:date:subject:cc:to:from:from:to:cc:subject:date - :message-id:reply-to; - bh=yaNuTTfeHI1WnGa3QC7carZ37ibM4EyUyUnDDBSr6nM=; - b=bTf0pdvAUXMqrJw4A+PLFfwONMAaXL3S4GDMJH3tYgRz/0Vpy7FkmgpWveMhjrqiDM - O6v17DizCYtzUrLXC9z9mMD3F8tl0SETaor8aE/MtvnxVq/Yq80WT5xnEh0iJBhWnRP1 - 0ZKyuoqWZQPnQ9vXbctEu3ZPxub9szdfmxWCtutESvAIvY7Y1qt4ZGg4ZDexov88P4lN - pgP6KaBNSVKSsVlDL4ukHqWyrkzpPtcOmDrPC8/HUTsraFnQ4iOL8vVR2Q26qKf2JI6m - yKwAd88ZKSnFRAyGdXAoXqiAKkcmDW5523samaxmBI23gxG5ryD3JnZBlo5r55gnPGo9 - C0uQ== -X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; - d=1e100.net; s=20210112; t=1679219748; - h=content-transfer-encoding:mime-version:references:in-reply-to - :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc - :subject:date:message-id:reply-to; - bh=yaNuTTfeHI1WnGa3QC7carZ37ibM4EyUyUnDDBSr6nM=; - b=buUr8oNY3Rb4YRuvXOFLPRXT0v9e8f87X1i1bsLfYPL/CokAJIhwAaChGjvMN1l3zP - YCfM9Xynl3tF3k4nrb1xKGmc7LuHONe8KjOgFgFuvtepBg7uvBdwi7A3UmbznhgUpzlo - HE7X3S5dfDNX/LLwpOyWM34Hk+w6i6pXP2FD7CB/1TwvdZFjnPHbhDhQTnJQNFaFmXz2 - d4WxBzxqU6k+4PC4AHRhGkn8QunICQ90MDk9NgNtuT5E1tTh5Bz4ykQ95sKKeTih3OBO - sx0mBrCqsTwpteTweNvnMuoOxmyKbM8Ca8cwS1QpfIWI0ASy+j7j2SiTG7nrKl2hUJTW - heAQ== -X-Gm-Message-State: AO0yUKWVY0SzDxRYNSJrKpnV6m176lCPK8B2N12aX56FfpuGNCuaPAmf - wYaBrXYVORDSYFVtNsY7gc3xMZp1z+ba7A== -X-Google-Smtp-Source: - AK7set8q+UMyrNpNNfcRo8o3ynviTGRk1oKu2CmzwYJVZxCAYYvXmLtyXvhcnqpBROVi7Kj+1rKDuw== -X-Received: by 2002:a05:600c:4f50:b0:3ed:4b0f:5378 with SMTP id - m16-20020a05600c4f5000b003ed4b0f5378mr11519857wmq.27.1679219747816; - Sun, 19 Mar 2023 02:55:47 -0700 (PDT) -Received: from atlantis.lan (255.red-79-146-124.dynamicip.rima-tde.net. - [79.146.124.255]) - by smtp.gmail.com with ESMTPSA id - i26-20020a1c541a000000b003ed246f76a2sm13390609wmb.1.2023.03.19.02.55.46 - (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); - Sun, 19 Mar 2023 02:55:47 -0700 (PDT) -From: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= -To: andrew@lunn.ch, f.fainelli@gmail.com, jonas.gorski@gmail.com, - olteanv@gmail.com, davem@davemloft.net, edumazet@google.com, - kuba@kernel.org, pabeni@redhat.com, netdev@vger.kernel.org, - linux-kernel@vger.kernel.org -Cc: =?utf-8?q?=C3=81lvaro_Fern=C3=A1ndez_Rojas?= , - Michal Swiatkowski -Subject: [PATCH v2] net: dsa: tag_brcm: legacy: fix daisy-chained switches -Date: Sun, 19 Mar 2023 10:55:40 +0100 -Message-Id: <20230319095540.239064-1-noltari@gmail.com> -X-Mailer: git-send-email 2.30.2 -In-Reply-To: <20230317120815.321871-1-noltari@gmail.com> -References: <20230317120815.321871-1-noltari@gmail.com> -MIME-Version: 1.0 -Precedence: bulk -List-ID: -X-Mailing-List: netdev@vger.kernel.org -X-Patchwork-Delegate: kuba@kernel.org - -When BCM63xx internal switches are connected to switches with a 4-byte -Broadcom tag, it does not identify the packet as VLAN tagged, so it adds one -based on its PVID (which is likely 0). -Right now, the packet is received by the BCM63xx internal switch and the 6-byte -tag is properly processed. The next step would to decode the corresponding -4-byte tag. However, the internal switch adds an invalid VLAN tag after the -6-byte tag and the 4-byte tag handling fails. -In order to fix this we need to remove the invalid VLAN tag after the 6-byte -tag before passing it to the 4-byte tag decoding. - -Fixes: 964dbf186eaa ("net: dsa: tag_brcm: add support for legacy tags") -Signed-off-by: Álvaro Fernández Rojas -Reviewed-by: Michal Swiatkowski ---- - v2: add missing fixes tag. - - net/dsa/tag_brcm.c | 10 ++++++++-- - 1 file changed, 8 insertions(+), 2 deletions(-) - ---- a/net/dsa/tag_brcm.c -+++ b/net/dsa/tag_brcm.c -@@ -7,6 +7,7 @@ - - #include - #include -+#include - #include - #include - -@@ -248,6 +249,7 @@ static struct sk_buff *brcm_leg_tag_xmit - static struct sk_buff *brcm_leg_tag_rcv(struct sk_buff *skb, - struct net_device *dev) - { -+ int len = BRCM_LEG_TAG_LEN; - int source_port; - u8 *brcm_tag; - -@@ -262,12 +264,16 @@ static struct sk_buff *brcm_leg_tag_rcv( - if (!skb->dev) - return NULL; - -+ /* VLAN tag is added by BCM63xx internal switch */ -+ if (netdev_uses_dsa(skb->dev)) -+ len += VLAN_HLEN; -+ - /* Remove Broadcom tag and update checksum */ -- skb_pull_rcsum(skb, BRCM_LEG_TAG_LEN); -+ skb_pull_rcsum(skb, len); - - dsa_default_offload_fwd_mark(skb); - -- dsa_strip_etype_header(skb, BRCM_LEG_TAG_LEN); -+ dsa_strip_etype_header(skb, len); - - return skb; - } diff --git a/target/linux/ipq40xx/base-files/lib/upgrade/linksys.sh b/target/linux/ipq40xx/base-files/lib/upgrade/linksys.sh index c0d45d640dd..696f653eb9e 100644 --- a/target/linux/ipq40xx/base-files/lib/upgrade/linksys.sh +++ b/target/linux/ipq40xx/base-files/lib/upgrade/linksys.sh @@ -100,7 +100,12 @@ platform_do_upgrade_linksys() { fi # complete std upgrade - nand_upgrade_tar "$1" + if nand_upgrade_tar "$1" ; then + nand_do_upgrade_success + else + nand_do_upgrade_failure + fi + } [ "$magic_long" = "27051956" ] && { diff --git a/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz7360-v2.dts b/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz7360-v2.dts index 063d6dca5e0..a30746cde2b 100644 --- a/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz7360-v2.dts +++ b/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz7360-v2.dts @@ -42,6 +42,18 @@ label = "urlader"; reg = <0x00000 0x20000>; read-only; + + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + cal_urlader_985: cal@985 { + reg = <0x985 0x440>; + }; + + macaddr_urlader_a91: macaddr@a91 { + reg = <0xa91 0x6>; + }; }; partition@20000 { @@ -78,12 +90,8 @@ reset-gpios = <&gpio 44 GPIO_ACTIVE_LOW>; }; -&urlader { - compatible = "nvmem-cells"; - #address-cells = <1>; - #size-cells = <1>; - - macaddr_urlader_a91: macaddr@a91 { - reg = <0xa91 0x6>; - }; +&wifi { + /delete-property/ qca,no-eeprom; + nvmem-cells = <&cal_urlader_985>; + nvmem-cell-names = "calibration"; }; diff --git a/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz736x.dtsi b/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz736x.dtsi index 73f6e152bfa..7324127d2a8 100644 --- a/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz736x.dtsi +++ b/target/linux/lantiq/files/arch/mips/boot/dts/lantiq/vr9_avm_fritz736x.dtsi @@ -151,7 +151,7 @@ #address-cells = <2>; device_type = "pci"; - wifi@168c,002e { + wifi: wifi@168c,002e { compatible = "pci168c,002e"; reg = <0 0 0 0 0>; qca,no-eeprom; /* load from ath9k-eeprom-pci-0000:01:00.0.bin */ diff --git a/target/linux/lantiq/image/ar9.mk b/target/linux/lantiq/image/ar9.mk index 4d9a008f6fc..43f91d0ee45 100644 --- a/target/linux/lantiq/image/ar9.mk +++ b/target/linux/lantiq/image/ar9.mk @@ -17,6 +17,8 @@ define Device/avm_fritz7320 DEVICE_MODEL := FRITZ!Box 7320 DEVICE_ALT0_VENDOR := 1&1 DEVICE_ALT0_MODEL := HomeServer + DEVICE_ALT1_VENDOR := AVM + DEVICE_ALT1_MODEL := Fritz!box 7330 SOC := ar9 IMAGE_SIZE := 15744k LOADER_FLASH_OFFS := 0x31000 diff --git a/target/linux/lantiq/patches-5.15/0001-MIPS-lantiq-add-pcie-driver.patch b/target/linux/lantiq/patches-5.15/0001-MIPS-lantiq-add-pcie-driver.patch index ab88b108631..294bafed4a6 100644 --- a/target/linux/lantiq/patches-5.15/0001-MIPS-lantiq-add-pcie-driver.patch +++ b/target/linux/lantiq/patches-5.15/0001-MIPS-lantiq-add-pcie-driver.patch @@ -5479,7 +5479,7 @@ Signed-off-by: John Crispin (transaction layer end-to-end CRC checking). --- a/include/linux/pci.h +++ b/include/linux/pci.h -@@ -1481,6 +1481,8 @@ void pci_walk_bus(struct pci_bus *top, i +@@ -1482,6 +1482,8 @@ void pci_walk_bus(struct pci_bus *top, i void *userdata); int pci_cfg_space_size(struct pci_dev *dev); unsigned char pci_bus_max_busnr(struct pci_bus *bus); diff --git a/target/linux/lantiq/xrx200/base-files/etc/hotplug.d/firmware/12-ath9k-eeprom b/target/linux/lantiq/xrx200/base-files/etc/hotplug.d/firmware/12-ath9k-eeprom index c6bacb8d3c1..eefc39b30b4 100644 --- a/target/linux/lantiq/xrx200/base-files/etc/hotplug.d/firmware/12-ath9k-eeprom +++ b/target/linux/lantiq/xrx200/base-files/etc/hotplug.d/firmware/12-ath9k-eeprom @@ -19,8 +19,7 @@ case "$FIRMWARE" in avm,fritz3390) caldata_extract_reverse "urlader" 0x2546 0x440 ;; - avm,fritz7360sl|\ - avm,fritz7360-v2) + avm,fritz7360sl) caldata_extract "urlader" 0x985 0x1000 ;; avm,fritz7412|\ diff --git a/target/linux/mediatek/dts/mt7986a-asus-tuf-ax4200.dts b/target/linux/mediatek/dts/mt7986a-asus-tuf-ax4200.dts new file mode 100644 index 00000000000..0a2f1d1d596 --- /dev/null +++ b/target/linux/mediatek/dts/mt7986a-asus-tuf-ax4200.dts @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +/dts-v1/; +#include +#include +#include + +#include "mt7986a.dtsi" + +/ { + model = "ASUS TUF-AX4200"; + compatible = "asus,tuf-ax4200", "mediatek,mt7986a"; + + aliases { + serial0 = &uart0; + led-boot = &led_system; + led-failsafe = &led_system; + led-running = &led_system; + led-upgrade = &led_system; + }; + + chosen { + stdout-path = "serial0:115200n8"; + bootargs-override = "ubi.mtd=UBI_DEV"; + }; + + memory { + reg = <0 0x40000000 0 0x20000000>; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&pio 9 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + + mesh { + label = "wps"; + gpios = <&pio 10 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + wlan24 { + label = "white:wlan24"; + gpios = <&pio 1 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy0tpt"; + }; + + wlan5 { + label = "white:wlan5"; + gpios = <&pio 2 GPIO_ACTIVE_HIGH>; + linux,default-trigger = "phy1tpt"; + }; + + led_system: system { + label = "white:system"; + gpios = <&pio 11 GPIO_ACTIVE_HIGH>; + }; + + wan-red { + label = "red:wan"; + gpios = <&pio 12 GPIO_ACTIVE_LOW>; + }; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + reg_5v: regulator-5v { + compatible = "regulator-fixed"; + regulator-name = "fixed-5V"; + regulator-min-microvolt = <5000000>; + regulator-max-microvolt = <5000000>; + regulator-boot-on; + regulator-always-on; + }; +}; + +ð { + status = "okay"; + + gmac0: mac@0 { + /* LAN */ + compatible = "mediatek,eth-mac"; + reg = <0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + + gmac1: mac@1 { + /* WAN */ + compatible = "mediatek,eth-mac"; + reg = <1>; + phy-mode = "2500base-x"; + phy-handle = <&phy6>; + }; + + mdio: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + }; +}; + +&mdio { + phy6: phy@6 { + compatible = "ethernet-phy-ieee802.3-c45"; + reg = <6>; + + reset-gpios = <&pio 6 GPIO_ACTIVE_LOW>; + reset-assert-us = <10000>; + reset-deassert-us = <10000>; + + /* LED0: CONN (WAN white) */ + mxl,led-config = <0x00f0 0x0 0x0 0x0>; + }; + + switch: switch@0 { + compatible = "mediatek,mt7531"; + reg = <31>; + + reset-gpios = <&pio 5 GPIO_ACTIVE_HIGH>; + reset-assert-us = <10000>; + reset-deassert-us = <10000>; + }; +}; + +&pio { + spi_flash_pins: spi-flash-pins-33-to-38 { + mux { + function = "spi"; + groups = "spi0", "spi0_wp_hold"; + }; + conf-pu { + pins = "SPI2_CS", "SPI2_HOLD", "SPI2_WP"; + drive-strength = <8>; + mediatek,pull-up-adv = <0>; /* bias-disable */ + }; + conf-pd { + pins = "SPI2_CLK", "SPI2_MOSI", "SPI2_MISO"; + drive-strength = <8>; + mediatek,pull-down-adv = <0>; /* bias-disable */ + }; + }; + + wf_2g_5g_pins: wf_2g_5g-pins { + mux { + function = "wifi"; + groups = "wf_2g", "wf_5g"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = <4>; + }; + }; + + wf_dbdc_pins: wf-dbdc-pins { + mux { + function = "wifi"; + groups = "wf_dbdc"; + }; + conf { + pins = "WF0_HB1", "WF0_HB2", "WF0_HB3", "WF0_HB4", + "WF0_HB0", "WF0_HB0_B", "WF0_HB5", "WF0_HB6", + "WF0_HB7", "WF0_HB8", "WF0_HB9", "WF0_HB10", + "WF0_TOP_CLK", "WF0_TOP_DATA", "WF1_HB1", + "WF1_HB2", "WF1_HB3", "WF1_HB4", "WF1_HB0", + "WF1_HB5", "WF1_HB6", "WF1_HB7", "WF1_HB8", + "WF1_TOP_CLK", "WF1_TOP_DATA"; + drive-strength = <4>; + }; + }; +}; + +&spi0 { + pinctrl-names = "default"; + pinctrl-0 = <&spi_flash_pins>; + status = "okay"; + + spi_nand_flash: flash@0 { + compatible = "spi-nand"; + #address-cells = <1>; + #size-cells = <1>; + reg = <0>; + + spi-max-frequency = <20000000>; + spi-tx-buswidth = <4>; + spi-rx-buswidth = <4>; + + partitions: partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "bootloader"; + reg = <0x0 0x400000>; + read-only; + }; + + partition@400000 { + label = "UBI_DEV"; + reg = <0x400000 0xfc00000>; + }; + }; + }; +}; + +&switch { + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@1 { + reg = <1>; + label = "lan1"; + }; + + port@2 { + reg = <2>; + label = "lan2"; + }; + + port@3 { + reg = <3>; + label = "lan3"; + }; + + port@4 { + reg = <4>; + label = "lan4"; + }; + + port@6 { + reg = <6>; + label = "cpu"; + ethernet = <&gmac0>; + phy-mode = "2500base-x"; + + fixed-link { + speed = <2500>; + full-duplex; + pause; + }; + }; + }; +}; + +&wmac { + status = "okay"; + pinctrl-names = "default", "dbdc"; + pinctrl-0 = <&wf_2g_5g_pins>; + pinctrl-1 = <&wf_dbdc_pins>; +}; + +&uart0 { + status = "okay"; +}; + +&ssusb { + vusb33-supply = <®_3p3v>; + vbus-supply = <®_5v>; + status = "okay"; +}; + +&usb_phy { + status = "okay"; +}; \ No newline at end of file diff --git a/target/linux/mediatek/files-5.15/arch/arm64/boot/dts/mediatek/mt7981.dtsi b/target/linux/mediatek/files-5.15/arch/arm64/boot/dts/mediatek/mt7981.dtsi new file mode 100644 index 00000000000..279bce14e86 --- /dev/null +++ b/target/linux/mediatek/files-5.15/arch/arm64/boot/dts/mediatek/mt7981.dtsi @@ -0,0 +1,766 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) +/* + * Copyright (c) 2020 MediaTek Inc. + * Author: Sam.Shih + * Author: Jianhui Zhao + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/ { + compatible = "mediatek,mt7981"; + interrupt-parent = <&gic>; + #address-cells = <2>; + #size-cells = <2>; + + cpus { + #address-cells = <1>; + #size-cells = <0>; + + cpu@0 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x0>; + }; + + cpu@1 { + device_type = "cpu"; + compatible = "arm,cortex-a53"; + enable-method = "psci"; + reg = <0x1>; + }; + }; + + pwm: pwm@10048000 { + compatible = "mediatek,mt7981-pwm"; + reg = <0 0x10048000 0 0x1000>; + #pwm-cells = <2>; + clocks = <&infracfg CLK_INFRA_PWM_STA>, + <&infracfg CLK_INFRA_PWM_HCK>, + <&infracfg CLK_INFRA_PWM1_CK>, + <&infracfg CLK_INFRA_PWM2_CK>, + <&infracfg CLK_INFRA_PWM3_CK>; + clock-names = "top", "main", "pwm1", "pwm2", "pwm3"; + }; + + fan: pwm-fan { + compatible = "pwm-fan"; + /* cooling level (0, 1, 2, 3) : (0% duty, 50% duty, 75% duty, 100% duty) */ + cooling-levels = <0 128 192 255>; + #cooling-cells = <2>; + status = "disabled"; + }; + + thermal-zones { + cpu_thermal: cpu-thermal { + polling-delay-passive = <1000>; + polling-delay = <1000>; + thermal-sensors = <&thermal 0>; + trips { + cpu_trip_crit: crit { + temperature = <125000>; + hysteresis = <2000>; + type = "critical"; + }; + + cpu_trip_hot: hot { + temperature = <120000>; + hysteresis = <2000>; + type = "hot"; + }; + + cpu_trip_active_high: active-high { + temperature = <115000>; + hysteresis = <2000>; + type = "active"; + }; + + cpu_trip_active_med: active-med { + temperature = <85000>; + hysteresis = <2000>; + type = "active"; + }; + + cpu_trip_active_low: active-low { + temperature = <60000>; + hysteresis = <2000>; + type = "active"; + }; + }; + + cooling-maps { + cpu-active-high { + /* active: set fan to cooling level 3 */ + cooling-device = <&fan 3 3>; + trip = <&cpu_trip_active_high>; + }; + + cpu-active-med { + /* active: set fan to cooling level 2 */ + cooling-device = <&fan 2 2>; + trip = <&cpu_trip_active_med>; + }; + + cpu-active-low { + /* passive: set fan to cooling level 1 */ + cooling-device = <&fan 1 1>; + trip = <&cpu_trip_active_low>; + }; + }; + }; + }; + + thermal: thermal@1100c800 { + #thermal-sensor-cells = <1>; + compatible = "mediatek,mt7981-thermal", "mediatek,mt7986-thermal"; + reg = <0 0x1100c800 0 0x800>; + interrupts = ; + clocks = <&infracfg CLK_INFRA_THERM_CK>, + <&infracfg CLK_INFRA_ADC_26M_CK>; + clock-names = "therm", "auxadc"; + mediatek,auxadc = <&auxadc>; + mediatek,apmixedsys = <&apmixedsys>; + nvmem-cells = <&thermal_calibration>; + nvmem-cell-names = "calibration-data"; + }; + + auxadc: adc@1100d000 { + compatible = "mediatek,mt7981-auxadc", + "mediatek,mt7986-auxadc", + "mediatek,mt7622-auxadc"; + reg = <0 0x1100d000 0 0x1000>; + clocks = <&infracfg CLK_INFRA_ADC_26M_CK>, + <&infracfg CLK_INFRA_ADC_FRC_CK>; + clock-names = "main", "32k"; + #io-channel-cells = <1>; + }; + + wdma: wdma@15104800 { + compatible = "mediatek,wed-wdma"; + reg = <0 0x15104800 0 0x400>, + <0 0x15104c00 0 0x400>; + }; + + ap2woccif: ap2woccif@151a5000 { + compatible = "mediatek,ap2woccif"; + reg = <0 0x151a5000 0 0x1000>, + <0 0x151ad000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = , + ; + }; + + reserved-memory { + #address-cells = <2>; + #size-cells = <2>; + ranges; + + /* 192 KiB reserved for ARM Trusted Firmware (BL31) */ + secmon_reserved: secmon@43000000 { + reg = <0 0x43000000 0 0x30000>; + no-map; + }; + + wmcpu_emi: wmcpu-reserved@47c80000 { + reg = <0 0x47c80000 0 0x100000>; + no-map; + }; + + wo_emi0: wo-emi@47d80000 { + reg = <0 0x47d80000 0 0x40000>; + no-map; + }; + + wo_data: wo-data@47dc0000 { + reg = <0 0x47dc0000 0 0x240000>; + no-map; + }; + + wo_ilm0: wo-ilm@151e0000 { + reg = <0 0x151e0000 0 0x8000>; + no-map; + }; + + wo_dlm0: wo-dlm@151e8000 { + reg = <0 0x151e8000 0 0x2000>; + no-map; + }; + + wo_boot: wo-boot@15194000 { + reg = <0 0x15194000 0 0x1000>; + no-map; + }; + }; + + psci { + compatible = "arm,psci-0.2"; + method = "smc"; + }; + + trng { + compatible = "mediatek,mt7981-rng"; + }; + + clk40m: oscillator@0 { + compatible = "fixed-clock"; + #clock-cells = <0>; + clock-frequency = <40000000>; + clock-output-names = "clkxtal"; + }; + + infracfg: infracfg@10001000 { + compatible = "mediatek,mt7981-infracfg", "syscon"; + reg = <0 0x10001000 0 0x1000>; + #clock-cells = <1>; + }; + + topckgen: topckgen@1001B000 { + compatible = "mediatek,mt7981-topckgen", "syscon"; + reg = <0 0x1001B000 0 0x1000>; + #clock-cells = <1>; + }; + + apmixedsys: apmixedsys@1001E000 { + compatible = "mediatek,mt7981-apmixedsys", "mediatek,mt7986-apmixedsys", "syscon"; + reg = <0 0x1001E000 0 0x1000>; + #clock-cells = <1>; + }; + + timer { + compatible = "arm,armv8-timer"; + interrupt-parent = <&gic>; + clock-frequency = <13000000>; + interrupts = , + , + , + ; + + }; + + watchdog: watchdog@1001c000 { + compatible = "mediatek,mt7986-wdt", + "mediatek,mt6589-wdt"; + reg = <0 0x1001c000 0 0x1000>; + interrupts = ; + #reset-cells = <1>; + status = "disabled"; + }; + + gic: interrupt-controller@c000000 { + compatible = "arm,gic-v3"; + #interrupt-cells = <3>; + interrupt-parent = <&gic>; + interrupt-controller; + reg = <0 0x0c000000 0 0x40000>, /* GICD */ + <0 0x0c080000 0 0x200000>; /* GICR */ + + interrupts = ; + }; + + uart0: serial@11002000 { + compatible = "mediatek,mt6577-uart"; + reg = <0 0x11002000 0 0x400>; + interrupts = ; + clocks = <&infracfg CLK_INFRA_UART0_SEL>, + <&infracfg CLK_INFRA_UART0_CK>; + clock-names = "baud", "bus"; + assigned-clocks = <&topckgen CLK_TOP_UART_SEL>, + <&infracfg CLK_INFRA_UART0_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_CB_CKSQ_40M>, + <&topckgen CLK_TOP_UART_SEL>; + pinctrl-0 = <&uart0_pins>; + pinctrl-names = "default"; + status = "disabled"; + }; + + uart1: serial@11003000 { + compatible = "mediatek,mt6577-uart"; + reg = <0 0x11003000 0 0x400>; + interrupts = ; + clocks = <&infracfg CLK_INFRA_UART1_SEL>, + <&infracfg CLK_INFRA_UART1_CK>; + clock-names = "baud", "bus"; + assigned-clocks = <&topckgen CLK_TOP_UART_SEL>, + <&infracfg CLK_INFRA_UART1_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_CB_CKSQ_40M>, + <&topckgen CLK_TOP_UART_SEL>; + status = "disabled"; + }; + + uart2: serial@11004000 { + compatible = "mediatek,mt6577-uart"; + reg = <0 0x11004000 0 0x400>; + interrupts = ; + clocks = <&infracfg CLK_INFRA_UART2_SEL>, + <&infracfg CLK_INFRA_UART2_CK>; + clock-names = "baud", "bus"; + assigned-clocks = <&topckgen CLK_TOP_UART_SEL>, + <&infracfg CLK_INFRA_UART2_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_CB_CKSQ_40M>, + <&topckgen CLK_TOP_UART_SEL>; + status = "disabled"; + }; + + i2c0: i2c@11007000 { + compatible = "mediatek,mt7981-i2c"; + reg = <0 0x11007000 0 0x1000>, + <0 0x10217080 0 0x80>; + interrupts = ; + clock-div = <1>; + clocks = <&infracfg CLK_INFRA_I2C0_CK>, + <&infracfg CLK_INFRA_AP_DMA_CK>; + clock-names = "main", "dma"; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + pcie: pcie@11280000 { + compatible = "mediatek,mt7981-pcie", + "mediatek,mt7986-pcie"; + device_type = "pci"; + reg = <0 0x11280000 0 0x4000>; + reg-names = "pcie-mac"; + #address-cells = <3>; + #size-cells = <2>; + interrupts = ; + bus-range = <0x00 0xff>; + ranges = <0x82000000 0 0x20000000 + 0x0 0x20000000 0 0x10000000>; + status = "disabled"; + + clocks = <&infracfg CLK_INFRA_IPCIE_CK>, + <&infracfg CLK_INFRA_IPCIE_PIPE_CK>, + <&infracfg CLK_INFRA_IPCIER_CK>, + <&infracfg CLK_INFRA_IPCIEB_CK>; + + phys = <&u3port0 PHY_TYPE_PCIE>; + phy-names = "pcie-phy"; + + #interrupt-cells = <1>; + interrupt-map-mask = <0 0 0 7>; + interrupt-map = <0 0 0 1 &pcie_intc 0>, + <0 0 0 2 &pcie_intc 1>, + <0 0 0 3 &pcie_intc 2>, + <0 0 0 4 &pcie_intc 3>; + pcie_intc: interrupt-controller { + interrupt-controller; + #address-cells = <0>; + #interrupt-cells = <1>; + }; + }; + + crypto: crypto@10320000 { + compatible = "inside-secure,safexcel-eip97"; + reg = <0 0x10320000 0 0x40000>; + interrupts = , + , + , + ; + interrupt-names = "ring0", "ring1", "ring2", "ring3"; + clocks = <&topckgen CLK_TOP_EIP97B>; + clock-names = "top_eip97_ck"; + assigned-clocks = <&topckgen CLK_TOP_EIP97B_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_CB_NET1_D5>; + }; + + pio: pinctrl@11d00000 { + compatible = "mediatek,mt7981-pinctrl"; + reg = <0 0x11d00000 0 0x1000>, + <0 0x11c00000 0 0x1000>, + <0 0x11c10000 0 0x1000>, + <0 0x11d20000 0 0x1000>, + <0 0x11e00000 0 0x1000>, + <0 0x11e20000 0 0x1000>, + <0 0x11f00000 0 0x1000>, + <0 0x11f10000 0 0x1000>, + <0 0x1000b000 0 0x1000>; + reg-names = "gpio", "iocfg_rt", "iocfg_rm", + "iocfg_rb", "iocfg_lb", "iocfg_bl", + "iocfg_tm", "iocfg_tl", "eint"; + gpio-controller; + #gpio-cells = <2>; + gpio-ranges = <&pio 0 0 56>; + interrupt-controller; + interrupts = ; + interrupt-parent = <&gic>; + #interrupt-cells = <2>; + + mdio_pins: mdc-mdio-pins { + mux { + function = "eth"; + groups = "smi_mdc_mdio"; + }; + }; + + uart0_pins: uart0-pins { + mux { + function = "uart"; + groups = "uart0"; + }; + }; + + wifi_dbdc_pins: wifi-dbdc-pins { + mux { + function = "eth"; + groups = "wf0_mode1"; + }; + conf { + pins = "WF_HB1", "WF_HB2", "WF_HB3", "WF_HB4", + "WF_HB0", "WF_HB0_B", "WF_HB5", "WF_HB6", + "WF_HB7", "WF_HB8", "WF_HB9", "WF_HB10", + "WF_TOP_CLK", "WF_TOP_DATA", "WF_XO_REQ", + "WF_CBA_RESETB", "WF_DIG_RESETB"; + drive-strength = <4>; + }; + }; + }; + + ethsys: syscon@15000000 { + #address-cells = <1>; + #size-cells = <1>; + compatible = "mediatek,mt7981-ethsys", + "mediatek,mt7986-ethsys", + "syscon"; + reg = <0 0x15000000 0 0x1000>; + #clock-cells = <1>; + #reset-cells = <1>; + }; + + wed: wed@15010000 { + compatible = "mediatek,mt7981-wed", + "mediatek,mt7986-wed", + "syscon"; + reg = <0 0x15010000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + memory-region = <&wo_emi0>, <&wo_ilm0>, <&wo_dlm0>, + <&wo_data>, <&wo_boot>; + memory-region-names = "wo-emi", "wo-ilm", "wo-dlm", + "wo-data", "wo-boot"; + mediatek,wo-ccif = <&wo_ccif0>; + }; + + eth: ethernet@15100000 { + compatible = "mediatek,mt7981-eth"; + reg = <0 0x15100000 0 0x80000>; + interrupts = , + , + , + ; + clocks = <ðsys CLK_ETH_FE_EN>, + <ðsys CLK_ETH_GP2_EN>, + <ðsys CLK_ETH_GP1_EN>, + <ðsys CLK_ETH_WOCPU0_EN>, + <&sgmiisys0 CLK_SGM0_TX_EN>, + <&sgmiisys0 CLK_SGM0_RX_EN>, + <&sgmiisys0 CLK_SGM0_CK0_EN>, + <&sgmiisys0 CLK_SGM0_CDR_CK0_EN>, + <&sgmiisys1 CLK_SGM1_TX_EN>, + <&sgmiisys1 CLK_SGM1_RX_EN>, + <&sgmiisys1 CLK_SGM1_CK1_EN>, + <&sgmiisys1 CLK_SGM1_CDR_CK1_EN>, + <&topckgen CLK_TOP_SGM_REG>, + <&topckgen CLK_TOP_NETSYS_SEL>, + <&topckgen CLK_TOP_NETSYS_500M_SEL>; + clock-names = "fe", "gp2", "gp1", "wocpu0", + "sgmii_tx250m", "sgmii_rx250m", + "sgmii_cdr_ref", "sgmii_cdr_fb", + "sgmii2_tx250m", "sgmii2_rx250m", + "sgmii2_cdr_ref", "sgmii2_cdr_fb", + "sgmii_ck", "netsys0", "netsys1"; + assigned-clocks = <&topckgen CLK_TOP_NETSYS_2X_SEL>, + <&topckgen CLK_TOP_SGM_325M_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_CB_NET2_800M>, + <&topckgen CLK_TOP_CB_SGM_325M>; + mediatek,ethsys = <ðsys>; + mediatek,sgmiisys = <&sgmiisys0>, <&sgmiisys1>; + mediatek,infracfg = <&topmisc>; + mediatek,wed = <&wed>; + #reset-cells = <1>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + + mdio_bus: mdio-bus { + #address-cells = <1>; + #size-cells = <0>; + + int_gbe_phy: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c22"; + phy-mode = "gmii"; + phy-is-integrated; + nvmem-cells = <&phy_calibration>; + nvmem-cell-names = "phy-cal-data"; + }; + }; + }; + + wo_ccif0: syscon@151a5000 { + compatible = "mediatek,mt7986-wo-ccif", "syscon"; + reg = <0 0x151a5000 0 0x1000>; + interrupt-parent = <&gic>; + interrupts = ; + }; + + sgmiisys0: syscon@10060000 { + compatible = "mediatek,mt7981-sgmiisys_0", "mediatek,mt7986-sgmiisys_0", "syscon"; + reg = <0 0x10060000 0 0x1000>; + mediatek,pnswap; + #clock-cells = <1>; + }; + + sgmiisys1: syscon@10070000 { + compatible = "mediatek,mt7981-sgmiisys_1", "mediatek,mt7986-sgmiisys_1", "syscon"; + reg = <0 0x10070000 0 0x1000>; + #clock-cells = <1>; + }; + + topmisc: topmisc@11d10000 { + compatible = "mediatek,mt7981-topmisc", "syscon"; + reg = <0 0x11d10000 0 0x10000>; + #clock-cells = <1>; + }; + + snand: snfi@11005000 { + compatible = "mediatek,mt7986-snand"; + reg = <0 0x11005000 0 0x1000>, <0 0x11006000 0 0x1000>; + reg-names = "nfi", "ecc"; + interrupts = ; + clocks = <&infracfg CLK_INFRA_SPINFI1_CK>, + <&infracfg CLK_INFRA_NFI1_CK>, + <&infracfg CLK_INFRA_NFI_HCK_CK>; + clock-names = "pad_clk", "nfi_clk", "nfi_hclk"; + assigned-clocks = <&topckgen CLK_TOP_SPINFI_SEL>, + <&topckgen CLK_TOP_NFI1X_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_CB_M_D8>, + <&topckgen CLK_TOP_CB_M_D8>; + #address-cells = <1>; + #size-cells = <0>; + status = "disabled"; + }; + + mmc0: mmc@11230000 { + compatible = "mediatek,mt7986-mmc", + "mediatek,mt7981-mmc"; + reg = <0 0x11230000 0 0x1000>, <0 0x11c20000 0 0x1000>; + interrupts = ; + clocks = <&topckgen CLK_TOP_EMMC_208M>, + <&topckgen CLK_TOP_EMMC_400M>, + <&infracfg CLK_INFRA_MSDC_CK>; + assigned-clocks = <&topckgen CLK_TOP_EMMC_208M_SEL>, + <&topckgen CLK_TOP_EMMC_400M_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_CB_M_D2>, + <&topckgen CLK_TOP_CB_NET2_D2>; + clock-names = "source", "hclk", "source_cg"; + status = "disabled"; + }; + + wed_pcie: wed_pcie@10003000 { + compatible = "mediatek,wed_pcie"; + reg = <0 0x10003000 0 0x10>; + }; + + spi0: spi@1100a000 { + compatible = "mediatek,mt7986-spi-ipm", "mediatek,spi-ipm"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0 0x1100a000 0 0x100>; + interrupts = ; + clocks = <&topckgen CLK_TOP_CB_M_D2>, + <&topckgen CLK_TOP_SPI_SEL>, + <&infracfg CLK_INFRA_SPI0_CK>, + <&infracfg CLK_INFRA_SPI0_HCK_CK>; + + clock-names = "parent-clk", "sel-clk", "spi-clk", "hclk"; + status = "disabled"; + }; + + spi1: spi@1100b000 { + compatible = "mediatek,mt7986-spi-ipm", "mediatek,spi-ipm"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0 0x1100b000 0 0x100>; + interrupts = ; + clocks = <&topckgen CLK_TOP_CB_M_D2>, + <&topckgen CLK_TOP_SPIM_MST_SEL>, + <&infracfg CLK_INFRA_SPI1_CK>, + <&infracfg CLK_INFRA_SPI1_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "hclk"; + status = "disabled"; + }; + + spi2: spi@11009000 { + compatible = "mediatek,mt7986-spi-ipm", "mediatek,spi-ipm"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0 0x11009000 0 0x100>; + interrupts = ; + clocks = <&topckgen CLK_TOP_CB_M_D2>, + <&topckgen CLK_TOP_SPI_SEL>, + <&infracfg CLK_INFRA_SPI2_CK>, + <&infracfg CLK_INFRA_SPI2_HCK_CK>; + clock-names = "parent-clk", "sel-clk", "spi-clk", "hclk"; + status = "disabled"; + }; + + consys: consys@10000000 { + compatible = "mediatek,mt7981-consys"; + reg = <0 0x10000000 0 0x8600000>; + memory-region = <&wmcpu_emi>; + }; + + xhci: usb@11200000 { + compatible = "mediatek,mt7986-xhci", + "mediatek,mtk-xhci"; + reg = <0 0x11200000 0 0x2e00>, + <0 0x11203e00 0 0x0100>; + reg-names = "mac", "ippc"; + interrupts = ; + clocks = <&infracfg CLK_INFRA_IUSB_SYS_CK>, + <&infracfg CLK_INFRA_IUSB_CK>, + <&infracfg CLK_INFRA_IUSB_133_CK>, + <&infracfg CLK_INFRA_IUSB_66M_CK>, + <&topckgen CLK_TOP_U2U3_XHCI_SEL>; + clock-names = "sys_ck", + "ref_ck", + "mcu_ck", + "dma_ck", + "xhci_ck"; + phys = <&u2port0 PHY_TYPE_USB2>, + <&u3port0 PHY_TYPE_USB3>; + vusb33-supply = <®_3p3v>; + mediatek,u3p-dis-msk = <0x01>; + status = "disabled"; + }; + + usb_phy: usb-phy@11e10000 { + compatible = "mediatek,mt7981", + "mediatek,generic-tphy-v2"; + #address-cells = <1>; + #size-cells = <1>; + ranges = <0 0 0x11e10000 0x1700>; + status = "disabled"; + + u2port0: usb-phy@0 { + reg = <0x0 0x700>; + clocks = <&topckgen CLK_TOP_USB_FRMCNT_SEL>; + clock-names = "ref"; + #phy-cells = <1>; + }; + + u3port0: usb-phy@700 { + reg = <0x700 0x900>; + clocks = <&topckgen CLK_TOP_USB3_PHY_SEL>; + clock-names = "ref"; + #phy-cells = <1>; + mediatek,syscon-type = <&topmisc 0x218 0>; + status = "okay"; + }; + }; + + reg_3p3v: regulator-3p3v { + compatible = "regulator-fixed"; + regulator-name = "fixed-3.3V"; + regulator-min-microvolt = <3300000>; + regulator-max-microvolt = <3300000>; + regulator-boot-on; + regulator-always-on; + }; + + efuse: efuse@11f20000 { + compatible = "mediatek,mt7981-efuse", + "mediatek,efuse"; + reg = <0 0x11f20000 0 0x1000>; + #address-cells = <1>; + #size-cells = <1>; + status = "okay"; + + thermal_calibration: thermal-calib@274 { + reg = <0x274 0xc>; + }; + + phy_calibration: phy-calib@8dc { + reg = <0x8dc 0x10>; + }; + + comb_rx_imp_p0: usb3-rx-imp@8c8 { + reg = <0x8c8 1>; + bits = <0 5>; + }; + + comb_tx_imp_p0: usb3-tx-imp@8c8 { + reg = <0x8c8 2>; + bits = <5 5>; + }; + + comb_intr_p0: usb3-intr@8c9 { + reg = <0x8c9 1>; + bits = <2 6>; + }; + }; + + afe: audio-controller@11210000 { + compatible = "mediatek,mt79xx-audio"; + reg = <0 0x11210000 0 0x9000>; + interrupts = ; + clocks = <&infracfg CLK_INFRA_AUD_BUS_CK>, + <&infracfg CLK_INFRA_AUD_26M_CK>, + <&infracfg CLK_INFRA_AUD_L_CK>, + <&infracfg CLK_INFRA_AUD_AUD_CK>, + <&infracfg CLK_INFRA_AUD_EG2_CK>, + <&topckgen CLK_TOP_AUD_SEL>; + clock-names = "aud_bus_ck", + "aud_26m_ck", + "aud_l_ck", + "aud_aud_ck", + "aud_eg2_ck", + "aud_sel"; + assigned-clocks = <&topckgen CLK_TOP_AUD_SEL>, + <&topckgen CLK_TOP_A1SYS_SEL>, + <&topckgen CLK_TOP_AUD_L_SEL>, + <&topckgen CLK_TOP_A_TUNER_SEL>; + assigned-clock-parents = <&topckgen CLK_TOP_CB_APLL2_196M>, + <&topckgen CLK_TOP_APLL2_D4>, + <&topckgen CLK_TOP_CB_APLL2_196M>, + <&topckgen CLK_TOP_APLL2_D4>; + status = "disabled"; + }; + + ice: ice_debug { + compatible = "mediatek,mt7981-ice_debug", + "mediatek,mt2701-ice_debug"; + clocks = <&infracfg CLK_INFRA_DBG_CK>; + clock-names = "ice_dbg"; + }; + + wifi: wifi@18000000 { + compatible = "mediatek,mt7981-wmac"; + resets = <&watchdog MT7986_TOPRGU_CONSYS_RST>; + reset-names = "consys"; + pinctrl-0 = <&wifi_dbdc_pins>; + pinctrl-names = "dbdc"; + clocks = <&topckgen CLK_TOP_NETSYS_MCU_SEL>, + <&topckgen CLK_TOP_AP2CNN_HOST_SEL>; + clock-names = "mcu", "ap2conn"; + reg = <0 0x18000000 0 0x1000000>, + <0 0x10003000 0 0x1000>, + <0 0x11d10000 0 0x1000>; + interrupts = , + , + , + ; + memory-region = <&wmcpu_emi>; + status = "disabled"; + }; +}; diff --git a/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-apmixed.c b/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-apmixed.c new file mode 100644 index 00000000000..1647021de88 --- /dev/null +++ b/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-apmixed.c @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Sam Shih + * Author: Wenzhen Yu + * Author: Jianhui Zhao + * Author: Daniel Golle + */ + +#include +#include +#include +#include +#include +#include "clk-mtk.h" +#include "clk-gate.h" +#include "clk-mux.h" + +#include +#include + +#define MT7981_PLL_FMAX (2500UL * MHZ) +#define CON0_MT7981_RST_BAR BIT(27) + +#define PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, \ + _div_table, _parent_name) \ + { \ + .id = _id, .name = _name, .reg = _reg, .pwr_reg = _pwr_reg, \ + .en_mask = _en_mask, .flags = _flags, \ + .rst_bar_mask = CON0_MT7981_RST_BAR, .fmax = MT7981_PLL_FMAX, \ + .pcwbits = _pcwbits, .pd_reg = _pd_reg, .pd_shift = _pd_shift, \ + .tuner_reg = _tuner_reg, .pcw_reg = _pcw_reg, \ + .pcw_shift = _pcw_shift, .div_table = _div_table, \ + .parent_name = _parent_name, \ + } + +#define PLL(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, _pd_reg, \ + _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift) \ + PLL_xtal(_id, _name, _reg, _pwr_reg, _en_mask, _flags, _pcwbits, \ + _pd_reg, _pd_shift, _tuner_reg, _pcw_reg, _pcw_shift, NULL, \ + "clkxtal") + +static const struct mtk_pll_data plls[] = { + PLL(CLK_APMIXED_ARMPLL, "armpll", 0x0200, 0x020C, 0x00000001, PLL_AO, + 32, 0x0200, 4, 0, 0x0204, 0), + PLL(CLK_APMIXED_NET2PLL, "net2pll", 0x0210, 0x021C, 0x00000001, 0, 32, + 0x0210, 4, 0, 0x0214, 0), + PLL(CLK_APMIXED_MMPLL, "mmpll", 0x0220, 0x022C, 0x00000001, 0, 32, + 0x0220, 4, 0, 0x0224, 0), + PLL(CLK_APMIXED_SGMPLL, "sgmpll", 0x0230, 0x023C, 0x00000001, 0, 32, + 0x0230, 4, 0, 0x0234, 0), + PLL(CLK_APMIXED_WEDMCUPLL, "wedmcupll", 0x0240, 0x024C, 0x00000001, 0, 32, + 0x0240, 4, 0, 0x0244, 0), + PLL(CLK_APMIXED_NET1PLL, "net1pll", 0x0250, 0x025C, 0x00000001, 0, 32, + 0x0250, 4, 0, 0x0254, 0), + PLL(CLK_APMIXED_MPLL, "mpll", 0x0260, 0x0270, 0x00000001, 0, 32, + 0x0260, 4, 0, 0x0264, 0), + PLL(CLK_APMIXED_APLL2, "apll2", 0x0278, 0x0288, 0x00000001, 0, 32, + 0x0278, 4, 0, 0x027C, 0), +}; + +static const struct of_device_id of_match_clk_mt7981_apmixed[] = { + { .compatible = "mediatek,mt7981-apmixedsys", }, + {} +}; + +static int clk_mt7981_apmixed_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + int r; + + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(plls)); + if (!clk_data) + return -ENOMEM; + + mtk_clk_register_plls(node, plls, ARRAY_SIZE(plls), clk_data); + + clk_prepare_enable(clk_data->clks[CLK_APMIXED_ARMPLL]); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) { + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + goto free_apmixed_data; + } + return r; + +free_apmixed_data: + mtk_free_clk_data(clk_data); + return r; +} + +static struct platform_driver clk_mt7981_apmixed_drv = { + .probe = clk_mt7981_apmixed_probe, + .driver = { + .name = "clk-mt7981-apmixed", + .of_match_table = of_match_clk_mt7981_apmixed, + }, +}; +builtin_platform_driver(clk_mt7981_apmixed_drv); diff --git a/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-eth.c b/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-eth.c new file mode 100644 index 00000000000..4aba657aa6f --- /dev/null +++ b/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-eth.c @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Sam Shih + * Author: Wenzhen Yu + * Author: Jianhui Zhao + * Author: Daniel Golle + */ + +#include +#include +#include +#include +#include + +#include "clk-mtk.h" +#include "clk-gate.h" + +#include + +static const struct mtk_gate_regs sgmii0_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII0(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &sgmii0_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii0_clks[] __initconst = { + GATE_SGMII0(CLK_SGM0_TX_EN, "sgm0_tx_en", "usb_tx250m", 2), + GATE_SGMII0(CLK_SGM0_RX_EN, "sgm0_rx_en", "usb_eq_rx250m", 3), + GATE_SGMII0(CLK_SGM0_CK0_EN, "sgm0_ck0_en", "usb_ln0", 4), + GATE_SGMII0(CLK_SGM0_CDR_CK0_EN, "sgm0_cdr_ck0_en", "usb_cdr", 5), +}; + +static const struct mtk_gate_regs sgmii1_cg_regs = { + .set_ofs = 0xE4, + .clr_ofs = 0xE4, + .sta_ofs = 0xE4, +}; + +#define GATE_SGMII1(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = &sgmii1_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate sgmii1_clks[] __initconst = { + GATE_SGMII1(CLK_SGM1_TX_EN, "sgm1_tx_en", "usb_tx250m", 2), + GATE_SGMII1(CLK_SGM1_RX_EN, "sgm1_rx_en", "usb_eq_rx250m", 3), + GATE_SGMII1(CLK_SGM1_CK1_EN, "sgm1_ck1_en", "usb_ln0", 4), + GATE_SGMII1(CLK_SGM1_CDR_CK1_EN, "sgm1_cdr_ck1_en", "usb_cdr", 5), +}; + +static const struct mtk_gate_regs eth_cg_regs = { + .set_ofs = 0x30, + .clr_ofs = 0x30, + .sta_ofs = 0x30, +}; + +#define GATE_ETH(_id, _name, _parent, _shift) { \ + .id = _id, \ + .name = _name, \ + .parent_name = _parent, \ + .regs = ð_cg_regs, \ + .shift = _shift, \ + .ops = &mtk_clk_gate_ops_no_setclr_inv, \ + } + +static const struct mtk_gate eth_clks[] __initconst = { + GATE_ETH(CLK_ETH_FE_EN, "eth_fe_en", "netsys_2x", 6), + GATE_ETH(CLK_ETH_GP2_EN, "eth_gp2_en", "sgm_325m", 7), + GATE_ETH(CLK_ETH_GP1_EN, "eth_gp1_en", "sgm_325m", 8), + GATE_ETH(CLK_ETH_WOCPU0_EN, "eth_wocpu0_en", "netsys_wed_mcu", 15), +}; + +static void __init mtk_sgmiisys_0_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(sgmii0_clks)); + + mtk_clk_register_gates(node, sgmii0_clks, ARRAY_SIZE(sgmii0_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_sgmiisys_0, "mediatek,mt7981-sgmiisys_0", + mtk_sgmiisys_0_init); + +static void __init mtk_sgmiisys_1_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(sgmii1_clks)); + + mtk_clk_register_gates(node, sgmii1_clks, ARRAY_SIZE(sgmii1_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_sgmiisys_1, "mediatek,mt7981-sgmiisys_1", + mtk_sgmiisys_1_init); + +static void __init mtk_ethsys_init(struct device_node *node) +{ + struct clk_onecell_data *clk_data; + int r; + + clk_data = mtk_alloc_clk_data(ARRAY_SIZE(eth_clks)); + + mtk_clk_register_gates(node, eth_clks, ARRAY_SIZE(eth_clks), clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); +} +CLK_OF_DECLARE(mtk_ethsys, "mediatek,mt7981-ethsys", mtk_ethsys_init); diff --git a/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-infracfg.c b/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-infracfg.c new file mode 100644 index 00000000000..8416829e358 --- /dev/null +++ b/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-infracfg.c @@ -0,0 +1,235 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Sam Shih + * Author: Wenzhen Yu + * Author: Jianhui Zhao + * Author: Daniel Golle + */ + +#include +#include +#include +#include +#include +#include "clk-mtk.h" +#include "clk-gate.h" +#include "clk-mux.h" + +#include +#include + +static DEFINE_SPINLOCK(mt7981_clk_lock); + +static const struct mtk_fixed_factor infra_divs[] = { + FACTOR(CLK_INFRA_66M_MCK, "infra_66m_mck", "sysaxi_sel", 1, 2), +}; + +static const char *const infra_uart_parent[] __initconst = { "csw_f26m_sel", + "uart_sel" }; + +static const char *const infra_spi0_parents[] __initconst = { "i2c_sel", + "spi_sel" }; + +static const char *const infra_spi1_parents[] __initconst = { "i2c_sel", + "spim_mst_sel" }; + +static const char *const infra_pwm1_parents[] __initconst = { "pwm_sel" }; + +static const char *const infra_pwm_bsel_parents[] __initconst = { + "cb_rtc_32p7k", "csw_f26m_sel", "infra_66m_mck", "pwm_sel" +}; + +static const char *const infra_pcie_parents[] __initconst = { + "cb_rtc_32p7k", "csw_f26m_sel", "cb_cksq_40m", "pextp_tl_ck_sel" +}; + +static const struct mtk_mux infra_muxes[] = { + /* MODULE_CLK_SEL_0 */ + MUX_GATE_CLR_SET_UPD(CLK_INFRA_UART0_SEL, "infra_uart0_sel", + infra_uart_parent, 0x0018, 0x0010, 0x0014, 0, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_UART1_SEL, "infra_uart1_sel", + infra_uart_parent, 0x0018, 0x0010, 0x0014, 1, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_UART2_SEL, "infra_uart2_sel", + infra_uart_parent, 0x0018, 0x0010, 0x0014, 2, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_SPI0_SEL, "infra_spi0_sel", + infra_spi0_parents, 0x0018, 0x0010, 0x0014, 4, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_SPI1_SEL, "infra_spi1_sel", + infra_spi1_parents, 0x0018, 0x0010, 0x0014, 5, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_SPI2_SEL, "infra_spi2_sel", + infra_spi0_parents, 0x0018, 0x0010, 0x0014, 6, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM1_SEL, "infra_pwm1_sel", + infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 9, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM2_SEL, "infra_pwm2_sel", + infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 11, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM3_SEL, "infra_pwm3_sel", + infra_pwm1_parents, 0x0018, 0x0010, 0x0014, 15, 1, + -1, -1, -1), + MUX_GATE_CLR_SET_UPD(CLK_INFRA_PWM_BSEL, "infra_pwm_bsel", + infra_pwm_bsel_parents, 0x0018, 0x0010, 0x0014, 13, + 2, -1, -1, -1), + /* MODULE_CLK_SEL_1 */ + MUX_GATE_CLR_SET_UPD(CLK_INFRA_PCIE_SEL, "infra_pcie_sel", + infra_pcie_parents, 0x0028, 0x0020, 0x0024, 0, 2, + -1, -1, -1), +}; + +static const struct mtk_gate_regs infra0_cg_regs = { + .set_ofs = 0x40, + .clr_ofs = 0x44, + .sta_ofs = 0x48, +}; + +static const struct mtk_gate_regs infra1_cg_regs = { + .set_ofs = 0x50, + .clr_ofs = 0x54, + .sta_ofs = 0x58, +}; + +static const struct mtk_gate_regs infra2_cg_regs = { + .set_ofs = 0x60, + .clr_ofs = 0x64, + .sta_ofs = 0x68, +}; + +#define GATE_INFRA0(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &infra0_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA1(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &infra1_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +#define GATE_INFRA2(_id, _name, _parent, _shift) \ + { \ + .id = _id, .name = _name, .parent_name = _parent, \ + .regs = &infra2_cg_regs, .shift = _shift, \ + .ops = &mtk_clk_gate_ops_setclr, \ + } + +static const struct mtk_gate infra_clks[] = { + /* INFRA0 */ + GATE_INFRA0(CLK_INFRA_GPT_STA, "infra_gpt_sta", "infra_66m_mck", 0), + GATE_INFRA0(CLK_INFRA_PWM_HCK, "infra_pwm_hck", "infra_66m_mck", 1), + GATE_INFRA0(CLK_INFRA_PWM_STA, "infra_pwm_sta", "infra_pwm_bsel", 2), + GATE_INFRA0(CLK_INFRA_PWM1_CK, "infra_pwm1", "infra_pwm1_sel", 3), + GATE_INFRA0(CLK_INFRA_PWM2_CK, "infra_pwm2", "infra_pwm2_sel", 4), + GATE_INFRA0(CLK_INFRA_CQ_DMA_CK, "infra_cq_dma", "sysaxi", 6), + + GATE_INFRA0(CLK_INFRA_AUD_BUS_CK, "infra_aud_bus", "sysaxi", 8), + GATE_INFRA0(CLK_INFRA_AUD_26M_CK, "infra_aud_26m", "csw_f26m_sel", 9), + GATE_INFRA0(CLK_INFRA_AUD_L_CK, "infra_aud_l", "aud_l", 10), + GATE_INFRA0(CLK_INFRA_AUD_AUD_CK, "infra_aud_aud", "a1sys", 11), + GATE_INFRA0(CLK_INFRA_AUD_EG2_CK, "infra_aud_eg2", "a_tuner", 13), + GATE_INFRA0(CLK_INFRA_DRAMC_26M_CK, "infra_dramc_26m", "csw_f26m_sel", + 14), + GATE_INFRA0(CLK_INFRA_DBG_CK, "infra_dbg", "infra_66m_mck", 15), + GATE_INFRA0(CLK_INFRA_AP_DMA_CK, "infra_ap_dma", "infra_66m_mck", 16), + GATE_INFRA0(CLK_INFRA_SEJ_CK, "infra_sej", "infra_66m_mck", 24), + GATE_INFRA0(CLK_INFRA_SEJ_13M_CK, "infra_sej_13m", "csw_f26m_sel", 25), + GATE_INFRA0(CLK_INFRA_PWM3_CK, "infra_pwm3", "infra_pwm3_sel", 27), + /* INFRA1 */ + GATE_INFRA1(CLK_INFRA_THERM_CK, "infra_therm", "csw_f26m_sel", 0), + GATE_INFRA1(CLK_INFRA_I2C0_CK, "infra_i2c0", "i2c_bck", 1), + GATE_INFRA1(CLK_INFRA_UART0_CK, "infra_uart0", "infra_uart0_sel", 2), + GATE_INFRA1(CLK_INFRA_UART1_CK, "infra_uart1", "infra_uart1_sel", 3), + GATE_INFRA1(CLK_INFRA_UART2_CK, "infra_uart2", "infra_uart2_sel", 4), + GATE_INFRA1(CLK_INFRA_SPI2_CK, "infra_spi2", "infra_spi2_sel", 6), + GATE_INFRA1(CLK_INFRA_SPI2_HCK_CK, "infra_spi2_hck", "infra_66m_mck", 7), + GATE_INFRA1(CLK_INFRA_NFI1_CK, "infra_nfi1", "nfi1x", 8), + GATE_INFRA1(CLK_INFRA_SPINFI1_CK, "infra_spinfi1", "spinfi_bck", 9), + GATE_INFRA1(CLK_INFRA_NFI_HCK_CK, "infra_nfi_hck", "infra_66m_mck", 10), + GATE_INFRA1(CLK_INFRA_SPI0_CK, "infra_spi0", "infra_spi0_sel", 11), + GATE_INFRA1(CLK_INFRA_SPI1_CK, "infra_spi1", "infra_spi1_sel", 12), + GATE_INFRA1(CLK_INFRA_SPI0_HCK_CK, "infra_spi0_hck", "infra_66m_mck", + 13), + GATE_INFRA1(CLK_INFRA_SPI1_HCK_CK, "infra_spi1_hck", "infra_66m_mck", + 14), + GATE_INFRA1(CLK_INFRA_FRTC_CK, "infra_frtc", "cb_rtc_32k", 15), + GATE_INFRA1(CLK_INFRA_MSDC_CK, "infra_msdc", "emmc_400m", 16), + GATE_INFRA1(CLK_INFRA_MSDC_HCK_CK, "infra_msdc_hck", "emmc_208m", 17), + GATE_INFRA1(CLK_INFRA_MSDC_133M_CK, "infra_msdc_133m", "sysaxi", 18), + GATE_INFRA1(CLK_INFRA_MSDC_66M_CK, "infra_msdc_66m", "sysaxi", 19), + GATE_INFRA1(CLK_INFRA_ADC_26M_CK, "infra_adc_26m", "infra_adc_frc", 20), + GATE_INFRA1(CLK_INFRA_ADC_FRC_CK, "infra_adc_frc", "csw_f26m", 21), + GATE_INFRA1(CLK_INFRA_FBIST2FPC_CK, "infra_fbist2fpc", "nfi1x", 23), + GATE_INFRA1(CLK_INFRA_I2C_MCK_CK, "infra_i2c_mck", "sysaxi", 25), + GATE_INFRA1(CLK_INFRA_I2C_PCK_CK, "infra_i2c_pck", "infra_66m_mck", 26), + /* INFRA2 */ + GATE_INFRA2(CLK_INFRA_IUSB_133_CK, "infra_iusb_133", "sysaxi", 0), + GATE_INFRA2(CLK_INFRA_IUSB_66M_CK, "infra_iusb_66m", "sysaxi", 1), + GATE_INFRA2(CLK_INFRA_IUSB_SYS_CK, "infra_iusb_sys", "u2u3_sys", 2), + GATE_INFRA2(CLK_INFRA_IUSB_CK, "infra_iusb", "u2u3_ref", 3), + GATE_INFRA2(CLK_INFRA_IPCIE_CK, "infra_ipcie", "pextp_tl", 12), + GATE_INFRA2(CLK_INFRA_IPCIE_PIPE_CK, "infra_ipcie_pipe", "cb_cksq_40m", + 13), + GATE_INFRA2(CLK_INFRA_IPCIER_CK, "infra_ipcier", "csw_f26m", 14), + GATE_INFRA2(CLK_INFRA_IPCIEB_CK, "infra_ipcieb", "sysaxi", 15), +}; + +static int clk_mt7981_infracfg_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + int r; + void __iomem *base; + int nr = ARRAY_SIZE(infra_divs) + ARRAY_SIZE(infra_muxes) + + ARRAY_SIZE(infra_clks); + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return -ENOMEM; + } + + clk_data = mtk_alloc_clk_data(nr); + + if (!clk_data) + return -ENOMEM; + + mtk_clk_register_factors(infra_divs, ARRAY_SIZE(infra_divs), clk_data); + mtk_clk_register_muxes(infra_muxes, ARRAY_SIZE(infra_muxes), node, + &mt7981_clk_lock, clk_data); + mtk_clk_register_gates(node, infra_clks, ARRAY_SIZE(infra_clks), + clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + if (r) { + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + goto free_infracfg_data; + } + return r; + +free_infracfg_data: + mtk_free_clk_data(clk_data); + return r; +} + +static const struct of_device_id of_match_clk_mt7981_infracfg[] = { + { .compatible = "mediatek,mt7981-infracfg", }, + {} +}; + +static struct platform_driver clk_mt7981_infracfg_drv = { + .probe = clk_mt7981_infracfg_probe, + .driver = { + .name = "clk-mt7981-infracfg", + .of_match_table = of_match_clk_mt7981_infracfg, + }, +}; +builtin_platform_driver(clk_mt7981_infracfg_drv); diff --git a/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-topckgen.c b/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-topckgen.c new file mode 100644 index 00000000000..ce6dc186726 --- /dev/null +++ b/target/linux/mediatek/files-5.15/drivers/clk/mediatek/clk-mt7981-topckgen.c @@ -0,0 +1,450 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Sam Shih + * Author: Wenzhen Yu + * Author: Jianhui Zhao + */ + + +#include +#include +#include +#include +#include +#include "clk-mtk.h" +#include "clk-gate.h" +#include "clk-mux.h" + +#include +#include + +static DEFINE_SPINLOCK(mt7981_clk_lock); + +static const struct mtk_fixed_factor top_divs[] = { + FACTOR(CLK_TOP_CB_CKSQ_40M, "cb_cksq_40m", "clkxtal", 1, 1), + FACTOR(CLK_TOP_CB_M_416M, "cb_m_416m", "mpll", 1, 1), + FACTOR(CLK_TOP_CB_M_D2, "cb_m_d2", "mpll", 1, 2), + FACTOR(CLK_TOP_CB_M_D3, "cb_m_d3", "mpll", 1, 3), + FACTOR(CLK_TOP_M_D3_D2, "m_d3_d2", "mpll", 1, 2), + FACTOR(CLK_TOP_CB_M_D4, "cb_m_d4", "mpll", 1, 4), + FACTOR(CLK_TOP_CB_M_D8, "cb_m_d8", "mpll", 1, 8), + FACTOR(CLK_TOP_M_D8_D2, "m_d8_d2", "mpll", 1, 16), + FACTOR(CLK_TOP_CB_MM_720M, "cb_mm_720m", "mmpll", 1, 1), + FACTOR(CLK_TOP_CB_MM_D2, "cb_mm_d2", "mmpll", 1, 2), + FACTOR(CLK_TOP_CB_MM_D3, "cb_mm_d3", "mmpll", 1, 3), + FACTOR(CLK_TOP_CB_MM_D3_D5, "cb_mm_d3_d5", "mmpll", 1, 15), + FACTOR(CLK_TOP_CB_MM_D4, "cb_mm_d4", "mmpll", 1, 4), + FACTOR(CLK_TOP_CB_MM_D6, "cb_mm_d6", "mmpll", 1, 6), + FACTOR(CLK_TOP_MM_D6_D2, "mm_d6_d2", "mmpll", 1, 12), + FACTOR(CLK_TOP_CB_MM_D8, "cb_mm_d8", "mmpll", 1, 8), + FACTOR(CLK_TOP_CB_APLL2_196M, "cb_apll2_196m", "apll2", 1, 1), + FACTOR(CLK_TOP_APLL2_D2, "apll2_d2", "apll2", 1, 2), + FACTOR(CLK_TOP_APLL2_D4, "apll2_d4", "apll2", 1, 4), + FACTOR(CLK_TOP_NET1_2500M, "net1_2500m", "net1pll", 1, 1), + FACTOR(CLK_TOP_CB_NET1_D4, "cb_net1_d4", "net1pll", 1, 4), + FACTOR(CLK_TOP_CB_NET1_D5, "cb_net1_d5", "net1pll", 1, 5), + FACTOR(CLK_TOP_NET1_D5_D2, "net1_d5_d2", "net1pll", 1, 10), + FACTOR(CLK_TOP_NET1_D5_D4, "net1_d5_d4", "net1pll", 1, 20), + FACTOR(CLK_TOP_CB_NET1_D8, "cb_net1_d8", "net1pll", 1, 8), + FACTOR(CLK_TOP_NET1_D8_D2, "net1_d8_d2", "net1pll", 1, 16), + FACTOR(CLK_TOP_NET1_D8_D4, "net1_d8_d4", "net1pll", 1, 32), + FACTOR(CLK_TOP_CB_NET2_800M, "cb_net2_800m", "net2pll", 1, 1), + FACTOR(CLK_TOP_CB_NET2_D2, "cb_net2_d2", "net2pll", 1, 2), + FACTOR(CLK_TOP_CB_NET2_D4, "cb_net2_d4", "net2pll", 1, 4), + FACTOR(CLK_TOP_NET2_D4_D2, "net2_d4_d2", "net2pll", 1, 8), + FACTOR(CLK_TOP_NET2_D4_D4, "net2_d4_d4", "net2pll", 1, 16), + FACTOR(CLK_TOP_CB_NET2_D6, "cb_net2_d6", "net2pll", 1, 6), + FACTOR(CLK_TOP_CB_WEDMCU_208M, "cb_wedmcu_208m", "wedmcupll", 1, 1), + FACTOR(CLK_TOP_CB_SGM_325M, "cb_sgm_325m", "sgmpll", 1, 1), + FACTOR(CLK_TOP_CKSQ_40M_D2, "cksq_40m_d2", "cb_cksq_40m", 1, 2), + FACTOR(CLK_TOP_CB_RTC_32K, "cb_rtc_32k", "cb_cksq_40m", 1, 1250), + FACTOR(CLK_TOP_CB_RTC_32P7K, "cb_rtc_32p7k", "cb_cksq_40m", 1, 1220), + FACTOR(CLK_TOP_USB_TX250M, "usb_tx250m", "cb_cksq_40m", 1, 1), + FACTOR(CLK_TOP_FAUD, "faud", "aud_sel", 1, 1), + FACTOR(CLK_TOP_NFI1X, "nfi1x", "nfi1x_sel", 1, 1), + FACTOR(CLK_TOP_USB_EQ_RX250M, "usb_eq_rx250m", "cb_cksq_40m", 1, 1), + FACTOR(CLK_TOP_USB_CDR_CK, "usb_cdr", "cb_cksq_40m", 1, 1), + FACTOR(CLK_TOP_USB_LN0_CK, "usb_ln0", "cb_cksq_40m", 1, 1), + FACTOR(CLK_TOP_SPINFI_BCK, "spinfi_bck", "spinfi_sel", 1, 1), + FACTOR(CLK_TOP_SPI, "spi", "spi_sel", 1, 1), + FACTOR(CLK_TOP_SPIM_MST, "spim_mst", "spim_mst_sel", 1, 1), + FACTOR(CLK_TOP_UART_BCK, "uart_bck", "uart_sel", 1, 1), + FACTOR(CLK_TOP_PWM_BCK, "pwm_bck", "pwm_sel", 1, 1), + FACTOR(CLK_TOP_I2C_BCK, "i2c_bck", "i2c_sel", 1, 1), + FACTOR(CLK_TOP_PEXTP_TL, "pextp_tl", "pextp_tl_ck_sel", 1, 1), + FACTOR(CLK_TOP_EMMC_208M, "emmc_208m", "emmc_208m_sel", 1, 1), + FACTOR(CLK_TOP_EMMC_400M, "emmc_400m", "emmc_400m_sel", 1, 1), + FACTOR(CLK_TOP_DRAMC_REF, "dramc_ref", "dramc_sel", 1, 1), + FACTOR(CLK_TOP_DRAMC_MD32, "dramc_md32", "dramc_md32_sel", 1, 1), + FACTOR(CLK_TOP_SYSAXI, "sysaxi", "sysaxi_sel", 1, 1), + FACTOR(CLK_TOP_SYSAPB, "sysapb", "sysapb_sel", 1, 1), + FACTOR(CLK_TOP_ARM_DB_MAIN, "arm_db_main", "arm_db_main_sel", 1, 1), + FACTOR(CLK_TOP_AP2CNN_HOST, "ap2cnn_host", "ap2cnn_host_sel", 1, 1), + FACTOR(CLK_TOP_NETSYS, "netsys", "netsys_sel", 1, 1), + FACTOR(CLK_TOP_NETSYS_500M, "netsys_500m", "netsys_500m_sel", 1, 1), + FACTOR(CLK_TOP_NETSYS_WED_MCU, "netsys_wed_mcu", "netsys_mcu_sel", 1, 1), + FACTOR(CLK_TOP_NETSYS_2X, "netsys_2x", "netsys_2x_sel", 1, 1), + FACTOR(CLK_TOP_SGM_325M, "sgm_325m", "sgm_325m_sel", 1, 1), + FACTOR(CLK_TOP_SGM_REG, "sgm_reg", "sgm_reg_sel", 1, 1), + FACTOR(CLK_TOP_F26M, "csw_f26m", "csw_f26m_sel", 1, 1), + FACTOR(CLK_TOP_EIP97B, "eip97b", "eip97b_sel", 1, 1), + FACTOR(CLK_TOP_USB3_PHY, "usb3_phy", "usb3_phy_sel", 1, 1), + FACTOR(CLK_TOP_AUD, "aud", "faud", 1, 1), + FACTOR(CLK_TOP_A1SYS, "a1sys", "a1sys_sel", 1, 1), + FACTOR(CLK_TOP_AUD_L, "aud_l", "aud_l_sel", 1, 1), + FACTOR(CLK_TOP_A_TUNER, "a_tuner", "a_tuner_sel", 1, 1), + FACTOR(CLK_TOP_U2U3_REF, "u2u3_ref", "u2u3_sel", 1, 1), + FACTOR(CLK_TOP_U2U3_SYS, "u2u3_sys", "u2u3_sys_sel", 1, 1), + FACTOR(CLK_TOP_U2U3_XHCI, "u2u3_xhci", "u2u3_xhci_sel", 1, 1), + FACTOR(CLK_TOP_USB_FRMCNT, "usb_frmcnt", "usb_frmcnt_sel", 1, 1), +}; + +static const char * const nfi1x_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d4", + "net1_d8_d2", + "cb_net2_d6", + "cb_m_d4", + "cb_mm_d8", + "net1_d8_d4", + "cb_m_d8" +}; + +static const char * const spinfi_parents[] __initconst = { + "cksq_40m_d2", + "cb_cksq_40m", + "net1_d5_d4", + "cb_m_d4", + "cb_mm_d8", + "net1_d8_d4", + "mm_d6_d2", + "cb_m_d8" +}; + +static const char * const spi_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d2", + "cb_mm_d4", + "net1_d8_d2", + "cb_net2_d6", + "net1_d5_d4", + "cb_m_d4", + "net1_d8_d4" +}; + +static const char * const uart_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d8", + "m_d8_d2" +}; + +static const char * const pwm_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d2", + "net1_d5_d4", + "cb_m_d4", + "m_d8_d2", + "cb_rtc_32k" +}; + +static const char * const i2c_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4", + "cb_m_d4", + "net1_d8_d4" +}; + +static const char * const pextp_tl_ck_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4", + "cb_m_d4", + "cb_rtc_32k" +}; + +static const char * const emmc_208m_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d2", + "cb_net2_d4", + "cb_apll2_196m", + "cb_mm_d4", + "net1_d8_d2", + "cb_mm_d6" +}; + +static const char * const emmc_400m_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_d2", + "cb_mm_d2", + "cb_net2_d2" +}; + +static const char * const csw_f26m_parents[] __initconst = { + "cksq_40m_d2", + "m_d8_d2" +}; + +static const char * const dramc_md32_parents[] __initconst = { + "cb_cksq_40m", + "cb_m_d2", + "cb_wedmcu_208m" +}; + +static const char * const sysaxi_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d2" +}; + +static const char * const sysapb_parents[] __initconst = { + "cb_cksq_40m", + "m_d3_d2" +}; + +static const char * const arm_db_main_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_d6" +}; + +static const char * const ap2cnn_host_parents[] __initconst = { + "cb_cksq_40m", + "net1_d8_d4" +}; + +static const char * const netsys_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d2" +}; + +static const char * const netsys_500m_parents[] __initconst = { + "cb_cksq_40m", + "cb_net1_d5" +}; + +static const char * const netsys_mcu_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_720m", + "cb_net1_d4", + "cb_net1_d5", + "cb_m_416m" +}; + +static const char * const netsys_2x_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_800m", + "cb_mm_720m" +}; + +static const char * const sgm_325m_parents[] __initconst = { + "cb_cksq_40m", + "cb_sgm_325m" +}; + +static const char * const sgm_reg_parents[] __initconst = { + "cb_cksq_40m", + "cb_net2_d4" +}; + +static const char * const eip97b_parents[] __initconst = { + "cb_cksq_40m", + "cb_net1_d5", + "cb_m_416m", + "cb_mm_d2", + "net1_d5_d2" +}; + +static const char * const aud_parents[] __initconst = { + "cb_cksq_40m", + "cb_apll2_196m" +}; + +static const char * const a1sys_parents[] __initconst = { + "cb_cksq_40m", + "apll2_d4" +}; + +static const char * const aud_l_parents[] __initconst = { + "cb_cksq_40m", + "cb_apll2_196m", + "m_d8_d2" +}; + +static const char * const a_tuner_parents[] __initconst = { + "cb_cksq_40m", + "apll2_d4", + "m_d8_d2" +}; + +static const char * const u2u3_parents[] __initconst = { + "cb_cksq_40m", + "m_d8_d2" +}; + +static const char * const u2u3_sys_parents[] __initconst = { + "cb_cksq_40m", + "net1_d5_d4" +}; + +static const char * const usb_frmcnt_parents[] __initconst = { + "cb_cksq_40m", + "cb_mm_d3_d5" +}; + +static const struct mtk_mux top_muxes[] = { + /* CLK_CFG_0 */ + MUX_GATE_CLR_SET_UPD(CLK_TOP_NFI1X_SEL, "nfi1x_sel", nfi1x_parents, + 0x000, 0x004, 0x008, 0, 3, 7, 0x1C0, 0), + MUX_GATE_CLR_SET_UPD(CLK_TOP_SPINFI_SEL, "spinfi_sel", spinfi_parents, + 0x000, 0x004, 0x008, 8, 3, 15, 0x1C0, 1), + MUX_GATE_CLR_SET_UPD(CLK_TOP_SPI_SEL, "spi_sel", spi_parents, + 0x000, 0x004, 0x008, 16, 3, 23, 0x1C0, 2), + MUX_GATE_CLR_SET_UPD(CLK_TOP_SPIM_MST_SEL, "spim_mst_sel", spi_parents, + 0x000, 0x004, 0x008, 24, 3, 31, 0x1C0, 3), + /* CLK_CFG_1 */ + MUX_GATE_CLR_SET_UPD(CLK_TOP_UART_SEL, "uart_sel", uart_parents, + 0x010, 0x014, 0x018, 0, 2, 7, 0x1C0, 4), + MUX_GATE_CLR_SET_UPD(CLK_TOP_PWM_SEL, "pwm_sel", pwm_parents, + 0x010, 0x014, 0x018, 8, 3, 15, 0x1C0, 5), + MUX_GATE_CLR_SET_UPD(CLK_TOP_I2C_SEL, "i2c_sel", i2c_parents, + 0x010, 0x014, 0x018, 16, 2, 23, 0x1C0, 6), + MUX_GATE_CLR_SET_UPD(CLK_TOP_PEXTP_TL_SEL, "pextp_tl_ck_sel", + pextp_tl_ck_parents, 0x010, 0x014, 0x018, 24, 2, 31, + 0x1C0, 7), + /* CLK_CFG_2 */ + MUX_GATE_CLR_SET_UPD(CLK_TOP_EMMC_208M_SEL, "emmc_208m_sel", + emmc_208m_parents, 0x020, 0x024, 0x028, 0, 3, 7, + 0x1C0, 8), + MUX_GATE_CLR_SET_UPD(CLK_TOP_EMMC_400M_SEL, "emmc_400m_sel", + emmc_400m_parents, 0x020, 0x024, 0x028, 8, 2, 15, + 0x1C0, 9), + MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_F26M_SEL, "csw_f26m_sel", + csw_f26m_parents, 0x020, 0x024, 0x028, 16, 1, 23, + 0x1C0, 10, + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT), + MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DRAMC_SEL, "dramc_sel", + csw_f26m_parents, 0x020, 0x024, 0x028, 24, 1, + 31, 0x1C0, 11, + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT), + /* CLK_CFG_3 */ + MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_DRAMC_MD32_SEL, "dramc_md32_sel", + dramc_md32_parents, 0x030, 0x034, 0x038, 0, 2, + 7, 0x1C0, 12, + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT), + MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SYSAXI_SEL, "sysaxi_sel", + sysaxi_parents, 0x030, 0x034, 0x038, 8, 1, 15, + 0x1C0, 13, + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT), + MUX_GATE_CLR_SET_UPD_FLAGS(CLK_TOP_SYSAPB_SEL, "sysapb_sel", + sysapb_parents, 0x030, 0x034, 0x038, 16, 1, + 23, 0x1C0, 14, + CLK_IS_CRITICAL | CLK_SET_RATE_PARENT), + MUX_GATE_CLR_SET_UPD(CLK_TOP_ARM_DB_MAIN_SEL, "arm_db_main_sel", + arm_db_main_parents, 0x030, 0x034, 0x038, 24, 1, 31, + 0x1C0, 15), + /* CLK_CFG_4 */ + MUX_GATE_CLR_SET_UPD(CLK_TOP_AP2CNN_HOST_SEL, "ap2cnn_host_sel", + ap2cnn_host_parents, 0x040, 0x044, 0x048, 0, 1, 7, + 0x1C0, 16), + MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_SEL, "netsys_sel", netsys_parents, + 0x040, 0x044, 0x048, 8, 1, 15, 0x1C0, 17), + MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_500M_SEL, "netsys_500m_sel", + netsys_500m_parents, 0x040, 0x044, 0x048, 16, 1, 23, + 0x1C0, 18), + MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_MCU_SEL, "netsys_mcu_sel", + netsys_mcu_parents, 0x040, 0x044, 0x048, 24, 3, 31, + 0x1C0, 19), + /* CLK_CFG_5 */ + MUX_GATE_CLR_SET_UPD(CLK_TOP_NETSYS_2X_SEL, "netsys_2x_sel", + netsys_2x_parents, 0x050, 0x054, 0x058, 0, 2, 7, + 0x1C0, 20), + MUX_GATE_CLR_SET_UPD(CLK_TOP_SGM_325M_SEL, "sgm_325m_sel", + sgm_325m_parents, 0x050, 0x054, 0x058, 8, 1, 15, + 0x1C0, 21), + MUX_GATE_CLR_SET_UPD(CLK_TOP_SGM_REG_SEL, "sgm_reg_sel", sgm_reg_parents, + 0x050, 0x054, 0x058, 16, 1, 23, 0x1C0, 22), + MUX_GATE_CLR_SET_UPD(CLK_TOP_EIP97B_SEL, "eip97b_sel", eip97b_parents, + 0x050, 0x054, 0x058, 24, 3, 31, 0x1C0, 23), + /* CLK_CFG_6 */ + MUX_GATE_CLR_SET_UPD(CLK_TOP_USB3_PHY_SEL, "usb3_phy_sel", + csw_f26m_parents, 0x060, 0x064, 0x068, 0, 1, + 7, 0x1C0, 24), + MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_SEL, "aud_sel", aud_parents, 0x060, + 0x064, 0x068, 8, 1, 15, 0x1C0, 25), + MUX_GATE_CLR_SET_UPD(CLK_TOP_A1SYS_SEL, "a1sys_sel", a1sys_parents, + 0x060, 0x064, 0x068, 16, 1, 23, 0x1C0, 26), + MUX_GATE_CLR_SET_UPD(CLK_TOP_AUD_L_SEL, "aud_l_sel", aud_l_parents, + 0x060, 0x064, 0x068, 24, 2, 31, 0x1C0, 27), + /* CLK_CFG_7 */ + MUX_GATE_CLR_SET_UPD(CLK_TOP_A_TUNER_SEL, "a_tuner_sel", + a_tuner_parents, 0x070, 0x074, 0x078, 0, 2, 7, + 0x1C0, 28), + MUX_GATE_CLR_SET_UPD(CLK_TOP_U2U3_SEL, "u2u3_sel", u2u3_parents, 0x070, + 0x074, 0x078, 8, 1, 15, 0x1C0, 29), + MUX_GATE_CLR_SET_UPD(CLK_TOP_U2U3_SYS_SEL, "u2u3_sys_sel", + u2u3_sys_parents, 0x070, 0x074, 0x078, 16, 1, 23, + 0x1C0, 30), + MUX_GATE_CLR_SET_UPD(CLK_TOP_U2U3_XHCI_SEL, "u2u3_xhci_sel", + u2u3_sys_parents, 0x070, 0x074, 0x078, 24, 1, 31, + 0x1C4, 0), + /* CLK_CFG_8 */ + MUX_GATE_CLR_SET_UPD(CLK_TOP_USB_FRMCNT_SEL, "usb_frmcnt_sel", + usb_frmcnt_parents, 0x080, 0x084, 0x088, 0, 1, 7, + 0x1C4, 1), +}; + +static struct mtk_composite top_aud_divs[] = { + DIV_GATE(CLK_TOP_AUD_I2S_M, "aud_i2s_m", "aud", + 0x0420, 0, 0x0420, 8, 8), +}; + +static int clk_mt7981_topckgen_probe(struct platform_device *pdev) +{ + struct clk_onecell_data *clk_data; + struct device_node *node = pdev->dev.of_node; + int r; + void __iomem *base; + int nr = ARRAY_SIZE(top_divs) + ARRAY_SIZE(top_muxes) + + ARRAY_SIZE(top_aud_divs); + + base = of_iomap(node, 0); + if (!base) { + pr_err("%s(): ioremap failed\n", __func__); + return -ENOMEM; + } + + clk_data = mtk_alloc_clk_data(nr); + if (!clk_data) + return -ENOMEM; + + mtk_clk_register_factors(top_divs, ARRAY_SIZE(top_divs), clk_data); + mtk_clk_register_muxes(top_muxes, ARRAY_SIZE(top_muxes), node, + &mt7981_clk_lock, clk_data); + mtk_clk_register_composites(top_aud_divs, ARRAY_SIZE(top_aud_divs), base, + &mt7981_clk_lock, clk_data); + + r = of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); + + if (r) { + pr_err("%s(): could not register clock provider: %d\n", + __func__, r); + goto free_topckgen_data; + } + return r; + +free_topckgen_data: + mtk_free_clk_data(clk_data); + return r; +} + +static const struct of_device_id of_match_clk_mt7981_topckgen[] = { + { .compatible = "mediatek,mt7981-topckgen", }, + {} +}; + +static struct platform_driver clk_mt7981_topckgen_drv = { + .probe = clk_mt7981_topckgen_probe, + .driver = { + .name = "clk-mt7981-topckgen", + .of_match_table = of_match_clk_mt7981_topckgen, + }, +}; +builtin_platform_driver(clk_mt7981_topckgen_drv); diff --git a/target/linux/mediatek/files-5.15/drivers/pinctrl/mediatek/pinctrl-mt7981.c b/target/linux/mediatek/files-5.15/drivers/pinctrl/mediatek/pinctrl-mt7981.c new file mode 100644 index 00000000000..35125f93a44 --- /dev/null +++ b/target/linux/mediatek/files-5.15/drivers/pinctrl/mediatek/pinctrl-mt7981.c @@ -0,0 +1,993 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * The MT7981 driver based on Linux generic pinctrl binding. + * + * Copyright (C) 2020 MediaTek Inc. + * Author: Sam Shih + */ + +#include "pinctrl-moore.h" + +#define MT7981_PIN(_number, _name) \ + MTK_PIN(_number, _name, 0, _number, DRV_GRP4) + +#define PIN_FIELD_BASE(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 32, 0) + +#define PINS_FIELD_BASE(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, _x_bits) \ + PIN_FIELD_CALC(_s_pin, _e_pin, _i_base, _s_addr, _x_addrs, _s_bit, \ + _x_bits, 32, 1) + +static const struct mtk_pin_field_calc mt7981_pin_mode_range[] = { + PIN_FIELD(0, 56, 0x300, 0x10, 0, 4), +}; + +static const struct mtk_pin_field_calc mt7981_pin_dir_range[] = { + PIN_FIELD(0, 56, 0x0, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_di_range[] = { + PIN_FIELD(0, 56, 0x200, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_do_range[] = { + PIN_FIELD(0, 56, 0x100, 0x10, 0, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_ies_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x10, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x10, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x20, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x20, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x20, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x20, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x20, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x20, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x20, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x20, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x20, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x20, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x20, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x20, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x20, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x20, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x10, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x10, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x20, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x20, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x10, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x10, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x10, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x10, 0x10, 1, 1), + + PIN_FIELD_BASE(40, 40, 7, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(41, 41, 7, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(42, 42, 7, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 7, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(44, 44, 7, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(45, 45, 7, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(46, 46, 7, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(47, 47, 7, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(48, 48, 7, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(49, 49, 7, 0x30, 0x10, 2, 1), + + PIN_FIELD_BASE(50, 50, 6, 0x10, 0x10, 0, 1), + PIN_FIELD_BASE(51, 51, 6, 0x10, 0x10, 2, 1), + PIN_FIELD_BASE(52, 52, 6, 0x10, 0x10, 3, 1), + PIN_FIELD_BASE(53, 53, 6, 0x10, 0x10, 4, 1), + PIN_FIELD_BASE(54, 54, 6, 0x10, 0x10, 5, 1), + PIN_FIELD_BASE(55, 55, 6, 0x10, 0x10, 6, 1), + PIN_FIELD_BASE(56, 56, 6, 0x10, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_smt_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x60, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x90, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x80, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x80, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x80, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x80, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x80, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x80, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x90, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x90, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x90, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x90, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x90, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x80, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x90, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x90, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x90, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x90, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x90, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x90, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x90, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x90, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x90, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x90, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x90, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x90, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x90, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x90, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x90, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x60, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x60, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x80, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x80, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x60, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x60, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x60, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x60, 0x10, 1, 1), + + PIN_FIELD_BASE(40, 40, 7, 0x70, 0x10, 1, 1), + PIN_FIELD_BASE(41, 41, 7, 0x70, 0x10, 0, 1), + PIN_FIELD_BASE(42, 42, 7, 0x70, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 7, 0x70, 0x10, 7, 1), + PIN_FIELD_BASE(44, 44, 7, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(45, 45, 7, 0x70, 0x10, 3, 1), + PIN_FIELD_BASE(46, 46, 7, 0x70, 0x10, 4, 1), + PIN_FIELD_BASE(47, 47, 7, 0x70, 0x10, 5, 1), + PIN_FIELD_BASE(48, 48, 7, 0x70, 0x10, 6, 1), + PIN_FIELD_BASE(49, 49, 7, 0x70, 0x10, 2, 1), + + PIN_FIELD_BASE(50, 50, 6, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(51, 51, 6, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(52, 52, 6, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(53, 53, 6, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(54, 54, 6, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(55, 55, 6, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(56, 56, 6, 0x50, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_pu_range[] = { + PIN_FIELD_BASE(40, 40, 7, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(41, 41, 7, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(42, 42, 7, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 7, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(44, 44, 7, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(45, 45, 7, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(46, 46, 7, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(47, 47, 7, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(48, 48, 7, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(49, 49, 7, 0x50, 0x10, 2, 1), + + PIN_FIELD_BASE(50, 50, 6, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(51, 51, 6, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(52, 52, 6, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(53, 53, 6, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(54, 54, 6, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(55, 55, 6, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(56, 56, 6, 0x30, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_pd_range[] = { + PIN_FIELD_BASE(40, 40, 7, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(41, 41, 7, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(42, 42, 7, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(43, 43, 7, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(44, 44, 7, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(45, 45, 7, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(46, 46, 7, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(47, 47, 7, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(48, 48, 7, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(49, 49, 7, 0x40, 0x10, 2, 1), + + PIN_FIELD_BASE(50, 50, 6, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(51, 51, 6, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(52, 52, 6, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(53, 53, 6, 0x20, 0x10, 4, 1), + PIN_FIELD_BASE(54, 54, 6, 0x20, 0x10, 5, 1), + PIN_FIELD_BASE(55, 55, 6, 0x20, 0x10, 6, 1), + PIN_FIELD_BASE(56, 56, 6, 0x20, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_drv_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(1, 1, 1, 0x00, 0x10, 0, 3), + + PIN_FIELD_BASE(2, 2, 5, 0x00, 0x10, 18, 3), + + PIN_FIELD_BASE(3, 3, 4, 0x00, 0x10, 18, 1), + PIN_FIELD_BASE(4, 4, 4, 0x00, 0x10, 6, 1), + PIN_FIELD_BASE(5, 5, 4, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(6, 6, 4, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(7, 7, 4, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(8, 8, 4, 0x00, 0x10, 12, 3), + + PIN_FIELD_BASE(9, 9, 5, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(10, 10, 5, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(11, 11, 5, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(12, 12, 5, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(13, 13, 5, 0x00, 0x10, 3, 3), + + PIN_FIELD_BASE(14, 14, 4, 0x00, 0x10, 27, 3), + + PIN_FIELD_BASE(15, 15, 2, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(16, 16, 2, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(17, 17, 2, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(18, 18, 2, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(19, 19, 2, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(20, 20, 2, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(21, 21, 2, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(22, 22, 2, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(23, 23, 2, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(24, 24, 2, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(25, 25, 2, 0x00, 0x10, 24, 3), + + PIN_FIELD_BASE(26, 26, 5, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(27, 27, 5, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(28, 28, 5, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(29, 29, 5, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(30, 30, 5, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(31, 31, 5, 0x00, 0x10, 15, 3), + + PIN_FIELD_BASE(32, 32, 1, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(33, 33, 1, 0x00, 0x10, 12, 3), + + PIN_FIELD_BASE(34, 34, 4, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(35, 35, 4, 0x00, 0x10, 21, 3), + + PIN_FIELD_BASE(36, 36, 3, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(37, 37, 3, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(38, 38, 3, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(39, 39, 3, 0x00, 0x10, 3, 3), + + PIN_FIELD_BASE(40, 40, 7, 0x00, 0x10, 3, 3), + PIN_FIELD_BASE(41, 41, 7, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(42, 42, 7, 0x00, 0x10, 27, 3), + PIN_FIELD_BASE(43, 43, 7, 0x00, 0x10, 21, 3), + PIN_FIELD_BASE(44, 44, 7, 0x00, 0x10, 24, 3), + PIN_FIELD_BASE(45, 45, 7, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(46, 46, 7, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(47, 47, 7, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(48, 48, 7, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(49, 49, 7, 0x00, 0x10, 6, 3), + + PIN_FIELD_BASE(50, 50, 6, 0x00, 0x10, 0, 3), + PIN_FIELD_BASE(51, 51, 6, 0x00, 0x10, 6, 3), + PIN_FIELD_BASE(52, 52, 6, 0x00, 0x10, 9, 3), + PIN_FIELD_BASE(53, 53, 6, 0x00, 0x10, 12, 3), + PIN_FIELD_BASE(54, 54, 6, 0x00, 0x10, 15, 3), + PIN_FIELD_BASE(55, 55, 6, 0x00, 0x10, 18, 3), + PIN_FIELD_BASE(56, 56, 6, 0x00, 0x10, 3, 3), +}; + +static const struct mtk_pin_field_calc mt7981_pin_pupd_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x20, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x30, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x30, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x30, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x30, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x90, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x30, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x30, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x30, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x30, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x30, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x30, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x30, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x20, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x30, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x30, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x20, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x20, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x20, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x20, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_r0_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x30, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x40, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x40, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x40, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x40, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x40, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x40, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x40, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x40, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x40, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x40, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x40, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x30, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x40, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x40, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x30, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x30, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x30, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x30, 0x10, 1, 1), +}; + +static const struct mtk_pin_field_calc mt7981_pin_r1_range[] = { + PIN_FIELD_BASE(0, 0, 1, 0x40, 0x10, 1, 1), + PIN_FIELD_BASE(1, 1, 1, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(2, 2, 5, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(3, 3, 4, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(4, 4, 4, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(5, 5, 4, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(6, 6, 4, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(7, 7, 4, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(8, 8, 4, 0x50, 0x10, 4, 1), + + PIN_FIELD_BASE(9, 9, 5, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(10, 10, 5, 0x50, 0x10, 8, 1), + PIN_FIELD_BASE(11, 11, 5, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(12, 12, 5, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(13, 13, 5, 0x50, 0x10, 11, 1), + + PIN_FIELD_BASE(14, 14, 4, 0x50, 0x10, 8, 1), + + PIN_FIELD_BASE(15, 15, 2, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(16, 16, 2, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(17, 17, 2, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(18, 18, 2, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(19, 19, 2, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(20, 20, 2, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(21, 21, 2, 0x50, 0x10, 6, 1), + PIN_FIELD_BASE(22, 22, 2, 0x50, 0x10, 7, 1), + PIN_FIELD_BASE(23, 23, 2, 0x50, 0x10, 10, 1), + PIN_FIELD_BASE(24, 24, 2, 0x50, 0x10, 9, 1), + PIN_FIELD_BASE(25, 25, 2, 0x50, 0x10, 8, 1), + + PIN_FIELD_BASE(26, 26, 5, 0x50, 0x10, 0, 1), + PIN_FIELD_BASE(27, 27, 5, 0x50, 0x10, 4, 1), + PIN_FIELD_BASE(28, 28, 5, 0x50, 0x10, 3, 1), + PIN_FIELD_BASE(29, 29, 5, 0x50, 0x10, 1, 1), + PIN_FIELD_BASE(30, 30, 5, 0x50, 0x10, 2, 1), + PIN_FIELD_BASE(31, 31, 5, 0x50, 0x10, 5, 1), + + PIN_FIELD_BASE(32, 32, 1, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(33, 33, 1, 0x40, 0x10, 3, 1), + + PIN_FIELD_BASE(34, 34, 4, 0x50, 0x10, 5, 1), + PIN_FIELD_BASE(35, 35, 4, 0x50, 0x10, 7, 1), + + PIN_FIELD_BASE(36, 36, 3, 0x40, 0x10, 2, 1), + PIN_FIELD_BASE(37, 37, 3, 0x40, 0x10, 3, 1), + PIN_FIELD_BASE(38, 38, 3, 0x40, 0x10, 0, 1), + PIN_FIELD_BASE(39, 39, 3, 0x40, 0x10, 1, 1), +}; + +static const struct mtk_pin_reg_calc mt7981_reg_cals[] = { + [PINCTRL_PIN_REG_MODE] = MTK_RANGE(mt7981_pin_mode_range), + [PINCTRL_PIN_REG_DIR] = MTK_RANGE(mt7981_pin_dir_range), + [PINCTRL_PIN_REG_DI] = MTK_RANGE(mt7981_pin_di_range), + [PINCTRL_PIN_REG_DO] = MTK_RANGE(mt7981_pin_do_range), + [PINCTRL_PIN_REG_SMT] = MTK_RANGE(mt7981_pin_smt_range), + [PINCTRL_PIN_REG_IES] = MTK_RANGE(mt7981_pin_ies_range), + [PINCTRL_PIN_REG_PU] = MTK_RANGE(mt7981_pin_pu_range), + [PINCTRL_PIN_REG_PD] = MTK_RANGE(mt7981_pin_pd_range), + [PINCTRL_PIN_REG_DRV] = MTK_RANGE(mt7981_pin_drv_range), + [PINCTRL_PIN_REG_PUPD] = MTK_RANGE(mt7981_pin_pupd_range), + [PINCTRL_PIN_REG_R0] = MTK_RANGE(mt7981_pin_r0_range), + [PINCTRL_PIN_REG_R1] = MTK_RANGE(mt7981_pin_r1_range), +}; + +static const struct mtk_pin_desc mt7981_pins[] = { + MT7981_PIN(0, "GPIO_WPS"), + MT7981_PIN(1, "GPIO_RESET"), + MT7981_PIN(2, "SYS_WATCHDOG"), + MT7981_PIN(3, "PCIE_PERESET_N"), + MT7981_PIN(4, "JTAG_JTDO"), + MT7981_PIN(5, "JTAG_JTDI"), + MT7981_PIN(6, "JTAG_JTMS"), + MT7981_PIN(7, "JTAG_JTCLK"), + MT7981_PIN(8, "JTAG_JTRST_N"), + MT7981_PIN(9, "WO_JTAG_JTDO"), + MT7981_PIN(10, "WO_JTAG_JTDI"), + MT7981_PIN(11, "WO_JTAG_JTMS"), + MT7981_PIN(12, "WO_JTAG_JTCLK"), + MT7981_PIN(13, "WO_JTAG_JTRST_N"), + MT7981_PIN(14, "USB_VBUS"), + MT7981_PIN(15, "PWM0"), + MT7981_PIN(16, "SPI0_CLK"), + MT7981_PIN(17, "SPI0_MOSI"), + MT7981_PIN(18, "SPI0_MISO"), + MT7981_PIN(19, "SPI0_CS"), + MT7981_PIN(20, "SPI0_HOLD"), + MT7981_PIN(21, "SPI0_WP"), + MT7981_PIN(22, "SPI1_CLK"), + MT7981_PIN(23, "SPI1_MOSI"), + MT7981_PIN(24, "SPI1_MISO"), + MT7981_PIN(25, "SPI1_CS"), + MT7981_PIN(26, "SPI2_CLK"), + MT7981_PIN(27, "SPI2_MOSI"), + MT7981_PIN(28, "SPI2_MISO"), + MT7981_PIN(29, "SPI2_CS"), + MT7981_PIN(30, "SPI2_HOLD"), + MT7981_PIN(31, "SPI2_WP"), + MT7981_PIN(32, "UART0_RXD"), + MT7981_PIN(33, "UART0_TXD"), + MT7981_PIN(34, "PCIE_CLK_REQ"), + MT7981_PIN(35, "PCIE_WAKE_N"), + MT7981_PIN(36, "SMI_MDC"), + MT7981_PIN(37, "SMI_MDIO"), + MT7981_PIN(38, "GBE_INT"), + MT7981_PIN(39, "GBE_RESET"), + MT7981_PIN(40, "WF_DIG_RESETB"), + MT7981_PIN(41, "WF_CBA_RESETB"), + MT7981_PIN(42, "WF_XO_REQ"), + MT7981_PIN(43, "WF_TOP_CLK"), + MT7981_PIN(44, "WF_TOP_DATA"), + MT7981_PIN(45, "WF_HB1"), + MT7981_PIN(46, "WF_HB2"), + MT7981_PIN(47, "WF_HB3"), + MT7981_PIN(48, "WF_HB4"), + MT7981_PIN(49, "WF_HB0"), + MT7981_PIN(50, "WF_HB0_B"), + MT7981_PIN(51, "WF_HB5"), + MT7981_PIN(52, "WF_HB6"), + MT7981_PIN(53, "WF_HB7"), + MT7981_PIN(54, "WF_HB8"), + MT7981_PIN(55, "WF_HB9"), + MT7981_PIN(56, "WF_HB10"), +}; + +/* List all groups consisting of these pins dedicated to the enablement of + * certain hardware block and the corresponding mode for all of the pins. + * The hardware probably has multiple combinations of these pinouts. + */ + +/* WA_AICE */ +static int mt7981_wa_aice1_pins[] = { 0, 1, }; +static int mt7981_wa_aice1_funcs[] = { 2, 2, }; + +static int mt7981_wa_aice2_pins[] = { 0, 1, }; +static int mt7981_wa_aice2_funcs[] = { 3, 3, }; + +static int mt7981_wa_aice3_pins[] = { 28, 29, }; +static int mt7981_wa_aice3_funcs[] = { 3, 3, }; + +static int mt7981_wm_aice1_pins[] = { 9, 10, }; +static int mt7981_wm_aice1_funcs[] = { 2, 2, }; + +static int mt7981_wm_aice2_pins[] = { 30, 31, }; +static int mt7981_wm_aice2_funcs[] = { 5, 5, }; + +/* WM_UART */ +static int mt7981_wm_uart_0_pins[] = { 0, 1, }; +static int mt7981_wm_uart_0_funcs[] = { 5, 5, }; + +static int mt7981_wm_uart_1_pins[] = { 20, 21, }; +static int mt7981_wm_uart_1_funcs[] = { 4, 4, }; + +static int mt7981_wm_uart_2_pins[] = { 30, 31, }; +static int mt7981_wm_uart_2_funcs[] = { 3, 3, }; + +/* DFD */ +static int mt7981_dfd_pins[] = { 0, 1, 4, 5, }; +static int mt7981_dfd_funcs[] = { 5, 5, 6, 6, }; + +/* SYS_WATCHDOG */ +static int mt7981_watchdog_pins[] = { 2, }; +static int mt7981_watchdog_funcs[] = { 1, }; + +static int mt7981_watchdog1_pins[] = { 13, }; +static int mt7981_watchdog1_funcs[] = { 5, }; + +/* PCIE_PERESET_N */ +static int mt7981_pcie_pereset_pins[] = { 3, }; +static int mt7981_pcie_pereset_funcs[] = { 1, }; + +/* JTAG */ +static int mt7981_jtag_pins[] = { 4, 5, 6, 7, 8, }; +static int mt7981_jtag_funcs[] = { 1, 1, 1, 1, 1, }; + +/* WM_JTAG */ +static int mt7981_wm_jtag_0_pins[] = { 4, 5, 6, 7, 8, }; +static int mt7981_wm_jtag_0_funcs[] = { 2, 2, 2, 2, 2, }; + +static int mt7981_wm_jtag_1_pins[] = { 20, 21, 22, 23, 24, }; +static int mt7981_wm_jtag_1_funcs[] = { 5, 5, 5, 5, 5, }; + +/* WO0_JTAG */ +static int mt7981_wo0_jtag_0_pins[] = { 9, 10, 11, 12, 13, }; +static int mt7981_wo0_jtag_0_funcs[] = { 1, 1, 1, 1, 1, }; + +static int mt7981_wo0_jtag_1_pins[] = { 25, 26, 27, 28, 29, }; +static int mt7981_wo0_jtag_1_funcs[] = { 5, 5, 5, 5, 5, }; + +/* UART2 */ +static int mt7981_uart2_0_pins[] = { 4, 5, 6, 7, }; +static int mt7981_uart2_0_funcs[] = { 3, 3, 3, 3, }; + +/* GBE_LED0 */ +static int mt7981_gbe_led0_pins[] = { 8, }; +static int mt7981_gbe_led0_funcs[] = { 3, }; + +/* PTA_EXT */ +static int mt7981_pta_ext_0_pins[] = { 4, 5, 6, }; +static int mt7981_pta_ext_0_funcs[] = { 4, 4, 4, }; + +static int mt7981_pta_ext_1_pins[] = { 22, 23, 24, }; +static int mt7981_pta_ext_1_funcs[] = { 4, 4, 4, }; + +/* PWM2 */ +static int mt7981_pwm2_pins[] = { 7, }; +static int mt7981_pwm2_funcs[] = { 4, }; + +/* NET_WO0_UART_TXD */ +static int mt7981_net_wo0_uart_txd_0_pins[] = { 8, }; +static int mt7981_net_wo0_uart_txd_0_funcs[] = { 4, }; + +static int mt7981_net_wo0_uart_txd_1_pins[] = { 14, }; +static int mt7981_net_wo0_uart_txd_1_funcs[] = { 3, }; + +static int mt7981_net_wo0_uart_txd_2_pins[] = { 15, }; +static int mt7981_net_wo0_uart_txd_2_funcs[] = { 4, }; + +/* SPI1 */ +static int mt7981_spi1_0_pins[] = { 4, 5, 6, 7, }; +static int mt7981_spi1_0_funcs[] = { 5, 5, 5, 5, }; + +/* I2C */ +static int mt7981_i2c0_0_pins[] = { 6, 7, }; +static int mt7981_i2c0_0_funcs[] = { 6, 6, }; + +static int mt7981_i2c0_1_pins[] = { 30, 31, }; +static int mt7981_i2c0_1_funcs[] = { 4, 4, }; + +static int mt7981_i2c0_2_pins[] = { 36, 37, }; +static int mt7981_i2c0_2_funcs[] = { 2, 2, }; + +static int mt7981_u2_phy_i2c_pins[] = { 30, 31, }; +static int mt7981_u2_phy_i2c_funcs[] = { 6, 6, }; + +static int mt7981_u3_phy_i2c_pins[] = { 32, 33, }; +static int mt7981_u3_phy_i2c_funcs[] = { 3, 3, }; + +static int mt7981_sgmii1_phy_i2c_pins[] = { 32, 33, }; +static int mt7981_sgmii1_phy_i2c_funcs[] = { 2, 2, }; + +static int mt7981_sgmii0_phy_i2c_pins[] = { 32, 33, }; +static int mt7981_sgmii0_phy_i2c_funcs[] = { 5, 5, }; + +/* DFD_NTRST */ +static int mt7981_dfd_ntrst_pins[] = { 8, }; +static int mt7981_dfd_ntrst_funcs[] = { 6, }; + +/* PWM0 */ +static int mt7981_pwm0_0_pins[] = { 13, }; +static int mt7981_pwm0_0_funcs[] = { 2, }; + +static int mt7981_pwm0_1_pins[] = { 15, }; +static int mt7981_pwm0_1_funcs[] = { 1, }; + +/* PWM1 */ +static int mt7981_pwm1_0_pins[] = { 14, }; +static int mt7981_pwm1_0_funcs[] = { 2, }; + +static int mt7981_pwm1_1_pins[] = { 15, }; +static int mt7981_pwm1_1_funcs[] = { 3, }; + +/* GBE_LED1 */ +static int mt7981_gbe_led1_pins[] = { 13, }; +static int mt7981_gbe_led1_funcs[] = { 3, }; + +/* PCM */ +static int mt7981_pcm_pins[] = { 9, 10, 11, 12, 13, 25 }; +static int mt7981_pcm_funcs[] = { 4, 4, 4, 4, 4, 4, }; + +/* UDI */ +static int mt7981_udi_pins[] = { 9, 10, 11, 12, 13, }; +static int mt7981_udi_funcs[] = { 6, 6, 6, 6, 6, }; + +/* DRV_VBUS */ +static int mt7981_drv_vbus_pins[] = { 14, }; +static int mt7981_drv_vbus_funcs[] = { 1, }; + +/* EMMC */ +static int mt7981_emmc_45_pins[] = { 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, }; +static int mt7981_emmc_45_funcs[] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, }; + +/* SNFI */ +static int mt7981_snfi_pins[] = { 16, 17, 18, 19, 20, 21, }; +static int mt7981_snfi_funcs[] = { 3, 3, 3, 3, 3, 3, }; + +/* SPI0 */ +static int mt7981_spi0_pins[] = { 16, 17, 18, 19, }; +static int mt7981_spi0_funcs[] = { 1, 1, 1, 1, }; + +/* SPI0 */ +static int mt7981_spi0_wp_hold_pins[] = { 20, 21, }; +static int mt7981_spi0_wp_hold_funcs[] = { 1, 1, }; + +/* SPI1 */ +static int mt7981_spi1_1_pins[] = { 22, 23, 24, 25, }; +static int mt7981_spi1_1_funcs[] = { 1, 1, 1, 1, }; + +/* SPI2 */ +static int mt7981_spi2_pins[] = { 26, 27, 28, 29, }; +static int mt7981_spi2_funcs[] = { 1, 1, 1, 1, }; + +/* SPI2 */ +static int mt7981_spi2_wp_hold_pins[] = { 30, 31, }; +static int mt7981_spi2_wp_hold_funcs[] = { 1, 1, }; + +/* UART1 */ +static int mt7981_uart1_0_pins[] = { 16, 17, 18, 19, }; +static int mt7981_uart1_0_funcs[] = { 4, 4, 4, 4, }; + +static int mt7981_uart1_1_pins[] = { 26, 27, 28, 29, }; +static int mt7981_uart1_1_funcs[] = { 2, 2, 2, 2, }; + +/* UART2 */ +static int mt7981_uart2_1_pins[] = { 22, 23, 24, 25, }; +static int mt7981_uart2_1_funcs[] = { 3, 3, 3, 3, }; + +/* UART0 */ +static int mt7981_uart0_pins[] = { 32, 33, }; +static int mt7981_uart0_funcs[] = { 1, 1, }; + +/* PCIE_CLK_REQ */ +static int mt7981_pcie_clk_pins[] = { 34, }; +static int mt7981_pcie_clk_funcs[] = { 2, }; + +/* PCIE_WAKE_N */ +static int mt7981_pcie_wake_pins[] = { 35, }; +static int mt7981_pcie_wake_funcs[] = { 2, }; + +/* MDC_MDIO */ +static int mt7981_smi_mdc_mdio_pins[] = { 36, 37, }; +static int mt7981_smi_mdc_mdio_funcs[] = { 1, 1, }; + +static int mt7981_gbe_ext_mdc_mdio_pins[] = { 36, 37, }; +static int mt7981_gbe_ext_mdc_mdio_funcs[] = { 3, 3, }; + +/* WF0_MODE1 */ +static int mt7981_wf0_mode1_pins[] = { 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56 }; +static int mt7981_wf0_mode1_funcs[] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + +/* WF0_MODE3 */ +static int mt7981_wf0_mode3_pins[] = { 45, 46, 47, 48, 49, 51 }; +static int mt7981_wf0_mode3_funcs[] = { 2, 2, 2, 2, 2, 2 }; + +/* WF2G_LED */ +static int mt7981_wf2g_led0_pins[] = { 30, }; +static int mt7981_wf2g_led0_funcs[] = { 2, }; + +static int mt7981_wf2g_led1_pins[] = { 34, }; +static int mt7981_wf2g_led1_funcs[] = { 1, }; + +/* WF5G_LED */ +static int mt7981_wf5g_led0_pins[] = { 31, }; +static int mt7981_wf5g_led0_funcs[] = { 2, }; + +static int mt7981_wf5g_led1_pins[] = { 35, }; +static int mt7981_wf5g_led1_funcs[] = { 1, }; + +/* MT7531_INT */ +static int mt7981_mt7531_int_pins[] = { 38, }; +static int mt7981_mt7531_int_funcs[] = { 1, }; + +/* ANT_SEL */ +static int mt7981_ant_sel_pins[] = { 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 34, 35 }; +static int mt7981_ant_sel_funcs[] = { 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6 }; + +static const struct group_desc mt7981_groups[] = { + /* @GPIO(0,1): WA_AICE(2) */ + PINCTRL_PIN_GROUP("wa_aice1", mt7981_wa_aice1), + /* @GPIO(0,1): WA_AICE(3) */ + PINCTRL_PIN_GROUP("wa_aice2", mt7981_wa_aice2), + /* @GPIO(0,1): WM_UART(5) */ + PINCTRL_PIN_GROUP("wm_uart_0", mt7981_wm_uart_0), + /* @GPIO(0,1,4,5): DFD(6) */ + PINCTRL_PIN_GROUP("dfd", mt7981_dfd), + /* @GPIO(2): SYS_WATCHDOG(1) */ + PINCTRL_PIN_GROUP("watchdog", mt7981_watchdog), + /* @GPIO(3): PCIE_PERESET_N(1) */ + PINCTRL_PIN_GROUP("pcie_pereset", mt7981_pcie_pereset), + /* @GPIO(4,8) JTAG(1) */ + PINCTRL_PIN_GROUP("jtag", mt7981_jtag), + /* @GPIO(4,8) WM_JTAG(2) */ + PINCTRL_PIN_GROUP("wm_jtag_0", mt7981_wm_jtag_0), + /* @GPIO(9,13) WO0_JTAG(1) */ + PINCTRL_PIN_GROUP("wo0_jtag_0", mt7981_wo0_jtag_0), + /* @GPIO(4,7) WM_JTAG(3) */ + PINCTRL_PIN_GROUP("uart2_0", mt7981_uart2_0), + /* @GPIO(8) GBE_LED0(3) */ + PINCTRL_PIN_GROUP("gbe_led0", mt7981_gbe_led0), + /* @GPIO(4,6) PTA_EXT(4) */ + PINCTRL_PIN_GROUP("pta_ext_0", mt7981_pta_ext_0), + /* @GPIO(7) PWM2(4) */ + PINCTRL_PIN_GROUP("pwm2", mt7981_pwm2), + /* @GPIO(8) NET_WO0_UART_TXD(4) */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_0", mt7981_net_wo0_uart_txd_0), + /* @GPIO(4,7) SPI1(5) */ + PINCTRL_PIN_GROUP("spi1_0", mt7981_spi1_0), + /* @GPIO(6,7) I2C(5) */ + PINCTRL_PIN_GROUP("i2c0_0", mt7981_i2c0_0), + /* @GPIO(0,1,4,5): DFD_NTRST(6) */ + PINCTRL_PIN_GROUP("dfd_ntrst", mt7981_dfd_ntrst), + /* @GPIO(9,10): WM_AICE(2) */ + PINCTRL_PIN_GROUP("wm_aice1", mt7981_wm_aice1), + /* @GPIO(13): PWM0(2) */ + PINCTRL_PIN_GROUP("pwm0_0", mt7981_pwm0_0), + /* @GPIO(15): PWM0(1) */ + PINCTRL_PIN_GROUP("pwm0_1", mt7981_pwm0_1), + /* @GPIO(14): PWM1(2) */ + PINCTRL_PIN_GROUP("pwm1_0", mt7981_pwm1_0), + /* @GPIO(15): PWM1(3) */ + PINCTRL_PIN_GROUP("pwm1_1", mt7981_pwm1_1), + /* @GPIO(14) NET_WO0_UART_TXD(3) */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_1", mt7981_net_wo0_uart_txd_1), + /* @GPIO(15) NET_WO0_UART_TXD(4) */ + PINCTRL_PIN_GROUP("net_wo0_uart_txd_2", mt7981_net_wo0_uart_txd_2), + /* @GPIO(13) GBE_LED0(3) */ + PINCTRL_PIN_GROUP("gbe_led1", mt7981_gbe_led1), + /* @GPIO(9,13) PCM(4) */ + PINCTRL_PIN_GROUP("pcm", mt7981_pcm), + /* @GPIO(13): SYS_WATCHDOG1(5) */ + PINCTRL_PIN_GROUP("watchdog1", mt7981_watchdog1), + /* @GPIO(9,13) UDI(4) */ + PINCTRL_PIN_GROUP("udi", mt7981_udi), + /* @GPIO(14) DRV_VBUS(1) */ + PINCTRL_PIN_GROUP("drv_vbus", mt7981_drv_vbus), + /* @GPIO(15,25): EMMC(2) */ + PINCTRL_PIN_GROUP("emmc_45", mt7981_emmc_45), + /* @GPIO(16,21): SNFI(3) */ + PINCTRL_PIN_GROUP("snfi", mt7981_snfi), + /* @GPIO(16,19): SPI0(1) */ + PINCTRL_PIN_GROUP("spi0", mt7981_spi0), + /* @GPIO(20,21): SPI0(1) */ + PINCTRL_PIN_GROUP("spi0_wp_hold", mt7981_spi0_wp_hold), + /* @GPIO(22,25) SPI1(1) */ + PINCTRL_PIN_GROUP("spi1_1", mt7981_spi1_1), + /* @GPIO(26,29): SPI2(1) */ + PINCTRL_PIN_GROUP("spi2", mt7981_spi2), + /* @GPIO(30,31): SPI0(1) */ + PINCTRL_PIN_GROUP("spi2_wp_hold", mt7981_spi2_wp_hold), + /* @GPIO(16,19): UART1(4) */ + PINCTRL_PIN_GROUP("uart1_0", mt7981_uart1_0), + /* @GPIO(26,29): UART1(2) */ + PINCTRL_PIN_GROUP("uart1_1", mt7981_uart1_1), + /* @GPIO(22,25): UART1(3) */ + PINCTRL_PIN_GROUP("uart2_1", mt7981_uart2_1), + /* @GPIO(22,24) PTA_EXT(4) */ + PINCTRL_PIN_GROUP("pta_ext_1", mt7981_pta_ext_1), + /* @GPIO(20,21): WM_UART(4) */ + PINCTRL_PIN_GROUP("wm_aurt_1", mt7981_wm_uart_1), + /* @GPIO(30,31): WM_UART(3) */ + PINCTRL_PIN_GROUP("wm_aurt_2", mt7981_wm_uart_2), + /* @GPIO(20,24) WM_JTAG(5) */ + PINCTRL_PIN_GROUP("wm_jtag_1", mt7981_wm_jtag_1), + /* @GPIO(25,29) WO0_JTAG(5) */ + PINCTRL_PIN_GROUP("wo0_jtag_1", mt7981_wo0_jtag_1), + /* @GPIO(28,29): WA_AICE(3) */ + PINCTRL_PIN_GROUP("wa_aice3", mt7981_wa_aice3), + /* @GPIO(30,31): WM_AICE(5) */ + PINCTRL_PIN_GROUP("wm_aice2", mt7981_wm_aice2), + /* @GPIO(30,31): I2C(4) */ + PINCTRL_PIN_GROUP("i2c0_1", mt7981_i2c0_1), + /* @GPIO(30,31): I2C(6) */ + PINCTRL_PIN_GROUP("u2_phy_i2c", mt7981_u2_phy_i2c), + /* @GPIO(32,33): I2C(1) */ + PINCTRL_PIN_GROUP("uart0", mt7981_uart0), + /* @GPIO(32,33): I2C(2) */ + PINCTRL_PIN_GROUP("sgmii1_phy_i2c", mt7981_sgmii1_phy_i2c), + /* @GPIO(32,33): I2C(3) */ + PINCTRL_PIN_GROUP("u3_phy_i2c", mt7981_u3_phy_i2c), + /* @GPIO(32,33): I2C(5) */ + PINCTRL_PIN_GROUP("sgmii0_phy_i2c", mt7981_sgmii0_phy_i2c), + /* @GPIO(34): PCIE_CLK_REQ(2) */ + PINCTRL_PIN_GROUP("pcie_clk", mt7981_pcie_clk), + /* @GPIO(35): PCIE_WAKE_N(2) */ + PINCTRL_PIN_GROUP("pcie_wake", mt7981_pcie_wake), + /* @GPIO(36,37): I2C(2) */ + PINCTRL_PIN_GROUP("i2c0_2", mt7981_i2c0_2), + /* @GPIO(36,37): MDC_MDIO(1) */ + PINCTRL_PIN_GROUP("smi_mdc_mdio", mt7981_smi_mdc_mdio), + /* @GPIO(36,37): MDC_MDIO(3) */ + PINCTRL_PIN_GROUP("gbe_ext_mdc_mdio", mt7981_gbe_ext_mdc_mdio), + /* @GPIO(69,85): WF0_MODE1(1) */ + PINCTRL_PIN_GROUP("wf0_mode1", mt7981_wf0_mode1), + /* @GPIO(74,80): WF0_MODE3(3) */ + PINCTRL_PIN_GROUP("wf0_mode3", mt7981_wf0_mode3), + /* @GPIO(30): WF2G_LED(2) */ + PINCTRL_PIN_GROUP("wf2g_led0", mt7981_wf2g_led0), + /* @GPIO(34): WF2G_LED(1) */ + PINCTRL_PIN_GROUP("wf2g_led1", mt7981_wf2g_led1), + /* @GPIO(31): WF5G_LED(2) */ + PINCTRL_PIN_GROUP("wf5g_led0", mt7981_wf5g_led0), + /* @GPIO(35): WF5G_LED(1) */ + PINCTRL_PIN_GROUP("wf5g_led1", mt7981_wf5g_led1), + /* @GPIO(38): MT7531_INT(1) */ + PINCTRL_PIN_GROUP("mt7531_int", mt7981_mt7531_int), + /* @GPIO(14,15,26,17,18,19,20,21,22,23,24,25,34,35): ANT_SEL(1) */ + PINCTRL_PIN_GROUP("ant_sel", mt7981_ant_sel), +}; + +/* Joint those groups owning the same capability in user point of view which + * allows that people tend to use through the device tree. + */ +static const char *mt7981_wa_aice_groups[] = { "wa_aice1", "wa_aice2", "wm_aice1_1", + "wa_aice3", "wm_aice1_2", }; +static const char *mt7981_uart_groups[] = { "wm_uart_0", "uart2_0", + "net_wo0_uart_txd_0", "net_wo0_uart_txd_1", "net_wo0_uart_txd_2", + "uart1_0", "uart1_1", "uart2_1", "wm_aurt_1", "wm_aurt_2", "uart0", }; +static const char *mt7981_dfd_groups[] = { "dfd", "dfd_ntrst", }; +static const char *mt7981_wdt_groups[] = { "watchdog", "watchdog1", }; +static const char *mt7981_pcie_groups[] = { "pcie_pereset", "pcie_clk", "pcie_wake", }; +static const char *mt7981_jtag_groups[] = { "jtag", "wm_jtag_0", "wo0_jtag_0", + "wo0_jtag_1", "wm_jtag_1", }; +static const char *mt7981_led_groups[] = { "gbe_led0", "gbe_led1", "wf2g_led0", + "wf2g_led1", "wf5g_led0", "wf5g_led1", }; +static const char *mt7981_pta_groups[] = { "pta_ext_0", "pta_ext_1", }; +static const char *mt7981_pwm_groups[] = { "pwm2", "pwm0_0", "pwm0_1", + "pwm1_0", "pwm1_1", }; +static const char *mt7981_spi_groups[] = { "spi1_0", "spi0", "spi0_wp_hold", "spi1_1", "spi2", + "spi2_wp_hold", }; +static const char *mt7981_i2c_groups[] = { "i2c0_0", "i2c0_1", "u2_phy_i2c", + "sgmii1_phy_i2c", "u3_phy_i2c", "sgmii0_phy_i2c", "i2c0_2", }; +static const char *mt7981_pcm_groups[] = { "pcm", }; +static const char *mt7981_udi_groups[] = { "udi", }; +static const char *mt7981_usb_groups[] = { "drv_vbus", }; +static const char *mt7981_flash_groups[] = { "emmc_45", "snfi", }; +static const char *mt7981_ethernet_groups[] = { "smi_mdc_mdio", "gbe_ext_mdc_mdio", + "wf0_mode1", "wf0_mode3", "mt7531_int", }; +static const char *mt7981_ant_groups[] = { "ant_sel", }; + +static const struct function_desc mt7981_functions[] = { + {"wa_aice", mt7981_wa_aice_groups, ARRAY_SIZE(mt7981_wa_aice_groups)}, + {"dfd", mt7981_dfd_groups, ARRAY_SIZE(mt7981_dfd_groups)}, + {"jtag", mt7981_jtag_groups, ARRAY_SIZE(mt7981_jtag_groups)}, + {"pta", mt7981_pta_groups, ARRAY_SIZE(mt7981_pta_groups)}, + {"pcm", mt7981_pcm_groups, ARRAY_SIZE(mt7981_pcm_groups)}, + {"udi", mt7981_udi_groups, ARRAY_SIZE(mt7981_udi_groups)}, + {"usb", mt7981_usb_groups, ARRAY_SIZE(mt7981_usb_groups)}, + {"ant", mt7981_ant_groups, ARRAY_SIZE(mt7981_ant_groups)}, + {"eth", mt7981_ethernet_groups, ARRAY_SIZE(mt7981_ethernet_groups)}, + {"i2c", mt7981_i2c_groups, ARRAY_SIZE(mt7981_i2c_groups)}, + {"led", mt7981_led_groups, ARRAY_SIZE(mt7981_led_groups)}, + {"pwm", mt7981_pwm_groups, ARRAY_SIZE(mt7981_pwm_groups)}, + {"spi", mt7981_spi_groups, ARRAY_SIZE(mt7981_spi_groups)}, + {"uart", mt7981_uart_groups, ARRAY_SIZE(mt7981_uart_groups)}, + {"watchdog", mt7981_wdt_groups, ARRAY_SIZE(mt7981_wdt_groups)}, + {"flash", mt7981_flash_groups, ARRAY_SIZE(mt7981_flash_groups)}, + {"pcie", mt7981_pcie_groups, ARRAY_SIZE(mt7981_pcie_groups)}, +}; + +static const struct mtk_eint_hw mt7981_eint_hw = { + .port_mask = 7, + .ports = 7, + .ap_num = ARRAY_SIZE(mt7981_pins), + .db_cnt = 16, +}; + +static const char * const mt7981_pinctrl_register_base_names[] = { + "gpio", "iocfg_rt", "iocfg_rm", "iocfg_rb", + "iocfg_lb", "iocfg_bl", "iocfg_tm", "iocfg_tl", +}; + +static struct mtk_pin_soc mt7981_data = { + .reg_cal = mt7981_reg_cals, + .pins = mt7981_pins, + .npins = ARRAY_SIZE(mt7981_pins), + .grps = mt7981_groups, + .ngrps = ARRAY_SIZE(mt7981_groups), + .funcs = mt7981_functions, + .nfuncs = ARRAY_SIZE(mt7981_functions), + .eint_hw = &mt7981_eint_hw, + .gpio_m = 0, + .ies_present = false, + .base_names = mt7981_pinctrl_register_base_names, + .nbase_names = ARRAY_SIZE(mt7981_pinctrl_register_base_names), + .bias_set_combo = mtk_pinconf_bias_set_combo, + .bias_get_combo = mtk_pinconf_bias_get_combo, + .drive_set = mtk_pinconf_drive_set_rev1, + .drive_get = mtk_pinconf_drive_get_rev1, + .adv_pull_get = mtk_pinconf_adv_pull_get, + .adv_pull_set = mtk_pinconf_adv_pull_set, +}; + +static const struct of_device_id mt7981_pinctrl_of_match[] = { + { .compatible = "mediatek,mt7981-pinctrl", }, + {} +}; + +static int mt7981_pinctrl_probe(struct platform_device *pdev) +{ + return mtk_moore_pinctrl_probe(pdev, &mt7981_data); +} + +static struct platform_driver mt7981_pinctrl_driver = { + .driver = { + .name = "mt7981-pinctrl", + .of_match_table = mt7981_pinctrl_of_match, + }, + .probe = mt7981_pinctrl_probe, +}; + +static int __init mt7981_pinctrl_init(void) +{ + return platform_driver_register(&mt7981_pinctrl_driver); +} +arch_initcall(mt7981_pinctrl_init); diff --git a/target/linux/mediatek/files-5.15/include/dt-bindings/clock/mediatek,mt7981-clk.h b/target/linux/mediatek/files-5.15/include/dt-bindings/clock/mediatek,mt7981-clk.h new file mode 100644 index 00000000000..192f8cefb58 --- /dev/null +++ b/target/linux/mediatek/files-5.15/include/dt-bindings/clock/mediatek,mt7981-clk.h @@ -0,0 +1,215 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (c) 2021 MediaTek Inc. + * Author: Wenzhen.Yu + * Author: Jianhui Zhao + * Author: Daniel Golle + */ + +#ifndef _DT_BINDINGS_CLK_MT7981_H +#define _DT_BINDINGS_CLK_MT7981_H + +/* TOPCKGEN */ +#define CLK_TOP_CB_CKSQ_40M 0 +#define CLK_TOP_CB_M_416M 1 +#define CLK_TOP_CB_M_D2 2 +#define CLK_TOP_CB_M_D3 3 +#define CLK_TOP_M_D3_D2 4 +#define CLK_TOP_CB_M_D4 5 +#define CLK_TOP_CB_M_D8 6 +#define CLK_TOP_M_D8_D2 7 +#define CLK_TOP_CB_MM_720M 8 +#define CLK_TOP_CB_MM_D2 9 +#define CLK_TOP_CB_MM_D3 10 +#define CLK_TOP_CB_MM_D3_D5 11 +#define CLK_TOP_CB_MM_D4 12 +#define CLK_TOP_CB_MM_D6 13 +#define CLK_TOP_MM_D6_D2 14 +#define CLK_TOP_CB_MM_D8 15 +#define CLK_TOP_CB_APLL2_196M 16 +#define CLK_TOP_APLL2_D2 17 +#define CLK_TOP_APLL2_D4 18 +#define CLK_TOP_NET1_2500M 19 +#define CLK_TOP_CB_NET1_D4 20 +#define CLK_TOP_CB_NET1_D5 21 +#define CLK_TOP_NET1_D5_D2 22 +#define CLK_TOP_NET1_D5_D4 23 +#define CLK_TOP_CB_NET1_D8 24 +#define CLK_TOP_NET1_D8_D2 25 +#define CLK_TOP_NET1_D8_D4 26 +#define CLK_TOP_CB_NET2_800M 27 +#define CLK_TOP_CB_NET2_D2 28 +#define CLK_TOP_CB_NET2_D4 29 +#define CLK_TOP_NET2_D4_D2 30 +#define CLK_TOP_NET2_D4_D4 31 +#define CLK_TOP_CB_NET2_D6 32 +#define CLK_TOP_CB_WEDMCU_208M 33 +#define CLK_TOP_CB_SGM_325M 34 +#define CLK_TOP_CKSQ_40M_D2 35 +#define CLK_TOP_CB_RTC_32K 36 +#define CLK_TOP_CB_RTC_32P7K 37 +#define CLK_TOP_USB_TX250M 38 +#define CLK_TOP_FAUD 39 +#define CLK_TOP_NFI1X 40 +#define CLK_TOP_USB_EQ_RX250M 41 +#define CLK_TOP_USB_CDR_CK 42 +#define CLK_TOP_USB_LN0_CK 43 +#define CLK_TOP_SPINFI_BCK 44 +#define CLK_TOP_SPI 45 +#define CLK_TOP_SPIM_MST 46 +#define CLK_TOP_UART_BCK 47 +#define CLK_TOP_PWM_BCK 48 +#define CLK_TOP_I2C_BCK 49 +#define CLK_TOP_PEXTP_TL 50 +#define CLK_TOP_EMMC_208M 51 +#define CLK_TOP_EMMC_400M 52 +#define CLK_TOP_DRAMC_REF 53 +#define CLK_TOP_DRAMC_MD32 54 +#define CLK_TOP_SYSAXI 55 +#define CLK_TOP_SYSAPB 56 +#define CLK_TOP_ARM_DB_MAIN 57 +#define CLK_TOP_AP2CNN_HOST 58 +#define CLK_TOP_NETSYS 59 +#define CLK_TOP_NETSYS_500M 60 +#define CLK_TOP_NETSYS_WED_MCU 61 +#define CLK_TOP_NETSYS_2X 62 +#define CLK_TOP_SGM_325M 63 +#define CLK_TOP_SGM_REG 64 +#define CLK_TOP_F26M 65 +#define CLK_TOP_EIP97B 66 +#define CLK_TOP_USB3_PHY 67 +#define CLK_TOP_AUD 68 +#define CLK_TOP_A1SYS 69 +#define CLK_TOP_AUD_L 70 +#define CLK_TOP_A_TUNER 71 +#define CLK_TOP_U2U3_REF 72 +#define CLK_TOP_U2U3_SYS 73 +#define CLK_TOP_U2U3_XHCI 74 +#define CLK_TOP_USB_FRMCNT 75 +#define CLK_TOP_NFI1X_SEL 76 +#define CLK_TOP_SPINFI_SEL 77 +#define CLK_TOP_SPI_SEL 78 +#define CLK_TOP_SPIM_MST_SEL 79 +#define CLK_TOP_UART_SEL 80 +#define CLK_TOP_PWM_SEL 81 +#define CLK_TOP_I2C_SEL 82 +#define CLK_TOP_PEXTP_TL_SEL 83 +#define CLK_TOP_EMMC_208M_SEL 84 +#define CLK_TOP_EMMC_400M_SEL 85 +#define CLK_TOP_F26M_SEL 86 +#define CLK_TOP_DRAMC_SEL 87 +#define CLK_TOP_DRAMC_MD32_SEL 88 +#define CLK_TOP_SYSAXI_SEL 89 +#define CLK_TOP_SYSAPB_SEL 90 +#define CLK_TOP_ARM_DB_MAIN_SEL 91 +#define CLK_TOP_AP2CNN_HOST_SEL 92 +#define CLK_TOP_NETSYS_SEL 93 +#define CLK_TOP_NETSYS_500M_SEL 94 +#define CLK_TOP_NETSYS_MCU_SEL 95 +#define CLK_TOP_NETSYS_2X_SEL 96 +#define CLK_TOP_SGM_325M_SEL 97 +#define CLK_TOP_SGM_REG_SEL 98 +#define CLK_TOP_EIP97B_SEL 99 +#define CLK_TOP_USB3_PHY_SEL 100 +#define CLK_TOP_AUD_SEL 101 +#define CLK_TOP_A1SYS_SEL 102 +#define CLK_TOP_AUD_L_SEL 103 +#define CLK_TOP_A_TUNER_SEL 104 +#define CLK_TOP_U2U3_SEL 105 +#define CLK_TOP_U2U3_SYS_SEL 106 +#define CLK_TOP_U2U3_XHCI_SEL 107 +#define CLK_TOP_USB_FRMCNT_SEL 108 +#define CLK_TOP_AUD_I2S_M 109 + +/* INFRACFG */ +#define CLK_INFRA_66M_MCK 0 +#define CLK_INFRA_UART0_SEL 1 +#define CLK_INFRA_UART1_SEL 2 +#define CLK_INFRA_UART2_SEL 3 +#define CLK_INFRA_SPI0_SEL 4 +#define CLK_INFRA_SPI1_SEL 5 +#define CLK_INFRA_SPI2_SEL 6 +#define CLK_INFRA_PWM1_SEL 7 +#define CLK_INFRA_PWM2_SEL 8 +#define CLK_INFRA_PWM3_SEL 9 +#define CLK_INFRA_PWM_BSEL 10 +#define CLK_INFRA_PCIE_SEL 11 +#define CLK_INFRA_GPT_STA 12 +#define CLK_INFRA_PWM_HCK 13 +#define CLK_INFRA_PWM_STA 14 +#define CLK_INFRA_PWM1_CK 15 +#define CLK_INFRA_PWM2_CK 16 +#define CLK_INFRA_PWM3_CK 17 +#define CLK_INFRA_CQ_DMA_CK 18 +#define CLK_INFRA_AUD_BUS_CK 19 +#define CLK_INFRA_AUD_26M_CK 20 +#define CLK_INFRA_AUD_L_CK 21 +#define CLK_INFRA_AUD_AUD_CK 22 +#define CLK_INFRA_AUD_EG2_CK 23 +#define CLK_INFRA_DRAMC_26M_CK 24 +#define CLK_INFRA_DBG_CK 25 +#define CLK_INFRA_AP_DMA_CK 26 +#define CLK_INFRA_SEJ_CK 27 +#define CLK_INFRA_SEJ_13M_CK 28 +#define CLK_INFRA_THERM_CK 29 +#define CLK_INFRA_I2C0_CK 30 +#define CLK_INFRA_UART0_CK 31 +#define CLK_INFRA_UART1_CK 32 +#define CLK_INFRA_UART2_CK 33 +#define CLK_INFRA_SPI2_CK 34 +#define CLK_INFRA_SPI2_HCK_CK 35 +#define CLK_INFRA_NFI1_CK 36 +#define CLK_INFRA_SPINFI1_CK 37 +#define CLK_INFRA_NFI_HCK_CK 38 +#define CLK_INFRA_SPI0_CK 39 +#define CLK_INFRA_SPI1_CK 40 +#define CLK_INFRA_SPI0_HCK_CK 41 +#define CLK_INFRA_SPI1_HCK_CK 42 +#define CLK_INFRA_FRTC_CK 43 +#define CLK_INFRA_MSDC_CK 44 +#define CLK_INFRA_MSDC_HCK_CK 45 +#define CLK_INFRA_MSDC_133M_CK 46 +#define CLK_INFRA_MSDC_66M_CK 47 +#define CLK_INFRA_ADC_26M_CK 48 +#define CLK_INFRA_ADC_FRC_CK 49 +#define CLK_INFRA_FBIST2FPC_CK 50 +#define CLK_INFRA_I2C_MCK_CK 51 +#define CLK_INFRA_I2C_PCK_CK 52 +#define CLK_INFRA_IUSB_133_CK 53 +#define CLK_INFRA_IUSB_66M_CK 54 +#define CLK_INFRA_IUSB_SYS_CK 55 +#define CLK_INFRA_IUSB_CK 56 +#define CLK_INFRA_IPCIE_CK 57 +#define CLK_INFRA_IPCIE_PIPE_CK 58 +#define CLK_INFRA_IPCIER_CK 59 +#define CLK_INFRA_IPCIEB_CK 60 + +/* APMIXEDSYS */ +#define CLK_APMIXED_ARMPLL 0 +#define CLK_APMIXED_NET2PLL 1 +#define CLK_APMIXED_MMPLL 2 +#define CLK_APMIXED_SGMPLL 3 +#define CLK_APMIXED_WEDMCUPLL 4 +#define CLK_APMIXED_NET1PLL 5 +#define CLK_APMIXED_MPLL 6 +#define CLK_APMIXED_APLL2 7 + +/* SGMIISYS_0 */ +#define CLK_SGM0_TX_EN 0 +#define CLK_SGM0_RX_EN 1 +#define CLK_SGM0_CK0_EN 2 +#define CLK_SGM0_CDR_CK0_EN 3 + +/* SGMIISYS_1 */ +#define CLK_SGM1_TX_EN 0 +#define CLK_SGM1_RX_EN 1 +#define CLK_SGM1_CK1_EN 2 +#define CLK_SGM1_CDR_CK1_EN 3 + +/* ETHSYS */ +#define CLK_ETH_FE_EN 0 +#define CLK_ETH_GP2_EN 1 +#define CLK_ETH_GP1_EN 2 +#define CLK_ETH_WOCPU0_EN 3 + +#endif /* _DT_BINDINGS_CLK_MT7981_H */ diff --git a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network index 13f52587c35..3d76e4d5029 100644 --- a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network @@ -8,6 +8,10 @@ mediatek_setup_interfaces() local board="$1" case $board in + asus,tuf-ax4200) + CI_UBIPART="UBI_DEV" + ucidef_set_interfaces_lan_wan "lan1 lan2 lan3 lan4" "eth1" + ;; mediatek,mt7986a-rfb|\ mediatek,mt7986b-rfb) ucidef_set_interfaces_lan_wan "lan0 lan1 lan2 lan3" eth1 @@ -34,6 +38,12 @@ mediatek_setup_macs() local label_mac="" case $board in + asus,tuf-ax4200) + CI_UBIPART="UBI_DEV" + addr=$(mtd_get_mac_binary_ubi "Factory" 0x4) + wan_mac="${addr}" + lan_mac="${addr}" + ;; xiaomi,redmi-router-ax6000-stock|\ xiaomi,redmi-router-ax6000-ubootmod) wan_mac=$(mtd_get_mac_ascii Bdata ethaddr_wan) diff --git a/target/linux/mediatek/filogic/base-files/etc/hotplug.d/firmware/11-mt76-caldata b/target/linux/mediatek/filogic/base-files/etc/hotplug.d/firmware/11-mt76-caldata new file mode 100644 index 00000000000..0fe4c699887 --- /dev/null +++ b/target/linux/mediatek/filogic/base-files/etc/hotplug.d/firmware/11-mt76-caldata @@ -0,0 +1,21 @@ +#!/bin/sh + +[ -e /lib/firmware/$FIRMWARE ] && exit 0 + +. /lib/functions/caldata.sh + +board=$(board_name) + +case "$FIRMWARE" in +"mediatek/mt7986_eeprom_mt7976_dbdc.bin") + case "$board" in + asus,tuf-ax4200) + CI_UBIPART="UBI_DEV" + caldata_extract_ubi "Factory" 0x0 0x1000 + ;; + esac + ;; +*) + exit 1 + ;; +esac diff --git a/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac b/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac index fd822e3d4ce..ecaf2eadcdb 100644 --- a/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac +++ b/target/linux/mediatek/filogic/base-files/etc/hotplug.d/ieee80211/11_fix_wifi_mac @@ -10,6 +10,15 @@ PHYNBR=${DEVPATH##*/phy} board=$(board_name) case "$board" in + asus,tuf-ax4200) + CI_UBIPART="UBI_DEV" + addr=$(mtd_get_mac_binary_ubi "Factory" 0x4) + # Originally, phy0 is phy1 mac with LA bit set. However, this would conflict + # addresses on mutiple VIFs with the other radio. Set LA bit and increment + # mac-address instead. + [ "$PHYNBR" = "0" ] && macaddr_setbit_la $(macaddr_add $addr 1) > /sys${DEVPATH}/macaddress + [ "$PHYNBR" = "1" ] && echo "$addr" > /sys${DEVPATH}/macaddress + ;; bananapi,bpi-r3) addr=$(macaddr_add $(cat /sys/class/net/eth0/address) 2) [ "$PHYNBR" = "0" ] && macaddr_unsetbit $addr 6 > /sys${DEVPATH}/macaddress diff --git a/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh b/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh new file mode 100644 index 00000000000..ec078741c98 --- /dev/null +++ b/target/linux/mediatek/filogic/base-files/lib/preinit/10_fix_eth_mac.sh @@ -0,0 +1,14 @@ +. /lib/functions/system.sh + +preinit_set_mac_address() { + case $(board_name) in + asus,tuf-ax4200) + CI_UBIPART="UBI_DEV" + addr=$(mtd_get_mac_binary_ubi "Factory" 0x4) + ip link set dev eth0 address "$addr" + ip link set dev eth1 address "$addr" + ;; + esac +} + +boot_hook_add preinit_main preinit_set_mac_address diff --git a/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh b/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh index 9a389d59b1d..ca097410308 100755 --- a/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh +++ b/target/linux/mediatek/filogic/base-files/lib/upgrade/platform.sh @@ -42,6 +42,11 @@ platform_do_upgrade() { local board=$(board_name) case "$board" in + asus,tuf-ax4200) + CI_UBIPART="UBI_DEV" + CI_KERNPART="linux" + nand_do_upgrade "$1" + ;; bananapi,bpi-r3) local rootdev="$(cmdline_get_var root)" rootdev="${rootdev##*/}" diff --git a/target/linux/mediatek/filogic/config-5.15 b/target/linux/mediatek/filogic/config-5.15 index 6a989310df9..ef528818b36 100644 --- a/target/linux/mediatek/filogic/config-5.15 +++ b/target/linux/mediatek/filogic/config-5.15 @@ -50,12 +50,15 @@ CONFIG_BSD_PROCESS_ACCT_V3=y CONFIG_CC_HAVE_STACKPROTECTOR_SYSREG=y CONFIG_CLKSRC_MMIO=y CONFIG_CLONE_BACKWARDS=y +CONFIG_CMDLINE_OVERRIDE=y CONFIG_COMMON_CLK=y CONFIG_COMMON_CLK_MEDIATEK=y # CONFIG_COMMON_CLK_MT2712 is not set # CONFIG_COMMON_CLK_MT6779 is not set # CONFIG_COMMON_CLK_MT6797 is not set # CONFIG_COMMON_CLK_MT7622 is not set +CONFIG_COMMON_CLK_MT7981=y +CONFIG_COMMON_CLK_MT7981_ETHSYS=y CONFIG_COMMON_CLK_MT7986=y CONFIG_COMMON_CLK_MT7986_ETHSYS=y # CONFIG_COMMON_CLK_MT8173 is not set @@ -203,6 +206,7 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MEDIATEK_GE_PHY=y +CONFIG_MEDIATEK_GE_PHY_SOC=y CONFIG_MEDIATEK_WATCHDOG=y CONFIG_MEMFD_CREATE=y CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 @@ -305,6 +309,7 @@ CONFIG_PINCTRL=y # CONFIG_PINCTRL_MT6765 is not set # CONFIG_PINCTRL_MT6797 is not set # CONFIG_PINCTRL_MT7622 is not set +CONFIG_PINCTRL_MT7981=y CONFIG_PINCTRL_MT7986=y # CONFIG_PINCTRL_MT8173 is not set # CONFIG_PINCTRL_MT8183 is not set diff --git a/target/linux/mediatek/filogic/target.mk b/target/linux/mediatek/filogic/target.mk index 44bc47dceac..dd4c4c14483 100644 --- a/target/linux/mediatek/filogic/target.mk +++ b/target/linux/mediatek/filogic/target.mk @@ -1,8 +1,8 @@ ARCH:=aarch64 SUBTARGET:=filogic -BOARDNAME:=Filogic 830 (MT7986) +BOARDNAME:=Filogic 8x0 (MT798x) CPU_TYPE:=cortex-a53 -DEFAULT_PACKAGES += kmod-crypto-hw-safexcel kmod-mt7915e kmod-mt7986-firmware wpad-basic-mbedtls uboot-envtools mt7986-wo-firmware +DEFAULT_PACKAGES += kmod-crypto-hw-safexcel kmod-mt7915e wpad-basic-mbedtls uboot-envtools KERNELNAME:=Image dtbs define Target/Description diff --git a/target/linux/mediatek/image/filogic.mk b/target/linux/mediatek/image/filogic.mk index 5c223d04ff1..318052a03c9 100644 --- a/target/linux/mediatek/image/filogic.mk +++ b/target/linux/mediatek/image/filogic.mk @@ -38,6 +38,24 @@ define Build/mt7986-gpt rm $@.tmp endef +define Device/asus_tuf-ax4200 + DEVICE_VENDOR := ASUS + DEVICE_MODEL := TUF-AX4200 + DEVICE_DTS := mt7986a-asus-tuf-ax4200 + DEVICE_DTS_DIR := ../dts + DEVICE_DTS_LOADADDR := 0x47000000 + DEVICE_PACKAGES := kmod-usb3 + IMAGES := sysupgrade.bin + KERNEL_LOADADDR := 0x48000000 + KERNEL = kernel-bin | lzma | \ + fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb + KERNEL_INITRAMFS := kernel-bin | lzma | \ + fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb with-initrd | pad-to 64k + IMAGE/sysupgrade.bin := sysupgrade-tar | append-metadata +endef +TARGET_DEVICES += asus_tuf-ax4200 + + define Device/bananapi_bpi-r3 DEVICE_VENDOR := Bananapi DEVICE_MODEL := BPi-R3 @@ -45,7 +63,7 @@ define Device/bananapi_bpi-r3 DEVICE_DTS_CONFIG := config-mt7986a-bananapi-bpi-r3 DEVICE_DTS_OVERLAY:= mt7986a-bananapi-bpi-r3-nor mt7986a-bananapi-bpi-r3-emmc-nor mt7986a-bananapi-bpi-r3-emmc-snand mt7986a-bananapi-bpi-r3-snand DEVICE_DTS_DIR := ../dts - DEVICE_PACKAGES := kmod-hwmon-pwmfan kmod-i2c-gpio kmod-sfp kmod-usb3 e2fsprogs f2fsck mkf2fs + DEVICE_PACKAGES := kmod-hwmon-pwmfan kmod-i2c-gpio kmod-mt7986-firmware kmod-sfp kmod-usb3 e2fsprogs f2fsck mkf2fs mt7986-wo-firmware IMAGES := sysupgrade.itb KERNEL_INITRAMFS_SUFFIX := -recovery.itb ARTIFACTS := \ @@ -89,6 +107,7 @@ define Device/mediatek_mt7986a-rfb-nand DEVICE_MODEL := MT7986 rfba AP (NAND) DEVICE_DTS := mt7986a-rfb-spim-nand DEVICE_DTS_DIR := $(DTS_DIR)/ + DEVICE_PACKAGES := kmod-mt7986-firmware mt7986-wo-firmware KERNEL_LOADADDR := 0x48000000 SUPPORTED_DEVICES := mediatek,mt7986a-rfb-snand UBINIZE_OPTS := -E 5 @@ -112,6 +131,7 @@ define Device/mediatek_mt7986b-rfb DEVICE_MODEL := MTK7986 rfbb AP DEVICE_DTS := mt7986b-rfb DEVICE_DTS_DIR := $(DTS_DIR)/ + DEVICE_PACKAGES := kmod-mt7986-firmware mt7986-wo-firmware KERNEL_LOADADDR := 0x48000000 SUPPORTED_DEVICES := mediatek,mt7986b-rfb UBINIZE_OPTS := -E 5 @@ -130,7 +150,7 @@ define Device/xiaomi_redmi-router-ax6000-stock DEVICE_MODEL := Redmi Router AX6000 (stock layout) DEVICE_DTS := mt7986a-xiaomi-redmi-router-ax6000-stock DEVICE_DTS_DIR := ../dts - DEVICE_PACKAGES := kmod-leds-ws2812b + DEVICE_PACKAGES := kmod-leds-ws2812b kmod-mt7986-firmware mt7986-wo-firmware KERNEL_LOADADDR := 0x48000000 UBINIZE_OPTS := -E 5 BLOCKSIZE := 128k @@ -148,7 +168,7 @@ define Device/xiaomi_redmi-router-ax6000-ubootmod DEVICE_MODEL := Redmi Router AX6000 (OpenWrt U-Boot layout) DEVICE_DTS := mt7986a-xiaomi-redmi-router-ax6000-ubootmod DEVICE_DTS_DIR := ../dts - DEVICE_PACKAGES := kmod-leds-ws2812b + DEVICE_PACKAGES := kmod-leds-ws2812b kmod-mt7986-firmware mt7986-wo-firmware KERNEL_INITRAMFS_SUFFIX := -recovery.itb IMAGES := sysupgrade.itb KERNEL_LOADADDR := 0x48000000 diff --git a/target/linux/mediatek/mt7622/config-5.15 b/target/linux/mediatek/mt7622/config-5.15 index 3f99221c212..14a0bec31a5 100644 --- a/target/linux/mediatek/mt7622/config-5.15 +++ b/target/linux/mediatek/mt7622/config-5.15 @@ -71,6 +71,7 @@ CONFIG_COMMON_CLK_MT7622=y CONFIG_COMMON_CLK_MT7622_AUDSYS=y CONFIG_COMMON_CLK_MT7622_ETHSYS=y CONFIG_COMMON_CLK_MT7622_HIFSYS=y +# CONFIG_COMMON_CLK_MT7981 is not set # CONFIG_COMMON_CLK_MT7986 is not set # CONFIG_COMMON_CLK_MT8173 is not set # CONFIG_COMMON_CLK_MT8183 is not set @@ -231,6 +232,7 @@ CONFIG_MDIO_BUS=y CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MEDIATEK_GE_PHY=y +# CONFIG_MEDIATEK_GE_PHY_SOC is not set CONFIG_MEDIATEK_WATCHDOG=y CONFIG_MEMFD_CREATE=y CONFIG_MESSAGE_LOGLEVEL_DEFAULT=7 @@ -332,6 +334,7 @@ CONFIG_PINCTRL=y # CONFIG_PINCTRL_MT6765 is not set # CONFIG_PINCTRL_MT6797 is not set CONFIG_PINCTRL_MT7622=y +# CONFIG_PINCTRL_MT7981 is not set # CONFIG_PINCTRL_MT7986 is not set # CONFIG_PINCTRL_MT8173 is not set # CONFIG_PINCTRL_MT8183 is not set diff --git a/target/linux/mediatek/mt7623/config-5.15 b/target/linux/mediatek/mt7623/config-5.15 index 984e85aba0b..fa99dd50bb0 100644 --- a/target/linux/mediatek/mt7623/config-5.15 +++ b/target/linux/mediatek/mt7623/config-5.15 @@ -68,6 +68,7 @@ CONFIG_COMMON_CLK_MT2701_MMSYS=y CONFIG_COMMON_CLK_MT2701_VDECSYS=y # CONFIG_COMMON_CLK_MT7622 is not set # CONFIG_COMMON_CLK_MT7629 is not set +# CONFIG_COMMON_CLK_MT7981 is not set # CONFIG_COMMON_CLK_MT7986 is not set # CONFIG_COMMON_CLK_MT8135 is not set # CONFIG_COMMON_CLK_MT8173 is not set @@ -320,6 +321,7 @@ CONFIG_MDIO_DEVICE=y CONFIG_MDIO_DEVRES=y CONFIG_MDIO_GPIO=y CONFIG_MEDIATEK_GE_PHY=y +# CONFIG_MEDIATEK_GE_PHY_SOC is not set CONFIG_MEDIATEK_MT6577_AUXADC=y CONFIG_MEDIATEK_WATCHDOG=y CONFIG_MEMFD_CREATE=y diff --git a/target/linux/mediatek/mt7629/config-5.15 b/target/linux/mediatek/mt7629/config-5.15 index fc53bec882e..32f75d6528b 100644 --- a/target/linux/mediatek/mt7629/config-5.15 +++ b/target/linux/mediatek/mt7629/config-5.15 @@ -52,6 +52,7 @@ CONFIG_COMMON_CLK_MEDIATEK=y CONFIG_COMMON_CLK_MT7629=y CONFIG_COMMON_CLK_MT7629_ETHSYS=y CONFIG_COMMON_CLK_MT7629_HIFSYS=y +# CONFIG_COMMON_CLK_MT7981 is not set # CONFIG_COMMON_CLK_MT7986 is not set # CONFIG_COMMON_CLK_MT8135 is not set # CONFIG_COMMON_CLK_MT8173 is not set diff --git a/target/linux/mediatek/patches-5.15/214-v6.3-clk-mediatek-add-mt7981-clock-support.patch b/target/linux/mediatek/patches-5.15/214-v6.3-clk-mediatek-add-mt7981-clock-support.patch new file mode 100644 index 00000000000..631eb04092d --- /dev/null +++ b/target/linux/mediatek/patches-5.15/214-v6.3-clk-mediatek-add-mt7981-clock-support.patch @@ -0,0 +1,39 @@ +--- a/drivers/clk/mediatek/Kconfig ++++ b/drivers/clk/mediatek/Kconfig +@@ -344,6 +344,23 @@ config COMMON_CLK_MT7629_HIFSYS + This driver supports MediaTek MT7629 HIFSYS clocks providing + to PCI-E and USB. + ++config COMMON_CLK_MT7981 ++ bool "Clock driver for MediaTek MT7981" ++ depends on ARCH_MEDIATEK || COMPILE_TEST ++ select COMMON_CLK_MEDIATEK ++ default ARCH_MEDIATEK ++ help ++ This driver supports MediaTek MT7981 basic clocks and clocks ++ required for various periperals found on MediaTek. ++ ++config COMMON_CLK_MT7981_ETHSYS ++ bool "Clock driver for MediaTek MT7981 ETHSYS" ++ depends on COMMON_CLK_MT7981 ++ default COMMON_CLK_MT7981 ++ help ++ This driver add support for clocks for Ethernet and SGMII ++ required on MediaTek MT7981 SoC. ++ + config COMMON_CLK_MT7986 + bool "Clock driver for MediaTek MT7986" + depends on ARCH_MEDIATEK || COMPILE_TEST +--- a/drivers/clk/mediatek/Makefile ++++ b/drivers/clk/mediatek/Makefile +@@ -46,6 +46,10 @@ obj-$(CONFIG_COMMON_CLK_MT7622_AUDSYS) + + obj-$(CONFIG_COMMON_CLK_MT7629) += clk-mt7629.o + obj-$(CONFIG_COMMON_CLK_MT7629_ETHSYS) += clk-mt7629-eth.o + obj-$(CONFIG_COMMON_CLK_MT7629_HIFSYS) += clk-mt7629-hif.o ++obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981-apmixed.o ++obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981-topckgen.o ++obj-$(CONFIG_COMMON_CLK_MT7981) += clk-mt7981-infracfg.o ++obj-$(CONFIG_COMMON_CLK_MT7981_ETHSYS) += clk-mt7981-eth.o + obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986-apmixed.o + obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986-topckgen.o + obj-$(CONFIG_COMMON_CLK_MT7986) += clk-mt7986-infracfg.o diff --git a/target/linux/mediatek/patches-5.15/215-v6.3-pinctrl-mediatek-add-support-for-MT7981-SoC.patch b/target/linux/mediatek/patches-5.15/215-v6.3-pinctrl-mediatek-add-support-for-MT7981-SoC.patch new file mode 100644 index 00000000000..ae99685df4c --- /dev/null +++ b/target/linux/mediatek/patches-5.15/215-v6.3-pinctrl-mediatek-add-support-for-MT7981-SoC.patch @@ -0,0 +1,26 @@ +--- a/drivers/pinctrl/mediatek/Kconfig ++++ b/drivers/pinctrl/mediatek/Kconfig +@@ -120,6 +120,13 @@ config PINCTRL_MT7622 + default ARM64 && ARCH_MEDIATEK + select PINCTRL_MTK_MOORE + ++config PINCTRL_MT7981 ++ bool "Mediatek MT7981 pin control" ++ depends on OF ++ depends on ARM64 || COMPILE_TEST ++ default ARM64 && ARCH_MEDIATEK ++ select PINCTRL_MTK_MOORE ++ + config PINCTRL_MT7986 + bool "Mediatek MT7986 pin control" + depends on OF +--- a/drivers/pinctrl/mediatek/Makefile ++++ b/drivers/pinctrl/mediatek/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_PINCTRL_MT6797) += pinctrl- + obj-$(CONFIG_PINCTRL_MT7622) += pinctrl-mt7622.o + obj-$(CONFIG_PINCTRL_MT7623) += pinctrl-mt7623.o + obj-$(CONFIG_PINCTRL_MT7629) += pinctrl-mt7629.o ++obj-$(CONFIG_PINCTRL_MT7986) += pinctrl-mt7981.o + obj-$(CONFIG_PINCTRL_MT7986) += pinctrl-mt7986.o + obj-$(CONFIG_PINCTRL_MT8167) += pinctrl-mt8167.o + obj-$(CONFIG_PINCTRL_MT8173) += pinctrl-mt8173.o diff --git a/target/linux/mediatek/patches-5.15/431-drivers-spi-mt65xx-Move-chip_config-to-driver-s-priv.patch b/target/linux/mediatek/patches-5.15/431-drivers-spi-mt65xx-Move-chip_config-to-driver-s-priv.patch new file mode 100644 index 00000000000..a57ee253938 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/431-drivers-spi-mt65xx-Move-chip_config-to-driver-s-priv.patch @@ -0,0 +1,130 @@ +From bfd3acc428085742d754a6d328d1a93ebf9451df Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:29:51 +0800 +Subject: [PATCH 1/6] drivers: spi-mt65xx: Move chip_config to driver's private + data + +Signed-off-by: SkyLake.Huang +--- + drivers/spi/spi-mt65xx.c | 29 +++++++++--------------- + include/linux/platform_data/spi-mt65xx.h | 17 -------------- + 2 files changed, 11 insertions(+), 35 deletions(-) + delete mode 100644 include/linux/platform_data/spi-mt65xx.h + +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -14,7 +14,6 @@ + #include + #include + #include +-#include + #include + #include + #include +@@ -142,6 +141,8 @@ struct mtk_spi { + struct device *dev; + dma_addr_t tx_dma; + dma_addr_t rx_dma; ++ u32 sample_sel; ++ u32 get_tick_dly; + }; + + static const struct mtk_spi_compatible mtk_common_compat; +@@ -187,15 +188,6 @@ static const struct mtk_spi_compatible m + .no_need_unprepare = true, + }; + +-/* +- * A piece of default chip info unless the platform +- * supplies it. +- */ +-static const struct mtk_chip_config mtk_default_chip_info = { +- .sample_sel = 0, +- .tick_delay = 0, +-}; +- + static const struct of_device_id mtk_spi_of_match[] = { + { .compatible = "mediatek,spi-ipm", + .data = (void *)&mtk_ipm_compat, +@@ -323,7 +315,6 @@ static int mtk_spi_hw_init(struct spi_ma + { + u16 cpha, cpol; + u32 reg_val; +- struct mtk_chip_config *chip_config = spi->controller_data; + struct mtk_spi *mdata = spi_master_get_devdata(master); + + cpha = spi->mode & SPI_CPHA ? 1 : 0; +@@ -373,7 +364,7 @@ static int mtk_spi_hw_init(struct spi_ma + else + reg_val &= ~SPI_CMD_CS_POL; + +- if (chip_config->sample_sel) ++ if (mdata->sample_sel) + reg_val |= SPI_CMD_SAMPLE_SEL; + else + reg_val &= ~SPI_CMD_SAMPLE_SEL; +@@ -400,20 +391,20 @@ static int mtk_spi_hw_init(struct spi_ma + if (mdata->dev_comp->ipm_design) { + reg_val = readl(mdata->base + SPI_CMD_REG); + reg_val &= ~SPI_CMD_IPM_GET_TICKDLY_MASK; +- reg_val |= ((chip_config->tick_delay & 0x7) ++ reg_val |= ((mdata->get_tick_dly & 0x7) + << SPI_CMD_IPM_GET_TICKDLY_OFFSET); + writel(reg_val, mdata->base + SPI_CMD_REG); + } else { + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK; +- reg_val |= ((chip_config->tick_delay & 0x7) ++ reg_val |= ((mdata->get_tick_dly & 0x7) + << SPI_CFG1_GET_TICK_DLY_OFFSET); + writel(reg_val, mdata->base + SPI_CFG1_REG); + } + } else { + reg_val = readl(mdata->base + SPI_CFG1_REG); + reg_val &= ~SPI_CFG1_GET_TICK_DLY_MASK_V1; +- reg_val |= ((chip_config->tick_delay & 0x3) ++ reg_val |= ((mdata->get_tick_dly & 0x3) + << SPI_CFG1_GET_TICK_DLY_OFFSET_V1); + writel(reg_val, mdata->base + SPI_CFG1_REG); + } +@@ -700,9 +691,6 @@ static int mtk_spi_setup(struct spi_devi + { + struct mtk_spi *mdata = spi_master_get_devdata(spi->master); + +- if (!spi->controller_data) +- spi->controller_data = (void *)&mtk_default_chip_info; +- + if (mdata->dev_comp->need_pad_sel && spi->cs_gpiod) + /* CS de-asserted, gpiolib will handle inversion */ + gpiod_direction_output(spi->cs_gpiod, 0); +@@ -1115,6 +1103,10 @@ static int mtk_spi_probe(struct platform + mdata = spi_master_get_devdata(master); + mdata->dev_comp = of_id->data; + ++ /* Set device configs to default first. Calibrate it later. */ ++ mdata->sample_sel = 0; ++ mdata->get_tick_dly = 2; ++ + if (mdata->dev_comp->enhance_timing) + master->mode_bits |= SPI_CS_HIGH; + +--- a/include/linux/platform_data/spi-mt65xx.h ++++ /dev/null +@@ -1,17 +0,0 @@ +-/* SPDX-License-Identifier: GPL-2.0-only */ +-/* +- * MTK SPI bus driver definitions +- * +- * Copyright (c) 2015 MediaTek Inc. +- * Author: Leilk Liu +- */ +- +-#ifndef ____LINUX_PLATFORM_DATA_SPI_MTK_H +-#define ____LINUX_PLATFORM_DATA_SPI_MTK_H +- +-/* Board specific platform_data */ +-struct mtk_chip_config { +- u32 sample_sel; +- u32 tick_delay; +-}; +-#endif diff --git a/target/linux/mediatek/patches-5.15/432-drivers-spi-Add-support-for-dynamic-calibration.patch b/target/linux/mediatek/patches-5.15/432-drivers-spi-Add-support-for-dynamic-calibration.patch new file mode 100644 index 00000000000..4c980e9438d --- /dev/null +++ b/target/linux/mediatek/patches-5.15/432-drivers-spi-Add-support-for-dynamic-calibration.patch @@ -0,0 +1,236 @@ +From 2ade0172154e50c8a2bfd8634c6eff943cffea29 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:35:52 +0800 +Subject: [PATCH 2/6] drivers: spi: Add support for dynamic calibration + +Signed-off-by: SkyLake.Huang +--- + drivers/spi/spi.c | 137 ++++++++++++++++++++++++++++++++++++++++ + include/linux/spi/spi.h | 42 ++++++++++++ + 2 files changed, 179 insertions(+) + +--- a/drivers/spi/spi.c ++++ b/drivers/spi/spi.c +@@ -1234,6 +1234,70 @@ static int spi_transfer_wait(struct spi_ + return 0; + } + ++int spi_do_calibration(struct spi_controller *ctlr, struct spi_device *spi, ++ int (*cal_read)(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen), void *drv_priv) ++{ ++ int datalen = ctlr->cal_rule->datalen; ++ int addrlen = ctlr->cal_rule->addrlen; ++ u8 *buf; ++ int ret; ++ int i; ++ struct list_head *cal_head, *listptr; ++ struct spi_cal_target *target; ++ ++ /* Calculate calibration result */ ++ int hit_val, total_hit, origin; ++ bool hit; ++ ++ /* Make sure we can start calibration */ ++ if(!ctlr->cal_target || !ctlr->cal_rule || !ctlr->append_caldata) ++ return 0; ++ ++ buf = kzalloc(datalen * sizeof(u8), GFP_KERNEL); ++ if(!buf) ++ return -ENOMEM; ++ ++ ret = ctlr->append_caldata(ctlr); ++ if (ret) ++ goto cal_end; ++ ++ cal_head = ctlr->cal_target; ++ list_for_each(listptr, cal_head) { ++ target = list_entry(listptr, struct spi_cal_target, list); ++ ++ hit = false; ++ hit_val = 0; ++ total_hit = 0; ++ origin = *target->cal_item; ++ ++ for(i=target->cal_min; i<=target->cal_max; i+=target->step) { ++ *target->cal_item = i; ++ ret = (*cal_read)(drv_priv, ctlr->cal_rule->addr, addrlen, buf, datalen); ++ if(ret) ++ break; ++ dev_dbg(&spi->dev, "controller cal item value: 0x%x\n", i); ++ if(memcmp(ctlr->cal_rule->match_data, buf, datalen * sizeof(u8)) == 0) { ++ hit = true; ++ hit_val += i; ++ total_hit++; ++ dev_dbg(&spi->dev, "golden data matches data read!\n"); ++ } ++ } ++ if(hit) { ++ *target->cal_item = DIV_ROUND_CLOSEST(hit_val, total_hit); ++ dev_info(&spi->dev, "calibration result: 0x%x", *target->cal_item); ++ } else { ++ *target->cal_item = origin; ++ dev_warn(&spi->dev, "calibration failed, fallback to default: 0x%x", origin); ++ } ++ } ++ ++cal_end: ++ kfree(buf); ++ return ret? ret: 0; ++} ++EXPORT_SYMBOL_GPL(spi_do_calibration); ++ + static void _spi_transfer_delay_ns(u32 ns) + { + if (!ns) +@@ -2021,6 +2085,75 @@ void spi_flush_queue(struct spi_controll + /*-------------------------------------------------------------------------*/ + + #if defined(CONFIG_OF) ++static inline void alloc_cal_data(struct list_head **cal_target, ++ struct spi_cal_rule **cal_rule, bool enable) ++{ ++ if(enable) { ++ *cal_target = kmalloc(sizeof(struct list_head), GFP_KERNEL); ++ INIT_LIST_HEAD(*cal_target); ++ *cal_rule = kmalloc(sizeof(struct spi_cal_rule), GFP_KERNEL); ++ } else { ++ kfree(*cal_target); ++ kfree(*cal_rule); ++ } ++} ++ ++static int of_spi_parse_cal_dt(struct spi_controller *ctlr, struct spi_device *spi, ++ struct device_node *nc) ++{ ++ u32 value; ++ int rc; ++ const char *cal_mode; ++ ++ rc = of_property_read_bool(nc, "spi-cal-enable"); ++ if (rc) ++ alloc_cal_data(&ctlr->cal_target, &ctlr->cal_rule, true); ++ else ++ return 0; ++ ++ rc = of_property_read_string(nc, "spi-cal-mode", &cal_mode); ++ if(!rc) { ++ if(strcmp("read-data", cal_mode) == 0){ ++ ctlr->cal_rule->mode = SPI_CAL_READ_DATA; ++ } else if(strcmp("read-pp", cal_mode) == 0) { ++ ctlr->cal_rule->mode = SPI_CAL_READ_PP; ++ return 0; ++ } else if(strcmp("read-sfdp", cal_mode) == 0){ ++ ctlr->cal_rule->mode = SPI_CAL_READ_SFDP; ++ return 0; ++ } ++ } else ++ goto err; ++ ++ ctlr->cal_rule->datalen = 0; ++ rc = of_property_read_u32(nc, "spi-cal-datalen", &value); ++ if(!rc && value > 0) { ++ ctlr->cal_rule->datalen = value; ++ ++ ctlr->cal_rule->match_data = kzalloc(value * sizeof(u8), GFP_KERNEL); ++ rc = of_property_read_u8_array(nc, "spi-cal-data", ++ ctlr->cal_rule->match_data, value); ++ if(rc) ++ kfree(ctlr->cal_rule->match_data); ++ } ++ ++ rc = of_property_read_u32(nc, "spi-cal-addrlen", &value); ++ if(!rc && value > 0) { ++ ctlr->cal_rule->addrlen = value; ++ ++ ctlr->cal_rule->addr = kzalloc(value * sizeof(u32), GFP_KERNEL); ++ rc = of_property_read_u32_array(nc, "spi-cal-addr", ++ ctlr->cal_rule->addr, value); ++ if(rc) ++ kfree(ctlr->cal_rule->addr); ++ } ++ return 0; ++ ++err: ++ alloc_cal_data(&ctlr->cal_target, &ctlr->cal_rule, false); ++ return 0; ++} ++ + static int of_spi_parse_dt(struct spi_controller *ctlr, struct spi_device *spi, + struct device_node *nc) + { +@@ -2139,6 +2272,10 @@ of_register_spi_device(struct spi_contro + if (rc) + goto err_out; + ++ rc = of_spi_parse_cal_dt(ctlr, spi, nc); ++ if (rc) ++ goto err_out; ++ + /* Store a pointer to the node in the device structure */ + of_node_get(nc); + spi->dev.of_node = nc; +--- a/include/linux/spi/spi.h ++++ b/include/linux/spi/spi.h +@@ -290,6 +290,40 @@ struct spi_driver { + struct device_driver driver; + }; + ++enum { ++ SPI_CAL_READ_DATA = 0, ++ SPI_CAL_READ_PP = 1, /* only for SPI-NAND */ ++ SPI_CAL_READ_SFDP = 2, /* only for SPI-NOR */ ++}; ++ ++struct nand_addr { ++ unsigned int lun; ++ unsigned int plane; ++ unsigned int eraseblock; ++ unsigned int page; ++ unsigned int dataoffs; ++}; ++ ++/** ++ * Read calibration rule from device dts node. ++ * Once calibration result matches the rule, we regard is as success. ++ */ ++struct spi_cal_rule { ++ int datalen; ++ u8 *match_data; ++ int addrlen; ++ u32 *addr; ++ int mode; ++}; ++ ++struct spi_cal_target { ++ u32 *cal_item; ++ int cal_min; /* min of cal_item */ ++ int cal_max; /* max of cal_item */ ++ int step; /* Increase/decrease cal_item */ ++ struct list_head list; ++}; ++ + static inline struct spi_driver *to_spi_driver(struct device_driver *drv) + { + return drv ? container_of(drv, struct spi_driver, driver) : NULL; +@@ -665,6 +699,11 @@ struct spi_controller { + void *dummy_rx; + void *dummy_tx; + ++ /* For calibration */ ++ int (*append_caldata)(struct spi_controller *ctlr); ++ struct list_head *cal_target; ++ struct spi_cal_rule *cal_rule; ++ + int (*fw_translate_cs)(struct spi_controller *ctlr, unsigned cs); + + /* +@@ -1477,6 +1516,9 @@ spi_register_board_info(struct spi_board + { return 0; } + #endif + ++extern int spi_do_calibration(struct spi_controller *ctlr, ++ struct spi_device *spi, int (*cal_read)(void *, u32 *, int, u8 *, int), void *drv_priv); ++ + /* If you're hotplugging an adapter with devices (parport, usb, etc) + * use spi_new_device() to describe each device. You can also call + * spi_unregister_device() to start making that device vanish, but diff --git a/target/linux/mediatek/patches-5.15/433-drivers-spi-mem-Add-spi-calibration-hook.patch b/target/linux/mediatek/patches-5.15/433-drivers-spi-mem-Add-spi-calibration-hook.patch new file mode 100644 index 00000000000..aaacab01312 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/433-drivers-spi-mem-Add-spi-calibration-hook.patch @@ -0,0 +1,41 @@ +From 06640a5da2973318c06e516da16a5b579622e7c5 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:37:55 +0800 +Subject: [PATCH 3/6] drivers: spi-mem: Add spi calibration hook + +Signed-off-by: SkyLake.Huang +--- + drivers/spi/spi-mem.c | 8 ++++++++ + include/linux/spi/spi-mem.h | 4 ++++ + 2 files changed, 12 insertions(+) + +--- a/drivers/spi/spi-mem.c ++++ b/drivers/spi/spi-mem.c +@@ -410,6 +410,14 @@ int spi_mem_exec_op(struct spi_mem *mem, + } + EXPORT_SYMBOL_GPL(spi_mem_exec_op); + ++int spi_mem_do_calibration(struct spi_mem *mem, ++ int (*cal_read)(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen), ++ void *priv) ++{ ++ return spi_do_calibration(mem->spi->controller, mem->spi, cal_read, priv); ++} ++EXPORT_SYMBOL_GPL(spi_mem_do_calibration); ++ + /** + * spi_mem_get_name() - Return the SPI mem device name to be used by the + * upper layer if necessary +--- a/include/linux/spi/spi-mem.h ++++ b/include/linux/spi/spi-mem.h +@@ -366,6 +366,10 @@ bool spi_mem_supports_op(struct spi_mem + int spi_mem_exec_op(struct spi_mem *mem, + const struct spi_mem_op *op); + ++int spi_mem_do_calibration(struct spi_mem *mem, ++ int (*cal_read)(void *, u32 *, int, u8 *, int), ++ void *priv); ++ + const char *spi_mem_get_name(struct spi_mem *mem); + + struct spi_mem_dirmap_desc * diff --git a/target/linux/mediatek/patches-5.15/434-drivers-spi-mt65xx-Add-controller-s-calibration-para.patch b/target/linux/mediatek/patches-5.15/434-drivers-spi-mt65xx-Add-controller-s-calibration-para.patch new file mode 100644 index 00000000000..a64d6229819 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/434-drivers-spi-mt65xx-Add-controller-s-calibration-para.patch @@ -0,0 +1,43 @@ +From d278c7a0bf730318a7ccf8d0a8b434c813e23fd0 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:39:03 +0800 +Subject: [PATCH 4/6] drivers: spi-mt65xx: Add controller's calibration + paramter + +Signed-off-by: SkyLake.Huang +--- + drivers/spi/spi-mt65xx.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +--- a/drivers/spi/spi-mt65xx.c ++++ b/drivers/spi/spi-mt65xx.c +@@ -800,6 +800,21 @@ static irqreturn_t mtk_spi_interrupt(int + return IRQ_HANDLED; + } + ++static int mtk_spi_append_caldata(struct spi_controller *ctlr) ++{ ++ struct spi_cal_target *cal_target = kmalloc(sizeof(*cal_target), GFP_KERNEL); ++ struct mtk_spi *mdata = spi_master_get_devdata(ctlr); ++ ++ cal_target->cal_item = &mdata->get_tick_dly; ++ cal_target->cal_min = 0; ++ cal_target->cal_max = 7; ++ cal_target->step = 1; ++ ++ list_add(&cal_target->list, ctlr->cal_target); ++ ++ return 0; ++} ++ + static int mtk_spi_mem_adjust_op_size(struct spi_mem *mem, + struct spi_mem_op *op) + { +@@ -1092,6 +1107,7 @@ static int mtk_spi_probe(struct platform + master->setup = mtk_spi_setup; + master->set_cs_timing = mtk_spi_set_hw_cs_timing; + master->use_gpio_descriptors = true; ++ master->append_caldata = mtk_spi_append_caldata; + + of_id = of_match_node(mtk_spi_of_match, pdev->dev.of_node); + if (!of_id) { diff --git a/target/linux/mediatek/patches-5.15/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch b/target/linux/mediatek/patches-5.15/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch new file mode 100644 index 00000000000..f71a1d2d589 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/435-drivers-mtd-spinand-Add-calibration-support-for-spin.patch @@ -0,0 +1,81 @@ +From 7670ec4a14891a1a182b98a9c403ffbf6b49e4b1 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:39:56 +0800 +Subject: [PATCH 5/6] drivers: mtd: spinand: Add calibration support for + spinand + +Signed-off-by: SkyLake.Huang +--- + drivers/mtd/nand/spi/core.c | 54 +++++++++++++++++++++++++++++++++++++ + 1 file changed, 54 insertions(+) + +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -967,6 +967,56 @@ static int spinand_manufacturer_match(st + return -ENOTSUPP; + } + ++int spinand_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen) { ++ struct spinand_device *spinand = (struct spinand_device *)priv; ++ struct device *dev = &spinand->spimem->spi->dev; ++ struct spi_mem_op op = SPINAND_PAGE_READ_FROM_CACHE_OP(false, 0, 1, buf, readlen); ++ struct nand_pos pos; ++ struct nand_page_io_req req; ++ u8 status; ++ int ret; ++ ++ if(addrlen != sizeof(struct nand_addr)/sizeof(unsigned int)) { ++ dev_err(dev, "Must provide correct addr(length) for spinand calibration\n"); ++ return -EINVAL; ++ } ++ ++ ret = spinand_reset_op(spinand); ++ if (ret) ++ return ret; ++ ++ /* We should store our golden data in first target because ++ * we can't switch target at this moment. ++ */ ++ pos = (struct nand_pos){ ++ .target = 0, ++ .lun = *addr, ++ .plane = *(addr+1), ++ .eraseblock = *(addr+2), ++ .page = *(addr+3), ++ }; ++ ++ req = (struct nand_page_io_req){ ++ .pos = pos, ++ .dataoffs = *(addr+4), ++ .datalen = readlen, ++ .databuf.in = buf, ++ .mode = MTD_OPS_AUTO_OOB, ++ }; ++ ++ ret = spinand_load_page_op(spinand, &req); ++ if (ret) ++ return ret; ++ ++ ret = spinand_wait(spinand, &status); ++ if (ret < 0) ++ return ret; ++ ++ ret = spi_mem_exec_op(spinand->spimem, &op); ++ ++ return 0; ++} ++ + static int spinand_id_detect(struct spinand_device *spinand) + { + u8 *id = spinand->id.data; +@@ -1217,6 +1267,10 @@ static int spinand_init(struct spinand_d + if (!spinand->scratchbuf) + return -ENOMEM; + ++ ret = spi_mem_do_calibration(spinand->spimem, spinand_cal_read, spinand); ++ if (ret) ++ dev_err(dev, "Failed to calibrate SPI-NAND (err = %d)\n", ret); ++ + ret = spinand_detect(spinand); + if (ret) + goto err_free_bufs; diff --git a/target/linux/mediatek/patches-5.15/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch b/target/linux/mediatek/patches-5.15/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch new file mode 100644 index 00000000000..a08b19870cc --- /dev/null +++ b/target/linux/mediatek/patches-5.15/436-drivers-mtd-spi-nor-Add-calibration-support-for-spi-.patch @@ -0,0 +1,57 @@ +From f3fe3b15eca7908eaac57f9b8387a5dbc45ec5b2 Mon Sep 17 00:00:00 2001 +From: "SkyLake.Huang" +Date: Thu, 23 Jun 2022 18:40:59 +0800 +Subject: [PATCH 6/6] drivers: mtd: spi-nor: Add calibration support for + spi-nor + +Signed-off-by: SkyLake.Huang +--- + drivers/mtd/nand/spi/core.c | 5 ++++- + drivers/mtd/spi-nor/core.c | 15 +++++++++++++++ + 2 files changed, 19 insertions(+), 1 deletion(-) + +--- a/drivers/mtd/nand/spi/core.c ++++ b/drivers/mtd/nand/spi/core.c +@@ -1008,7 +1008,10 @@ int spinand_cal_read(void *priv, u32 *ad + if (ret) + return ret; + +- ret = spinand_wait(spinand, &status); ++ ret = spinand_wait(spinand, ++ SPINAND_READ_INITIAL_DELAY_US, ++ SPINAND_READ_POLL_DELAY_US, ++ &status); + if (ret < 0) + return ret; + +--- a/drivers/mtd/spi-nor/core.c ++++ b/drivers/mtd/spi-nor/core.c +@@ -3060,6 +3060,18 @@ static void spi_nor_debugfs_init(struct + info->id_len, info->id); + } + ++static int spi_nor_cal_read(void *priv, u32 *addr, int addrlen, u8 *buf, int readlen) ++{ ++ struct spi_nor *nor = (struct spi_nor *)priv; ++ ++ nor->reg_proto = SNOR_PROTO_1_1_1; ++ nor->read_proto = SNOR_PROTO_1_1_1; ++ nor->read_opcode = SPINOR_OP_READ; ++ nor->read_dummy = 0; ++ ++ return nor->controller_ops->read(nor, *addr, readlen, buf); ++} ++ + static const struct flash_info *spi_nor_get_flash_info(struct spi_nor *nor, + const char *name) + { +@@ -3133,6 +3145,9 @@ int spi_nor_scan(struct spi_nor *nor, co + if (!nor->bouncebuf) + return -ENOMEM; + ++ if(nor->spimem) ++ spi_mem_do_calibration(nor->spimem, spi_nor_cal_read, nor); ++ + info = spi_nor_get_flash_info(nor, name); + if (IS_ERR(info)) + return PTR_ERR(info); diff --git a/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch b/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch index 757750832c3..ffa98e3f0d9 100644 --- a/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch +++ b/target/linux/mediatek/patches-5.15/703-v5.17-net-ethernet-mtk_eth_soc-implement-Clause-45-MDIO-ac.patch @@ -20,7 +20,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -230,13 +230,35 @@ static int _mtk_mdio_write(struct mtk_et +@@ -231,13 +231,35 @@ static int _mtk_mdio_write(struct mtk_et if (ret < 0) return ret; @@ -63,7 +63,7 @@ Signed-off-by: David S. Miller ret = mtk_mdio_busy_wait(eth); if (ret < 0) -@@ -253,12 +275,33 @@ static int _mtk_mdio_read(struct mtk_eth +@@ -254,12 +276,33 @@ static int _mtk_mdio_read(struct mtk_eth if (ret < 0) return ret; @@ -103,7 +103,7 @@ Signed-off-by: David S. Miller ret = mtk_mdio_busy_wait(eth); if (ret < 0) -@@ -726,6 +769,7 @@ static int mtk_mdio_init(struct mtk_eth +@@ -730,6 +773,7 @@ static int mtk_mdio_init(struct mtk_eth eth->mii_bus->name = "mdio"; eth->mii_bus->read = mtk_mdio_read; eth->mii_bus->write = mtk_mdio_write; @@ -113,7 +113,7 @@ Signed-off-by: David S. Miller --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.h +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.h -@@ -369,9 +369,12 @@ +@@ -376,9 +376,12 @@ #define PHY_IAC_ADDR_MASK GENMASK(24, 20) #define PHY_IAC_ADDR(x) FIELD_PREP(PHY_IAC_ADDR_MASK, (x)) #define PHY_IAC_CMD_MASK GENMASK(19, 18) diff --git a/target/linux/mediatek/patches-5.15/730-net-phy-add-driver-for-MediaTek-SoC-built-in-GE-PHYs.patch b/target/linux/mediatek/patches-5.15/730-net-phy-add-driver-for-MediaTek-SoC-built-in-GE-PHYs.patch new file mode 100644 index 00000000000..f2145dbc16f --- /dev/null +++ b/target/linux/mediatek/patches-5.15/730-net-phy-add-driver-for-MediaTek-SoC-built-in-GE-PHYs.patch @@ -0,0 +1,1443 @@ +From a6f143af419bfc3f52d82e88ac033d9833e720af Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 13 Feb 2023 02:33:14 +0000 +Subject: [PATCH] net: phy: add driver for MediaTek SoC built-in GE PHYs + +Some of MediaTek's Filogic SoCs come with built-in gigabit Ethernet +PHYs which require calibration data from the SoC's efuse. +Add support for these PHYs to the mediatek-ge driver if built for +MediaTek's ARM64 SoCs. + +Signed-off-by: Daniel Golle +--- + MAINTAINERS | 8 + + drivers/net/phy/Kconfig | 12 + + drivers/net/phy/mediatek-ge.c | 1351 +++++++++++++++++++++++++++++++++ + 3 files changed, 1371 insertions(+) + +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -11798,6 +11798,14 @@ S: Maintained + F: drivers/net/pcs/pcs-mtk-lynxi.c + F: include/linux/pcs/pcs-mtk-lynxi.h + ++MEDIATEK ETHERNET PHY DRIVERS ++M: Daniel Golle ++M: Qingfang Deng ++M: SkyLake Huang ++L: netdev@vger.kernel.org ++S: Maintained ++F: drivers/net/phy/mediatek-ge.c ++ + MEDIATEK I2C CONTROLLER DRIVER + M: Qii Wang + L: linux-i2c@vger.kernel.org +--- a/drivers/net/phy/Kconfig ++++ b/drivers/net/phy/Kconfig +@@ -292,6 +292,18 @@ config MEDIATEK_GE_PHY + help + Supports the MediaTek Gigabit Ethernet PHYs. + ++config MEDIATEK_GE_PHY_SOC ++ bool "MediaTek SoC Ethernet PHYs" ++ depends on (ARM64 && ARCH_MEDIATEK && MEDIATEK_GE_PHY) || COMPILE_TEST ++ select NVMEM_MTK_EFUSE ++ help ++ Supports MediaTek SoC built-in Gigabit Ethernet PHYs. ++ ++ Include support for built-in Ethernet PHYs which are present in ++ the MT7981 and MT7988 SoCs. These PHYs need calibration data ++ present in the SoCs efuse and will dynamically calibrate VCM ++ (common-mode voltage) during startup. ++ + config MICREL_PHY + tristate "Micrel PHYs" + help +--- a/drivers/net/phy/mediatek-ge.c ++++ b/drivers/net/phy/mediatek-ge.c +@@ -1,6 +1,9 @@ + // SPDX-License-Identifier: GPL-2.0+ + #include + #include ++#include ++#include ++#include + #include + + #define MTK_EXT_PAGE_ACCESS 0x1f +@@ -11,6 +14,275 @@ + #define MTK_PHY_PAGE_EXTENDED_2A30 0x2a30 + #define MTK_PHY_PAGE_EXTENDED_52B5 0x52b5 + ++#define ANALOG_INTERNAL_OPERATION_MAX_US (20) ++#define ZCAL_CTRL_MIN (0) ++#define ZCAL_CTRL_MAX (63) ++#define TXRESERVE_MIN (0) ++#define TXRESERVE_MAX (7) ++ ++#define MTK_PHY_ANARG_RG (0x10) ++#define MTK_PHY_TCLKOFFSET_MASK GENMASK(12, 8) ++ ++/* Registers on MDIO_MMD_VEND1 */ ++enum { ++ MTK_PHY_MIDDLE_LEVEL_SHAPPER_0TO1 = 0, ++ MTK_PHY_1st_OVERSHOOT_LEVEL_0TO1, ++ MTK_PHY_2nd_OVERSHOOT_LEVEL_0TO1, ++ MTK_PHY_MIDDLE_LEVEL_SHAPPER_1TO0, ++ MTK_PHY_1st_OVERSHOOT_LEVEL_1TO0, ++ MTK_PHY_2nd_OVERSHOOT_LEVEL_1TO0, ++ MTK_PHY_MIDDLE_LEVEL_SHAPPER_0TON1, /* N means negative */ ++ MTK_PHY_1st_OVERSHOOT_LEVEL_0TON1, ++ MTK_PHY_2nd_OVERSHOOT_LEVEL_0TON1, ++ MTK_PHY_MIDDLE_LEVEL_SHAPPER_N1TO0, ++ MTK_PHY_1st_OVERSHOOT_LEVEL_N1TO0, ++ MTK_PHY_2nd_OVERSHOOT_LEVEL_N1TO0, ++ MTK_PHY_TX_MLT3_END, ++}; ++ ++#define MTK_PHY_TXVLD_DA_RG (0x12) ++#define MTK_PHY_DA_TX_I2MPB_A_GBE_MASK GENMASK(15, 10) ++#define MTK_PHY_DA_TX_I2MPB_A_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_A2 (0x16) ++#define MTK_PHY_DA_TX_I2MPB_A_HBT_MASK GENMASK(15, 10) ++#define MTK_PHY_DA_TX_I2MPB_A_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_B1 (0x17) ++#define MTK_PHY_DA_TX_I2MPB_B_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_B_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_B2 (0x18) ++#define MTK_PHY_DA_TX_I2MPB_B_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_B_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_C1 (0x19) ++#define MTK_PHY_DA_TX_I2MPB_C_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_C_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_C2 (0x20) ++#define MTK_PHY_DA_TX_I2MPB_C_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_C_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_D1 (0x21) ++#define MTK_PHY_DA_TX_I2MPB_D_GBE_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_D_TBT_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TX_I2MPB_TEST_MODE_D2 (0x22) ++#define MTK_PHY_DA_TX_I2MPB_D_HBT_MASK GENMASK(13, 8) ++#define MTK_PHY_DA_TX_I2MPB_D_TST_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_TANA_CAL_MODE (0xc1) ++#define MTK_PHY_TANA_CAL_MODE_SHIFT (8) ++ ++#define MTK_PHY_RXADC_CTRL_RG7 (0xc6) ++#define MTK_PHY_DA_AD_BUF_BIAS_LP_MASK GENMASK(9, 8) ++ ++#define MTK_PHY_RXADC_CTRL_RG9 (0xc8) ++#define MTK_PHY_DA_RX_PSBN_TBT_MASK GENMASK(14, 12) ++#define MTK_PHY_DA_RX_PSBN_HBT_MASK GENMASK(10, 8) ++#define MTK_PHY_DA_RX_PSBN_GBE_MASK GENMASK(6, 4) ++#define MTK_PHY_DA_RX_PSBN_LP_MASK GENMASK(2, 0) ++ ++#define MTK_PHY_LDO_OUTPUT_V (0xd7) ++ ++#define MTK_PHY_RG_ANA_CAL_RG0 (0xdb) ++#define MTK_PHY_RG_CAL_CKINV BIT(12) ++#define MTK_PHY_RG_ANA_CALEN BIT(8) ++#define MTK_PHY_RG_REXT_CALEN BIT(4) ++#define MTK_PHY_RG_ZCALEN_A BIT(0) ++ ++#define MTK_PHY_RG_ANA_CAL_RG1 (0xdc) ++#define MTK_PHY_RG_ZCALEN_B BIT(12) ++#define MTK_PHY_RG_ZCALEN_C BIT(8) ++#define MTK_PHY_RG_ZCALEN_D BIT(4) ++#define MTK_PHY_RG_TXVOS_CALEN BIT(0) ++ ++#define MTK_PHY_RG_ANA_CAL_RG2 (0xdd) ++#define MTK_PHY_RG_TXG_CALEN_A BIT(12) ++#define MTK_PHY_RG_TXG_CALEN_B BIT(8) ++#define MTK_PHY_RG_TXG_CALEN_C BIT(4) ++#define MTK_PHY_RG_TXG_CALEN_D BIT(0) ++ ++#define MTK_PHY_RG_ANA_CAL_RG5 (0xe0) ++#define MTK_PHY_RG_REXT_TRIM_MASK GENMASK(13, 8) ++#define MTK_PHY_RG_ZCAL_CTRL_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_RG_TX_FILTER (0xfe) ++ ++#define MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B (0x172) ++#define MTK_PHY_CR_TX_AMP_OFFSET_A_MASK GENMASK(13, 8) ++#define MTK_PHY_CR_TX_AMP_OFFSET_B_MASK GENMASK(6, 0) ++ ++#define MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D (0x173) ++#define MTK_PHY_CR_TX_AMP_OFFSET_C_MASK GENMASK(13, 8) ++#define MTK_PHY_CR_TX_AMP_OFFSET_D_MASK GENMASK(6, 0) ++ ++#define MTK_PHY_RG_AD_CAL_COMP (0x17a) ++#define MTK_PHY_AD_CAL_COMP_OUT_SHIFT (8) ++ ++#define MTK_PHY_RG_AD_CAL_CLK (0x17b) ++#define MTK_PHY_DA_CAL_CLK BIT(0) ++ ++#define MTK_PHY_RG_AD_CALIN (0x17c) ++#define MTK_PHY_DA_CALIN_FLAG BIT(0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_A (0x17d) ++#define MTK_PHY_DASN_DAC_IN0_A_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_B (0x17e) ++#define MTK_PHY_DASN_DAC_IN0_B_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_C (0x17f) ++#define MTK_PHY_DASN_DAC_IN0_C_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN0_D (0x180) ++#define MTK_PHY_DASN_DAC_IN0_D_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_A (0x181) ++#define MTK_PHY_DASN_DAC_IN1_A_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_B (0x182) ++#define MTK_PHY_DASN_DAC_IN1_B_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_C (0x183) ++#define MTK_PHY_DASN_DAC_IN1_C_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_DASN_DAC_IN1_D (0x180) ++#define MTK_PHY_DASN_DAC_IN1_D_MASK GENMASK(9, 0) ++ ++#define MTK_PHY_RG_LP_IIR2_K1_L (0x22a) ++#define MTK_PHY_RG_LP_IIR2_K1_U (0x22b) ++#define MTK_PHY_RG_LP_IIR2_K2_L (0x22c) ++#define MTK_PHY_RG_LP_IIR2_K2_U (0x22d) ++#define MTK_PHY_RG_LP_IIR2_K3_L (0x22e) ++#define MTK_PHY_RG_LP_IIR2_K3_U (0x22f) ++#define MTK_PHY_RG_LP_IIR2_K4_L (0x230) ++#define MTK_PHY_RG_LP_IIR2_K4_U (0x231) ++#define MTK_PHY_RG_LP_IIR2_K5_L (0x232) ++#define MTK_PHY_RG_LP_IIR2_K5_U (0x233) ++ ++#define MTK_PHY_RG_DEV1E_REG234 (0x234) ++#define MTK_PHY_TR_OPEN_LOOP_EN_MASK GENMASK(0, 0) ++#define MTK_PHY_LPF_X_AVERAGE_MASK GENMASK(7, 4) ++ ++#define MTK_PHY_RG_LPF_CNT_VAL (0x235) ++ ++#define MTK_PHY_RG_DEV1E_REG27C (0x27c) ++#define MTK_PHY_VGASTATE_FFE_THR_ST1_MASK GENMASK(12, 8) ++#define MTK_PHY_RG_DEV1E_REG27D (0x27d) ++#define MTK_PHY_VGASTATE_FFE_THR_ST2_MASK GENMASK(4, 0) ++ ++#define MTK_PHY_LDO_PUMP_EN_PAIRAB (0x502) ++#define MTK_PHY_LDO_PUMP_EN_PAIRCD (0x503) ++ ++#define MTK_PHY_DA_TX_R50_PAIR_A (0x53d) ++#define MTK_PHY_DA_TX_R50_PAIR_B (0x53e) ++#define MTK_PHY_DA_TX_R50_PAIR_C (0x53f) ++#define MTK_PHY_DA_TX_R50_PAIR_D (0x540) ++ ++/* Registers on MDIO_MMD_VEND2 */ ++#define MTK_PHY_LED0_ON_CTRL (0x24) ++#define MTK_PHY_LED0_ON_MASK GENMASK(6, 0) ++#define MTK_PHY_LED0_ON_LINK1000 BIT(0) ++#define MTK_PHY_LED0_ON_LINK100 BIT(1) ++#define MTK_PHY_LED0_ON_LINK10 BIT(2) ++#define MTK_PHY_LED0_ON_LINKDOWN BIT(3) ++#define MTK_PHY_LED0_ON_FDX BIT(4) /* Full duplex */ ++#define MTK_PHY_LED0_ON_HDX BIT(5) /* Half duplex */ ++#define MTK_PHY_LED0_FORCE_ON BIT(6) ++#define MTK_PHY_LED0_POLARITY BIT(14) ++#define MTK_PHY_LED0_ENABLE BIT(15) ++ ++#define MTK_PHY_LED0_BLINK_CTRL (0x25) ++#define MTK_PHY_LED0_1000TX BIT(0) ++#define MTK_PHY_LED0_1000RX BIT(1) ++#define MTK_PHY_LED0_100TX BIT(2) ++#define MTK_PHY_LED0_100RX BIT(3) ++#define MTK_PHY_LED0_10TX BIT(4) ++#define MTK_PHY_LED0_10RX BIT(5) ++#define MTK_PHY_LED0_COLLISION BIT(6) ++#define MTK_PHY_LED0_RX_CRC_ERR BIT(7) ++#define MTK_PHY_LED0_RX_IDLE_ERR BIT(8) ++#define MTK_PHY_LED0_FORCE_BLINK BIT(9) ++ ++#define MTK_PHY_ANA_TEST_BUS_CTRL_RG (0x100) ++#define MTK_PHY_ANA_TEST_MODE_MASK GENMASK(15, 8) ++ ++#define MTK_PHY_RG_DASN_TXT_DMY2 (0x110) ++#define MTK_PHY_TST_DMY2_MASK GENMASK(5, 0) ++ ++#define MTK_PHY_RG_BG_RASEL (0x115) ++#define MTK_PHY_RG_BG_RASEL_MASK GENMASK(2, 0) ++ ++/* These macro privides efuse parsing for internal phy. */ ++#define EFS_DA_TX_I2MPB_A(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_B(x) (((x) >> 6) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_C(x) (((x) >> 12) & GENMASK(5, 0)) ++#define EFS_DA_TX_I2MPB_D(x) (((x) >> 18) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_A(x) (((x) >> 24) & GENMASK(5, 0)) ++ ++#define EFS_DA_TX_AMP_OFFSET_B(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_C(x) (((x) >> 6) & GENMASK(5, 0)) ++#define EFS_DA_TX_AMP_OFFSET_D(x) (((x) >> 12) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_A(x) (((x) >> 18) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_B(x) (((x) >> 24) & GENMASK(5, 0)) ++ ++#define EFS_DA_TX_R50_C(x) (((x) >> 0) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_D(x) (((x) >> 6) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_A_10M(x) (((x) >> 12) & GENMASK(5, 0)) ++#define EFS_DA_TX_R50_B_10M(x) (((x) >> 18) & GENMASK(5, 0)) ++ ++#define EFS_RG_BG_RASEL(x) (((x) >> 4) & GENMASK(2, 0)) ++#define EFS_RG_REXT_TRIM(x) (((x) >> 7) & GENMASK(5, 0)) ++ ++enum { ++ NO_PAIR, ++ PAIR_A, ++ PAIR_B, ++ PAIR_C, ++ PAIR_D, ++}; ++ ++enum { ++ GPHY_PORT0, ++ GPHY_PORT1, ++ GPHY_PORT2, ++ GPHY_PORT3, ++}; ++ ++enum calibration_mode { ++ EFUSE_K, ++ SW_K ++}; ++ ++enum CAL_ITEM { ++ REXT, ++ TX_OFFSET, ++ TX_AMP, ++ TX_R50, ++ TX_VCM ++}; ++ ++enum CAL_MODE { ++ SW_EFUSE_M, ++ EFUSE_M, ++ SW_M ++}; ++ ++const u8 mt798x_zcal_to_r50[64] = { ++ 7, 8, 9, 9, 10, 10, 11, 11, ++ 12, 13, 13, 14, 14, 15, 16, 16, ++ 17, 18, 18, 19, 20, 21, 21, 22, ++ 23, 24, 24, 25, 26, 27, 28, 29, ++ 30, 31, 32, 33, 34, 35, 36, 37, ++ 38, 40, 41, 42, 43, 45, 46, 48, ++ 49, 51, 52, 54, 55, 57, 59, 61, ++ 62, 63, 63, 63, 63, 63, 63, 63 ++}; ++ ++const char pair[4] = {'A', 'B', 'C', 'D'}; ++ + static int mtk_gephy_read_page(struct phy_device *phydev) + { + return __phy_read(phydev, MTK_EXT_PAGE_ACCESS); +@@ -68,6 +340,1059 @@ static int mt7531_phy_config_init(struct + return 0; + } + ++#ifdef CONFIG_MEDIATEK_GE_PHY_SOC ++/* One calibration cycle consists of: ++ * 1.Set DA_CALIN_FLAG high to start calibration. Keep it high ++ * until AD_CAL_COMP is ready to output calibration result. ++ * 2.Wait until DA_CAL_CLK is available. ++ * 3.Fetch AD_CAL_COMP_OUT. ++ */ ++static int cal_cycle(struct phy_device *phydev, int devad, ++ u32 regnum, u16 mask, u16 cal_val) ++{ ++ unsigned long timeout; ++ int reg_val; ++ int ret; ++ ++ phy_modify_mmd(phydev, devad, regnum, ++ mask, cal_val); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, ++ MTK_PHY_DA_CALIN_FLAG); ++ ++ timeout = jiffies + usecs_to_jiffies(ANALOG_INTERNAL_OPERATION_MAX_US); ++ do { ++ reg_val = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_AD_CAL_CLK); ++ } while (time_before(jiffies, timeout) && !(reg_val & BIT(0))); ++ ++ if (!(reg_val & BIT(0))) { ++ dev_err(&phydev->mdio.dev, "Calibration cycle timeout\n"); ++ return -ETIMEDOUT; ++ } ++ ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CALIN, ++ MTK_PHY_DA_CALIN_FLAG); ++ ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_AD_CAL_COMP) >> ++ MTK_PHY_AD_CAL_COMP_OUT_SHIFT; ++ dev_dbg(&phydev->mdio.dev, "cal_val: 0x%x, ret: %d\n", cal_val, ret); ++ ++ return ret; ++} ++ ++static int rext_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_REXT_TRIM_MASK, buf[0] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_BG_RASEL, ++ MTK_PHY_RG_BG_RASEL_MASK, buf[1]); ++ ++ return 0; ++} ++ ++static int rext_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 rext_cal_val[2]; ++ ++ rext_cal_val[0] = EFS_RG_REXT_TRIM(buf[3]); ++ rext_cal_val[1] = EFS_RG_BG_RASEL(buf[3]); ++ rext_fill_result(phydev, rext_cal_val); ++ ++ return 0; ++} ++ ++static int rext_cal_sw(struct phy_device *phydev) ++{ ++ u8 rg_zcal_ctrl_def; ++ u8 zcal_lower, zcal_upper, rg_zcal_ctrl; ++ u8 lower_ret, upper_ret; ++ u16 rext_cal_val[2]; ++ int ret; ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, ++ MTK_PHY_ANA_TEST_MODE_MASK, MTK_PHY_TANA_CAL_MODE << 8); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_TXVOS_CALEN); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN | ++ MTK_PHY_RG_REXT_CALEN); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DASN_TXT_DMY2, ++ MTK_PHY_TST_DMY2_MASK, 0x1); ++ ++ rg_zcal_ctrl_def = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG5) & ++ MTK_PHY_RG_ZCAL_CTRL_MASK; ++ zcal_lower = ZCAL_CTRL_MIN; ++ zcal_upper = ZCAL_CTRL_MAX; ++ ++ dev_dbg(&phydev->mdio.dev, "Start REXT SW cal.\n"); ++ while ((zcal_upper - zcal_lower) > 1) { ++ rg_zcal_ctrl = DIV_ROUND_CLOSEST(zcal_lower + zcal_upper, 2); ++ ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl); ++ if (ret == 1) { ++ zcal_upper = rg_zcal_ctrl; ++ upper_ret = ret; ++ } else if (ret == 0) { ++ zcal_lower = rg_zcal_ctrl; ++ lower_ret = ret; ++ } else { ++ goto restore; ++ } ++ } ++ ++ if (zcal_lower == ZCAL_CTRL_MIN) { ++ lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_lower); ++ ret = lower_ret; ++ } else if (zcal_upper == ZCAL_CTRL_MAX) { ++ upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_upper); ++ ret = upper_ret; ++ } ++ if (ret < 0) ++ goto restore; ++ ++ ret = upper_ret - lower_ret; ++ if (ret == 1) { ++ rext_cal_val[0] = zcal_upper; ++ rext_cal_val[1] = zcal_upper >> 3; ++ rext_fill_result(phydev, rext_cal_val); ++ dev_info(&phydev->mdio.dev, "REXT SW cal result: 0x%x\n", ++ zcal_upper); ++ ret = 0; ++ } else { ++ ret = -EINVAL; ++ } ++ ++restore: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, ++ MTK_PHY_ANA_TEST_BUS_CTRL_RG, ++ MTK_PHY_ANA_TEST_MODE_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN | ++ MTK_PHY_RG_REXT_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DASN_TXT_DMY2, ++ MTK_PHY_TST_DMY2_MASK); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl_def); ++ ++ return ret; ++} ++ ++static int tx_offset_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, ++ MTK_PHY_CR_TX_AMP_OFFSET_A_MASK, buf[0] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_A_B, ++ MTK_PHY_CR_TX_AMP_OFFSET_B_MASK, buf[1]); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, ++ MTK_PHY_CR_TX_AMP_OFFSET_C_MASK, buf[2] << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_CR_TX_AMP_OFFSET_C_D, ++ MTK_PHY_CR_TX_AMP_OFFSET_D_MASK, buf[3]); ++ ++ return 0; ++} ++ ++static int tx_offset_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 tx_offset_cal_val[4]; ++ ++ tx_offset_cal_val[0] = EFS_DA_TX_AMP_OFFSET_A(buf[0]); ++ tx_offset_cal_val[1] = EFS_DA_TX_AMP_OFFSET_B(buf[1]); ++ tx_offset_cal_val[2] = EFS_DA_TX_AMP_OFFSET_C(buf[1]); ++ tx_offset_cal_val[3] = EFS_DA_TX_AMP_OFFSET_D(buf[1]); ++ ++ tx_offset_fill_result(phydev, tx_offset_cal_val); ++ ++ return 0; ++} ++ ++static int tx_amp_fill_result(struct phy_device *phydev, u16 *buf) ++{ ++ int i; ++ int bias[16] = {0}; ++ const int vals_9461[16] = { 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7, ++ 7, 1, 4, 7 }; ++ const int vals_9481[16] = { 10, 6, 6, 10, ++ 10, 6, 6, 10, ++ 10, 6, 6, 10, ++ 10, 6, 6, 10 }; ++ ++ switch (phydev->drv->phy_id) { ++ case 0x03a29461: ++ /* We add some calibration to efuse values ++ * due to board level influence. ++ * GBE: +7, TBT: +1, HBT: +4, TST: +7 ++ */ ++ memcpy(bias, (const void *)vals_9461, sizeof(bias)); ++ for (i = 0; i <= 12; i += 4) { ++ if (likely(buf[i >> 2] + bias[i] >= 32)) { ++ bias[i] -= 13; ++ } else { ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, ++ 0x5c, 0x7 << i, bias[i] << i); ++ bias[i + 1] += 13; ++ bias[i + 2] += 13; ++ bias[i + 3] += 13; ++ } ++ } ++ break; ++ case 0x03a29481: ++ memcpy(bias, (const void *)vals_9481, sizeof(bias)); ++ break; ++ default: ++ break; ++ } ++ ++ /* Prevent overflow */ ++ for (i = 0; i < 12; i++) { ++ if (buf[i >> 2] + bias[i] > 63) { ++ buf[i >> 2] = 63; ++ bias[i] = 0; ++ } else if (buf[i >> 2] + bias[i] < 0) { ++ /* Bias caused by board design may change in the future. ++ * So check negative cases, too. ++ */ ++ buf[i >> 2] = 0; ++ bias[i] = 0; ++ } ++ } ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, ++ MTK_PHY_DA_TX_I2MPB_A_GBE_MASK, (buf[0] + bias[0]) << 10); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, ++ MTK_PHY_DA_TX_I2MPB_A_TBT_MASK, buf[0] + bias[1]); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, ++ MTK_PHY_DA_TX_I2MPB_A_HBT_MASK, (buf[0] + bias[2]) << 10); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, ++ MTK_PHY_DA_TX_I2MPB_A_TST_MASK, buf[0] + bias[3]); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, ++ MTK_PHY_DA_TX_I2MPB_B_GBE_MASK, (buf[1] + bias[4]) << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, ++ MTK_PHY_DA_TX_I2MPB_B_TBT_MASK, buf[1] + bias[5]); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, ++ MTK_PHY_DA_TX_I2MPB_B_HBT_MASK, (buf[1] + bias[6]) << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, ++ MTK_PHY_DA_TX_I2MPB_B_TST_MASK, buf[1] + bias[7]); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, ++ MTK_PHY_DA_TX_I2MPB_C_GBE_MASK, (buf[2] + bias[8]) << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, ++ MTK_PHY_DA_TX_I2MPB_C_TBT_MASK, buf[2] + bias[9]); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, ++ MTK_PHY_DA_TX_I2MPB_C_HBT_MASK, (buf[2] + bias[10]) << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, ++ MTK_PHY_DA_TX_I2MPB_C_TST_MASK, buf[2] + bias[11]); ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, ++ MTK_PHY_DA_TX_I2MPB_D_GBE_MASK, (buf[3] + bias[12]) << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, ++ MTK_PHY_DA_TX_I2MPB_D_TBT_MASK, buf[3] + bias[13]); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, ++ MTK_PHY_DA_TX_I2MPB_D_HBT_MASK, (buf[3] + bias[14]) << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, ++ MTK_PHY_DA_TX_I2MPB_D_TST_MASK, buf[3] + bias[15]); ++ ++ return 0; ++} ++ ++static int tx_amp_cal_efuse(struct phy_device *phydev, u32 *buf) ++{ ++ u16 tx_amp_cal_val[4]; ++ ++ tx_amp_cal_val[0] = EFS_DA_TX_I2MPB_A(buf[0]); ++ tx_amp_cal_val[1] = EFS_DA_TX_I2MPB_B(buf[0]); ++ tx_amp_cal_val[2] = EFS_DA_TX_I2MPB_C(buf[0]); ++ tx_amp_cal_val[3] = EFS_DA_TX_I2MPB_D(buf[0]); ++ tx_amp_fill_result(phydev, tx_amp_cal_val); ++ ++ return 0; ++} ++ ++static int tx_r50_fill_result(struct phy_device *phydev, u16 tx_r50_cal_val, ++ u8 txg_calen_x) ++{ ++ int bias = 0; ++ u16 reg, val; ++ ++ switch (phydev->drv->phy_id) { ++ case 0x03a29481: ++ { ++ bias = -2; ++ break; ++ } ++ /* 0x03a29461 enters default case */ ++ default: ++ break; ++ } ++ ++ val = clamp_val(bias + tx_r50_cal_val, 0, 63); ++ ++ switch (txg_calen_x) { ++ case PAIR_A: ++ reg = MTK_PHY_DA_TX_R50_PAIR_A; ++ break; ++ case PAIR_B: ++ reg = MTK_PHY_DA_TX_R50_PAIR_B; ++ break; ++ case PAIR_C: ++ reg = MTK_PHY_DA_TX_R50_PAIR_C; ++ break; ++ case PAIR_D: ++ reg = MTK_PHY_DA_TX_R50_PAIR_D; ++ break; ++ } ++ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, reg, val | val << 8); ++ ++ return 0; ++} ++ ++static int tx_r50_cal_efuse(struct phy_device *phydev, u32 *buf, ++ u8 txg_calen_x) ++{ ++ u16 tx_r50_cal_val; ++ ++ switch (txg_calen_x) { ++ case PAIR_A: ++ tx_r50_cal_val = EFS_DA_TX_R50_A(buf[1]); ++ break; ++ case PAIR_B: ++ tx_r50_cal_val = EFS_DA_TX_R50_B(buf[1]); ++ break; ++ case PAIR_C: ++ tx_r50_cal_val = EFS_DA_TX_R50_C(buf[2]); ++ break; ++ case PAIR_D: ++ tx_r50_cal_val = EFS_DA_TX_R50_D(buf[2]); ++ break; ++ } ++ tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); ++ ++ return 0; ++} ++ ++static int tx_r50_cal_sw(struct phy_device *phydev, u8 txg_calen_x) ++{ ++ u8 zcal_lower, zcal_upper, rg_zcal_ctrl; ++ u8 lower_ret, upper_ret; ++ u8 rg_zcal_ctrl_def; ++ u16 tx_r50_cal_val; ++ int ret; ++ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, ++ MTK_PHY_ANA_TEST_MODE_MASK, MTK_PHY_TANA_CAL_MODE << 8); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_TXVOS_CALEN); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG2, ++ BIT(txg_calen_x * 4)); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DASN_TXT_DMY2, ++ MTK_PHY_TST_DMY2_MASK, 0x1); ++ ++ rg_zcal_ctrl_def = phy_read_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG5) & ++ MTK_PHY_RG_ZCAL_CTRL_MASK; ++ zcal_lower = ZCAL_CTRL_MIN; ++ zcal_upper = ZCAL_CTRL_MAX; ++ ++ dev_dbg(&phydev->mdio.dev, "Start TX-R50 Pair%c SW cal.\n", ++ pair[txg_calen_x]); ++ while ((zcal_upper - zcal_lower) > 1) { ++ rg_zcal_ctrl = DIV_ROUND_CLOSEST(zcal_lower + zcal_upper, 2); ++ ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl); ++ if (ret == 1) { ++ zcal_upper = rg_zcal_ctrl; ++ upper_ret = ret; ++ } else if (ret == 0) { ++ zcal_lower = rg_zcal_ctrl; ++ lower_ret = ret; ++ } else { ++ goto restore; ++ } ++ } ++ ++ if (zcal_lower == ZCAL_CTRL_MIN) { ++ lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_lower); ++ ret = lower_ret; ++ } else if (zcal_upper == ZCAL_CTRL_MAX) { ++ upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_ZCAL_CTRL_MASK, zcal_upper); ++ ret = upper_ret; ++ } ++ if (ret < 0) ++ goto restore; ++ ++ ret = upper_ret - lower_ret; ++ if (ret == 1) { ++ tx_r50_cal_val = mt798x_zcal_to_r50[zcal_upper]; ++ tx_r50_fill_result(phydev, tx_r50_cal_val, txg_calen_x); ++ dev_info(&phydev->mdio.dev, ++ "TX-R50 Pair%c SW cal result: 0x%x\n", ++ pair[txg_calen_x], zcal_lower); ++ ret = 0; ++ } else { ++ ret = -EINVAL; ++ } ++ ++restore: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_ANA_TEST_BUS_CTRL_RG, ++ MTK_PHY_ANA_TEST_MODE_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_CAL_CKINV | MTK_PHY_RG_ANA_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG2, ++ BIT(txg_calen_x * 4)); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_RG_DASN_TXT_DMY2, ++ MTK_PHY_TST_DMY2_MASK); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG5, ++ MTK_PHY_RG_ZCAL_CTRL_MASK, rg_zcal_ctrl_def); ++ ++ return ret; ++} ++ ++static int tx_vcm_cal_sw(struct phy_device *phydev, u8 rg_txreserve_x) ++{ ++ u8 lower_idx, upper_idx, txreserve_val; ++ u8 lower_ret, upper_ret; ++ int ret; ++ ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ANA_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_CAL_CKINV); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_TXVOS_CALEN); ++ ++ switch (rg_txreserve_x) { ++ case PAIR_A: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_A, ++ MTK_PHY_DASN_DAC_IN0_A_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_A, ++ MTK_PHY_DASN_DAC_IN1_A_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ZCALEN_A); ++ break; ++ case PAIR_B: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_B, ++ MTK_PHY_DASN_DAC_IN0_B_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_B, ++ MTK_PHY_DASN_DAC_IN1_B_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_B); ++ break; ++ case PAIR_C: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_C, ++ MTK_PHY_DASN_DAC_IN0_C_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_C, ++ MTK_PHY_DASN_DAC_IN1_C_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_C); ++ break; ++ case PAIR_D: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN0_D, ++ MTK_PHY_DASN_DAC_IN0_D_MASK); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_DASN_DAC_IN1_D, ++ MTK_PHY_DASN_DAC_IN1_D_MASK); ++ phy_set_bits_mmd(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_D); ++ break; ++ default: ++ ret = -EINVAL; ++ goto restore; ++ } ++ ++ lower_idx = TXRESERVE_MIN; ++ upper_idx = TXRESERVE_MAX; ++ ++ dev_dbg(&phydev->mdio.dev, "Start TX-VCM SW cal.\n"); ++ while ((upper_idx - lower_idx) > 1) { ++ txreserve_val = DIV_ROUND_CLOSEST(lower_idx + upper_idx, 2); ++ ret = cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ txreserve_val << 12 | txreserve_val << 8 | ++ txreserve_val << 4 | txreserve_val); ++ if (ret == 1) { ++ upper_idx = txreserve_val; ++ upper_ret = ret; ++ } else if (ret == 0) { ++ lower_idx = txreserve_val; ++ lower_ret = ret; ++ } else { ++ goto restore; ++ } ++ } ++ ++ if (lower_idx == TXRESERVE_MIN) { ++ lower_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ lower_idx << 12 | lower_idx << 8 | ++ lower_idx << 4 | lower_idx); ++ ret = lower_ret; ++ } else if (upper_idx == TXRESERVE_MAX) { ++ upper_ret = cal_cycle(phydev, MDIO_MMD_VEND1, ++ MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ upper_idx << 12 | upper_idx << 8 | ++ upper_idx << 4 | upper_idx); ++ ret = upper_ret; ++ } ++ if (ret < 0) ++ goto restore; ++ ++ /* We calibrate TX-VCM in different logic. Check upper index and then ++ * lower index. If this calibration is valid, apply lower index's result. ++ */ ++ ret = upper_ret - lower_ret; ++ if (ret == 1) { ++ ret = 0; ++ /* Make sure we use upper_idx in our calibration system */ ++ cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ upper_idx << 12 | upper_idx << 8 | ++ upper_idx << 4 | upper_idx); ++ dev_info(&phydev->mdio.dev, "TX-VCM SW cal result: 0x%x\n", ++ upper_idx); ++ } else if (lower_idx == TXRESERVE_MIN && upper_ret == 1 && ++ lower_ret == 1) { ++ ret = 0; ++ cal_cycle(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG9, ++ MTK_PHY_DA_RX_PSBN_TBT_MASK | ++ MTK_PHY_DA_RX_PSBN_HBT_MASK | ++ MTK_PHY_DA_RX_PSBN_GBE_MASK | ++ MTK_PHY_DA_RX_PSBN_LP_MASK, ++ lower_idx << 12 | lower_idx << 8 | ++ lower_idx << 4 | lower_idx); ++ dev_warn(&phydev->mdio.dev, ++ "TX-VCM SW cal result at low margin 0x%x\n", ++ lower_idx); ++ } else if (upper_idx == TXRESERVE_MAX && upper_ret == 0 && ++ lower_ret == 0) { ++ ret = 0; ++ dev_warn(&phydev->mdio.dev, ++ "TX-VCM SW cal result at high margin 0x%x\n", ++ upper_idx); ++ } else { ++ ret = -EINVAL; ++ } ++ ++restore: ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ANA_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_TXVOS_CALEN); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG0, ++ MTK_PHY_RG_ZCALEN_A); ++ phy_clear_bits_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_ANA_CAL_RG1, ++ MTK_PHY_RG_ZCALEN_B | MTK_PHY_RG_ZCALEN_C | ++ MTK_PHY_RG_ZCALEN_D); ++ ++ return ret; ++} ++ ++static inline void mt7981_phy_finetune(struct phy_device *phydev) ++{ ++ u32 i; ++ ++ /* 100M eye finetune: ++ * Keep middle level of TX MLT3 shapper as default. ++ * Only change TX MLT3 overshoot level here. ++ */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_0TO1, ++ 0x1ce); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_0TO1, ++ 0x1c1); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_1TO0, ++ 0x20f); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_1TO0, ++ 0x202); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_0TON1, ++ 0x3d0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_0TON1, ++ 0x3c0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_1st_OVERSHOOT_LEVEL_N1TO0, ++ 0x13); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_2nd_OVERSHOOT_LEVEL_N1TO0, ++ 0x5); ++ ++ /* TX-AMP finetune: ++ * 100M +4, 1000M +6 to default value. ++ * If efuse values aren't valid, TX-AMP uses the below values. ++ */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TXVLD_DA_RG, 0x9824); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_A2, ++ 0x9026); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B1, ++ 0x2624); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_B2, ++ 0x2426); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C1, ++ 0x2624); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_C2, ++ 0x2426); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D1, ++ 0x2624); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_TX_I2MPB_TEST_MODE_D2, ++ 0x2426); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* EnabRandUpdTrig = 1 */ ++ __phy_write(phydev, 0x11, 0x2f00); ++ __phy_write(phydev, 0x12, 0xe); ++ __phy_write(phydev, 0x10, 0x8fb0); ++ ++ /* SlvDSPreadyTime = 0xc */ ++ __phy_write(phydev, 0x11, 0x671); ++ __phy_write(phydev, 0x12, 0xc); ++ __phy_write(phydev, 0x10, 0x8fae); ++ ++ /* NormMseLoThresh = 85 */ ++ __phy_write(phydev, 0x11, 0x55a0); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x83aa); ++ ++ /* InhibitDisableDfeTail1000 = 1 */ ++ __phy_write(phydev, 0x11, 0x2b); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8f80); ++ ++ /* SSTr related */ ++ __phy_write(phydev, 0x11, 0xbaef); ++ __phy_write(phydev, 0x12, 0x2e); ++ __phy_write(phydev, 0x10, 0x968c); ++ ++ /* VcoSlicerThreshBitsHigh */ ++ __phy_write(phydev, 0x11, 0x5555); ++ __phy_write(phydev, 0x12, 0x55); ++ __phy_write(phydev, 0x10, 0x8ec0); ++ ++ /* ResetSyncOffset = 6 */ ++ __phy_write(phydev, 0x11, 0x600); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8fc0); ++ ++ /* VgaDecRate = 1 */ ++ __phy_write(phydev, 0x11, 0x4c2a); ++ __phy_write(phydev, 0x12, 0x3e); ++ __phy_write(phydev, 0x10, 0x8fa4); ++ ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, ++ BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); ++ ++ /* rg_tr_lpf_cnt_val = 512 */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); ++ ++ /* IIR2 related */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); ++ ++ /* FFE peaking */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, ++ MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, ++ MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); ++ ++ /* TX shape */ ++ /* 10/100/1000 TX shaper is enabled by default */ ++ for (i = 0x202; i < 0x230; i += 2) { ++ if (i == 0x20c || i == 0x218 || i == 0x224) ++ continue; ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, i, 0x2219); ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, i + 1, 0x23); ++ } ++} ++ ++static inline void mt7988_phy_finetune(struct phy_device *phydev) ++{ ++ u16 val[12] = { 0x0187, 0x01cd, 0x01c8, 0x0182, ++ 0x020d, 0x0206, 0x0384, 0x03d0, ++ 0x03c6, 0x030a, 0x0011, 0x0005 }; ++ int i; ++ ++ for (i = 0; i < MTK_PHY_TX_MLT3_END; i++) ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, i, val[i]); ++ ++ /* TCT finetune */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_TX_FILTER, 0x5); ++ ++ /* Disable TX power saving */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RXADC_CTRL_RG7, ++ MTK_PHY_DA_AD_BUF_BIAS_LP_MASK, 0x3 << 8); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_52B5); ++ /* EnabRandUpdTrig = 1 */ ++ __phy_write(phydev, 0x11, 0x2f00); ++ __phy_write(phydev, 0x12, 0xe); ++ __phy_write(phydev, 0x10, 0x8fb0); ++ ++ /* SlvDSPreadyTime = 0xc */ ++ __phy_write(phydev, 0x11, 0x671); ++ __phy_write(phydev, 0x12, 0xc); ++ __phy_write(phydev, 0x10, 0x8fae); ++ ++ /* NormMseLoThresh = 85 */ ++ __phy_write(phydev, 0x11, 0x55a0); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x83aa); ++ ++ /* InhibitDisableDfeTail1000 = 1 */ ++ __phy_write(phydev, 0x11, 0x2b); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8f80); ++ ++ /* SSTr related */ ++ __phy_write(phydev, 0x11, 0xbaef); ++ __phy_write(phydev, 0x12, 0x2e); ++ __phy_write(phydev, 0x10, 0x968c); ++ ++ /* MrvlTrFix100Kp = 3, MrvlTrFix100Kf = 2, ++ * MrvlTrFix1000Kp = 3, MrvlTrFix1000Kf = 2 ++ */ ++ __phy_write(phydev, 0x11, 0xd10a); ++ __phy_write(phydev, 0x12, 0x34); ++ __phy_write(phydev, 0x10, 0x8f82); ++ ++ /* VcoSlicerThreshBitsHigh */ ++ __phy_write(phydev, 0x11, 0x5555); ++ __phy_write(phydev, 0x12, 0x55); ++ __phy_write(phydev, 0x10, 0x8ec0); ++ ++ /* ResetSyncOffset = 5 */ ++ __phy_write(phydev, 0x11, 0x500); ++ __phy_write(phydev, 0x12, 0x0); ++ __phy_write(phydev, 0x10, 0x8fc0); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ phy_select_page(phydev, MTK_PHY_PAGE_EXTENDED_2A30); ++ /* TxClkOffset = 2 */ ++ __phy_modify(phydev, MTK_PHY_ANARG_RG, MTK_PHY_TCLKOFFSET_MASK, ++ FIELD_PREP(MTK_PHY_TCLKOFFSET_MASK, 0x2)); ++ phy_restore_page(phydev, MTK_PHY_PAGE_STANDARD, 0); ++ ++ /* TR_OPEN_LOOP_EN = 1, lpf_x_average = 9*/ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG234, ++ MTK_PHY_TR_OPEN_LOOP_EN_MASK | MTK_PHY_LPF_X_AVERAGE_MASK, ++ BIT(0) | FIELD_PREP(MTK_PHY_LPF_X_AVERAGE_MASK, 0x9)); ++ ++ /* rg_tr_lpf_cnt_val = 512 */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LPF_CNT_VAL, 0x200); ++ ++ /* IIR2 related */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_L, 0x82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K1_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_L, 0x103); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K2_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_L, 0x82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K3_U, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_L, 0xd177); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K4_U, 0x3); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_L, 0x2c82); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_LP_IIR2_K5_U, 0xe); ++ ++ /* FFE peaking */ ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27C, ++ MTK_PHY_VGASTATE_FFE_THR_ST1_MASK, 0x1b << 8); ++ phy_modify_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_RG_DEV1E_REG27D, ++ MTK_PHY_VGASTATE_FFE_THR_ST2_MASK, 0x1e); ++ ++ /* TX shape */ ++ /* 10/100/1000 TX shaper is enabled by default */ ++ for (i = 0x202; i < 0x230; i += 2) { ++ if (i == 0x20c || i == 0x218 || i == 0x224) ++ continue; ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, i, 0x2219); ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, i + 1, 0x23); ++ } ++ ++ /* Disable LDO pump */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRAB, 0x0); ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_PUMP_EN_PAIRCD, 0x0); ++ ++ /* Adjust LDO output voltage */ ++ phy_write_mmd(phydev, MDIO_MMD_VEND1, MTK_PHY_LDO_OUTPUT_V, 0x2222); ++} ++ ++static inline int cal_sw(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ u8 start_pair, u8 end_pair) ++{ ++ u8 pair_n; ++ int ret; ++ ++ for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { ++ /* TX_OFFSET & TX_AMP have no SW calibration. */ ++ switch (cal_item) { ++ case REXT: ++ ret = rext_cal_sw(phydev); ++ break; ++ case TX_R50: ++ ret = tx_r50_cal_sw(phydev, pair_n); ++ break; ++ case TX_VCM: ++ ret = tx_vcm_cal_sw(phydev, pair_n); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (ret) ++ return ret; ++ } ++ return 0; ++} ++ ++static inline int cal_efuse(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ u8 start_pair, u8 end_pair, u32 *buf) ++{ ++ u8 pair_n; ++ int ret; ++ ++ for (pair_n = start_pair; pair_n <= end_pair; pair_n++) { ++ /* TX_VCM has no efuse calibration. */ ++ switch (cal_item) { ++ case REXT: ++ ret = rext_cal_efuse(phydev, buf); ++ break; ++ case TX_OFFSET: ++ ret = tx_offset_cal_efuse(phydev, buf); ++ break; ++ case TX_AMP: ++ ret = tx_amp_cal_efuse(phydev, buf); ++ break; ++ case TX_R50: ++ ret = tx_r50_cal_efuse(phydev, buf, pair_n); ++ break; ++ default: ++ return -EINVAL; ++ } ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int start_cal(struct phy_device *phydev, enum CAL_ITEM cal_item, ++ bool efs_valid, enum CAL_MODE cal_mode, u8 start_pair, ++ u8 end_pair, u32 *buf) ++{ ++ char cal_prop[5][20] = { "mediatek,rext", "mediatek,tx_offset", ++ "mediatek,tx_amp", "mediatek,tx_r50", ++ "mediatek,tx_vcm" }; ++ const char *dts_cal_mode; ++ u8 final_cal_mode = 0; ++ bool is_cal = true; ++ int ret, cal_ret; ++ ++ ret = of_property_read_string(phydev->mdio.dev.of_node, ++ cal_prop[cal_item], &dts_cal_mode); ++ ++ switch (cal_mode) { ++ case SW_EFUSE_M: ++ if ((efs_valid && ret) || ++ (efs_valid && !ret && strcmp("efuse", dts_cal_mode) == 0)) { ++ cal_ret = cal_efuse(phydev, cal_item, start_pair, ++ end_pair, buf); ++ final_cal_mode = EFUSE_K; ++ } else if ((!efs_valid && ret) || ++ (!ret && strcmp("sw", dts_cal_mode) == 0)) { ++ cal_ret = cal_sw(phydev, cal_item, start_pair, end_pair); ++ final_cal_mode = SW_K; ++ } else { ++ is_cal = false; ++ } ++ break; ++ case EFUSE_M: ++ if ((efs_valid && ret) || ++ (efs_valid && !ret && strcmp("efuse", dts_cal_mode) == 0)) { ++ cal_ret = cal_efuse(phydev, cal_item, start_pair, ++ end_pair, buf); ++ final_cal_mode = EFUSE_K; ++ } else { ++ is_cal = false; ++ } ++ break; ++ case SW_M: ++ if (ret || (!ret && strcmp("sw", dts_cal_mode) == 0)) { ++ cal_ret = cal_sw(phydev, cal_item, start_pair, end_pair); ++ final_cal_mode = SW_K; ++ } else { ++ is_cal = false; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ if (cal_ret) { ++ dev_err(&phydev->mdio.dev, "[%s]cal failed\n", cal_prop[cal_item]); ++ return -EIO; ++ } ++ ++ if (!is_cal) { ++ dev_dbg(&phydev->mdio.dev, "[%s]K mode: %s(not supported)\n", ++ cal_prop[cal_item], dts_cal_mode); ++ return -EIO; ++ } ++ ++ dev_dbg(&phydev->mdio.dev, "[%s]K mode: %s(dts: %s), efs-valid: %s\n", ++ cal_prop[cal_item], ++ final_cal_mode ? "SW" : "EFUSE", ++ ret ? "not set" : dts_cal_mode, ++ efs_valid ? "yes" : "no"); ++ return 0; ++} ++ ++static int mt798x_phy_calibration(struct phy_device *phydev) ++{ ++ int ret = 0; ++ u32 *buf; ++ bool efs_valid = true; ++ size_t len; ++ struct nvmem_cell *cell; ++ ++ if (phydev->interface != PHY_INTERFACE_MODE_GMII) ++ return -EINVAL; ++ ++ cell = nvmem_cell_get(&phydev->mdio.dev, "phy-cal-data"); ++ if (IS_ERR(cell)) { ++ if (PTR_ERR(cell) == -EPROBE_DEFER) ++ return PTR_ERR(cell); ++ return 0; ++ } ++ ++ buf = (u32 *)nvmem_cell_read(cell, &len); ++ if (IS_ERR(buf)) ++ return PTR_ERR(buf); ++ nvmem_cell_put(cell); ++ ++ if (!buf[0] || !buf[1] || !buf[2] || !buf[3]) ++ efs_valid = false; ++ ++ if (len < 4 * sizeof(u32)) { ++ dev_err(&phydev->mdio.dev, "invalid calibration data\n"); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ ret = start_cal(phydev, REXT, efs_valid, SW_EFUSE_M, ++ NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_OFFSET, efs_valid, EFUSE_M, ++ NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_AMP, efs_valid, EFUSE_M, ++ NO_PAIR, NO_PAIR, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_R50, efs_valid, EFUSE_M, ++ PAIR_A, PAIR_D, buf); ++ if (ret) ++ goto out; ++ ret = start_cal(phydev, TX_VCM, efs_valid, SW_M, ++ PAIR_A, PAIR_A, buf); ++ if (ret) ++ goto out; ++ ++out: ++ kfree(buf); ++ return ret; ++} ++ ++static int mt7981_phy_probe(struct phy_device *phydev) ++{ ++ mt7981_phy_finetune(phydev); ++ ++ return mt798x_phy_calibration(phydev); ++} ++ ++static int mt7988_phy_probe(struct phy_device *phydev) ++{ ++ struct device_node *np; ++ void __iomem *boottrap; ++ u32 reg; ++ int port; ++ ++ /* Setup LED polarity according to boottrap's polarity */ ++ np = of_find_compatible_node(NULL, NULL, "mediatek,boottrap"); ++ if (!np) ++ return -ENOENT; ++ boottrap = of_iomap(np, 0); ++ if (!boottrap) ++ return -ENOMEM; ++ reg = readl(boottrap); ++ port = phydev->mdio.addr; ++ if ((port == GPHY_PORT0 && reg & BIT(8)) || ++ (port == GPHY_PORT1 && reg & BIT(9)) || ++ (port == GPHY_PORT2 && reg & BIT(10)) || ++ (port == GPHY_PORT3 && reg & BIT(11))) { ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED0_ENABLE | MTK_PHY_LED0_ON_LINK10 | ++ MTK_PHY_LED0_ON_LINK100 | ++ MTK_PHY_LED0_ON_LINK1000); ++ } else { ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_ON_CTRL, ++ MTK_PHY_LED0_ENABLE | MTK_PHY_LED0_POLARITY | ++ MTK_PHY_LED0_ON_LINK10 | ++ MTK_PHY_LED0_ON_LINK100 | ++ MTK_PHY_LED0_ON_LINK1000); ++ } ++ phy_write_mmd(phydev, MDIO_MMD_VEND2, MTK_PHY_LED0_BLINK_CTRL, ++ MTK_PHY_LED0_1000TX | MTK_PHY_LED0_1000RX | ++ MTK_PHY_LED0_100TX | MTK_PHY_LED0_100RX | ++ MTK_PHY_LED0_10TX | MTK_PHY_LED0_10RX); ++ ++ mt7988_phy_finetune(phydev); ++ ++ return mt798x_phy_calibration(phydev); ++} ++#endif ++ + static struct phy_driver mtk_gephy_driver[] = { + { + PHY_ID_MATCH_EXACT(0x03a29412), +@@ -97,6 +1422,30 @@ static struct phy_driver mtk_gephy_drive + .read_page = mtk_gephy_read_page, + .write_page = mtk_gephy_write_page, + }, ++#ifdef CONFIG_MEDIATEK_GE_PHY_SOC ++ { ++ PHY_ID_MATCH_EXACT(0x03a29461), ++ .name = "MediaTek MT7981 PHY", ++ .probe = mt7981_phy_probe, ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_gephy_read_page, ++ .write_page = mtk_gephy_write_page, ++ }, ++ { ++ PHY_ID_MATCH_EXACT(0x03a29481), ++ .name = "MediaTek MT7988 PHY", ++ .probe = mt7988_phy_probe, ++ .config_intr = genphy_no_config_intr, ++ .handle_interrupt = genphy_handle_interrupt_no_ack, ++ .suspend = genphy_suspend, ++ .resume = genphy_resume, ++ .read_page = mtk_gephy_read_page, ++ .write_page = mtk_gephy_write_page, ++ }, ++#endif + }; + + module_phy_driver(mtk_gephy_driver); +@@ -107,6 +1456,8 @@ static struct mdio_device_id __maybe_unu + }; + + MODULE_DESCRIPTION("MediaTek Gigabit Ethernet PHY driver"); ++MODULE_AUTHOR("Daniel Golle "); ++MODULE_AUTHOR("SkyLake Huang "); + MODULE_AUTHOR("DENG, Qingfang "); + MODULE_LICENSE("GPL"); + diff --git a/target/linux/mediatek/patches-5.15/731-net-phy-hack-mxl-gpy-disable-sgmii-an.patch b/target/linux/mediatek/patches-5.15/731-net-phy-hack-mxl-gpy-disable-sgmii-an.patch new file mode 100644 index 00000000000..2e39ca3c26a --- /dev/null +++ b/target/linux/mediatek/patches-5.15/731-net-phy-hack-mxl-gpy-disable-sgmii-an.patch @@ -0,0 +1,166 @@ +--- a/drivers/net/phy/mxl-gpy.c ++++ b/drivers/net/phy/mxl-gpy.c +@@ -126,6 +126,12 @@ static int gpy_config_init(struct phy_de + if (ret < 0) + return ret; + ++ /* Disable SGMII auto-negotiation */ ++ ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, ++ VSPEC1_SGMII_CTRL_ANEN, 0); ++ if (ret < 0) ++ return ret; ++ + return gpy_led_write(phydev); + } + +@@ -151,65 +157,6 @@ static int gpy_probe(struct phy_device * + return 0; + } + +-static bool gpy_sgmii_need_reaneg(struct phy_device *phydev) +-{ +- int fw_ver, fw_type, fw_minor; +- size_t i; +- +- fw_ver = phy_read(phydev, PHY_FWV); +- if (fw_ver < 0) +- return true; +- +- fw_type = FIELD_GET(PHY_FWV_TYPE_MASK, fw_ver); +- fw_minor = FIELD_GET(PHY_FWV_MINOR_MASK, fw_ver); +- +- for (i = 0; i < ARRAY_SIZE(ver_need_sgmii_reaneg); i++) { +- if (fw_type != ver_need_sgmii_reaneg[i].type) +- continue; +- if (fw_minor < ver_need_sgmii_reaneg[i].minor) +- return true; +- break; +- } +- +- return false; +-} +- +-static bool gpy_2500basex_chk(struct phy_device *phydev) +-{ +- int ret; +- +- ret = phy_read(phydev, PHY_MIISTAT); +- if (ret < 0) { +- phydev_err(phydev, "Error: MDIO register access failed: %d\n", +- ret); +- return false; +- } +- +- if (!(ret & PHY_MIISTAT_LS) || +- FIELD_GET(PHY_MIISTAT_SPD_MASK, ret) != PHY_MIISTAT_SPD_2500) +- return false; +- +- phydev->speed = SPEED_2500; +- phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +- VSPEC1_SGMII_CTRL_ANEN, 0); +- return true; +-} +- +-static bool gpy_sgmii_aneg_en(struct phy_device *phydev) +-{ +- int ret; +- +- ret = phy_read_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL); +- if (ret < 0) { +- phydev_err(phydev, "Error: MMD register access failed: %d\n", +- ret); +- return true; +- } +- +- return (ret & VSPEC1_SGMII_CTRL_ANEN) ? true : false; +-} +- + static int gpy_config_aneg(struct phy_device *phydev) + { + bool changed = false; +@@ -248,53 +195,11 @@ static int gpy_config_aneg(struct phy_de + phydev->interface == PHY_INTERFACE_MODE_INTERNAL) + return 0; + +- /* No need to trigger re-ANEG if link speed is 2.5G or SGMII ANEG is +- * disabled. +- */ +- if (!gpy_sgmii_need_reaneg(phydev) || gpy_2500basex_chk(phydev) || +- !gpy_sgmii_aneg_en(phydev)) +- return 0; +- +- /* There is a design constraint in GPY2xx device where SGMII AN is +- * only triggered when there is change of speed. If, PHY link +- * partner`s speed is still same even after PHY TPI is down and up +- * again, SGMII AN is not triggered and hence no new in-band message +- * from GPY to MAC side SGMII. +- * This could cause an issue during power up, when PHY is up prior to +- * MAC. At this condition, once MAC side SGMII is up, MAC side SGMII +- * wouldn`t receive new in-band message from GPY with correct link +- * status, speed and duplex info. +- * +- * 1) If PHY is already up and TPI link status is still down (such as +- * hard reboot), TPI link status is polled for 4 seconds before +- * retriggerring SGMII AN. +- * 2) If PHY is already up and TPI link status is also up (such as soft +- * reboot), polling of TPI link status is not needed and SGMII AN is +- * immediately retriggered. +- * 3) Other conditions such as PHY is down, speed change etc, skip +- * retriggering SGMII AN. Note: in case of speed change, GPY FW will +- * initiate SGMII AN. +- */ +- +- if (phydev->state != PHY_UP) +- return 0; +- +- ret = phy_read_poll_timeout(phydev, MII_BMSR, ret, ret & BMSR_LSTATUS, +- 20000, 4000000, false); +- if (ret == -ETIMEDOUT) +- return 0; +- else if (ret < 0) +- return ret; +- +- /* Trigger SGMII AN. */ +- return phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +- VSPEC1_SGMII_CTRL_ANRS, VSPEC1_SGMII_CTRL_ANRS); ++ return 0; + } + + static void gpy_update_interface(struct phy_device *phydev) + { +- int ret; +- + /* Interface mode is fixed for USXGMII and integrated PHY */ + if (phydev->interface == PHY_INTERFACE_MODE_USXGMII || + phydev->interface == PHY_INTERFACE_MODE_INTERNAL) +@@ -306,29 +211,11 @@ static void gpy_update_interface(struct + switch (phydev->speed) { + case SPEED_2500: + phydev->interface = PHY_INTERFACE_MODE_2500BASEX; +- ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +- VSPEC1_SGMII_CTRL_ANEN, 0); +- if (ret < 0) +- phydev_err(phydev, +- "Error: Disable of SGMII ANEG failed: %d\n", +- ret); + break; + case SPEED_1000: + case SPEED_100: + case SPEED_10: + phydev->interface = PHY_INTERFACE_MODE_SGMII; +- if (gpy_sgmii_aneg_en(phydev)) +- break; +- /* Enable and restart SGMII ANEG for 10/100/1000Mbps link speed +- * if ANEG is disabled (in 2500-BaseX mode). +- */ +- ret = phy_modify_mmd(phydev, MDIO_MMD_VEND1, VSPEC1_SGMII_CTRL, +- VSPEC1_SGMII_ANEN_ANRS, +- VSPEC1_SGMII_ANEN_ANRS); +- if (ret < 0) +- phydev_err(phydev, +- "Error: Enable of SGMII ANEG failed: %d\n", +- ret); + break; + } + } diff --git a/target/linux/mediatek/patches-5.15/810-i2c-mediatek-add-support-for-MT7981-SoC.patch b/target/linux/mediatek/patches-5.15/810-i2c-mediatek-add-support-for-MT7981-SoC.patch new file mode 100644 index 00000000000..f9ad79c4430 --- /dev/null +++ b/target/linux/mediatek/patches-5.15/810-i2c-mediatek-add-support-for-MT7981-SoC.patch @@ -0,0 +1,74 @@ +From bd4f7dae6a1f2fd65bb2112783c92ffe0839bc77 Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Tue, 28 Feb 2023 23:53:56 +0000 +Subject: [PATCH] i2c: mediatek: add support for MT7981 SoC + +Signed-off-by: Daniel Golle +--- + drivers/i2c/busses/i2c-mt65xx.c | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + +--- a/drivers/i2c/busses/i2c-mt65xx.c ++++ b/drivers/i2c/busses/i2c-mt65xx.c +@@ -202,6 +202,34 @@ static const u16 mt_i2c_regs_v2[] = { + [OFFSET_DCM_EN] = 0xf88, + }; + ++static const u16 mt_i2c_regs_v3[] = { ++ [OFFSET_DATA_PORT] = 0x0, ++ [OFFSET_INTR_MASK] = 0x8, ++ [OFFSET_INTR_STAT] = 0xc, ++ [OFFSET_CONTROL] = 0x10, ++ [OFFSET_TRANSFER_LEN] = 0x14, ++ [OFFSET_TRANSAC_LEN] = 0x18, ++ [OFFSET_DELAY_LEN] = 0x1c, ++ [OFFSET_TIMING] = 0x20, ++ [OFFSET_START] = 0x24, ++ [OFFSET_EXT_CONF] = 0x28, ++ [OFFSET_LTIMING] = 0x2c, ++ [OFFSET_HS] = 0x30, ++ [OFFSET_IO_CONFIG] = 0x34, ++ [OFFSET_FIFO_ADDR_CLR] = 0x38, ++ [OFFSET_SDA_TIMING] = 0x3c, ++ [OFFSET_TRANSFER_LEN_AUX] = 0x44, ++ [OFFSET_CLOCK_DIV] = 0x48, ++ [OFFSET_SOFTRESET] = 0x50, ++ [OFFSET_SCL_MIS_COMP_POINT] = 0x90, ++ [OFFSET_SLAVE_ADDR] = 0x94, ++ [OFFSET_DEBUGSTAT] = 0xe4, ++ [OFFSET_DEBUGCTRL] = 0xe8, ++ [OFFSET_FIFO_STAT] = 0xf4, ++ [OFFSET_FIFO_THRESH] = 0xf8, ++ [OFFSET_DCM_EN] = 0xf88, ++}; ++ + struct mtk_i2c_compatible { + const struct i2c_adapter_quirks *quirks; + const u16 *regs; +@@ -365,6 +393,18 @@ static const struct mtk_i2c_compatible m + .max_dma_support = 32, + }; + ++static const struct mtk_i2c_compatible mt7981_compat = { ++ .regs = mt_i2c_regs_v3, ++ .pmic_i2c = 0, ++ .dcm = 0, ++ .auto_restart = 1, ++ .aux_len_reg = 1, ++ .timing_adjust = 1, ++ .dma_sync = 1, ++ .ltiming_adjust = 1, ++ .max_dma_support = 33 ++}; ++ + static const struct mtk_i2c_compatible mt7986_compat = { + .quirks = &mt7622_i2c_quirks, + .regs = mt_i2c_regs_v1, +@@ -424,6 +464,7 @@ static const struct of_device_id mtk_i2c + { .compatible = "mediatek,mt6577-i2c", .data = &mt6577_compat }, + { .compatible = "mediatek,mt6589-i2c", .data = &mt6589_compat }, + { .compatible = "mediatek,mt7622-i2c", .data = &mt7622_compat }, ++ { .compatible = "mediatek,mt7981-i2c", .data = &mt7981_compat }, + { .compatible = "mediatek,mt7986-i2c", .data = &mt7986_compat }, + { .compatible = "mediatek,mt8173-i2c", .data = &mt8173_compat }, + { .compatible = "mediatek,mt8183-i2c", .data = &mt8183_compat }, diff --git a/target/linux/mediatek/patches-5.15/811-pwm-mediatek-Add-support-for-MT7981.patch b/target/linux/mediatek/patches-5.15/811-pwm-mediatek-Add-support-for-MT7981.patch new file mode 100644 index 00000000000..ab465a7582f --- /dev/null +++ b/target/linux/mediatek/patches-5.15/811-pwm-mediatek-Add-support-for-MT7981.patch @@ -0,0 +1,212 @@ +From 947b535ebfe161e1725f1030a09de10d1460371c Mon Sep 17 00:00:00 2001 +From: Daniel Golle +Date: Mon, 23 Jan 2023 20:47:34 +0000 +Subject: [PATCH] pwm: mediatek: Add support for MT7981 + +The PWM unit on MT7981 uses different register offsets than previous +MediaTek PWM units. Add support for these new offsets and add support +for PWM on MT7981 which has 3 PWM channels, one of them is typically +used for a temperature controlled fan. + +Signed-off-by: Daniel Golle +--- + drivers/pwm/pwm-mediatek.c | 54 ++++++++++++++++++++++++++++++++------ + 1 file changed, 46 insertions(+), 8 deletions(-) + +--- a/drivers/pwm/pwm-mediatek.c ++++ b/drivers/pwm/pwm-mediatek.c +@@ -34,10 +34,14 @@ + + #define PWM_CLK_DIV_MAX 7 + ++#define REG_V1 1 ++#define REG_V2 2 ++ + struct pwm_mediatek_of_data { + unsigned int num_pwms; + bool pwm45_fixup; + bool has_ck_26m_sel; ++ u8 reg_ver; + }; + + /** +@@ -59,10 +63,14 @@ struct pwm_mediatek_chip { + const struct pwm_mediatek_of_data *soc; + }; + +-static const unsigned int pwm_mediatek_reg_offset[] = { ++static const unsigned int mtk_pwm_reg_offset_v1[] = { + 0x0010, 0x0050, 0x0090, 0x00d0, 0x0110, 0x0150, 0x0190, 0x0220 + }; + ++static const unsigned int mtk_pwm_reg_offset_v2[] = { ++ 0x0080, 0x00c0, 0x0100, 0x0140, 0x0180, 0x1c0, 0x200, 0x0240 ++}; ++ + static inline struct pwm_mediatek_chip * + to_pwm_mediatek_chip(struct pwm_chip *chip) + { +@@ -111,7 +119,19 @@ static inline void pwm_mediatek_writel(s + unsigned int num, unsigned int offset, + u32 value) + { +- writel(value, chip->regs + pwm_mediatek_reg_offset[num] + offset); ++ u32 pwm_offset; ++ ++ switch (chip->soc->reg_ver) { ++ case REG_V2: ++ pwm_offset = mtk_pwm_reg_offset_v2[num]; ++ break; ++ ++ case REG_V1: ++ default: ++ pwm_offset = mtk_pwm_reg_offset_v1[num]; ++ } ++ ++ writel(value, chip->regs + pwm_offset + offset); + } + + static int pwm_mediatek_config(struct pwm_chip *chip, struct pwm_device *pwm, +@@ -146,7 +166,7 @@ static int pwm_mediatek_config(struct pw + + if (clkdiv > PWM_CLK_DIV_MAX) { + pwm_mediatek_clk_disable(chip, pwm); +- dev_err(chip->dev, "period %d not supported\n", period_ns); ++ dev_err(chip->dev, "period of %d ns not supported\n", period_ns); + return -EINVAL; + } + +@@ -221,24 +241,20 @@ static int pwm_mediatek_probe(struct pla + if (IS_ERR(pc->regs)) + return PTR_ERR(pc->regs); + +- pc->clk_pwms = devm_kcalloc(&pdev->dev, pc->soc->num_pwms, ++ pc->clk_pwms = devm_kmalloc_array(&pdev->dev, pc->soc->num_pwms, + sizeof(*pc->clk_pwms), GFP_KERNEL); + if (!pc->clk_pwms) + return -ENOMEM; + + pc->clk_top = devm_clk_get(&pdev->dev, "top"); +- if (IS_ERR(pc->clk_top)) { +- dev_err(&pdev->dev, "clock: top fail: %ld\n", +- PTR_ERR(pc->clk_top)); +- return PTR_ERR(pc->clk_top); +- } ++ if (IS_ERR(pc->clk_top)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_top), ++ "Failed to get top clock\n"); + + pc->clk_main = devm_clk_get(&pdev->dev, "main"); +- if (IS_ERR(pc->clk_main)) { +- dev_err(&pdev->dev, "clock: main fail: %ld\n", +- PTR_ERR(pc->clk_main)); +- return PTR_ERR(pc->clk_main); +- } ++ if (IS_ERR(pc->clk_main)) ++ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_main), ++ "Failed to get main clock\n"); + + for (i = 0; i < pc->soc->num_pwms; i++) { + char name[8]; +@@ -246,11 +262,9 @@ static int pwm_mediatek_probe(struct pla + snprintf(name, sizeof(name), "pwm%d", i + 1); + + pc->clk_pwms[i] = devm_clk_get(&pdev->dev, name); +- if (IS_ERR(pc->clk_pwms[i])) { +- dev_err(&pdev->dev, "clock: %s fail: %ld\n", +- name, PTR_ERR(pc->clk_pwms[i])); +- return PTR_ERR(pc->clk_pwms[i]); +- } ++ if (IS_ERR(pc->clk_pwms[i])) ++ return dev_err_probe(&pdev->dev, PTR_ERR(pc->clk_pwms[i]), ++ "Failed to get %s clock\n", name); + } + + pc->chip.dev = &pdev->dev; +@@ -258,10 +272,8 @@ static int pwm_mediatek_probe(struct pla + pc->chip.npwm = pc->soc->num_pwms; + + ret = devm_pwmchip_add(&pdev->dev, &pc->chip); +- if (ret < 0) { +- dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret); +- return ret; +- } ++ if (ret < 0) ++ return dev_err_probe(&pdev->dev, ret, "pwmchip_add() failed\n"); + + return 0; + } +@@ -270,48 +282,63 @@ static const struct pwm_mediatek_of_data + .num_pwms = 8, + .pwm45_fixup = false, + .has_ck_26m_sel = false, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt7622_pwm_data = { + .num_pwms = 6, + .pwm45_fixup = false, + .has_ck_26m_sel = true, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt7623_pwm_data = { + .num_pwms = 5, + .pwm45_fixup = true, + .has_ck_26m_sel = false, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt7628_pwm_data = { + .num_pwms = 4, + .pwm45_fixup = true, + .has_ck_26m_sel = false, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt7629_pwm_data = { + .num_pwms = 1, + .pwm45_fixup = false, + .has_ck_26m_sel = false, ++ .reg_ver = REG_V1, + }; + +-static const struct pwm_mediatek_of_data mt8183_pwm_data = { +- .num_pwms = 4, ++static const struct pwm_mediatek_of_data mt7981_pwm_data = { ++ .num_pwms = 3, + .pwm45_fixup = false, + .has_ck_26m_sel = true, ++ .reg_ver = REG_V2, + }; + + static const struct pwm_mediatek_of_data mt7986_pwm_data = { + .num_pwms = 2, + .pwm45_fixup = false, + .has_ck_26m_sel = true, ++ .reg_ver = REG_V1, ++}; ++ ++static const struct pwm_mediatek_of_data mt8183_pwm_data = { ++ .num_pwms = 4, ++ .pwm45_fixup = false, ++ .has_ck_26m_sel = true, ++ .reg_ver = REG_V1, + }; + + static const struct pwm_mediatek_of_data mt8516_pwm_data = { + .num_pwms = 5, + .pwm45_fixup = false, + .has_ck_26m_sel = true, ++ .reg_ver = REG_V1, + }; + + static const struct of_device_id pwm_mediatek_of_match[] = { +@@ -320,6 +347,7 @@ static const struct of_device_id pwm_med + { .compatible = "mediatek,mt7623-pwm", .data = &mt7623_pwm_data }, + { .compatible = "mediatek,mt7628-pwm", .data = &mt7628_pwm_data }, + { .compatible = "mediatek,mt7629-pwm", .data = &mt7629_pwm_data }, ++ { .compatible = "mediatek,mt7981-pwm", .data = &mt7981_pwm_data }, + { .compatible = "mediatek,mt7986-pwm", .data = &mt7986_pwm_data }, + { .compatible = "mediatek,mt8183-pwm", .data = &mt8183_pwm_data }, + { .compatible = "mediatek,mt8516-pwm", .data = &mt8516_pwm_data }, diff --git a/target/linux/mediatek/patches-5.15/901-arm-add-cmdline-override.patch b/target/linux/mediatek/patches-5.15/901-arm-add-cmdline-override.patch index acbb190030c..6016128bd4a 100644 --- a/target/linux/mediatek/patches-5.15/901-arm-add-cmdline-override.patch +++ b/target/linux/mediatek/patches-5.15/901-arm-add-cmdline-override.patch @@ -35,3 +35,20 @@ /* * CONFIG_CMDLINE is meant to be a default in case nothing else * managed to set the command line, unless CONFIG_CMDLINE_FORCE +--- a/arch/arm64/Kconfig ++++ b/arch/arm64/Kconfig +@@ -1942,6 +1942,14 @@ config CMDLINE_FORCE + + endchoice + ++config CMDLINE_OVERRIDE ++ bool "Use alternative cmdline from device tree" ++ help ++ Some bootloaders may have uneditable bootargs. While CMDLINE_FORCE can ++ be used, this is not a good option for kernels that are shared across ++ devices. This setting enables using "chosen/cmdline-override" as the ++ cmdline if it exists in the device tree. ++ + config EFI_STUB + bool + diff --git a/target/linux/mediatek/patches-5.15/930-spi-mt65xx-enable-sel-clk.patch b/target/linux/mediatek/patches-5.15/930-spi-mt65xx-enable-sel-clk.patch index 904f2ef3a2f..ebb6c060b54 100644 --- a/target/linux/mediatek/patches-5.15/930-spi-mt65xx-enable-sel-clk.patch +++ b/target/linux/mediatek/patches-5.15/930-spi-mt65xx-enable-sel-clk.patch @@ -1,6 +1,6 @@ --- a/drivers/spi/spi-mt65xx.c +++ b/drivers/spi/spi-mt65xx.c -@@ -1223,10 +1223,16 @@ static int mtk_spi_probe(struct platform +@@ -1231,10 +1231,16 @@ static int mtk_spi_probe(struct platform goto err_disable_spi_hclk; } @@ -18,7 +18,7 @@ } mdata->spi_clk_hz = clk_get_rate(mdata->spi_clk); -@@ -1277,6 +1283,8 @@ static int mtk_spi_probe(struct platform +@@ -1285,6 +1291,8 @@ static int mtk_spi_probe(struct platform err_disable_runtime_pm: pm_runtime_disable(&pdev->dev); diff --git a/target/linux/ramips/dts/mt7620n_zyxel_keenetic-lite-iii-a.dts b/target/linux/ramips/dts/mt7620n_zyxel_keenetic-lite-iii-a.dts new file mode 100644 index 00000000000..0d1c4696d79 --- /dev/null +++ b/target/linux/ramips/dts/mt7620n_zyxel_keenetic-lite-iii-a.dts @@ -0,0 +1,144 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7620n.dtsi" + +#include +#include +#include + +/ { + compatible = "zyxel,keenetic-lite-iii-a", "ralink,mt7620n-soc"; + model = "ZyXEL Keenetic Lite III (rev. A)"; + + aliases { + led-boot = &led_wan; + led-failsafe = &led_wan; + led-running = &led_wan; + led-upgrade = &led_wan; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio0 1 GPIO_ACTIVE_LOW>; // #GPIO1 + linux,code = ; + }; + + wps { + label = "wps"; + gpios = <&gpio0 2 GPIO_ACTIVE_LOW>; // #GPIO2 + linux,code = ; + }; + + sw0 { + label = "sw0"; + gpios = <&gpio0 17 GPIO_ACTIVE_LOW>; // #GPIO17 + linux,code = ; + }; + + sw1 { + label = "sw1"; + gpios = <&gpio0 20 GPIO_ACTIVE_LOW>; // #GPIO20 + linux,code = ; + }; + + sw2 { + label = "sw2"; + gpios = <&gpio0 21 GPIO_ACTIVE_LOW>; // #GPIO21 + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_wan: wan { + label = "green:wan"; + color = ; + function = LED_FUNCTION_STATUS; + gpios = <&gpio1 14 GPIO_ACTIVE_LOW>; // #GPIO38 + }; + + wifi { + label = "green:wifi"; + color = ; + function = LED_FUNCTION_WLAN; + gpios = <&gpio3 0 GPIO_ACTIVE_HIGH>; // #GPIO72 + }; + }; + +}; + +&gpio0 { + status = "okay"; +}; + +&gpio1 { + status = "okay"; +}; + +&gpio3 { + status = "okay"; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <48000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "U-Boot"; + reg = <0x0 0x30000>; + read-only; + }; + + partition@30000 { + label = "U-Config"; + reg = <0x30000 0x10000>; + }; + + factory: partition@40000 { + label = "RF-EEPROM"; + reg = <0x40000 0x10000>; + read-only; + }; + + partition@50000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x50000 0x7a0000>; + }; + + partition@7f0000 { + label = "Config"; + reg = <0x7f0000 0x10000>; + }; + + partition@0_1 { + label = "Full"; + reg = <0x0 0x800000>; + }; + }; + }; +}; + +&state_default { + gpio { + groups = "i2c"; + function = "gpio"; + }; +}; + +&wmac { + ralink,mtd-eeprom = <&factory 0x0>; +}; diff --git a/target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi b/target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi index 54d7908e406..fe374bfa6b0 100644 --- a/target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi +++ b/target/linux/ramips/dts/mt7621_h3c_tx180x.dtsi @@ -138,10 +138,6 @@ }; }; -&pcie2 { - status = "disabled"; -}; - &state_default { gpio { groups = "jtag"; diff --git a/target/linux/ramips/dts/mt7621_haier-sim_wr1800k.dtsi b/target/linux/ramips/dts/mt7621_haier-sim_wr1800k.dtsi index a272d1ad755..4dd6f3b0ae5 100644 --- a/target/linux/ramips/dts/mt7621_haier-sim_wr1800k.dtsi +++ b/target/linux/ramips/dts/mt7621_haier-sim_wr1800k.dtsi @@ -152,10 +152,6 @@ }; }; -&pcie2 { - status = "disabled"; -}; - &state_default { gpio { groups = "i2c", "uart3", "wdt"; diff --git a/target/linux/ramips/dts/mt7621_mercusys_mr70x-v1.dts b/target/linux/ramips/dts/mt7621_mercusys_mr70x-v1.dts new file mode 100644 index 00000000000..7eb94ba9cfc --- /dev/null +++ b/target/linux/ramips/dts/mt7621_mercusys_mr70x-v1.dts @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later OR MIT + +#include "mt7621.dtsi" + +#include +#include + +/ { + compatible = "mercusys,mr70x-v1", "mediatek,mt7621-soc"; + model = "Mercusys MR70X v1"; + + aliases { + led-boot = &led_power_green; + led-failsafe = &led_power_orange; + led-running = &led_power_green; + led-upgrade = &led_power_orange; + label-mac-device = &gmac0; + }; + + chosen { + bootargs = "console=ttyS0,115200"; + }; + + keys { + compatible = "gpio-keys"; + + reset { + label = "reset"; + gpios = <&gpio 8 GPIO_ACTIVE_LOW>; + linux,code = ; + }; + }; + + leds { + compatible = "gpio-leds"; + + led_power_green: power-green { + label = "green:power"; + gpios = <&gpio 4 GPIO_ACTIVE_LOW>; + }; + + led_power_orange: power-orange { + label = "orange:power"; + gpios = <&gpio 3 GPIO_ACTIVE_LOW>; + }; + }; +}; + +&spi0 { + status = "okay"; + + flash@0 { + compatible = "jedec,spi-nor"; + reg = <0>; + spi-max-frequency = <80000000>; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + partition@0 { + label = "u-boot"; + reg = <0x000000 0x040000>; + read-only; + }; + + partition@40000 { + compatible = "denx,uimage"; + label = "firmware"; + reg = <0x040000 0xf60000>; + }; + + config: partition@fa0000 { + label = "config"; + reg = <0xfa0000 0x010000>; + read-only; + }; + + partition@fb0000 { + label = "tplink"; + reg = <0xfb0000 0x040000>; + read-only; + }; + + radio: partition@ff0000 { + label = "radio"; + reg = <0xff0000 0x010000>; + read-only; + }; + }; + }; +}; + +&pcie { + status = "okay"; +}; + +&pcie1 { + wifi@0,0 { + compatible = "mediatek,mt76"; + reg = <0x0000 0 0 0 0>; + mediatek,mtd-eeprom = <&radio 0x0>; + nvmem-cells = <&macaddr_config_8>; + nvmem-cell-names = "mac-address"; + mediatek,disable-radar-background; + }; +}; + +&gmac0 { + nvmem-cells = <&macaddr_config_8>; + nvmem-cell-names = "mac-address"; +}; + +&gmac1 { + status = "okay"; + label = "wan"; + phy-handle = <ðphy0>; + + nvmem-cells = <&macaddr_config_8>; + nvmem-cell-names = "mac-address"; + mac-address-increment = <1>; +}; + +&mdio { + ethphy0: ethernet-phy@0 { + reg = <0>; + }; +}; + +&switch0 { + ports { + port@1 { + status = "okay"; + label = "lan1"; + }; + + port@2 { + status = "okay"; + label = "lan2"; + }; + + port@3 { + status = "okay"; + label = "lan3"; + }; + }; +}; + +&state_default { + gpio { + groups = "i2c", "uart3"; + function = "gpio"; + }; +}; + +&config { + compatible = "nvmem-cells"; + #address-cells = <1>; + #size-cells = <1>; + + macaddr_config_8: macaddr@8 { + reg = <0x8 0x6>; + }; +}; diff --git a/target/linux/ramips/dts/mt7621_netgear_wax202.dts b/target/linux/ramips/dts/mt7621_netgear_wax202.dts index 02f540d7431..fbe17ef0d38 100644 --- a/target/linux/ramips/dts/mt7621_netgear_wax202.dts +++ b/target/linux/ramips/dts/mt7621_netgear_wax202.dts @@ -217,10 +217,6 @@ }; }; -&pcie2 { - status = "disabled"; -}; - &state_default { gpio { groups = "uart3", "uart2", "jtag", "wdt"; diff --git a/target/linux/ramips/dts/mt7621_tplink_eap615-wall-v1.dts b/target/linux/ramips/dts/mt7621_tplink_eap615-wall-v1.dts index 36809d48ff2..58e4bbf7574 100644 --- a/target/linux/ramips/dts/mt7621_tplink_eap615-wall-v1.dts +++ b/target/linux/ramips/dts/mt7621_tplink_eap615-wall-v1.dts @@ -148,10 +148,6 @@ }; }; -&pcie2 { - status = "disabled"; -}; - &gmac0 { nvmem-cells = <&macaddr_info_8>; nvmem-cell-names = "mac-address"; diff --git a/target/linux/ramips/image/mt7620.mk b/target/linux/ramips/image/mt7620.mk index 6e528a930b5..de84b232c13 100644 --- a/target/linux/ramips/image/mt7620.mk +++ b/target/linux/ramips/image/mt7620.mk @@ -1471,6 +1471,18 @@ define Device/zte_q7 endef TARGET_DEVICES += zte_q7 +define Device/zyxel_keenetic-lite-iii-a + SOC := mt7620n + IMAGE_SIZE := 7872k + DEVICE_VENDOR := ZyXEL + DEVICE_MODEL := Keenetic Lite III + DEVICE_VARIANT := A + IMAGES += factory.bin + IMAGE/factory.bin := $$(sysupgrade_bin) | pad-to 64k | check-size | \ + zyimage -d 2102018 -v "ZyXEL Keenetic Lite III" +endef +TARGET_DEVICES += zyxel_keenetic-lite-iii-a + define Device/zyxel_keenetic-omni SOC := mt7620n IMAGE_SIZE := 7872k diff --git a/target/linux/ramips/image/mt7621.mk b/target/linux/ramips/image/mt7621.mk index 5d2a4844269..dfe9d346f10 100644 --- a/target/linux/ramips/image/mt7621.mk +++ b/target/linux/ramips/image/mt7621.mk @@ -1496,6 +1496,19 @@ define Device/mediatek_mt7621-eval-board endef TARGET_DEVICES += mediatek_mt7621-eval-board +define Device/mercusys_mr70x-v1 + $(Device/dsa-migration) + $(Device/tplink-safeloader) + DEVICE_VENDOR := Mercusys + DEVICE_MODEL := MR70X + DEVICE_VARIANT := v1 + DEVICE_PACKAGES := kmod-mt7915-firmware -uboot-envtools + TPLINK_BOARD_ID := MR70X + KERNEL := $(KERNEL_DTB) | uImage lzma + IMAGE_SIZE := 15744k +endef +TARGET_DEVICES += mercusys_mr70x-v1 + define Device/MikroTik $(Device/dsa-migration) DEVICE_VENDOR := MikroTik diff --git a/target/linux/ramips/mt7620/base-files/etc/board.d/01_leds b/target/linux/ramips/mt7620/base-files/etc/board.d/01_leds index 36e3045a38a..2deda0861a9 100644 --- a/target/linux/ramips/mt7620/base-files/etc/board.d/01_leds +++ b/target/linux/ramips/mt7620/base-files/etc/board.d/01_leds @@ -55,6 +55,7 @@ planex,mzk-ex300np|\ zbtlink,zbt-we826-16m|\ zbtlink,zbt-we826-32m|\ zbtlink,zbt-wr8305rt|\ +zyxel,keenetic-lite-iii-a|\ zyxel,keenetic-omni|\ zyxel,keenetic-omni-ii|\ zyxel,keenetic-viva) diff --git a/target/linux/ramips/mt7620/base-files/etc/board.d/02_network b/target/linux/ramips/mt7620/base-files/etc/board.d/02_network index 992d8a9c40c..787e123e31b 100644 --- a/target/linux/ramips/mt7620/base-files/etc/board.d/02_network +++ b/target/linux/ramips/mt7620/base-files/etc/board.d/02_network @@ -28,6 +28,7 @@ ramips_setup_interfaces() zbtlink,zbt-we826-32m|\ zbtlink,zbt-we826-e|\ zbtlink,zbt-wr8305rt|\ + zyxel,keenetic-lite-iii-a|\ zyxel,keenetic-omni) ucidef_add_switch "switch0" \ "0:lan" "1:lan" "2:lan" "3:lan" "4:wan" "6@eth0" @@ -404,6 +405,11 @@ ramips_setup_macs() zbtlink,zbt-we1026-5g-16m) label_mac=$(mtd_get_mac_binary factory 0x4) ;; + zyxel,keenetic-lite-iii-a) + lan_mac=$(mtd_get_mac_binary RF-EEPROM 0x4) + wan_mac=$(mtd_get_mac_binary RF-EEPROM 0x28) + label_mac=$wan_mac + ;; zyxel,keenetic-omni|\ zyxel,keenetic-omni-ii|\ zyxel,keenetic-viva) diff --git a/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/10_fix_wifi_mac b/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/10_fix_wifi_mac index bf6c55a8fe0..b3de1f39cf0 100644 --- a/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/10_fix_wifi_mac +++ b/target/linux/ramips/mt7621/base-files/etc/hotplug.d/ieee80211/10_fix_wifi_mac @@ -120,6 +120,11 @@ case "$board" in [ "$PHYNBR" = "0" ] && macaddr_add $hw_mac_addr 2 > /sys${DEVPATH}/macaddress [ "$PHYNBR" = "1" ] && macaddr_add $hw_mac_addr 3 > /sys${DEVPATH}/macaddress ;; + mercusys,mr70x-v1|\ + tplink,archer-ax23-v1) + hw_mac_addr="$(mtd_get_mac_binary config 0x8)" + [ "$PHYNBR" = "1" ] && macaddr_add "$hw_mac_addr" -1 > "/sys${DEVPATH}/macaddress" + ;; mts,wg430223) hw_mac_addr=$(macaddr_add $(mtd_get_mac_encrypted_arcadyan "board_data") 1) [ "$PHYNBR" = "0" ] && echo -n "$hw_mac_addr" > /sys${DEVPATH}/macaddress @@ -147,9 +152,9 @@ case "$board" in hw_mac_addr="$(mtd_get_mac_binary factory 0x4)" [ "$PHYNBR" = "1" ] && macaddr_add $hw_mac_addr "0x100000" > /sys${DEVPATH}/macaddress ;; - tplink,archer-ax23-v1) - hw_mac_addr="$(mtd_get_mac_binary config 0x8)" - [ "$PHYNBR" = "1" ] && macaddr_add "$hw_mac_addr" -1 > "/sys${DEVPATH}/macaddress" + totolink,x5000r) + hw_mac_addr="$(mtd_get_mac_binary factory 0x4)" + [ "$PHYNBR" = "1" ] && macaddr_add $hw_mac_addr 4 > /sys${DEVPATH}/macaddress ;; tplink,eap615-wall-v1) hw_mac_addr="$(mtd_get_mac_binary product-info 0x8)" diff --git a/target/linux/ramips/patches-5.15/700-net-ethernet-mediatek-support-net-labels.patch b/target/linux/ramips/patches-5.15/700-net-ethernet-mediatek-support-net-labels.patch index 29aba20e97d..c3e0a342a19 100644 --- a/target/linux/ramips/patches-5.15/700-net-ethernet-mediatek-support-net-labels.patch +++ b/target/linux/ramips/patches-5.15/700-net-ethernet-mediatek-support-net-labels.patch @@ -14,7 +14,7 @@ Signed-off-by: René van Dorst --- a/drivers/net/ethernet/mediatek/mtk_eth_soc.c +++ b/drivers/net/ethernet/mediatek/mtk_eth_soc.c -@@ -4227,6 +4227,7 @@ static const struct net_device_ops mtk_n +@@ -4259,6 +4259,7 @@ static const struct net_device_ops mtk_n static int mtk_add_mac(struct mtk_eth *eth, struct device_node *np) { @@ -22,7 +22,7 @@ Signed-off-by: René van Dorst const __be32 *_id = of_get_property(np, "reg", NULL); phy_interface_t phy_mode; struct phylink *phylink; -@@ -4355,6 +4356,9 @@ static int mtk_add_mac(struct mtk_eth *e +@@ -4387,6 +4388,9 @@ static int mtk_add_mac(struct mtk_eth *e register_netdevice_notifier(&mac->device_notifier); } diff --git a/target/linux/ramips/patches-5.15/720-Revert-net-phy-simplify-phy_link_change-arguments.patch b/target/linux/ramips/patches-5.15/720-Revert-net-phy-simplify-phy_link_change-arguments.patch index d53a8c2d792..9283900edf8 100644 --- a/target/linux/ramips/patches-5.15/720-Revert-net-phy-simplify-phy_link_change-arguments.patch +++ b/target/linux/ramips/patches-5.15/720-Revert-net-phy-simplify-phy_link_change-arguments.patch @@ -95,7 +95,7 @@ still required by target/linux/ramips/files/drivers/net/ethernet/ralink/mdio.c phydev->mii_ts->link_state(phydev->mii_ts, phydev); --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c -@@ -1323,7 +1323,8 @@ void phylink_destroy(struct phylink *pl) +@@ -1322,7 +1322,8 @@ void phylink_destroy(struct phylink *pl) } EXPORT_SYMBOL_GPL(phylink_destroy); diff --git a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch index 93f0fe5cf47..e562dd8396c 100644 --- a/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch +++ b/target/linux/realtek/patches-5.15/008-5.17-watchdog-add-realtek-otto-watchdog-timer.patch @@ -32,7 +32,7 @@ Signed-off-by: Guenter Roeck --- a/MAINTAINERS +++ b/MAINTAINERS -@@ -15891,6 +15891,13 @@ S: Maintained +@@ -15899,6 +15899,13 @@ S: Maintained F: include/sound/rt*.h F: sound/soc/codecs/rt* diff --git a/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch b/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch index 71724034d4c..8f400012635 100644 --- a/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch +++ b/target/linux/realtek/patches-5.15/700-net-dsa-add-support-for-rtl838x-switch.patch @@ -22,7 +22,7 @@ Submitted-by: John Crispin --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig -@@ -60,6 +60,8 @@ source "drivers/net/dsa/sja1105/Kconfig" +@@ -61,6 +61,8 @@ source "drivers/net/dsa/sja1105/Kconfig" source "drivers/net/dsa/xrs700x/Kconfig" diff --git a/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch b/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch index 1cea91e0d13..591f0cea13a 100644 --- a/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch +++ b/target/linux/realtek/patches-5.15/704-drivers-net-phy-eee-support-for-rtl838x.patch @@ -21,7 +21,7 @@ Submitted-by: John Crispin --- a/drivers/net/phy/phylink.c +++ b/drivers/net/phy/phylink.c -@@ -1943,6 +1943,11 @@ int phylink_ethtool_ksettings_set(struct +@@ -1942,6 +1942,11 @@ int phylink_ethtool_ksettings_set(struct * the presence of a PHY, this should not be changed as that * should be determined from the media side advertisement. */ @@ -33,7 +33,7 @@ Submitted-by: John Crispin return phy_ethtool_ksettings_set(pl->phydev, kset); } -@@ -2246,8 +2251,11 @@ int phylink_ethtool_get_eee(struct phyli +@@ -2245,8 +2250,11 @@ int phylink_ethtool_get_eee(struct phyli ASSERT_RTNL(); @@ -46,7 +46,7 @@ Submitted-by: John Crispin return ret; } -@@ -2264,8 +2272,11 @@ int phylink_ethtool_set_eee(struct phyli +@@ -2263,8 +2271,11 @@ int phylink_ethtool_set_eee(struct phyli ASSERT_RTNL(); diff --git a/tools/automake/patches/200-other-V-values-for-verbosity.patch b/tools/automake/patches/200-other-V-values-for-verbosity.patch new file mode 100644 index 00000000000..1ea9d38b0ed --- /dev/null +++ b/tools/automake/patches/200-other-V-values-for-verbosity.patch @@ -0,0 +1,59 @@ +From: Bogdan Drozdowski +Date: Sat, 31 Dec 2022 20:17:35 +0100 +Subject: [PATCH] Allow other V values for verbosity + +--- + m4/silent.m4 | 2 +- + t/silent-gen.sh | 24 ++++++++++++++++++++++++ + 2 files changed, 25 insertions(+), 1 deletion(-) + +--- a/m4/silent.m4 ++++ b/m4/silent.m4 +@@ -43,7 +43,7 @@ else + fi]) + if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. +- AM_V='$(V)' ++ AM_V='$(shell if ( test "x$(V)" = "x0" ); then echo 0; elif ( test "x$(V)" = "x" ); then echo $(AM_DEFAULT_VERBOSITY); else echo 1; fi)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' + else + AM_V=$AM_DEFAULT_VERBOSITY +--- a/t/silent-gen.sh ++++ b/t/silent-gen.sh +@@ -54,6 +54,18 @@ grep 'cp ' stdout + grep 'echo ' stdout + + $MAKE clean ++run_make -O V=99 ++grep 'GEN ' stdout && exit 1 ++grep 'cp ' stdout ++grep 'echo ' stdout ++ ++$MAKE clean ++run_make -O V=vvv ++grep 'GEN ' stdout && exit 1 ++grep 'cp ' stdout ++grep 'echo ' stdout ++ ++$MAKE clean + run_make -O V=0 + grep 'GEN .*foo' stdout + grep 'cp ' stdout && exit 1 +@@ -78,5 +90,17 @@ run_make -O V=1 + grep 'GEN ' stdout && exit 1 + grep 'cp ' stdout + grep 'echo ' stdout ++ ++$MAKE clean ++run_make -O V=99 ++grep 'GEN ' stdout && exit 1 ++grep 'cp ' stdout ++grep 'echo ' stdout ++ ++$MAKE clean ++run_make -O V=v ++grep 'GEN ' stdout && exit 1 ++grep 'cp ' stdout ++grep 'echo ' stdout + + : diff --git a/tools/libtool/Makefile b/tools/libtool/Makefile index 66261422949..b4f3ecc36ba 100644 --- a/tools/libtool/Makefile +++ b/tools/libtool/Makefile @@ -23,7 +23,7 @@ HOST_CONFIGURE_VARS += \ define Host/Prepare $(call Host/Prepare/Default) - (cd $(STAGING_DIR_HOST)/share/aclocal/ && rm -f libtool.m4 ltdl.m4 lt~obsolete.m4 ltoptions.m4 ltsugar.m4 ltversion.m4) + $(call Host/Uninstall) $(if $(QUILT),,(cd $(HOST_BUILD_DIR); touch README-release; $(AM_TOOL_PATHS) ./bootstrap --skip-git --skip-po --force)) endef @@ -33,18 +33,13 @@ define Host/Configure endef define Host/Install - $(MAKE) -C $(HOST_BUILD_DIR) install + $(call Host/Compile/Default,install) $(SED) 's,\(hardcode_into_libs\)=yes,\1=no,g' $(STAGING_DIR_HOST)/bin/libtool - $(CP) $(STAGING_DIR_HOST)/bin/libtool $(STAGING_DIR_HOST)/bin/libtool-ucxx - $(SED) 's,-lstdc++,-luClibc++,g' $(STAGING_DIR_HOST)/bin/libtool-ucxx endef define Host/Uninstall -$(call Host/Compile/Default,uninstall) -endef - -define Host/Clean - $(call Host/Clean/Default) + (cd $(STAGING_DIR_HOST)/share/aclocal/ && rm -f libtool.m4 ltdl.m4 lt~obsolete.m4 ltoptions.m4 ltsugar.m4 ltversion.m4) endef $(eval $(call HostBuild))