]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
Fixes for 5.15
authorSasha Levin <sashal@kernel.org>
Sat, 26 Nov 2022 14:00:49 +0000 (09:00 -0500)
committerSasha Levin <sashal@kernel.org>
Sat, 26 Nov 2022 14:00:49 +0000 (09:00 -0500)
Signed-off-by: Sasha Levin <sashal@kernel.org>
55 files changed:
queue-5.15/alsa-usb-audio-add-quirk-to-fix-hamedal-c20-disconne.patch [new file with mode: 0644]
queue-5.15/arm64-syscall-include-asm-ptrace.h-in-syscall_wrappe.patch [new file with mode: 0644]
queue-5.15/asoc-fsl_asrc-fsl_esai-fsl_sai-allow-config_pm-n.patch [new file with mode: 0644]
queue-5.15/asoc-fsl_sai-use-local-device-pointer.patch [new file with mode: 0644]
queue-5.15/ata-libata-core-do-not-issue-non-internal-commands-o.patch [new file with mode: 0644]
queue-5.15/ata-libata-scsi-simplify-__ata_scsi_queuecmd.patch [new file with mode: 0644]
queue-5.15/audit-fix-undefined-behavior-in-bit-shift-for-audit_.patch [new file with mode: 0644]
queue-5.15/binder-validate-alloc-mm-in-mmap-handler.patch [new file with mode: 0644]
queue-5.15/block-bfq-fix-null-pointer-dereference-in-bfq_bio_bf.patch [new file with mode: 0644]
queue-5.15/ceph-avoid-putting-the-realm-twice-when-decoding-sna.patch [new file with mode: 0644]
queue-5.15/ceph-do-not-update-snapshot-context-when-there-is-no.patch [new file with mode: 0644]
queue-5.15/ceph-fix-null-pointer-dereference-for-req-r_session.patch [new file with mode: 0644]
queue-5.15/ceph-use-kcalloc-for-allocating-multiple-elements.patch [new file with mode: 0644]
queue-5.15/cifs-fix-connections-leak-when-tlink-setup-failed.patch [new file with mode: 0644]
queue-5.15/cifs-introduce-new-helper-for-cifs_reconnect.patch [new file with mode: 0644]
queue-5.15/cifs-split-out-dfs-code-from-cifs_reconnect.patch [new file with mode: 0644]
queue-5.15/cifs-support-nested-dfs-links-over-reconnect.patch [new file with mode: 0644]
queue-5.15/drm-display-don-t-assume-dual-mode-adaptors-support-.patch [new file with mode: 0644]
queue-5.15/drm-panel-orientation-quirks-add-quirk-for-acer-swit.patch [new file with mode: 0644]
queue-5.15/iio-ms5611-simplify-io-callback-parameters.patch [new file with mode: 0644]
queue-5.15/iio-pressure-ms5611-fixed-value-compensation-bug.patch [new file with mode: 0644]
queue-5.15/mips-pic32-treat-port-as-signed-integer.patch [new file with mode: 0644]
queue-5.15/nvme-add-a-bogus-subsystem-nqn-quirk-for-micron-mtfd.patch [new file with mode: 0644]
queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-micron-nitro.patch [new file with mode: 0644]
queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-netac-nv7000.patch [new file with mode: 0644]
queue-5.15/nvme-pci-disable-namespace-identifiers-for-the-maxio.patch [new file with mode: 0644]
queue-5.15/nvme-pci-disable-write-zeroes-on-various-kingston-ss.patch [new file with mode: 0644]
queue-5.15/nvmet-fix-memory-leak-in-nvmet_subsys_attr_model_sto.patch [new file with mode: 0644]
queue-5.15/platform-x86-ideapad-laptop-disable-touchpad_switch.patch [new file with mode: 0644]
queue-5.15/platform-x86-intel-hid-add-some-acpi-device-ids.patch [new file with mode: 0644]
queue-5.15/platform-x86-intel-pmt-sapphire-rapids-pmt-errata-fi.patch [new file with mode: 0644]
queue-5.15/platform-x86-touchscreen_dmi-add-info-for-the-rca-ca.patch [new file with mode: 0644]
queue-5.15/revert-drm-amdgpu-revert-drm-amdgpu-getting-fan-spee.patch [new file with mode: 0644]
queue-5.15/revert-net-macsec-report-real_dev-features-when-hw-o.patch [new file with mode: 0644]
queue-5.15/risc-v-vdso-do-not-add-missing-symbols-to-version-se.patch [new file with mode: 0644]
queue-5.15/riscv-dts-sifive-unleashed-add-pwm-controlled-leds.patch [new file with mode: 0644]
queue-5.15/scsi-ibmvfc-avoid-path-failures-during-live-migratio.patch [new file with mode: 0644]
queue-5.15/scsi-scsi_debug-make-the-read-capacity-response-comp.patch [new file with mode: 0644]
queue-5.15/sctp-clear-out_curr-if-all-frag-chunks-of-current-ms.patch [new file with mode: 0644]
queue-5.15/sctp-remove-the-unnecessary-sinfo_stream-check-in-sc.patch [new file with mode: 0644]
queue-5.15/selftests-bpf-add-verifier-test-for-release_referenc.patch [new file with mode: 0644]
queue-5.15/serial-add-rs485_supported-to-uart_port.patch [new file with mode: 0644]
queue-5.15/serial-fsl_lpuart-fill-in-rs485_supported.patch [new file with mode: 0644]
queue-5.15/series [new file with mode: 0644]
queue-5.15/speakup-generate-speakupmap.h-automatically.patch [new file with mode: 0644]
queue-5.15/speakup-replace-utils-u_char-with-unsigned-char.patch [new file with mode: 0644]
queue-5.15/spi-stm32-fix-stm32_spi_prepare_mbr-that-halves-spi-.patch [new file with mode: 0644]
queue-5.15/tty-serial-fsl_lpuart-don-t-break-the-on-going-trans.patch [new file with mode: 0644]
queue-5.15/wifi-airo-do-not-assign-1-to-unsigned-char.patch [new file with mode: 0644]
queue-5.15/wifi-ath11k-fix-qcn9074-firmware-boot-on-x86.patch [new file with mode: 0644]
queue-5.15/wifi-mac80211-fix-ack-frame-idr-leak-when-mesh-has-n.patch [new file with mode: 0644]
queue-5.15/wifi-mac80211-fix-memory-free-error-when-registering.patch [new file with mode: 0644]
queue-5.15/wifi-mac80211_hwsim-fix-debugfs-attribute-ps-with-rc.patch [new file with mode: 0644]
queue-5.15/x86-sgx-add-overflow-check-in-sgx_validate_offset_le.patch [new file with mode: 0644]
queue-5.15/x86-sgx-create-utility-to-validate-user-provided-off.patch [new file with mode: 0644]

diff --git a/queue-5.15/alsa-usb-audio-add-quirk-to-fix-hamedal-c20-disconne.patch b/queue-5.15/alsa-usb-audio-add-quirk-to-fix-hamedal-c20-disconne.patch
new file mode 100644 (file)
index 0000000..9f01ba6
--- /dev/null
@@ -0,0 +1,74 @@
+From 209b5fe05434f962da516216c36a7b9af9a48d57 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 10 Nov 2022 14:34:52 +0800
+Subject: ALSA: usb-audio: add quirk to fix Hamedal C20 disconnect issue
+
+From: Ai Chao <aichao@kylinos.cn>
+
+[ Upstream commit bf990c10231937c0f51e5da5558e08cf5adc6a78 ]
+
+For Hamedal C20, the current rate is different from the runtime rate,
+snd_usb_endpoint stop and close endpoint to resetting rate.
+if snd_usb_endpoint close the endpoint, sometimes usb will
+disconnect the device.
+
+Signed-off-by: Ai Chao <aichao@kylinos.cn>
+Link: https://lore.kernel.org/r/20221110063452.295110-1-aichao@kylinos.cn
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/usb/endpoint.c | 3 ++-
+ sound/usb/quirks.c   | 2 ++
+ sound/usb/usbaudio.h | 3 +++
+ 3 files changed, 7 insertions(+), 1 deletion(-)
+
+diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
+index 3bbc227769d0..092350eb5f4e 100644
+--- a/sound/usb/endpoint.c
++++ b/sound/usb/endpoint.c
+@@ -887,7 +887,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip,
+       usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n",
+                     ep->ep_num, ep->opened);
+-      if (!--ep->iface_ref->opened)
++      if (!--ep->iface_ref->opened &&
++              !(chip->quirk_flags & QUIRK_FLAG_IFACE_SKIP_CLOSE))
+               endpoint_set_interface(chip, ep, false);
+       if (!--ep->opened) {
+diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
+index 879d8b1f301c..2ae9ad993ff4 100644
+--- a/sound/usb/quirks.c
++++ b/sound/usb/quirks.c
+@@ -1884,6 +1884,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = {
+                  QUIRK_FLAG_GENERIC_IMPLICIT_FB),
+       DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */
+                  QUIRK_FLAG_GENERIC_IMPLICIT_FB),
++      DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */
++                 QUIRK_FLAG_IFACE_SKIP_CLOSE),
+       /* Vendor matches */
+       VENDOR_FLG(0x045e, /* MS Lifecam */
+diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h
+index 39c3c61a7e49..ec06f441e890 100644
+--- a/sound/usb/usbaudio.h
++++ b/sound/usb/usbaudio.h
+@@ -169,6 +169,8 @@ extern bool snd_usb_skip_validation;
+  *  Apply the generic implicit feedback sync mode (same as implicit_fb=1 option)
+  * QUIRK_FLAG_SKIP_IMPLICIT_FB
+  *  Don't apply implicit feedback sync mode
++ * QUIRK_FLAG_IFACE_SKIP_CLOSE
++ *  Don't closed interface during setting sample rate
+  */
+ #define QUIRK_FLAG_GET_SAMPLE_RATE    (1U << 0)
+@@ -190,5 +192,6 @@ extern bool snd_usb_skip_validation;
+ #define QUIRK_FLAG_SET_IFACE_FIRST    (1U << 16)
+ #define QUIRK_FLAG_GENERIC_IMPLICIT_FB        (1U << 17)
+ #define QUIRK_FLAG_SKIP_IMPLICIT_FB   (1U << 18)
++#define QUIRK_FLAG_IFACE_SKIP_CLOSE   (1U << 19)
+ #endif /* __USBAUDIO_H */
+-- 
+2.35.1
+
diff --git a/queue-5.15/arm64-syscall-include-asm-ptrace.h-in-syscall_wrappe.patch b/queue-5.15/arm64-syscall-include-asm-ptrace.h-in-syscall_wrappe.patch
new file mode 100644 (file)
index 0000000..e1fe12d
--- /dev/null
@@ -0,0 +1,51 @@
+From 42e69f15bdb040630eb9a3081eea6878c7fd560d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 31 Oct 2022 14:57:28 -0700
+Subject: arm64/syscall: Include asm/ptrace.h in syscall_wrapper header.
+
+From: Kuniyuki Iwashima <kuniyu@amazon.com>
+
+[ Upstream commit acfc35cfcee5df419391671ef1a631f43feee4e3 ]
+
+Add the same change for ARM64 as done in the commit 9440c4294160
+("x86/syscall: Include asm/ptrace.h in syscall_wrapper header") to
+make sure all syscalls see 'struct pt_regs' definition and resulted
+BTF for '__arm64_sys_*(struct pt_regs *regs)' functions point to
+actual struct.
+
+Without this patch, the BPF verifier refuses to load a tracing prog
+which accesses pt_regs.
+
+  bpf(BPF_PROG_LOAD, {prog_type=0x1a, ...}, 128) = -1 EACCES
+
+With this patch, we can see the correct error, which saves us time
+in debugging the prog.
+
+  bpf(BPF_PROG_LOAD, {prog_type=0x1a, ...}, 128) = 4
+  bpf(BPF_RAW_TRACEPOINT_OPEN, {raw_tracepoint={name=NULL, prog_fd=4}}, 128) = -1 ENOTSUPP
+
+Signed-off-by: Kuniyuki Iwashima <kuniyu@amazon.com>
+Acked-by: Andrii Nakryiko <andrii@kernel.org>
+Link: https://lore.kernel.org/r/20221031215728.50389-1-kuniyu@amazon.com
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/arm64/include/asm/syscall_wrapper.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/arch/arm64/include/asm/syscall_wrapper.h b/arch/arm64/include/asm/syscall_wrapper.h
+index b383b4802a7b..d30217c21eff 100644
+--- a/arch/arm64/include/asm/syscall_wrapper.h
++++ b/arch/arm64/include/asm/syscall_wrapper.h
+@@ -8,7 +8,7 @@
+ #ifndef __ASM_SYSCALL_WRAPPER_H
+ #define __ASM_SYSCALL_WRAPPER_H
+-struct pt_regs;
++#include <asm/ptrace.h>
+ #define SC_ARM64_REGS_TO_ARGS(x, ...)                         \
+       __MAP(x,__SC_ARGS                                       \
+-- 
+2.35.1
+
diff --git a/queue-5.15/asoc-fsl_asrc-fsl_esai-fsl_sai-allow-config_pm-n.patch b/queue-5.15/asoc-fsl_asrc-fsl_esai-fsl_sai-allow-config_pm-n.patch
new file mode 100644 (file)
index 0000000..311d933
--- /dev/null
@@ -0,0 +1,74 @@
+From deff5af75c7951e8e3b3d57d91136c85dd6943ec Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 28 Oct 2022 16:11:28 +0200
+Subject: ASoC: fsl_asrc fsl_esai fsl_sai: allow CONFIG_PM=N
+
+From: Maarten Zanders <maarten.zanders@mind.be>
+
+[ Upstream commit 6a564338a23cefcfc29c4a535b98402d13efdda6 ]
+
+When CONFIG_PM=N, pm_runtime_put_sync() returns -ENOSYS
+which breaks the probe function of these drivers.
+
+Other users of pm_runtime_put_sync() typically don't check
+the return value. In order to keep the program flow as
+intended, check for -ENOSYS.
+
+This commit is similar to commit 0434d3f (omap-mailbox.c).
+
+Fixes: cab04ab5900f ("ASoC: fsl_asrc: Don't use devm_regmap_init_mmio_clk")
+Fixes: 203773e39347 ("ASoC: fsl_esai: Don't use devm_regmap_init_mmio_clk")
+Fixes: 2277e7e36b4b ("ASoC: fsl_sai: Don't use devm_regmap_init_mmio_clk")
+Signed-off-by: Maarten Zanders <maarten.zanders@mind.be>
+Reviewed-by: Daniel Baluta <daniel.baluta@nxp.com>
+Link: https://lore.kernel.org/r/20221028141129.100702-1-maarten.zanders@mind.be
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/fsl/fsl_asrc.c | 2 +-
+ sound/soc/fsl/fsl_esai.c | 2 +-
+ sound/soc/fsl/fsl_sai.c  | 2 +-
+ 3 files changed, 3 insertions(+), 3 deletions(-)
+
+diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
+index 44dcbf49456c..08ca410ef551 100644
+--- a/sound/soc/fsl/fsl_asrc.c
++++ b/sound/soc/fsl/fsl_asrc.c
+@@ -1226,7 +1226,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
+       }
+       ret = pm_runtime_put_sync(&pdev->dev);
+-      if (ret < 0)
++      if (ret < 0 && ret != -ENOSYS)
+               goto err_pm_get_sync;
+       ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component,
+diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
+index bda66b30e063..763f5f0592af 100644
+--- a/sound/soc/fsl/fsl_esai.c
++++ b/sound/soc/fsl/fsl_esai.c
+@@ -1070,7 +1070,7 @@ static int fsl_esai_probe(struct platform_device *pdev)
+       regmap_write(esai_priv->regmap, REG_ESAI_RSMB, 0);
+       ret = pm_runtime_put_sync(&pdev->dev);
+-      if (ret < 0)
++      if (ret < 0 && ret != -ENOSYS)
+               goto err_pm_get_sync;
+       /*
+diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
+index bcf6b66a5ac0..5ba06df2ace5 100644
+--- a/sound/soc/fsl/fsl_sai.c
++++ b/sound/soc/fsl/fsl_sai.c
+@@ -1150,7 +1150,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
+       }
+       ret = pm_runtime_put_sync(dev);
+-      if (ret < 0)
++      if (ret < 0 && ret != -ENOSYS)
+               goto err_pm_get_sync;
+       /*
+-- 
+2.35.1
+
diff --git a/queue-5.15/asoc-fsl_sai-use-local-device-pointer.patch b/queue-5.15/asoc-fsl_sai-use-local-device-pointer.patch
new file mode 100644 (file)
index 0000000..41c701a
--- /dev/null
@@ -0,0 +1,188 @@
+From 0ee3f8e183d270916f47d73144aa738363e6f86a Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 1 Jun 2022 11:23:40 +0200
+Subject: ASoC: fsl_sai: use local device pointer
+
+From: Marco Felsch <m.felsch@pengutronix.de>
+
+[ Upstream commit f53f50ee21d46094a8c48970e95e38a4deaa128e ]
+
+Use a local variable to dereference the device pointer once and use the
+local variable in further calls. No functional changes.
+
+Signed-off-by: Marco Felsch <m.felsch@pengutronix.de>
+Acked-by: Shengjiu Wang <shengjiu.wang@gmail.com>
+Link: https://lore.kernel.org/r/20220601092342.3328644-1-m.felsch@pengutronix.de
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Stable-dep-of: 6a564338a23c ("ASoC: fsl_asrc fsl_esai fsl_sai: allow CONFIG_PM=N")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ sound/soc/fsl/fsl_sai.c | 53 +++++++++++++++++++++--------------------
+ 1 file changed, 27 insertions(+), 26 deletions(-)
+
+diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
+index 38f6362099d5..bcf6b66a5ac0 100644
+--- a/sound/soc/fsl/fsl_sai.c
++++ b/sound/soc/fsl/fsl_sai.c
+@@ -1000,6 +1000,7 @@ static int fsl_sai_runtime_resume(struct device *dev);
+ static int fsl_sai_probe(struct platform_device *pdev)
+ {
+       struct device_node *np = pdev->dev.of_node;
++      struct device *dev = &pdev->dev;
+       struct fsl_sai *sai;
+       struct regmap *gpr;
+       struct resource *res;
+@@ -1008,12 +1009,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
+       int irq, ret, i;
+       int index;
+-      sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
++      sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL);
+       if (!sai)
+               return -ENOMEM;
+       sai->pdev = pdev;
+-      sai->soc_data = of_device_get_match_data(&pdev->dev);
++      sai->soc_data = of_device_get_match_data(dev);
+       sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
+@@ -1028,18 +1029,18 @@ static int fsl_sai_probe(struct platform_device *pdev)
+                       ARRAY_SIZE(fsl_sai_reg_defaults_ofs8);
+       }
+-      sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, &fsl_sai_regmap_config);
++      sai->regmap = devm_regmap_init_mmio(dev, base, &fsl_sai_regmap_config);
+       if (IS_ERR(sai->regmap)) {
+-              dev_err(&pdev->dev, "regmap init failed\n");
++              dev_err(dev, "regmap init failed\n");
+               return PTR_ERR(sai->regmap);
+       }
+-      sai->bus_clk = devm_clk_get(&pdev->dev, "bus");
++      sai->bus_clk = devm_clk_get(dev, "bus");
+       /* Compatible with old DTB cases */
+       if (IS_ERR(sai->bus_clk) && PTR_ERR(sai->bus_clk) != -EPROBE_DEFER)
+-              sai->bus_clk = devm_clk_get(&pdev->dev, "sai");
++              sai->bus_clk = devm_clk_get(dev, "sai");
+       if (IS_ERR(sai->bus_clk)) {
+-              dev_err(&pdev->dev, "failed to get bus clock: %ld\n",
++              dev_err(dev, "failed to get bus clock: %ld\n",
+                               PTR_ERR(sai->bus_clk));
+               /* -EPROBE_DEFER */
+               return PTR_ERR(sai->bus_clk);
+@@ -1047,9 +1048,9 @@ static int fsl_sai_probe(struct platform_device *pdev)
+       for (i = 1; i < FSL_SAI_MCLK_MAX; i++) {
+               sprintf(tmp, "mclk%d", i);
+-              sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp);
++              sai->mclk_clk[i] = devm_clk_get(dev, tmp);
+               if (IS_ERR(sai->mclk_clk[i])) {
+-                      dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n",
++                      dev_err(dev, "failed to get mclk%d clock: %ld\n",
+                                       i + 1, PTR_ERR(sai->mclk_clk[i]));
+                       sai->mclk_clk[i] = NULL;
+               }
+@@ -1064,10 +1065,10 @@ static int fsl_sai_probe(struct platform_device *pdev)
+       if (irq < 0)
+               return irq;
+-      ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, IRQF_SHARED,
++      ret = devm_request_irq(dev, irq, fsl_sai_isr, IRQF_SHARED,
+                              np->name, sai);
+       if (ret) {
+-              dev_err(&pdev->dev, "failed to claim irq %u\n", irq);
++              dev_err(dev, "failed to claim irq %u\n", irq);
+               return ret;
+       }
+@@ -1084,7 +1085,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
+       if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) &&
+           of_find_property(np, "fsl,sai-asynchronous", NULL)) {
+               /* error out if both synchronous and asynchronous are present */
+-              dev_err(&pdev->dev, "invalid binding for synchronous mode\n");
++              dev_err(dev, "invalid binding for synchronous mode\n");
+               return -EINVAL;
+       }
+@@ -1105,7 +1106,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
+           of_device_is_compatible(np, "fsl,imx6ul-sai")) {
+               gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
+               if (IS_ERR(gpr)) {
+-                      dev_err(&pdev->dev, "cannot find iomuxc registers\n");
++                      dev_err(dev, "cannot find iomuxc registers\n");
+                       return PTR_ERR(gpr);
+               }
+@@ -1123,23 +1124,23 @@ static int fsl_sai_probe(struct platform_device *pdev)
+       sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX;
+       platform_set_drvdata(pdev, sai);
+-      pm_runtime_enable(&pdev->dev);
+-      if (!pm_runtime_enabled(&pdev->dev)) {
+-              ret = fsl_sai_runtime_resume(&pdev->dev);
++      pm_runtime_enable(dev);
++      if (!pm_runtime_enabled(dev)) {
++              ret = fsl_sai_runtime_resume(dev);
+               if (ret)
+                       goto err_pm_disable;
+       }
+-      ret = pm_runtime_get_sync(&pdev->dev);
++      ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+-              pm_runtime_put_noidle(&pdev->dev);
++              pm_runtime_put_noidle(dev);
+               goto err_pm_get_sync;
+       }
+       /* Get sai version */
+-      ret = fsl_sai_check_version(&pdev->dev);
++      ret = fsl_sai_check_version(dev);
+       if (ret < 0)
+-              dev_warn(&pdev->dev, "Error reading SAI version: %d\n", ret);
++              dev_warn(dev, "Error reading SAI version: %d\n", ret);
+       /* Select MCLK direction */
+       if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
+@@ -1148,7 +1149,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
+                                  FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN);
+       }
+-      ret = pm_runtime_put_sync(&pdev->dev);
++      ret = pm_runtime_put_sync(dev);
+       if (ret < 0)
+               goto err_pm_get_sync;
+@@ -1161,12 +1162,12 @@ static int fsl_sai_probe(struct platform_device *pdev)
+               if (ret)
+                       goto err_pm_get_sync;
+       } else {
+-              ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
++              ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0);
+               if (ret)
+                       goto err_pm_get_sync;
+       }
+-      ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
++      ret = devm_snd_soc_register_component(dev, &fsl_component,
+                                             &sai->cpu_dai_drv, 1);
+       if (ret)
+               goto err_pm_get_sync;
+@@ -1174,10 +1175,10 @@ static int fsl_sai_probe(struct platform_device *pdev)
+       return ret;
+ err_pm_get_sync:
+-      if (!pm_runtime_status_suspended(&pdev->dev))
+-              fsl_sai_runtime_suspend(&pdev->dev);
++      if (!pm_runtime_status_suspended(dev))
++              fsl_sai_runtime_suspend(dev);
+ err_pm_disable:
+-      pm_runtime_disable(&pdev->dev);
++      pm_runtime_disable(dev);
+       return ret;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.15/ata-libata-core-do-not-issue-non-internal-commands-o.patch b/queue-5.15/ata-libata-core-do-not-issue-non-internal-commands-o.patch
new file mode 100644 (file)
index 0000000..4b6acf3
--- /dev/null
@@ -0,0 +1,87 @@
+From 64b10491e240fb3024aa8cf84a52e034011901bb Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 9 Nov 2022 00:15:34 +0100
+Subject: ata: libata-core: do not issue non-internal commands once EH is
+ pending
+
+From: Niklas Cassel <niklas.cassel@wdc.com>
+
+[ Upstream commit e20e81a24a4d58744a29715aac2f795cd1651955 ]
+
+While the ATA specification states that a device should return command
+aborted for all commands queued after the device has entered error state,
+since ATA only keeps the sense data for the latest command (in non-NCQ
+case), we really don't want to send block layer commands to the device
+after it has entered error state. (Only ATA EH commands should be sent,
+to read the sense data etc.)
+
+Currently, scsi_queue_rq() will check if scsi_host_in_recovery()
+(state is SHOST_RECOVERY), and if so, it will _not_ issue a command via:
+scsi_dispatch_cmd() -> host->hostt->queuecommand() (ata_scsi_queuecmd())
+-> __ata_scsi_queuecmd() -> ata_scsi_translate() -> ata_qc_issue()
+
+Before commit e494f6a72839 ("[SCSI] improved eh timeout handler"),
+when receiving a TFES error IRQ, the call chain looked like this:
+ahci_error_intr() -> ata_port_abort() -> ata_do_link_abort() ->
+ata_qc_complete() -> ata_qc_schedule_eh() -> blk_abort_request() ->
+blk_rq_timed_out() -> q->rq_timed_out_fn() (scsi_times_out()) ->
+scsi_eh_scmd_add() -> scsi_host_set_state(shost, SHOST_RECOVERY)
+
+Which meant that as soon as an error IRQ was serviced, SHOST_RECOVERY
+would be set.
+
+However, after commit e494f6a72839 ("[SCSI] improved eh timeout handler"),
+scsi_times_out() will instead call scsi_abort_command() which will queue
+delayed work, and the worker function scmd_eh_abort_handler() will call
+scsi_eh_scmd_add(), which calls scsi_host_set_state(shost, SHOST_RECOVERY).
+
+So now, after the TFES error IRQ has been serviced, we need to wait for
+the SCSI workqueue to run its work before SHOST_RECOVERY gets set.
+
+It is worth noting that, even before commit e494f6a72839 ("[SCSI] improved
+eh timeout handler"), we could receive an error IRQ from the time when
+scsi_queue_rq() checks scsi_host_in_recovery(), to the time when
+ata_scsi_queuecmd() is actually called.
+
+In order to handle both the delayed setting of SHOST_RECOVERY and the
+window where we can receive an error IRQ, add a check against
+ATA_PFLAG_EH_PENDING (which gets set when servicing the error IRQ),
+inside ata_scsi_queuecmd() itself, while holding the ap->lock.
+(Since the ap->lock is held while servicing IRQs.)
+
+Fixes: e494f6a72839 ("[SCSI] improved eh timeout handler")
+Signed-off-by: Niklas Cassel <niklas.cassel@wdc.com>
+Tested-by: John Garry <john.g.garry@oracle.com>
+Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/libata-scsi.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
+index 61b7e0b0bbf6..061d2f8feeb5 100644
+--- a/drivers/ata/libata-scsi.c
++++ b/drivers/ata/libata-scsi.c
+@@ -3988,9 +3988,19 @@ void ata_scsi_dump_cdb(struct ata_port *ap, struct scsi_cmnd *cmd)
+ int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev)
+ {
++      struct ata_port *ap = dev->link->ap;
+       u8 scsi_op = scmd->cmnd[0];
+       ata_xlat_func_t xlat_func;
++      /*
++       * scsi_queue_rq() will defer commands if scsi_host_in_recovery().
++       * However, this check is done without holding the ap->lock (a libata
++       * specific lock), so we can have received an error irq since then,
++       * therefore we must check if EH is pending, while holding ap->lock.
++       */
++      if (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS))
++              return SCSI_MLQUEUE_DEVICE_BUSY;
++
+       if (unlikely(!scmd->cmd_len))
+               goto bad_cdb_len;
+-- 
+2.35.1
+
diff --git a/queue-5.15/ata-libata-scsi-simplify-__ata_scsi_queuecmd.patch b/queue-5.15/ata-libata-scsi-simplify-__ata_scsi_queuecmd.patch
new file mode 100644 (file)
index 0000000..266368d
--- /dev/null
@@ -0,0 +1,98 @@
+From 65f099cdb989809ccb8fa2a6d5ed6111e503dbac Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Jan 2022 19:13:54 -0500
+Subject: ata: libata-scsi: simplify __ata_scsi_queuecmd()
+
+From: Wenchao Hao <haowenchao@huawei.com>
+
+[ Upstream commit 84eac327af543f03172085d5ef9f98ea25a51191 ]
+
+This patch cleans up the code of __ata_scsi_queuecmd(). Since each
+branch of the "if" condition check that scmd->cmd_len is not zero, move
+this check out of the "if" to simplify the conditions being checked in
+the "else" branch.
+
+While at it, avoid the if-else-if-else structure using if-else if
+structure and remove the redundant rc local variable.
+
+This patch does not change the function logic.
+
+Signed-off-by: Wenchao Hao <haowenchao@huawei.com>
+Signed-off-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Stable-dep-of: e20e81a24a4d ("ata: libata-core: do not issue non-internal commands once EH is pending")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/ata/libata-scsi.c | 45 ++++++++++++++++++---------------------
+ 1 file changed, 21 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c
+index ef41cb385a0d..61b7e0b0bbf6 100644
+--- a/drivers/ata/libata-scsi.c
++++ b/drivers/ata/libata-scsi.c
+@@ -3990,42 +3990,39 @@ int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev)
+ {
+       u8 scsi_op = scmd->cmnd[0];
+       ata_xlat_func_t xlat_func;
+-      int rc = 0;
++
++      if (unlikely(!scmd->cmd_len))
++              goto bad_cdb_len;
+       if (dev->class == ATA_DEV_ATA || dev->class == ATA_DEV_ZAC) {
+-              if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len))
++              if (unlikely(scmd->cmd_len > dev->cdb_len))
+                       goto bad_cdb_len;
+               xlat_func = ata_get_xlat_func(dev, scsi_op);
+-      } else {
+-              if (unlikely(!scmd->cmd_len))
+-                      goto bad_cdb_len;
++      } else if (likely((scsi_op != ATA_16) || !atapi_passthru16)) {
++              /* relay SCSI command to ATAPI device */
++              int len = COMMAND_SIZE(scsi_op);
+-              xlat_func = NULL;
+-              if (likely((scsi_op != ATA_16) || !atapi_passthru16)) {
+-                      /* relay SCSI command to ATAPI device */
+-                      int len = COMMAND_SIZE(scsi_op);
+-                      if (unlikely(len > scmd->cmd_len ||
+-                                   len > dev->cdb_len ||
+-                                   scmd->cmd_len > ATAPI_CDB_LEN))
+-                              goto bad_cdb_len;
++              if (unlikely(len > scmd->cmd_len ||
++                           len > dev->cdb_len ||
++                           scmd->cmd_len > ATAPI_CDB_LEN))
++                      goto bad_cdb_len;
+-                      xlat_func = atapi_xlat;
+-              } else {
+-                      /* ATA_16 passthru, treat as an ATA command */
+-                      if (unlikely(scmd->cmd_len > 16))
+-                              goto bad_cdb_len;
++              xlat_func = atapi_xlat;
++      } else {
++              /* ATA_16 passthru, treat as an ATA command */
++              if (unlikely(scmd->cmd_len > 16))
++                      goto bad_cdb_len;
+-                      xlat_func = ata_get_xlat_func(dev, scsi_op);
+-              }
++              xlat_func = ata_get_xlat_func(dev, scsi_op);
+       }
+       if (xlat_func)
+-              rc = ata_scsi_translate(dev, scmd, xlat_func);
+-      else
+-              ata_scsi_simulate(dev, scmd);
++              return ata_scsi_translate(dev, scmd, xlat_func);
+-      return rc;
++      ata_scsi_simulate(dev, scmd);
++
++      return 0;
+  bad_cdb_len:
+       DPRINTK("bad CDB len=%u, scsi_op=0x%02x, max=%u\n",
+-- 
+2.35.1
+
diff --git a/queue-5.15/audit-fix-undefined-behavior-in-bit-shift-for-audit_.patch b/queue-5.15/audit-fix-undefined-behavior-in-bit-shift-for-audit_.patch
new file mode 100644 (file)
index 0000000..ed370d5
--- /dev/null
@@ -0,0 +1,52 @@
+From 34ff33e8d8f92bcd794dbe71cd91dcda313de021 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 31 Oct 2022 10:10:21 +0800
+Subject: audit: fix undefined behavior in bit shift for AUDIT_BIT
+
+From: Gaosheng Cui <cuigaosheng1@huawei.com>
+
+[ Upstream commit 986d93f55bdeab1cac858d1e47b41fac10b2d7f6 ]
+
+Shifting signed 32-bit value by 31 bits is undefined, so changing
+significant bit to unsigned. The UBSAN warning calltrace like below:
+
+UBSAN: shift-out-of-bounds in kernel/auditfilter.c:179:23
+left shift of 1 by 31 places cannot be represented in type 'int'
+Call Trace:
+ <TASK>
+ dump_stack_lvl+0x7d/0xa5
+ dump_stack+0x15/0x1b
+ ubsan_epilogue+0xe/0x4e
+ __ubsan_handle_shift_out_of_bounds+0x1e7/0x20c
+ audit_register_class+0x9d/0x137
+ audit_classes_init+0x4d/0xb8
+ do_one_initcall+0x76/0x430
+ kernel_init_freeable+0x3b3/0x422
+ kernel_init+0x24/0x1e0
+ ret_from_fork+0x1f/0x30
+ </TASK>
+
+Signed-off-by: Gaosheng Cui <cuigaosheng1@huawei.com>
+[PM: remove bad 'Fixes' tag as issue predates git, added in v2.6.6-rc1]
+Signed-off-by: Paul Moore <paul@paul-moore.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ include/uapi/linux/audit.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
+index daa481729e9b..27799acd0e5e 100644
+--- a/include/uapi/linux/audit.h
++++ b/include/uapi/linux/audit.h
+@@ -182,7 +182,7 @@
+ #define AUDIT_MAX_KEY_LEN  256
+ #define AUDIT_BITMASK_SIZE 64
+ #define AUDIT_WORD(nr) ((__u32)((nr)/32))
+-#define AUDIT_BIT(nr)  (1 << ((nr) - AUDIT_WORD(nr)*32))
++#define AUDIT_BIT(nr)  (1U << ((nr) - AUDIT_WORD(nr)*32))
+ #define AUDIT_SYSCALL_CLASSES 16
+ #define AUDIT_CLASS_DIR_WRITE 0
+-- 
+2.35.1
+
diff --git a/queue-5.15/binder-validate-alloc-mm-in-mmap-handler.patch b/queue-5.15/binder-validate-alloc-mm-in-mmap-handler.patch
new file mode 100644 (file)
index 0000000..eca996e
--- /dev/null
@@ -0,0 +1,66 @@
+From fb3a616383db1095d3df8b5122410d5006464e79 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 4 Nov 2022 23:12:35 +0000
+Subject: binder: validate alloc->mm in ->mmap() handler
+
+From: Carlos Llamas <cmllamas@google.com>
+
+[ Upstream commit 3ce00bb7e91cf57d723905371507af57182c37ef ]
+
+Since commit 1da52815d5f1 ("binder: fix alloc->vma_vm_mm null-ptr
+dereference") binder caches a pointer to the current->mm during open().
+This fixes a null-ptr dereference reported by syzkaller. Unfortunately,
+it also opens the door for a process to update its mm after the open(),
+(e.g. via execve) making the cached alloc->mm pointer invalid.
+
+Things get worse when the process continues to mmap() a vma. From this
+point forward, binder will attempt to find this vma using an obsolete
+alloc->mm reference. Such as in binder_update_page_range(), where the
+wrong vma is obtained via vma_lookup(), yet binder proceeds to happily
+insert new pages into it.
+
+To avoid this issue fail the ->mmap() callback if we detect a mismatch
+between the vma->vm_mm and the original alloc->mm pointer. This prevents
+alloc->vm_addr from getting set, so that any subsequent vma_lookup()
+calls fail as expected.
+
+Fixes: 1da52815d5f1 ("binder: fix alloc->vma_vm_mm null-ptr dereference")
+Reported-by: Jann Horn <jannh@google.com>
+Cc: <stable@vger.kernel.org> # 5.15+
+Signed-off-by: Carlos Llamas <cmllamas@google.com>
+Acked-by: Todd Kjos <tkjos@google.com>
+Link: https://lore.kernel.org/r/20221104231235.348958-1-cmllamas@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/android/binder_alloc.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c
+index 8ed450125c92..6acfb896b2e5 100644
+--- a/drivers/android/binder_alloc.c
++++ b/drivers/android/binder_alloc.c
+@@ -753,6 +753,12 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
+       const char *failure_string;
+       struct binder_buffer *buffer;
++      if (unlikely(vma->vm_mm != alloc->vma_vm_mm)) {
++              ret = -EINVAL;
++              failure_string = "invalid vma->vm_mm";
++              goto err_invalid_mm;
++      }
++
+       mutex_lock(&binder_alloc_mmap_lock);
+       if (alloc->buffer_size) {
+               ret = -EBUSY;
+@@ -799,6 +805,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc,
+       alloc->buffer_size = 0;
+ err_already_mapped:
+       mutex_unlock(&binder_alloc_mmap_lock);
++err_invalid_mm:
+       binder_alloc_debug(BINDER_DEBUG_USER_ERROR,
+                          "%s: %d %lx-%lx %s failed %d\n", __func__,
+                          alloc->pid, vma->vm_start, vma->vm_end,
+-- 
+2.35.1
+
diff --git a/queue-5.15/block-bfq-fix-null-pointer-dereference-in-bfq_bio_bf.patch b/queue-5.15/block-bfq-fix-null-pointer-dereference-in-bfq_bio_bf.patch
new file mode 100644 (file)
index 0000000..223e184
--- /dev/null
@@ -0,0 +1,151 @@
+From 54ef109871876f87838011d206e62211b277ff68 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 8 Nov 2022 18:34:34 +0800
+Subject: block, bfq: fix null pointer dereference in bfq_bio_bfqg()
+
+From: Yu Kuai <yukuai3@huawei.com>
+
+[ Upstream commit f02be9002c480cd3ec0fcf184ad27cf531bd6ece ]
+
+Out test found a following problem in kernel 5.10, and the same problem
+should exist in mainline:
+
+BUG: kernel NULL pointer dereference, address: 0000000000000094
+PGD 0 P4D 0
+Oops: 0000 [#1] SMP
+CPU: 7 PID: 155 Comm: kworker/7:1 Not tainted 5.10.0-01932-g19e0ace2ca1d-dirty 4
+Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS ?-20190727_073836-b4
+Workqueue: kthrotld blk_throtl_dispatch_work_fn
+RIP: 0010:bfq_bio_bfqg+0x52/0xc0
+Code: 94 00 00 00 00 75 2e 48 8b 40 30 48 83 05 35 06 c8 0b 01 48 85 c0 74 3d 4b
+RSP: 0018:ffffc90001a1fba0 EFLAGS: 00010002
+RAX: ffff888100d60400 RBX: ffff8881132e7000 RCX: 0000000000000000
+RDX: 0000000000000017 RSI: ffff888103580a18 RDI: ffff888103580a18
+RBP: ffff8881132e7000 R08: 0000000000000000 R09: ffffc90001a1fe10
+R10: 0000000000000a20 R11: 0000000000034320 R12: 0000000000000000
+R13: ffff888103580a18 R14: ffff888114447000 R15: 0000000000000000
+FS:  0000000000000000(0000) GS:ffff88881fdc0000(0000) knlGS:0000000000000000
+CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+CR2: 0000000000000094 CR3: 0000000100cdb000 CR4: 00000000000006e0
+DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
+Call Trace:
+ bfq_bic_update_cgroup+0x3c/0x350
+ ? ioc_create_icq+0x42/0x270
+ bfq_init_rq+0xfd/0x1060
+ bfq_insert_requests+0x20f/0x1cc0
+ ? ioc_create_icq+0x122/0x270
+ blk_mq_sched_insert_requests+0x86/0x1d0
+ blk_mq_flush_plug_list+0x193/0x2a0
+ blk_flush_plug_list+0x127/0x170
+ blk_finish_plug+0x31/0x50
+ blk_throtl_dispatch_work_fn+0x151/0x190
+ process_one_work+0x27c/0x5f0
+ worker_thread+0x28b/0x6b0
+ ? rescuer_thread+0x590/0x590
+ kthread+0x153/0x1b0
+ ? kthread_flush_work+0x170/0x170
+ ret_from_fork+0x1f/0x30
+Modules linked in:
+CR2: 0000000000000094
+---[ end trace e2e59ac014314547 ]---
+RIP: 0010:bfq_bio_bfqg+0x52/0xc0
+Code: 94 00 00 00 00 75 2e 48 8b 40 30 48 83 05 35 06 c8 0b 01 48 85 c0 74 3d 4b
+RSP: 0018:ffffc90001a1fba0 EFLAGS: 00010002
+RAX: ffff888100d60400 RBX: ffff8881132e7000 RCX: 0000000000000000
+RDX: 0000000000000017 RSI: ffff888103580a18 RDI: ffff888103580a18
+RBP: ffff8881132e7000 R08: 0000000000000000 R09: ffffc90001a1fe10
+R10: 0000000000000a20 R11: 0000000000034320 R12: 0000000000000000
+R13: ffff888103580a18 R14: ffff888114447000 R15: 0000000000000000
+FS:  0000000000000000(0000) GS:ffff88881fdc0000(0000) knlGS:0000000000000000
+CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
+CR2: 0000000000000094 CR3: 0000000100cdb000 CR4: 00000000000006e0
+DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000
+DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400
+
+Root cause is quite complex:
+
+1) use bfq elevator for the test device.
+2) create a cgroup CG
+3) config blk throtl in CG
+
+   blkg_conf_prep
+    blkg_create
+
+4) create a thread T1 and issue async io in CG:
+
+   bio_init
+    bio_associate_blkg
+   ...
+   submit_bio
+    submit_bio_noacct
+     blk_throtl_bio -> io is throttled
+     // io submit is done
+
+5) switch elevator:
+
+   bfq_exit_queue
+    blkcg_deactivate_policy
+     list_for_each_entry(blkg, &q->blkg_list, q_node)
+      blkg->pd[] = NULL
+      // bfq policy is removed
+
+5) thread t1 exist, then remove the cgroup CG:
+
+   blkcg_unpin_online
+    blkcg_destroy_blkgs
+     blkg_destroy
+      list_del_init(&blkg->q_node)
+      // blkg is removed from queue list
+
+6) switch elevator back to bfq
+
+ bfq_init_queue
+  bfq_create_group_hierarchy
+   blkcg_activate_policy
+    list_for_each_entry_reverse(blkg, &q->blkg_list)
+     // blkg is removed from list, hence bfq policy is still NULL
+
+7) throttled io is dispatched to bfq:
+
+ bfq_insert_requests
+  bfq_init_rq
+   bfq_bic_update_cgroup
+    bfq_bio_bfqg
+     bfqg = blkg_to_bfqg(blkg)
+     // bfqg is NULL because bfq policy is NULL
+
+The problem is only possible in bfq because only bfq can be deactivated and
+activated while queue is online, while others can only be deactivated while
+the device is removed.
+
+Fix the problem in bfq by checking if blkg is online before calling
+blkg_to_bfqg().
+
+Signed-off-by: Yu Kuai <yukuai3@huawei.com>
+Reviewed-by: Jan Kara <jack@suse.cz>
+Link: https://lore.kernel.org/r/20221108103434.2853269-1-yukuai1@huaweicloud.com
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ block/bfq-cgroup.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c
+index e2e765a54fe9..a8d0b4c71b05 100644
+--- a/block/bfq-cgroup.c
++++ b/block/bfq-cgroup.c
+@@ -613,6 +613,10 @@ struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio)
+       struct bfq_group *bfqg;
+       while (blkg) {
++              if (!blkg->online) {
++                      blkg = blkg->parent;
++                      continue;
++              }
+               bfqg = blkg_to_bfqg(blkg);
+               if (bfqg->online) {
+                       bio_associate_blkg_from_css(bio, &blkg->blkcg->css);
+-- 
+2.35.1
+
diff --git a/queue-5.15/ceph-avoid-putting-the-realm-twice-when-decoding-sna.patch b/queue-5.15/ceph-avoid-putting-the-realm-twice-when-decoding-sna.patch
new file mode 100644 (file)
index 0000000..11b3d81
--- /dev/null
@@ -0,0 +1,48 @@
+From a2d20ab9390fdcbac68704359db4ef153ebf89f3 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 9 Nov 2022 11:00:39 +0800
+Subject: ceph: avoid putting the realm twice when decoding snaps fails
+
+From: Xiubo Li <xiubli@redhat.com>
+
+[ Upstream commit 51884d153f7ec85e18d607b2467820a90e0f4359 ]
+
+When decoding the snaps fails it maybe leaving the 'first_realm'
+and 'realm' pointing to the same snaprealm memory. And then it'll
+put it twice and could cause random use-after-free, BUG_ON, etc
+issues.
+
+Cc: stable@vger.kernel.org
+Link: https://tracker.ceph.com/issues/57686
+Signed-off-by: Xiubo Li <xiubli@redhat.com>
+Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
+Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ceph/snap.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c
+index ae9cf37374e3..b512c82f9ccd 100644
+--- a/fs/ceph/snap.c
++++ b/fs/ceph/snap.c
+@@ -705,7 +705,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
+       struct ceph_mds_snap_realm *ri;    /* encoded */
+       __le64 *snaps;                     /* encoded */
+       __le64 *prior_parent_snaps;        /* encoded */
+-      struct ceph_snap_realm *realm = NULL;
++      struct ceph_snap_realm *realm;
+       struct ceph_snap_realm *first_realm = NULL;
+       struct ceph_snap_realm *realm_to_rebuild = NULL;
+       int rebuild_snapcs;
+@@ -716,6 +716,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
+       dout("update_snap_trace deletion=%d\n", deletion);
+ more:
++      realm = NULL;
+       rebuild_snapcs = 0;
+       ceph_decode_need(&p, e, sizeof(*ri), bad);
+       ri = p;
+-- 
+2.35.1
+
diff --git a/queue-5.15/ceph-do-not-update-snapshot-context-when-there-is-no.patch b/queue-5.15/ceph-do-not-update-snapshot-context-when-there-is-no.patch
new file mode 100644 (file)
index 0000000..6fa613e
--- /dev/null
@@ -0,0 +1,129 @@
+From e655095b18a368ff27abdd11a2bcdcad03e5599e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 19 Feb 2022 14:28:33 +0800
+Subject: ceph: do not update snapshot context when there is no new snapshot
+
+From: Xiubo Li <xiubli@redhat.com>
+
+[ Upstream commit 2e586641c950e7f3e7e008404bd783a466b9b590 ]
+
+We will only track the uppest parent snapshot realm from which we
+need to rebuild the snapshot contexts _downward_ in hierarchy. For
+all the others having no new snapshot we will do nothing.
+
+This fix will avoid calling ceph_queue_cap_snap() on some inodes
+inappropriately. For example, with the code in mainline, suppose there
+are 2 directory hierarchies (with 6 directories total), like this:
+
+/dir_X1/dir_X2/dir_X3/
+/dir_Y1/dir_Y2/dir_Y3/
+
+Firstly, make a snapshot under /dir_X1/dir_X2/.snap/snap_X2, then make a
+root snapshot under /.snap/root_snap. Every time we make snapshots under
+/dir_Y1/..., the kclient will always try to rebuild the snap context for
+snap_X2 realm and finally will always try to queue cap snaps for dir_Y2
+and dir_Y3, which makes no sense.
+
+That's because the snap_X2's seq is 2 and root_snap's seq is 3. So when
+creating a new snapshot under /dir_Y1/... the new seq will be 4, and
+the mds will send the kclient a snapshot backtrace in _downward_
+order: seqs 4, 3.
+
+When ceph_update_snap_trace() is called, it will always rebuild the from
+the last realm, that's the root_snap. So later when rebuilding the snap
+context, the current logic will always cause it to rebuild the snap_X2
+realm and then try to queue cap snaps for all the inodes related in that
+realm, even though it's not necessary.
+
+This is accompanied by a lot of these sorts of dout messages:
+
+    "ceph:  queue_cap_snap 00000000a42b796b nothing dirty|writing"
+
+Fix the logic to avoid this situation.
+
+Also, the 'invalidate' word is not precise here. In actuality, it will
+cause a rebuild of the existing snapshot contexts or just build
+non-existent ones. Rename it to 'rebuild_snapcs'.
+
+URL: https://tracker.ceph.com/issues/44100
+Signed-off-by: Xiubo Li <xiubli@redhat.com>
+Reviewed-by: Jeff Layton <jlayton@kernel.org>
+Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
+Stable-dep-of: 51884d153f7e ("ceph: avoid putting the realm twice when decoding snaps fails")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ceph/snap.c | 28 +++++++++++++++++++---------
+ 1 file changed, 19 insertions(+), 9 deletions(-)
+
+diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c
+index b41e6724c591..ae9cf37374e3 100644
+--- a/fs/ceph/snap.c
++++ b/fs/ceph/snap.c
+@@ -707,7 +707,8 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
+       __le64 *prior_parent_snaps;        /* encoded */
+       struct ceph_snap_realm *realm = NULL;
+       struct ceph_snap_realm *first_realm = NULL;
+-      int invalidate = 0;
++      struct ceph_snap_realm *realm_to_rebuild = NULL;
++      int rebuild_snapcs;
+       int err = -ENOMEM;
+       LIST_HEAD(dirty_realms);
+@@ -715,6 +716,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
+       dout("update_snap_trace deletion=%d\n", deletion);
+ more:
++      rebuild_snapcs = 0;
+       ceph_decode_need(&p, e, sizeof(*ri), bad);
+       ri = p;
+       p += sizeof(*ri);
+@@ -738,7 +740,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
+       err = adjust_snap_realm_parent(mdsc, realm, le64_to_cpu(ri->parent));
+       if (err < 0)
+               goto fail;
+-      invalidate += err;
++      rebuild_snapcs += err;
+       if (le64_to_cpu(ri->seq) > realm->seq) {
+               dout("update_snap_trace updating %llx %p %lld -> %lld\n",
+@@ -763,22 +765,30 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc,
+               if (realm->seq > mdsc->last_snap_seq)
+                       mdsc->last_snap_seq = realm->seq;
+-              invalidate = 1;
++              rebuild_snapcs = 1;
+       } else if (!realm->cached_context) {
+               dout("update_snap_trace %llx %p seq %lld new\n",
+                    realm->ino, realm, realm->seq);
+-              invalidate = 1;
++              rebuild_snapcs = 1;
+       } else {
+               dout("update_snap_trace %llx %p seq %lld unchanged\n",
+                    realm->ino, realm, realm->seq);
+       }
+-      dout("done with %llx %p, invalidated=%d, %p %p\n", realm->ino,
+-           realm, invalidate, p, e);
++      dout("done with %llx %p, rebuild_snapcs=%d, %p %p\n", realm->ino,
++           realm, rebuild_snapcs, p, e);
+-      /* invalidate when we reach the _end_ (root) of the trace */
+-      if (invalidate && p >= e)
+-              rebuild_snap_realms(realm, &dirty_realms);
++      /*
++       * this will always track the uppest parent realm from which
++       * we need to rebuild the snapshot contexts _downward_ in
++       * hierarchy.
++       */
++      if (rebuild_snapcs)
++              realm_to_rebuild = realm;
++
++      /* rebuild_snapcs when we reach the _end_ (root) of the trace */
++      if (realm_to_rebuild && p >= e)
++              rebuild_snap_realms(realm_to_rebuild, &dirty_realms);
+       if (!first_realm)
+               first_realm = realm;
+-- 
+2.35.1
+
diff --git a/queue-5.15/ceph-fix-null-pointer-dereference-for-req-r_session.patch b/queue-5.15/ceph-fix-null-pointer-dereference-for-req-r_session.patch
new file mode 100644 (file)
index 0000000..e5c332e
--- /dev/null
@@ -0,0 +1,125 @@
+From f1c2e878b7cca9505dbf66af76fa8fc2b3273a6f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 10 Nov 2022 21:01:59 +0800
+Subject: ceph: fix NULL pointer dereference for req->r_session
+
+From: Xiubo Li <xiubli@redhat.com>
+
+[ Upstream commit 5bd76b8de5b74fa941a6eafee87728a0fe072267 ]
+
+The request's r_session maybe changed when it was forwarded or
+resent. Both the forwarding and resending cases the requests will
+be protected by the mdsc->mutex.
+
+Cc: stable@vger.kernel.org
+Link: https://bugzilla.redhat.com/show_bug.cgi?id=2137955
+Signed-off-by: Xiubo Li <xiubli@redhat.com>
+Reviewed-by: Ilya Dryomov <idryomov@gmail.com>
+Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ceph/caps.c | 48 ++++++++++++------------------------------------
+ 1 file changed, 12 insertions(+), 36 deletions(-)
+
+diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
+index a6e2aaff17dd..be96fe615bec 100644
+--- a/fs/ceph/caps.c
++++ b/fs/ceph/caps.c
+@@ -2217,7 +2217,6 @@ static int unsafe_request_wait(struct inode *inode)
+       struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc;
+       struct ceph_inode_info *ci = ceph_inode(inode);
+       struct ceph_mds_request *req1 = NULL, *req2 = NULL;
+-      unsigned int max_sessions;
+       int ret, err = 0;
+       spin_lock(&ci->i_unsafe_lock);
+@@ -2235,28 +2234,24 @@ static int unsafe_request_wait(struct inode *inode)
+       }
+       spin_unlock(&ci->i_unsafe_lock);
+-      /*
+-       * The mdsc->max_sessions is unlikely to be changed
+-       * mostly, here we will retry it by reallocating the
+-       * sessions array memory to get rid of the mdsc->mutex
+-       * lock.
+-       */
+-retry:
+-      max_sessions = mdsc->max_sessions;
+-
+       /*
+        * Trigger to flush the journal logs in all the relevant MDSes
+        * manually, or in the worst case we must wait at most 5 seconds
+        * to wait the journal logs to be flushed by the MDSes periodically.
+        */
+-      if ((req1 || req2) && likely(max_sessions)) {
+-              struct ceph_mds_session **sessions = NULL;
+-              struct ceph_mds_session *s;
++      if (req1 || req2) {
+               struct ceph_mds_request *req;
++              struct ceph_mds_session **sessions;
++              struct ceph_mds_session *s;
++              unsigned int max_sessions;
+               int i;
++              mutex_lock(&mdsc->mutex);
++              max_sessions = mdsc->max_sessions;
++
+               sessions = kcalloc(max_sessions, sizeof(s), GFP_KERNEL);
+               if (!sessions) {
++                      mutex_unlock(&mdsc->mutex);
+                       err = -ENOMEM;
+                       goto out;
+               }
+@@ -2268,16 +2263,6 @@ static int unsafe_request_wait(struct inode *inode)
+                               s = req->r_session;
+                               if (!s)
+                                       continue;
+-                              if (unlikely(s->s_mds >= max_sessions)) {
+-                                      spin_unlock(&ci->i_unsafe_lock);
+-                                      for (i = 0; i < max_sessions; i++) {
+-                                              s = sessions[i];
+-                                              if (s)
+-                                                      ceph_put_mds_session(s);
+-                                      }
+-                                      kfree(sessions);
+-                                      goto retry;
+-                              }
+                               if (!sessions[s->s_mds]) {
+                                       s = ceph_get_mds_session(s);
+                                       sessions[s->s_mds] = s;
+@@ -2290,16 +2275,6 @@ static int unsafe_request_wait(struct inode *inode)
+                               s = req->r_session;
+                               if (!s)
+                                       continue;
+-                              if (unlikely(s->s_mds >= max_sessions)) {
+-                                      spin_unlock(&ci->i_unsafe_lock);
+-                                      for (i = 0; i < max_sessions; i++) {
+-                                              s = sessions[i];
+-                                              if (s)
+-                                                      ceph_put_mds_session(s);
+-                                      }
+-                                      kfree(sessions);
+-                                      goto retry;
+-                              }
+                               if (!sessions[s->s_mds]) {
+                                       s = ceph_get_mds_session(s);
+                                       sessions[s->s_mds] = s;
+@@ -2311,11 +2286,12 @@ static int unsafe_request_wait(struct inode *inode)
+               /* the auth MDS */
+               spin_lock(&ci->i_ceph_lock);
+               if (ci->i_auth_cap) {
+-                    s = ci->i_auth_cap->session;
+-                    if (!sessions[s->s_mds])
+-                            sessions[s->s_mds] = ceph_get_mds_session(s);
++                      s = ci->i_auth_cap->session;
++                      if (!sessions[s->s_mds])
++                              sessions[s->s_mds] = ceph_get_mds_session(s);
+               }
+               spin_unlock(&ci->i_ceph_lock);
++              mutex_unlock(&mdsc->mutex);
+               /* send flush mdlog request to MDSes */
+               for (i = 0; i < max_sessions; i++) {
+-- 
+2.35.1
+
diff --git a/queue-5.15/ceph-use-kcalloc-for-allocating-multiple-elements.patch b/queue-5.15/ceph-use-kcalloc-for-allocating-multiple-elements.patch
new file mode 100644 (file)
index 0000000..d032fa2
--- /dev/null
@@ -0,0 +1,37 @@
+From 0f58d7c37861a931d5c3a321f41409c073609c34 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 18 Aug 2022 22:42:55 -0700
+Subject: ceph: Use kcalloc for allocating multiple elements
+
+From: Kenneth Lee <klee33@uw.edu>
+
+[ Upstream commit aa1d627207cace003163dee24d1c06fa4e910c6b ]
+
+Prefer using kcalloc(a, b) over kzalloc(a * b) as this improves
+semantics since kcalloc is intended for allocating an array of memory.
+
+Signed-off-by: Kenneth Lee <klee33@uw.edu>
+Reviewed-by: Xiubo Li <xiubli@redhat.com>
+Signed-off-by: Ilya Dryomov <idryomov@gmail.com>
+Stable-dep-of: 5bd76b8de5b7 ("ceph: fix NULL pointer dereference for req->r_session")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/ceph/caps.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c
+index 883bb91ee257..a6e2aaff17dd 100644
+--- a/fs/ceph/caps.c
++++ b/fs/ceph/caps.c
+@@ -2255,7 +2255,7 @@ static int unsafe_request_wait(struct inode *inode)
+               struct ceph_mds_request *req;
+               int i;
+-              sessions = kzalloc(max_sessions * sizeof(s), GFP_KERNEL);
++              sessions = kcalloc(max_sessions, sizeof(s), GFP_KERNEL);
+               if (!sessions) {
+                       err = -ENOMEM;
+                       goto out;
+-- 
+2.35.1
+
diff --git a/queue-5.15/cifs-fix-connections-leak-when-tlink-setup-failed.patch b/queue-5.15/cifs-fix-connections-leak-when-tlink-setup-failed.patch
new file mode 100644 (file)
index 0000000..1b98e1f
--- /dev/null
@@ -0,0 +1,64 @@
+From 0c3d07dc46d1809c84602e13e534b3781e2b0d81 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 11 Nov 2022 15:12:12 +0800
+Subject: cifs: Fix connections leak when tlink setup failed
+
+From: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
+
+[ Upstream commit 1dcdf5f5b2137185cbdd5385f29949ab3da4f00c ]
+
+If the tlink setup failed, lost to put the connections, then
+the module refcnt leak since the cifsd kthread not exit.
+
+Also leak the fscache info, and for next mount with fsc, it will
+print the follow errors:
+  CIFS: Cache volume key already in use (cifs,127.0.0.1:445,TEST)
+
+Let's check the result of tlink setup, and do some cleanup.
+
+Fixes: 56c762eb9bee ("cifs: Refactor out cifs_mount()")
+Reviewed-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
+Signed-off-by: Zhang Xiaoxu <zhangxiaoxu5@huawei.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/connect.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+index 902eb8a5afd2..839059b8a9c9 100644
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -3550,9 +3550,13 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
+       uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id);
+ out:
+-      free_xid(mnt_ctx.xid);
+       cifs_try_adding_channels(cifs_sb, mnt_ctx.ses);
+-      return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
++      rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
++      if (rc)
++              goto error;
++
++      free_xid(mnt_ctx.xid);
++      return rc;
+ error:
+       dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id);
+@@ -3579,8 +3583,12 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
+                       goto error;
+       }
++      rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
++      if (rc)
++              goto error;
++
+       free_xid(mnt_ctx.xid);
+-      return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
++      return rc;
+ error:
+       mount_put_conns(&mnt_ctx);
+-- 
+2.35.1
+
diff --git a/queue-5.15/cifs-introduce-new-helper-for-cifs_reconnect.patch b/queue-5.15/cifs-introduce-new-helper-for-cifs_reconnect.patch
new file mode 100644 (file)
index 0000000..811ee5c
--- /dev/null
@@ -0,0 +1,203 @@
+From 0872775701ce78034efa93b0673e58561c272230 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 Oct 2021 13:57:21 -0300
+Subject: cifs: introduce new helper for cifs_reconnect()
+
+From: Paulo Alcantara <pc@cjr.nz>
+
+[ Upstream commit 43b459aa5e222cb6610dac8723b40c19532ea00d ]
+
+Create cifs_mark_tcp_ses_conns_for_reconnect() helper to mark all
+sessions and tcons for reconnect when reconnecting tcp server.
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
+Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 1dcdf5f5b213 ("cifs: Fix connections leak when tlink setup failed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/connect.c | 139 +++++++++++++++++++++++++---------------------
+ 1 file changed, 75 insertions(+), 64 deletions(-)
+
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+index ad5c935f7f06..ed429a442808 100644
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -199,80 +199,29 @@ static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb,
+ }
+ #endif
+-/*
+- * cifs tcp session reconnection
++/**
++ * Mark all sessions and tcons for reconnect.
+  *
+- * mark tcp session as reconnecting so temporarily locked
+- * mark all smb sessions as reconnecting for tcp session
+- * reconnect tcp session
+- * wake up waiters on reconnection? - (not needed currently)
++ * @server needs to be previously set to CifsNeedReconnect.
+  */
+-int
+-cifs_reconnect(struct TCP_Server_Info *server)
++static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server)
+ {
+-      int rc = 0;
+       struct list_head *tmp, *tmp2;
+       struct cifs_ses *ses;
+       struct cifs_tcon *tcon;
+       struct mid_q_entry *mid_entry;
+       struct list_head retry_list;
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-      struct super_block *sb = NULL;
+-      struct cifs_sb_info *cifs_sb = NULL;
+-      struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
+-      struct dfs_cache_tgt_iterator *tgt_it = NULL;
+-#endif
+-      spin_lock(&GlobalMid_Lock);
+-      server->nr_targets = 1;
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-      spin_unlock(&GlobalMid_Lock);
+-      sb = cifs_get_tcp_super(server);
+-      if (IS_ERR(sb)) {
+-              rc = PTR_ERR(sb);
+-              cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
+-                       __func__, rc);
+-              sb = NULL;
+-      } else {
+-              cifs_sb = CIFS_SB(sb);
+-              rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list);
+-              if (rc) {
+-                      cifs_sb = NULL;
+-                      if (rc != -EOPNOTSUPP) {
+-                              cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n",
+-                                              __func__);
+-                      }
+-              } else {
+-                      server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list);
+-              }
+-      }
+-      cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__,
+-               server->nr_targets);
+-      spin_lock(&GlobalMid_Lock);
+-#endif
+-      if (server->tcpStatus == CifsExiting) {
+-              /* the demux thread will exit normally
+-              next time through the loop */
+-              spin_unlock(&GlobalMid_Lock);
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-              dfs_cache_free_tgts(&tgt_list);
+-              cifs_put_tcp_super(sb);
+-#endif
+-              wake_up(&server->response_q);
+-              return rc;
+-      } else
+-              server->tcpStatus = CifsNeedReconnect;
+-      spin_unlock(&GlobalMid_Lock);
+       server->maxBuf = 0;
+       server->max_read = 0;
+       cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
+       trace_smb3_reconnect(server->CurrentMid, server->conn_id, server->hostname);
+-
+-      /* before reconnecting the tcp session, mark the smb session (uid)
+-              and the tid bad so they are not used until reconnected */
+-      cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n",
+-               __func__);
++      /*
++       * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they
++       * are not used until reconnected.
++       */
++      cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", __func__);
+       spin_lock(&cifs_tcp_ses_lock);
+       list_for_each(tmp, &server->smb_ses_list) {
+               ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
+@@ -290,11 +239,11 @@ cifs_reconnect(struct TCP_Server_Info *server)
+       cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
+       mutex_lock(&server->srv_mutex);
+       if (server->ssocket) {
+-              cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n",
+-                       server->ssocket->state, server->ssocket->flags);
++              cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state,
++                       server->ssocket->flags);
+               kernel_sock_shutdown(server->ssocket, SHUT_WR);
+-              cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n",
+-                       server->ssocket->state, server->ssocket->flags);
++              cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", server->ssocket->state,
++                       server->ssocket->flags);
+               sock_release(server->ssocket);
+               server->ssocket = NULL;
+       }
+@@ -333,6 +282,68 @@ cifs_reconnect(struct TCP_Server_Info *server)
+               smbd_destroy(server);
+               mutex_unlock(&server->srv_mutex);
+       }
++}
++
++/*
++ * cifs tcp session reconnection
++ *
++ * mark tcp session as reconnecting so temporarily locked
++ * mark all smb sessions as reconnecting for tcp session
++ * reconnect tcp session
++ * wake up waiters on reconnection? - (not needed currently)
++ */
++int
++cifs_reconnect(struct TCP_Server_Info *server)
++{
++      int rc = 0;
++#ifdef CONFIG_CIFS_DFS_UPCALL
++      struct super_block *sb = NULL;
++      struct cifs_sb_info *cifs_sb = NULL;
++      struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
++      struct dfs_cache_tgt_iterator *tgt_it = NULL;
++#endif
++
++      spin_lock(&GlobalMid_Lock);
++      server->nr_targets = 1;
++#ifdef CONFIG_CIFS_DFS_UPCALL
++      spin_unlock(&GlobalMid_Lock);
++      sb = cifs_get_tcp_super(server);
++      if (IS_ERR(sb)) {
++              rc = PTR_ERR(sb);
++              cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
++                       __func__, rc);
++              sb = NULL;
++      } else {
++              cifs_sb = CIFS_SB(sb);
++              rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list);
++              if (rc) {
++                      cifs_sb = NULL;
++                      if (rc != -EOPNOTSUPP) {
++                              cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n",
++                                              __func__);
++                      }
++              } else {
++                      server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list);
++              }
++      }
++      cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__,
++               server->nr_targets);
++      spin_lock(&GlobalMid_Lock);
++#endif
++      if (server->tcpStatus == CifsExiting) {
++              /* the demux thread will exit normally next time through the loop */
++              spin_unlock(&GlobalMid_Lock);
++#ifdef CONFIG_CIFS_DFS_UPCALL
++              dfs_cache_free_tgts(&tgt_list);
++              cifs_put_tcp_super(sb);
++#endif
++              wake_up(&server->response_q);
++              return rc;
++      } else
++              server->tcpStatus = CifsNeedReconnect;
++      spin_unlock(&GlobalMid_Lock);
++
++      cifs_mark_tcp_ses_conns_for_reconnect(server);
+       do {
+               try_to_freeze();
+-- 
+2.35.1
+
diff --git a/queue-5.15/cifs-split-out-dfs-code-from-cifs_reconnect.patch b/queue-5.15/cifs-split-out-dfs-code-from-cifs_reconnect.patch
new file mode 100644 (file)
index 0000000..839b31b
--- /dev/null
@@ -0,0 +1,383 @@
+From 8a24fd41b19a31b160998ad0fe52a095013acddd Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 14 Oct 2021 17:49:54 -0300
+Subject: cifs: split out dfs code from cifs_reconnect()
+
+From: Paulo Alcantara <pc@cjr.nz>
+
+[ Upstream commit bbcce368044572d0802c3bbb8ef3fe98f581d803 ]
+
+Make two separate functions that handle dfs and non-dfs reconnect
+logics since cifs_reconnect() became way too complex to handle both.
+While at it, add some documentation.
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
+Reviewed-by: Shyam Prasad N <sprasad@microsoft.com>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 1dcdf5f5b213 ("cifs: Fix connections leak when tlink setup failed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/connect.c | 295 +++++++++++++++++++++++++---------------------
+ 1 file changed, 162 insertions(+), 133 deletions(-)
+
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+index ed429a442808..5d87d5c01762 100644
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -148,57 +148,6 @@ static void cifs_resolve_server(struct work_struct *work)
+       mutex_unlock(&server->srv_mutex);
+ }
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-/* These functions must be called with server->srv_mutex held */
+-static void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
+-                                     struct cifs_sb_info *cifs_sb,
+-                                     struct dfs_cache_tgt_list *tgt_list,
+-                                     struct dfs_cache_tgt_iterator **tgt_it)
+-{
+-      const char *name;
+-      int rc;
+-
+-      if (!cifs_sb || !cifs_sb->origin_fullpath)
+-              return;
+-
+-      if (!*tgt_it) {
+-              *tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
+-      } else {
+-              *tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it);
+-              if (!*tgt_it)
+-                      *tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
+-      }
+-
+-      cifs_dbg(FYI, "%s: UNC: %s\n", __func__, cifs_sb->origin_fullpath);
+-
+-      name = dfs_cache_get_tgt_name(*tgt_it);
+-
+-      kfree(server->hostname);
+-
+-      server->hostname = extract_hostname(name);
+-      if (IS_ERR(server->hostname)) {
+-              cifs_dbg(FYI,
+-                       "%s: failed to extract hostname from target: %ld\n",
+-                       __func__, PTR_ERR(server->hostname));
+-              return;
+-      }
+-
+-      rc = reconn_set_ipaddr_from_hostname(server);
+-      if (rc) {
+-              cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
+-                       __func__, rc);
+-      }
+-}
+-
+-static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb,
+-                                         struct dfs_cache_tgt_list *tl)
+-{
+-      if (!cifs_sb->origin_fullpath)
+-              return -EOPNOTSUPP;
+-      return dfs_cache_noreq_find(cifs_sb->origin_fullpath + 1, NULL, tl);
+-}
+-#endif
+-
+ /**
+  * Mark all sessions and tcons for reconnect.
+  *
+@@ -284,6 +233,21 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
+       }
+ }
++static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
++{
++      spin_lock(&GlobalMid_Lock);
++      server->nr_targets = num_targets;
++      if (server->tcpStatus == CifsExiting) {
++              /* the demux thread will exit normally next time through the loop */
++              spin_unlock(&GlobalMid_Lock);
++              wake_up(&server->response_q);
++              return false;
++      }
++      server->tcpStatus = CifsNeedReconnect;
++      spin_unlock(&GlobalMid_Lock);
++      return true;
++}
++
+ /*
+  * cifs tcp session reconnection
+  *
+@@ -292,90 +256,23 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server
+  * reconnect tcp session
+  * wake up waiters on reconnection? - (not needed currently)
+  */
+-int
+-cifs_reconnect(struct TCP_Server_Info *server)
++static int __cifs_reconnect(struct TCP_Server_Info *server)
+ {
+       int rc = 0;
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-      struct super_block *sb = NULL;
+-      struct cifs_sb_info *cifs_sb = NULL;
+-      struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
+-      struct dfs_cache_tgt_iterator *tgt_it = NULL;
+-#endif
+-      spin_lock(&GlobalMid_Lock);
+-      server->nr_targets = 1;
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-      spin_unlock(&GlobalMid_Lock);
+-      sb = cifs_get_tcp_super(server);
+-      if (IS_ERR(sb)) {
+-              rc = PTR_ERR(sb);
+-              cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
+-                       __func__, rc);
+-              sb = NULL;
+-      } else {
+-              cifs_sb = CIFS_SB(sb);
+-              rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list);
+-              if (rc) {
+-                      cifs_sb = NULL;
+-                      if (rc != -EOPNOTSUPP) {
+-                              cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n",
+-                                              __func__);
+-                      }
+-              } else {
+-                      server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list);
+-              }
+-      }
+-      cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__,
+-               server->nr_targets);
+-      spin_lock(&GlobalMid_Lock);
+-#endif
+-      if (server->tcpStatus == CifsExiting) {
+-              /* the demux thread will exit normally next time through the loop */
+-              spin_unlock(&GlobalMid_Lock);
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-              dfs_cache_free_tgts(&tgt_list);
+-              cifs_put_tcp_super(sb);
+-#endif
+-              wake_up(&server->response_q);
+-              return rc;
+-      } else
+-              server->tcpStatus = CifsNeedReconnect;
+-      spin_unlock(&GlobalMid_Lock);
++      if (!cifs_tcp_ses_needs_reconnect(server, 1))
++              return 0;
+       cifs_mark_tcp_ses_conns_for_reconnect(server);
+       do {
+               try_to_freeze();
+-
+               mutex_lock(&server->srv_mutex);
+-
+               if (!cifs_swn_set_server_dstaddr(server)) {
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-              if (cifs_sb && cifs_sb->origin_fullpath)
+-                      /*
+-                       * Set up next DFS target server (if any) for reconnect. If DFS
+-                       * feature is disabled, then we will retry last server we
+-                       * connected to before.
+-                       */
+-                      reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
+-              else {
+-#endif
+-                      /*
+-                       * Resolve the hostname again to make sure that IP address is up-to-date.
+-                       */
++                      /* resolve the hostname again to make sure that IP address is up-to-date */
+                       rc = reconn_set_ipaddr_from_hostname(server);
+-                      if (rc) {
+-                              cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
+-                                              __func__, rc);
+-                      }
+-
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-              }
+-#endif
+-
+-
++                      cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
+               }
+               if (cifs_rdma_enabled(server))
+@@ -383,8 +280,8 @@ cifs_reconnect(struct TCP_Server_Info *server)
+               else
+                       rc = generic_ip_connect(server);
+               if (rc) {
+-                      cifs_dbg(FYI, "reconnect error %d\n", rc);
+                       mutex_unlock(&server->srv_mutex);
++                      cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
+                       msleep(3000);
+               } else {
+                       atomic_inc(&tcpSesReconnectCount);
+@@ -398,19 +295,109 @@ cifs_reconnect(struct TCP_Server_Info *server)
+               }
+       } while (server->tcpStatus == CifsNeedReconnect);
++      if (server->tcpStatus == CifsNeedNegotiate)
++              mod_delayed_work(cifsiod_wq, &server->echo, 0);
++
++      wake_up(&server->response_q);
++      return rc;
++}
++
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+-      if (tgt_it) {
+-              rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1,
+-                                                  tgt_it);
++static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb)
++{
++      int rc = 0;
++      const char *refpath = cifs_sb->origin_fullpath + 1;
++      struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
++      struct dfs_cache_tgt_iterator *tit = NULL;
++      int num_targets = 1;
++      char *hostname;
++
++      /*
++       * Determine the number of dfs targets the referral path in @cifs_sb resolves to.
++       *
++       * smb2_reconnect() needs to know how long it should wait based upon the number of dfs
++       * targets (server->nr_targets).  It's also possible that the cached referral was cleared
++       * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
++       * refreshing the referral, so, in this case, default it to 1.
++       */
++      if (!dfs_cache_noreq_find(refpath, NULL, &tl)) {
++              num_targets = dfs_cache_get_nr_tgts(&tl);
++              if (!num_targets)
++                      num_targets = 1;
++      }
++
++      if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
++              return 0;
++
++      cifs_mark_tcp_ses_conns_for_reconnect(server);
++
++      do {
++              /* Get next dfs target from target list (if any) */
++              if (!tit)
++                      tit = dfs_cache_get_tgt_iterator(&tl);
++              else
++                      tit = dfs_cache_get_next_tgt(&tl, tit);
++
++              try_to_freeze();
++              mutex_lock(&server->srv_mutex);
++
++              if (!cifs_swn_set_server_dstaddr(server)) {
++                      /*
++                       * If any dfs target was selected, then update @server with either a
++                       * hostname or an address.
++                       */
++                      if (tit) {
++                              hostname = extract_hostname(dfs_cache_get_tgt_name(tit));
++                              if (!IS_ERR(hostname)) {
++                                      kfree(server->hostname);
++                                      server->hostname = hostname;
++                              } else {
++                                      cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
++                                               __func__, PTR_ERR(hostname));
++                                      cifs_dbg(FYI, "%s: default to last target server: %s\n",
++                                               __func__, server->hostname);
++                              }
++                      }
++                      /* resolve the hostname again to make sure that IP address is up-to-date. */
++                      rc = reconn_set_ipaddr_from_hostname(server);
++                      cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
++              }
++
++              /* Reconnect the socket */
++              if (cifs_rdma_enabled(server))
++                      rc = smbd_reconnect(server);
++              else
++                      rc = generic_ip_connect(server);
++
+               if (rc) {
+-                      cifs_server_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n",
+-                               __func__, rc);
++                      /* Failed to reconnect socket.  Retry next dfs target. */
++                      mutex_unlock(&server->srv_mutex);
++                      cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
++                      msleep(3000);
++                      continue;
+               }
+-              dfs_cache_free_tgts(&tgt_list);
+-      }
+-      cifs_put_tcp_super(sb);
+-#endif
++              /*
++               * Socket was created.  Update tcp session status to CifsNeedNegotiate so that a
++               * process waiting for reconnect will know it needs to re-establish session and tcon
++               * through the reconnected target server.
++               */
++              atomic_inc(&tcpSesReconnectCount);
++              set_credits(server, 1);
++              spin_lock(&GlobalMid_Lock);
++              if (server->tcpStatus != CifsExiting)
++                      server->tcpStatus = CifsNeedNegotiate;
++              spin_unlock(&GlobalMid_Lock);
++              cifs_swn_reset_server_dstaddr(server);
++              mutex_unlock(&server->srv_mutex);
++      } while (server->tcpStatus == CifsNeedReconnect);
++
++      if (tit)
++              dfs_cache_noreq_update_tgthint(refpath, tit);
++
++      dfs_cache_free_tgts(&tl);
++
++      /* Need to set up echo worker again once connection has been established */
+       if (server->tcpStatus == CifsNeedNegotiate)
+               mod_delayed_work(cifsiod_wq, &server->echo, 0);
+@@ -418,6 +405,48 @@ cifs_reconnect(struct TCP_Server_Info *server)
+       return rc;
+ }
++int cifs_reconnect(struct TCP_Server_Info *server)
++{
++      int rc;
++      struct super_block *sb;
++      struct cifs_sb_info *cifs_sb;
++
++      /*
++       * If tcp session is not an dfs connection or it is a channel, then reconnect to last target
++       * server.
++       */
++      spin_lock(&cifs_tcp_ses_lock);
++      if (!server->is_dfs_conn || server->is_channel) {
++              spin_unlock(&cifs_tcp_ses_lock);
++              return __cifs_reconnect(server);
++      }
++      spin_unlock(&cifs_tcp_ses_lock);
++
++      /* If no superblock, then it might be an ipc connection */
++      sb = cifs_get_tcp_super(server);
++      if (IS_ERR(sb))
++              return __cifs_reconnect(server);
++
++      /*
++       * Check for a referral path to look up in superblock.  If unset, then simply reconnect to
++       * last target server.
++       */
++      cifs_sb = CIFS_SB(sb);
++      if (!cifs_sb->origin_fullpath || !cifs_sb->origin_fullpath[0])
++              rc = __cifs_reconnect(server);
++      else
++              rc = reconnect_dfs_server(server, cifs_sb);
++
++      cifs_put_tcp_super(sb);
++      return rc;
++}
++#else
++int cifs_reconnect(struct TCP_Server_Info *server)
++{
++      return __cifs_reconnect(server);
++}
++#endif
++
+ static void
+ cifs_echo_request(struct work_struct *work)
+ {
+-- 
+2.35.1
+
diff --git a/queue-5.15/cifs-support-nested-dfs-links-over-reconnect.patch b/queue-5.15/cifs-support-nested-dfs-links-over-reconnect.patch
new file mode 100644 (file)
index 0000000..6e2c540
--- /dev/null
@@ -0,0 +1,1918 @@
+From d917a89ec903aa8dd5735ecc50dd3dfc52204dc0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 3 Nov 2021 13:53:29 -0300
+Subject: cifs: support nested dfs links over reconnect
+
+From: Paulo Alcantara <pc@cjr.nz>
+
+[ Upstream commit c88f7dcd6d6429197fc2fd87b54a894ffcd48e8e ]
+
+Mounting a dfs link that has nested links was already supported at
+mount(2), so make it work over reconnect as well.
+
+Make the following case work:
+
+* mount //root/dfs/link /mnt -o ...
+  - final share: /server/share
+
+* in server settings
+  - change target folder of /root/dfs/link3 to /server/share2
+  - change target folder of /root/dfs/link2 to /root/dfs/link3
+  - change target folder of /root/dfs/link to /root/dfs/link2
+
+* mount -o remount,... /mnt
+ - refresh all dfs referrals
+ - mark current connection for failover
+ - cifs_reconnect() reconnects to root server
+ - tree_connect()
+   * checks that /root/dfs/link2 is a link, then chase it
+   * checks that root/dfs/link3 is a link, then chase it
+   * finally tree connect to /server/share2
+
+If the mounted share is no longer accessible and a reconnect had been
+triggered, the client will retry it from both last referral
+path (/root/dfs/link3) and original referral path (/root/dfs/link).
+
+Any new referral paths found while chasing dfs links over reconnect,
+it will be updated to TCP_Server_Info::leaf_fullpath, accordingly.
+
+Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
+Signed-off-by: Steve French <stfrench@microsoft.com>
+Stable-dep-of: 1dcdf5f5b213 ("cifs: Fix connections leak when tlink setup failed")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ fs/cifs/cifs_dfs_ref.c |   59 +--
+ fs/cifs/cifs_fs_sb.h   |    5 -
+ fs/cifs/cifsglob.h     |   24 +-
+ fs/cifs/cifsproto.h    |    5 +-
+ fs/cifs/connect.c      | 1138 ++++++++++++++++++++--------------------
+ fs/cifs/dfs_cache.c    |   44 +-
+ fs/cifs/misc.c         |   62 +--
+ fs/cifs/smb2ops.c      |   10 +-
+ fs/cifs/smb2pdu.c      |    6 +-
+ 9 files changed, 660 insertions(+), 693 deletions(-)
+
+diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c
+index 007427ba75e5..b0864da9ef43 100644
+--- a/fs/cifs/cifs_dfs_ref.c
++++ b/fs/cifs/cifs_dfs_ref.c
+@@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
+ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
+ {
+       struct cifs_sb_info *cifs_sb;
+-      struct cifs_ses *ses;
+-      struct cifs_tcon *tcon;
+       void *page;
+-      char *full_path, *root_path;
+-      unsigned int xid;
+-      int rc;
++      char *full_path;
+       struct vfsmount *mnt;
+       cifs_dbg(FYI, "in %s\n", __func__);
+@@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
+        * the double backslashes usually used in the UNC. This function
+        * gives us the latter, so we must adjust the result.
+        */
+-      mnt = ERR_PTR(-ENOMEM);
+-
+       cifs_sb = CIFS_SB(mntpt->d_sb);
+       if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) {
+               mnt = ERR_PTR(-EREMOTE);
+@@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
+       }
+       convert_delimiter(full_path, '\\');
+-
+       cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
+-      if (!cifs_sb_master_tlink(cifs_sb)) {
+-              cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__);
+-              goto free_full_path;
+-      }
+-
+-      tcon = cifs_sb_master_tcon(cifs_sb);
+-      if (!tcon) {
+-              cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__);
+-              goto free_full_path;
+-      }
+-
+-      root_path = kstrdup(tcon->treeName, GFP_KERNEL);
+-      if (!root_path) {
+-              mnt = ERR_PTR(-ENOMEM);
+-              goto free_full_path;
+-      }
+-      cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path);
+-
+-      ses = tcon->ses;
+-      xid = get_xid();
+-
+-      /*
+-       * If DFS root has been expired, then unconditionally fetch it again to
+-       * refresh DFS referral cache.
+-       */
+-      rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
+-                          root_path + 1, NULL, NULL);
+-      if (!rc) {
+-              rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
+-                                  cifs_remap(cifs_sb), full_path + 1,
+-                                  NULL, NULL);
+-      }
+-
+-      free_xid(xid);
+-
+-      if (rc) {
+-              mnt = ERR_PTR(rc);
+-              goto free_root_path;
+-      }
+-      /*
+-       * OK - we were able to get and cache a referral for @full_path.
+-       *
+-       * Now, pass it down to cifs_mount() and it will retry every available
+-       * node server in case of failures - no need to do it here.
+-       */
+       mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
+-      cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__,
+-               full_path + 1, mnt);
++      cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt);
+-free_root_path:
+-      kfree(root_path);
+ free_full_path:
+       free_dentry_path(page);
+ cdda_exit:
+diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h
+index f97407520ea1..013a4bd65280 100644
+--- a/fs/cifs/cifs_fs_sb.h
++++ b/fs/cifs/cifs_fs_sb.h
+@@ -61,11 +61,6 @@ struct cifs_sb_info {
+       /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */
+       char *prepath;
+-      /*
+-       * Canonical DFS path initially provided by the mount call. We might connect to something
+-       * different via DFS but we want to keep it to do failover properly.
+-       */
+-      char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */
+       /* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
+       uuid_t dfs_mount_id;
+       /*
+diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h
+index a97ed30843cf..1ab72c3d0bff 100644
+--- a/fs/cifs/cifsglob.h
++++ b/fs/cifs/cifsglob.h
+@@ -693,6 +693,19 @@ struct TCP_Server_Info {
+ #endif
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+       bool is_dfs_conn; /* if a dfs connection */
++      struct mutex refpath_lock; /* protects leaf_fullpath */
++      /*
++       * Canonical DFS full paths that were used to chase referrals in mount and reconnect.
++       *
++       * origin_fullpath: first or original referral path
++       * leaf_fullpath: last referral path (might be changed due to nested links in reconnect)
++       *
++       * current_fullpath: pointer to either origin_fullpath or leaf_fullpath
++       * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect()
++       *
++       * format: \\HOST\SHARE\[OPTIONAL PATH]
++       */
++      char *origin_fullpath, *leaf_fullpath, *current_fullpath;
+ #endif
+ };
+@@ -1097,7 +1110,6 @@ struct cifs_tcon {
+       struct cached_fid crfid; /* Cached root fid */
+       /* BB add field for back pointer to sb struct(s)? */
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+-      char *dfs_path; /* canonical DFS path */
+       struct list_head ulist; /* cache update list */
+ #endif
+ };
+@@ -1950,4 +1962,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon)
+               tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT);
+ }
++static inline bool cifs_is_referral_server(struct cifs_tcon *tcon,
++                                         const struct dfs_info3_param *ref)
++{
++      /*
++       * Check if all targets are capable of handling DFS referrals as per
++       * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL.
++       */
++      return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER));
++}
++
+ #endif        /* _CIFS_GLOB_H */
+diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h
+index d0f85b666662..b2697356b5e7 100644
+--- a/fs/cifs/cifsproto.h
++++ b/fs/cifs/cifsproto.h
+@@ -607,7 +607,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,
+ struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
+ void cifs_put_tcp_super(struct super_block *sb);
+-int update_super_prepath(struct cifs_tcon *tcon, char *prefix);
++int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix);
+ char *extract_hostname(const char *unc);
+ char *extract_sharename(const char *unc);
+@@ -634,4 +634,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
+               return options;
+ }
++struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon);
++void cifs_put_tcon_super(struct super_block *sb);
++
+ #endif                        /* _CIFSPROTO_H */
+diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c
+index 5d87d5c01762..902eb8a5afd2 100644
+--- a/fs/cifs/connect.c
++++ b/fs/cifs/connect.c
+@@ -61,6 +61,20 @@ extern bool disable_legacy_dialects;
+ /* Drop the connection to not overload the server */
+ #define NUM_STATUS_IO_TIMEOUT   5
++struct mount_ctx {
++      struct cifs_sb_info *cifs_sb;
++      struct smb3_fs_context *fs_ctx;
++      unsigned int xid;
++      struct TCP_Server_Info *server;
++      struct cifs_ses *ses;
++      struct cifs_tcon *tcon;
++#ifdef CONFIG_CIFS_DFS_UPCALL
++      struct cifs_ses *root_ses;
++      uuid_t mount_id;
++      char *origin_fullpath, *leaf_fullpath;
++#endif
++};
++
+ static int ip_connect(struct TCP_Server_Info *server);
+ static int generic_ip_connect(struct TCP_Server_Info *server);
+ static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
+@@ -303,14 +317,68 @@ static int __cifs_reconnect(struct TCP_Server_Info *server)
+ }
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+-static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb)
++static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target)
++{
++      int rc;
++      char *hostname;
++
++      if (!cifs_swn_set_server_dstaddr(server)) {
++              if (server->hostname != target) {
++                      hostname = extract_hostname(target);
++                      if (!IS_ERR(hostname)) {
++                              kfree(server->hostname);
++                              server->hostname = hostname;
++                      } else {
++                              cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
++                                       __func__, PTR_ERR(hostname));
++                              cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__,
++                                       server->hostname);
++                      }
++              }
++              /* resolve the hostname again to make sure that IP address is up-to-date. */
++              rc = reconn_set_ipaddr_from_hostname(server);
++              cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
++      }
++      /* Reconnect the socket */
++      if (cifs_rdma_enabled(server))
++              rc = smbd_reconnect(server);
++      else
++              rc = generic_ip_connect(server);
++
++      return rc;
++}
++
++static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl,
++                                   struct dfs_cache_tgt_iterator **target_hint)
++{
++      int rc;
++      struct dfs_cache_tgt_iterator *tit;
++
++      *target_hint = NULL;
++
++      /* If dfs target list is empty, then reconnect to last server */
++      tit = dfs_cache_get_tgt_iterator(tl);
++      if (!tit)
++              return __reconnect_target_unlocked(server, server->hostname);
++
++      /* Otherwise, try every dfs target in @tl */
++      for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
++              rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit));
++              if (!rc) {
++                      *target_hint = tit;
++                      break;
++              }
++      }
++      return rc;
++}
++
++static int reconnect_dfs_server(struct TCP_Server_Info *server)
+ {
+       int rc = 0;
+-      const char *refpath = cifs_sb->origin_fullpath + 1;
++      const char *refpath = server->current_fullpath + 1;
+       struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
+-      struct dfs_cache_tgt_iterator *tit = NULL;
+-      int num_targets = 1;
+-      char *hostname;
++      struct dfs_cache_tgt_iterator *target_hint = NULL;
++      int num_targets = 0;
+       /*
+        * Determine the number of dfs targets the referral path in @cifs_sb resolves to.
+@@ -320,11 +388,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i
+        * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
+        * refreshing the referral, so, in this case, default it to 1.
+        */
+-      if (!dfs_cache_noreq_find(refpath, NULL, &tl)) {
++      if (!dfs_cache_noreq_find(refpath, NULL, &tl))
+               num_targets = dfs_cache_get_nr_tgts(&tl);
+-              if (!num_targets)
+-                      num_targets = 1;
+-      }
++      if (!num_targets)
++              num_targets = 1;
+       if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
+               return 0;
+@@ -332,51 +399,17 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i
+       cifs_mark_tcp_ses_conns_for_reconnect(server);
+       do {
+-              /* Get next dfs target from target list (if any) */
+-              if (!tit)
+-                      tit = dfs_cache_get_tgt_iterator(&tl);
+-              else
+-                      tit = dfs_cache_get_next_tgt(&tl, tit);
+-
+               try_to_freeze();
+               mutex_lock(&server->srv_mutex);
+-              if (!cifs_swn_set_server_dstaddr(server)) {
+-                      /*
+-                       * If any dfs target was selected, then update @server with either a
+-                       * hostname or an address.
+-                       */
+-                      if (tit) {
+-                              hostname = extract_hostname(dfs_cache_get_tgt_name(tit));
+-                              if (!IS_ERR(hostname)) {
+-                                      kfree(server->hostname);
+-                                      server->hostname = hostname;
+-                              } else {
+-                                      cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
+-                                               __func__, PTR_ERR(hostname));
+-                                      cifs_dbg(FYI, "%s: default to last target server: %s\n",
+-                                               __func__, server->hostname);
+-                              }
+-                      }
+-                      /* resolve the hostname again to make sure that IP address is up-to-date. */
+-                      rc = reconn_set_ipaddr_from_hostname(server);
+-                      cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
+-              }
+-
+-              /* Reconnect the socket */
+-              if (cifs_rdma_enabled(server))
+-                      rc = smbd_reconnect(server);
+-              else
+-                      rc = generic_ip_connect(server);
+-
++              rc = reconnect_target_unlocked(server, &tl, &target_hint);
+               if (rc) {
+-                      /* Failed to reconnect socket.  Retry next dfs target. */
++                      /* Failed to reconnect socket */
+                       mutex_unlock(&server->srv_mutex);
+                       cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
+                       msleep(3000);
+                       continue;
+               }
+-
+               /*
+                * Socket was created.  Update tcp session status to CifsNeedNegotiate so that a
+                * process waiting for reconnect will know it needs to re-establish session and tcon
+@@ -392,8 +425,8 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i
+               mutex_unlock(&server->srv_mutex);
+       } while (server->tcpStatus == CifsNeedReconnect);
+-      if (tit)
+-              dfs_cache_noreq_update_tgthint(refpath, tit);
++      if (target_hint)
++              dfs_cache_noreq_update_tgthint(refpath, target_hint);
+       dfs_cache_free_tgts(&tl);
+@@ -407,38 +440,15 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i
+ int cifs_reconnect(struct TCP_Server_Info *server)
+ {
+-      int rc;
+-      struct super_block *sb;
+-      struct cifs_sb_info *cifs_sb;
+-
+-      /*
+-       * If tcp session is not an dfs connection or it is a channel, then reconnect to last target
+-       * server.
+-       */
++      /* If tcp session is not an dfs connection, then reconnect to last target server */
+       spin_lock(&cifs_tcp_ses_lock);
+-      if (!server->is_dfs_conn || server->is_channel) {
++      if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) {
+               spin_unlock(&cifs_tcp_ses_lock);
+               return __cifs_reconnect(server);
+       }
+       spin_unlock(&cifs_tcp_ses_lock);
+-      /* If no superblock, then it might be an ipc connection */
+-      sb = cifs_get_tcp_super(server);
+-      if (IS_ERR(sb))
+-              return __cifs_reconnect(server);
+-
+-      /*
+-       * Check for a referral path to look up in superblock.  If unset, then simply reconnect to
+-       * last target server.
+-       */
+-      cifs_sb = CIFS_SB(sb);
+-      if (!cifs_sb->origin_fullpath || !cifs_sb->origin_fullpath[0])
+-              rc = __cifs_reconnect(server);
+-      else
+-              rc = reconnect_dfs_server(server, cifs_sb);
+-
+-      cifs_put_tcp_super(sb);
+-      return rc;
++      return reconnect_dfs_server(server);
+ }
+ #else
+ int cifs_reconnect(struct TCP_Server_Info *server)
+@@ -829,6 +839,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
+                */
+       }
++#ifdef CONFIG_CIFS_DFS_UPCALL
++      kfree(server->origin_fullpath);
++      kfree(server->leaf_fullpath);
++#endif
+       kfree(server);
+       length = atomic_dec_return(&tcpSesAllocCount);
+@@ -1445,6 +1459,9 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
+       INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server);
+       INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
+       mutex_init(&tcp_ses->reconnect_mutex);
++#ifdef CONFIG_CIFS_DFS_UPCALL
++      mutex_init(&tcp_ses->refpath_lock);
++#endif
+       memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
+              sizeof(tcp_ses->srcaddr));
+       memcpy(&tcp_ses->dstaddr, &ctx->dstaddr,
+@@ -2909,73 +2926,64 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
+ }
+ /* Release all succeed connections */
+-static inline void mount_put_conns(struct cifs_sb_info *cifs_sb,
+-                                 unsigned int xid,
+-                                 struct TCP_Server_Info *server,
+-                                 struct cifs_ses *ses, struct cifs_tcon *tcon)
++static inline void mount_put_conns(struct mount_ctx *mnt_ctx)
+ {
+       int rc = 0;
+-      if (tcon)
+-              cifs_put_tcon(tcon);
+-      else if (ses)
+-              cifs_put_smb_ses(ses);
+-      else if (server)
+-              cifs_put_tcp_session(server, 0);
+-      cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
+-      free_xid(xid);
++      if (mnt_ctx->tcon)
++              cifs_put_tcon(mnt_ctx->tcon);
++      else if (mnt_ctx->ses)
++              cifs_put_smb_ses(mnt_ctx->ses);
++      else if (mnt_ctx->server)
++              cifs_put_tcp_session(mnt_ctx->server, 0);
++      mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
++      free_xid(mnt_ctx->xid);
+ }
+ /* Get connections for tcp, ses and tcon */
+-static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
+-                         unsigned int *xid,
+-                         struct TCP_Server_Info **nserver,
+-                         struct cifs_ses **nses, struct cifs_tcon **ntcon)
++static int mount_get_conns(struct mount_ctx *mnt_ctx)
+ {
+       int rc = 0;
+-      struct TCP_Server_Info *server;
+-      struct cifs_ses *ses;
+-      struct cifs_tcon *tcon;
+-
+-      *nserver = NULL;
+-      *nses = NULL;
+-      *ntcon = NULL;
++      struct TCP_Server_Info *server = NULL;
++      struct cifs_ses *ses = NULL;
++      struct cifs_tcon *tcon = NULL;
++      struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
++      struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
++      unsigned int xid;
+-      *xid = get_xid();
++      xid = get_xid();
+       /* get a reference to a tcp session */
+       server = cifs_get_tcp_session(ctx);
+       if (IS_ERR(server)) {
+               rc = PTR_ERR(server);
+-              return rc;
++              server = NULL;
++              goto out;
+       }
+-      *nserver = server;
+-
+       /* get a reference to a SMB session */
+       ses = cifs_get_smb_ses(server, ctx);
+       if (IS_ERR(ses)) {
+               rc = PTR_ERR(ses);
+-              return rc;
++              ses = NULL;
++              goto out;
+       }
+-      *nses = ses;
+-
+       if ((ctx->persistent == true) && (!(ses->server->capabilities &
+                                           SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) {
+               cifs_server_dbg(VFS, "persistent handles not supported by server\n");
+-              return -EOPNOTSUPP;
++              rc = -EOPNOTSUPP;
++              goto out;
+       }
+       /* search for existing tcon to this server share */
+       tcon = cifs_get_tcon(ses, ctx);
+       if (IS_ERR(tcon)) {
+               rc = PTR_ERR(tcon);
+-              return rc;
++              tcon = NULL;
++              goto out;
+       }
+-      *ntcon = tcon;
+-
+       /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
+       if (tcon->posix_extensions)
+               cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
+@@ -2986,17 +2994,19 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif
+                * reset of caps checks mount to see if unix extensions disabled
+                * for just this mount.
+                */
+-              reset_cifs_unix_caps(*xid, tcon, cifs_sb, ctx);
++              reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx);
+               if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
+                   (le64_to_cpu(tcon->fsUnixInfo.Capability) &
+-                   CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP))
+-                      return -EACCES;
++                   CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) {
++                      rc = -EACCES;
++                      goto out;
++              }
+       } else
+               tcon->unix_ext = 0; /* server does not support them */
+       /* do not care if a following call succeed - informational */
+       if (!tcon->pipe && server->ops->qfs_tcon) {
+-              server->ops->qfs_tcon(*xid, tcon, cifs_sb);
++              server->ops->qfs_tcon(xid, tcon, cifs_sb);
+               if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) {
+                       if (tcon->fsDevInfo.DeviceCharacteristics &
+                           cpu_to_le32(FILE_READ_ONLY_DEVICE))
+@@ -3020,7 +3030,13 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif
+           (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx)))
+               cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx);
+-      return 0;
++out:
++      mnt_ctx->server = server;
++      mnt_ctx->ses = ses;
++      mnt_ctx->tcon = tcon;
++      mnt_ctx->xid = xid;
++
++      return rc;
+ }
+ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
+@@ -3050,18 +3066,17 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
+ }
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+-static int mount_get_dfs_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
+-                             unsigned int *xid, struct TCP_Server_Info **nserver,
+-                             struct cifs_ses **nses, struct cifs_tcon **ntcon)
++/* Get unique dfs connections */
++static int mount_get_dfs_conns(struct mount_ctx *mnt_ctx)
+ {
+       int rc;
+-      ctx->nosharesock = true;
+-      rc = mount_get_conns(ctx, cifs_sb, xid, nserver, nses, ntcon);
+-      if (*nserver) {
++      mnt_ctx->fs_ctx->nosharesock = true;
++      rc = mount_get_conns(mnt_ctx);
++      if (mnt_ctx->server) {
+               cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__);
+               spin_lock(&cifs_tcp_ses_lock);
+-              (*nserver)->is_dfs_conn = true;
++              mnt_ctx->server->is_dfs_conn = true;
+               spin_unlock(&cifs_tcp_ses_lock);
+       }
+       return rc;
+@@ -3103,190 +3118,38 @@ build_unc_path_to_root(const struct smb3_fs_context *ctx,
+ }
+ /*
+- * expand_dfs_referral - Perform a dfs referral query and update the cifs_sb
++ * expand_dfs_referral - Update cifs_sb from dfs referral path
+  *
+- * If a referral is found, cifs_sb->ctx->mount_options will be (re-)allocated
+- * to a string containing updated options for the submount.  Otherwise it
+- * will be left untouched.
+- *
+- * Returns the rc from get_dfs_path to the caller, which can be used to
+- * determine whether there were referrals.
++ * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the
++ * submount.  Otherwise it will be left untouched.
+  */
+-static int
+-expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
+-                  struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
+-                  char *ref_path)
++static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path,
++                             struct dfs_info3_param *referral)
+ {
+       int rc;
+-      struct dfs_info3_param referral = {0};
+-      char *full_path = NULL, *mdata = NULL;
+-
+-      if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
+-              return -EREMOTE;
+-
+-      full_path = build_unc_path_to_root(ctx, cifs_sb, true);
+-      if (IS_ERR(full_path))
+-              return PTR_ERR(full_path);
+-
+-      rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
+-                          ref_path, &referral, NULL);
+-      if (!rc) {
+-              char *fake_devname = NULL;
+-
+-              mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options,
+-                                                 full_path + 1, &referral,
+-                                                 &fake_devname);
+-              free_dfs_info_param(&referral);
+-
+-              if (IS_ERR(mdata)) {
+-                      rc = PTR_ERR(mdata);
+-                      mdata = NULL;
+-              } else {
+-                      /*
+-                       * We can not clear out the whole structure since we
+-                       * no longer have an explicit function to parse
+-                       * a mount-string. Instead we need to clear out the
+-                       * individual fields that are no longer valid.
+-                       */
+-                      kfree(ctx->prepath);
+-                      ctx->prepath = NULL;
+-                      rc = cifs_setup_volume_info(ctx, mdata, fake_devname);
+-              }
+-              kfree(fake_devname);
+-              kfree(cifs_sb->ctx->mount_options);
+-              cifs_sb->ctx->mount_options = mdata;
+-      }
+-      kfree(full_path);
+-      return rc;
+-}
+-
+-static int get_next_dfs_tgt(struct dfs_cache_tgt_list *tgt_list,
+-                          struct dfs_cache_tgt_iterator **tgt_it)
+-{
+-      if (!*tgt_it)
+-              *tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
+-      else
+-              *tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it);
+-      return !*tgt_it ? -EHOSTDOWN : 0;
+-}
+-
+-static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it,
+-                         struct smb3_fs_context *fake_ctx, struct smb3_fs_context *ctx)
+-{
+-      const char *tgt = dfs_cache_get_tgt_name(tgt_it);
+-      int len = strlen(tgt) + 2;
+-      char *new_unc;
+-
+-      new_unc = kmalloc(len, GFP_KERNEL);
+-      if (!new_unc)
+-              return -ENOMEM;
+-      scnprintf(new_unc, len, "\\%s", tgt);
+-
+-      kfree(ctx->UNC);
+-      ctx->UNC = new_unc;
+-
+-      if (fake_ctx->prepath) {
+-              kfree(ctx->prepath);
+-              ctx->prepath = fake_ctx->prepath;
+-              fake_ctx->prepath = NULL;
+-      }
+-      memcpy(&ctx->dstaddr, &fake_ctx->dstaddr, sizeof(ctx->dstaddr));
+-
+-      return 0;
+-}
+-
+-static int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb,
+-                         struct smb3_fs_context *ctx, struct cifs_ses *root_ses,
+-                         unsigned int *xid, struct TCP_Server_Info **server,
+-                         struct cifs_ses **ses, struct cifs_tcon **tcon)
+-{
+-      int rc;
+-      char *npath = NULL;
+-      struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list);
+-      struct dfs_cache_tgt_iterator *tgt_it = NULL;
+-      struct smb3_fs_context tmp_ctx = {NULL};
+-
+-      if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
+-              return -EOPNOTSUPP;
+-
+-      npath = dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb));
+-      if (IS_ERR(npath))
+-              return PTR_ERR(npath);
+-
+-      cifs_dbg(FYI, "%s: path=%s full_path=%s\n", __func__, npath, full_path);
+-
+-      rc = dfs_cache_noreq_find(npath, NULL, &tgt_list);
+-      if (rc)
+-              goto out;
+-      /*
+-       * We use a 'tmp_ctx' here because we need pass it down to the mount_{get,put} functions to
+-       * test connection against new DFS targets.
+-       */
+-      rc = smb3_fs_context_dup(&tmp_ctx, ctx);
+-      if (rc)
+-              goto out;
+-
+-      for (;;) {
+-              struct dfs_info3_param ref = {0};
+-              char *fake_devname = NULL, *mdata = NULL;
+-
+-              /* Get next DFS target server - if any */
+-              rc = get_next_dfs_tgt(&tgt_list, &tgt_it);
+-              if (rc)
+-                      break;
+-
+-              rc = dfs_cache_get_tgt_referral(npath, tgt_it, &ref);
+-              if (rc)
+-                      break;
+-
+-              cifs_dbg(FYI, "%s: old ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
+-                       tmp_ctx.prepath);
+-
+-              mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, &ref,
+-                                                 &fake_devname);
+-              free_dfs_info_param(&ref);
+-
+-              if (IS_ERR(mdata)) {
+-                      rc = PTR_ERR(mdata);
+-                      mdata = NULL;
+-              } else
+-                      rc = cifs_setup_volume_info(&tmp_ctx, mdata, fake_devname);
+-
+-              kfree(mdata);
+-              kfree(fake_devname);
+-
+-              if (rc)
+-                      break;
+-
+-              cifs_dbg(FYI, "%s: new ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
+-                       tmp_ctx.prepath);
+-
+-              mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
+-              rc = mount_get_dfs_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon);
+-              if (!rc || (*server && *ses)) {
+-                      /*
+-                       * We were able to connect to new target server. Update current context with
+-                       * new target server.
+-                       */
+-                      rc = update_vol_info(tgt_it, &tmp_ctx, ctx);
+-                      break;
+-              }
+-      }
+-      if (!rc) {
+-              cifs_dbg(FYI, "%s: final ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC,
+-                       tmp_ctx.prepath);
++      struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
++      struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
++      char *fake_devname = NULL, *mdata = NULL;
++
++      mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral,
++                                         &fake_devname);
++      if (IS_ERR(mdata)) {
++              rc = PTR_ERR(mdata);
++              mdata = NULL;
++      } else {
+               /*
+-               * Update DFS target hint in DFS referral cache with the target server we
+-               * successfully reconnected to.
++               * We can not clear out the whole structure since we no longer have an explicit
++               * function to parse a mount-string. Instead we need to clear out the individual
++               * fields that are no longer valid.
+                */
+-              rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses, cifs_sb->local_nls,
+-                                            cifs_remap(cifs_sb), path, tgt_it);
++              kfree(ctx->prepath);
++              ctx->prepath = NULL;
++              rc = cifs_setup_volume_info(ctx, mdata, fake_devname);
+       }
++      kfree(fake_devname);
++      kfree(cifs_sb->ctx->mount_options);
++      cifs_sb->ctx->mount_options = mdata;
+-out:
+-      kfree(npath);
+-      smb3_cleanup_fs_context_contents(&tmp_ctx);
+-      dfs_cache_free_tgts(&tgt_list);
+       return rc;
+ }
+ #endif
+@@ -3393,12 +3256,14 @@ cifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
+  * Check if path is remote (e.g. a DFS share). Return -EREMOTE if it is,
+  * otherwise 0.
+  */
+-static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
+-                        const unsigned int xid,
+-                        struct TCP_Server_Info *server,
+-                        struct cifs_tcon *tcon)
++static int is_path_remote(struct mount_ctx *mnt_ctx)
+ {
+       int rc;
++      struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
++      struct TCP_Server_Info *server = mnt_ctx->server;
++      unsigned int xid = mnt_ctx->xid;
++      struct cifs_tcon *tcon = mnt_ctx->tcon;
++      struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+       char *full_path;
+       if (!server->ops->is_path_accessible)
+@@ -3436,280 +3301,289 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *
+ }
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+-static void set_root_ses(struct cifs_sb_info *cifs_sb, const uuid_t *mount_id, struct cifs_ses *ses,
+-                       struct cifs_ses **root_ses)
++static void set_root_ses(struct mount_ctx *mnt_ctx)
+ {
+-      if (ses) {
++      if (mnt_ctx->ses) {
+               spin_lock(&cifs_tcp_ses_lock);
+-              ses->ses_count++;
++              mnt_ctx->ses->ses_count++;
+               spin_unlock(&cifs_tcp_ses_lock);
+-              dfs_cache_add_refsrv_session(mount_id, ses);
++              dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses);
+       }
+-      *root_ses = ses;
++      mnt_ctx->root_ses = mnt_ctx->ses;
+ }
+-/* Set up next dfs prefix path in @dfs_path */
+-static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx,
+-                          const unsigned int xid, struct TCP_Server_Info *server,
+-                          struct cifs_tcon *tcon, char **dfs_path)
++static int is_dfs_mount(struct mount_ctx *mnt_ctx, bool *isdfs, struct dfs_cache_tgt_list *root_tl)
+ {
+-      char *path, *npath;
+-      int added_treename = is_tcon_dfs(tcon);
+       int rc;
++      struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
++      struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
+-      path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename);
+-      if (!path)
+-              return -ENOMEM;
++      *isdfs = true;
+-      rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
+-      if (rc == -EREMOTE) {
+-              struct smb3_fs_context v = {NULL};
+-              /* if @path contains a tree name, skip it in the prefix path */
+-              if (added_treename) {
+-                      rc = smb3_parse_devname(path, &v);
+-                      if (rc)
+-                              goto out;
+-                      npath = build_unc_path_to_root(&v, cifs_sb, true);
+-                      smb3_cleanup_fs_context_contents(&v);
+-              } else {
+-                      v.UNC = ctx->UNC;
+-                      v.prepath = path + 1;
+-                      npath = build_unc_path_to_root(&v, cifs_sb, true);
+-              }
++      rc = mount_get_conns(mnt_ctx);
++      /*
++       * If called with 'nodfs' mount option, then skip DFS resolving.  Otherwise unconditionally
++       * try to get an DFS referral (even cached) to determine whether it is an DFS mount.
++       *
++       * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
++       * to respond with PATH_NOT_COVERED to requests that include the prefix.
++       */
++      if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
++          dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
++                         ctx->UNC + 1, NULL, root_tl)) {
++              if (rc)
++                      return rc;
++              /* Check if it is fully accessible and then mount it */
++              rc = is_path_remote(mnt_ctx);
++              if (!rc)
++                      *isdfs = false;
++              else if (rc != -EREMOTE)
++                      return rc;
++      }
++      return 0;
++}
+-              if (IS_ERR(npath)) {
+-                      rc = PTR_ERR(npath);
+-                      goto out;
+-              }
++static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path,
++                            const char *ref_path, struct dfs_cache_tgt_iterator *tit)
++{
++      int rc;
++      struct dfs_info3_param ref = {};
++      struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
++      char *oldmnt = cifs_sb->ctx->mount_options;
+-              kfree(*dfs_path);
+-              *dfs_path = npath;
+-              rc = -EREMOTE;
++      rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref);
++      if (rc)
++              goto out;
++
++      rc = expand_dfs_referral(mnt_ctx, full_path, &ref);
++      if (rc)
++              goto out;
++
++      /* Connect to new target only if we were redirected (e.g. mount options changed) */
++      if (oldmnt != cifs_sb->ctx->mount_options) {
++              mount_put_conns(mnt_ctx);
++              rc = mount_get_dfs_conns(mnt_ctx);
++      }
++      if (!rc) {
++              if (cifs_is_referral_server(mnt_ctx->tcon, &ref))
++                      set_root_ses(mnt_ctx);
++              rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls,
++                                            cifs_remap(cifs_sb), ref_path, tit);
+       }
+ out:
+-      kfree(path);
++      free_dfs_info_param(&ref);
+       return rc;
+ }
+-/* Check if resolved targets can handle any DFS referrals */
+-static int is_referral_server(const char *ref_path, struct cifs_sb_info *cifs_sb,
+-                            struct cifs_tcon *tcon, bool *ref_server)
++static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl)
+ {
+       int rc;
+-      struct dfs_info3_param ref = {0};
++      char *full_path;
++      struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
++      struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
++      struct dfs_cache_tgt_iterator *tit;
+-      cifs_dbg(FYI, "%s: ref_path=%s\n", __func__, ref_path);
++      /* Put initial connections as they might be shared with other mounts.  We need unique dfs
++       * connections per mount to properly failover, so mount_get_dfs_conns() must be used from
++       * now on.
++       */
++      mount_put_conns(mnt_ctx);
++      mount_get_dfs_conns(mnt_ctx);
+-      if (is_tcon_dfs(tcon)) {
+-              *ref_server = true;
+-      } else {
+-              char *npath;
++      full_path = build_unc_path_to_root(ctx, cifs_sb, true);
++      if (IS_ERR(full_path))
++              return PTR_ERR(full_path);
+-              npath = dfs_cache_canonical_path(ref_path, cifs_sb->local_nls, cifs_remap(cifs_sb));
+-              if (IS_ERR(npath))
+-                      return PTR_ERR(npath);
++      mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls,
++                                                          cifs_remap(cifs_sb));
++      if (IS_ERR(mnt_ctx->origin_fullpath)) {
++              rc = PTR_ERR(mnt_ctx->origin_fullpath);
++              mnt_ctx->origin_fullpath = NULL;
++              goto out;
++      }
+-              rc = dfs_cache_noreq_find(npath, &ref, NULL);
+-              kfree(npath);
+-              if (rc) {
+-                      cifs_dbg(VFS, "%s: dfs_cache_noreq_find: failed (rc=%d)\n", __func__, rc);
+-                      return rc;
++      /* Try all dfs root targets */
++      for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl);
++           tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) {
++              rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit);
++              if (!rc) {
++                      mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL);
++                      if (!mnt_ctx->leaf_fullpath)
++                              rc = -ENOMEM;
++                      break;
+               }
+-              cifs_dbg(FYI, "%s: ref.flags=0x%x\n", __func__, ref.flags);
+-              /*
+-               * Check if all targets are capable of handling DFS referrals as per
+-               * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL.
+-               */
+-              *ref_server = !!(ref.flags & DFSREF_REFERRAL_SERVER);
+-              free_dfs_info_param(&ref);
+       }
+-      return 0;
++
++out:
++      kfree(full_path);
++      return rc;
+ }
+-int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
++static int __follow_dfs_link(struct mount_ctx *mnt_ctx)
+ {
+-      int rc = 0;
+-      unsigned int xid;
+-      struct TCP_Server_Info *server = NULL;
+-      struct cifs_ses *ses = NULL, *root_ses = NULL;
+-      struct cifs_tcon *tcon = NULL;
+-      int count = 0;
+-      uuid_t mount_id = {0};
+-      char *ref_path = NULL, *full_path = NULL;
+-      char *oldmnt = NULL;
+-      bool ref_server = false;
++      int rc;
++      struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
++      struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
++      char *full_path;
++      struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
++      struct dfs_cache_tgt_iterator *tit;
+-      rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
+-      /*
+-       * If called with 'nodfs' mount option, then skip DFS resolving.  Otherwise unconditionally
+-       * try to get an DFS referral (even cached) to determine whether it is an DFS mount.
+-       *
+-       * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
+-       * to respond with PATH_NOT_COVERED to requests that include the prefix.
+-       */
+-      if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) ||
+-          dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ctx->UNC + 1, NULL,
+-                         NULL)) {
+-              if (rc)
+-                      goto error;
+-              /* Check if it is fully accessible and then mount it */
+-              rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
+-              if (!rc)
+-                      goto out;
+-              if (rc != -EREMOTE)
+-                      goto error;
++      full_path = build_unc_path_to_root(ctx, cifs_sb, true);
++      if (IS_ERR(full_path))
++              return PTR_ERR(full_path);
++
++      kfree(mnt_ctx->leaf_fullpath);
++      mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls,
++                                                        cifs_remap(cifs_sb));
++      if (IS_ERR(mnt_ctx->leaf_fullpath)) {
++              rc = PTR_ERR(mnt_ctx->leaf_fullpath);
++              mnt_ctx->leaf_fullpath = NULL;
++              goto out;
+       }
+-      mount_put_conns(cifs_sb, xid, server, ses, tcon);
+-      /*
+-       * Ignore error check here because we may failover to other targets from cached a
+-       * referral.
+-       */
+-      (void)mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
++      /* Get referral from dfs link */
++      rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls,
++                          cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl);
++      if (rc)
++              goto out;
+-      /* Get path of DFS root */
+-      ref_path = build_unc_path_to_root(ctx, cifs_sb, false);
+-      if (IS_ERR(ref_path)) {
+-              rc = PTR_ERR(ref_path);
+-              ref_path = NULL;
+-              goto error;
++      /* Try all dfs link targets */
++      for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl);
++           tit; tit = dfs_cache_get_next_tgt(&tl, tit)) {
++              rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit);
++              if (!rc) {
++                      rc = is_path_remote(mnt_ctx);
++                      break;
++              }
++      }
++
++out:
++      kfree(full_path);
++      dfs_cache_free_tgts(&tl);
++      return rc;
++}
++
++static int follow_dfs_link(struct mount_ctx *mnt_ctx)
++{
++      int rc;
++      struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
++      struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
++      char *full_path;
++      int num_links = 0;
++
++      full_path = build_unc_path_to_root(ctx, cifs_sb, true);
++      if (IS_ERR(full_path))
++              return PTR_ERR(full_path);
++
++      kfree(mnt_ctx->origin_fullpath);
++      mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls,
++                                                          cifs_remap(cifs_sb));
++      kfree(full_path);
++
++      if (IS_ERR(mnt_ctx->origin_fullpath)) {
++              rc = PTR_ERR(mnt_ctx->origin_fullpath);
++              mnt_ctx->origin_fullpath = NULL;
++              return rc;
+       }
+-      uuid_gen(&mount_id);
+-      set_root_ses(cifs_sb, &mount_id, ses, &root_ses);
+       do {
+-              /* Save full path of last DFS path we used to resolve final target server */
+-              kfree(full_path);
+-              full_path = build_unc_path_to_root(ctx, cifs_sb, !!count);
+-              if (IS_ERR(full_path)) {
+-                      rc = PTR_ERR(full_path);
+-                      full_path = NULL;
+-                      break;
+-              }
+-              /* Chase referral */
+-              oldmnt = cifs_sb->ctx->mount_options;
+-              rc = expand_dfs_referral(xid, root_ses, ctx, cifs_sb, ref_path + 1);
+-              if (rc)
++              rc = __follow_dfs_link(mnt_ctx);
++              if (!rc || rc != -EREMOTE)
+                       break;
+-              /* Connect to new DFS target only if we were redirected */
+-              if (oldmnt != cifs_sb->ctx->mount_options) {
+-                      mount_put_conns(cifs_sb, xid, server, ses, tcon);
+-                      rc = mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
+-              }
+-              if (rc && !server && !ses) {
+-                      /* Failed to connect. Try to connect to other targets in the referral. */
+-                      rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, ctx, root_ses, &xid,
+-                                           &server, &ses, &tcon);
+-              }
+-              if (rc == -EACCES || rc == -EOPNOTSUPP || !server || !ses)
+-                      break;
+-              if (!tcon)
+-                      continue;
++      } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
+-              /* Make sure that requests go through new root servers */
+-              rc = is_referral_server(ref_path + 1, cifs_sb, tcon, &ref_server);
+-              if (rc)
+-                      break;
+-              if (ref_server)
+-                      set_root_ses(cifs_sb, &mount_id, ses, &root_ses);
++      return rc;
++}
+-              /* Get next dfs path and then continue chasing them if -EREMOTE */
+-              rc = next_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path);
+-              /* Prevent recursion on broken link referrals */
+-              if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS)
+-                      rc = -ELOOP;
+-      } while (rc == -EREMOTE);
++/* Set up DFS referral paths for failover */
++static void setup_server_referral_paths(struct mount_ctx *mnt_ctx)
++{
++      struct TCP_Server_Info *server = mnt_ctx->server;
++
++      server->origin_fullpath = mnt_ctx->origin_fullpath;
++      server->leaf_fullpath = mnt_ctx->leaf_fullpath;
++      server->current_fullpath = mnt_ctx->leaf_fullpath;
++      mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL;
++}
+-      if (rc || !tcon || !ses)
++int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
++{
++      int rc;
++      struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
++      struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
++      bool isdfs;
++
++      rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl);
++      if (rc)
+               goto error;
++      if (!isdfs)
++              goto out;
+-      kfree(ref_path);
+-      /*
+-       * Store DFS full path in both superblock and tree connect structures.
+-       *
+-       * For DFS root mounts, the prefix path (cifs_sb->prepath) is preserved during reconnect so
+-       * only the root path is set in cifs_sb->origin_fullpath and tcon->dfs_path. And for DFS
+-       * links, the prefix path is included in both and may be changed during reconnect.  See
+-       * cifs_tree_connect().
+-       */
+-      ref_path = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, cifs_remap(cifs_sb));
+-      kfree(full_path);
+-      full_path = NULL;
++      uuid_gen(&mnt_ctx.mount_id);
++      rc = connect_dfs_root(&mnt_ctx, &tl);
++      dfs_cache_free_tgts(&tl);
+-      if (IS_ERR(ref_path)) {
+-              rc = PTR_ERR(ref_path);
+-              ref_path = NULL;
++      if (rc)
+               goto error;
+-      }
+-      cifs_sb->origin_fullpath = ref_path;
+-      ref_path = kstrdup(cifs_sb->origin_fullpath, GFP_KERNEL);
+-      if (!ref_path) {
+-              rc = -ENOMEM;
++      rc = is_path_remote(&mnt_ctx);
++      if (rc == -EREMOTE)
++              rc = follow_dfs_link(&mnt_ctx);
++      if (rc)
+               goto error;
+-      }
+-      spin_lock(&cifs_tcp_ses_lock);
+-      tcon->dfs_path = ref_path;
+-      ref_path = NULL;
+-      spin_unlock(&cifs_tcp_ses_lock);
++      setup_server_referral_paths(&mnt_ctx);
+       /*
+-       * After reconnecting to a different server, unique ids won't
+-       * match anymore, so we disable serverino. This prevents
+-       * dentry revalidation to think the dentry are stale (ESTALE).
++       * After reconnecting to a different server, unique ids won't match anymore, so we disable
++       * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
+        */
+       cifs_autodisable_serverino(cifs_sb);
+       /*
+-       * Force the use of prefix path to support failover on DFS paths that
+-       * resolve to targets that have different prefix paths.
++       * Force the use of prefix path to support failover on DFS paths that resolve to targets
++       * that have different prefix paths.
+        */
+       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+       kfree(cifs_sb->prepath);
+       cifs_sb->prepath = ctx->prepath;
+       ctx->prepath = NULL;
+-      uuid_copy(&cifs_sb->dfs_mount_id, &mount_id);
++      uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id);
+ out:
+-      free_xid(xid);
+-      cifs_try_adding_channels(cifs_sb, ses);
+-      return mount_setup_tlink(cifs_sb, ses, tcon);
++      free_xid(mnt_ctx.xid);
++      cifs_try_adding_channels(cifs_sb, mnt_ctx.ses);
++      return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
+ error:
+-      kfree(ref_path);
+-      kfree(full_path);
+-      kfree(cifs_sb->origin_fullpath);
+-      dfs_cache_put_refsrv_sessions(&mount_id);
+-      mount_put_conns(cifs_sb, xid, server, ses, tcon);
++      dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id);
++      kfree(mnt_ctx.origin_fullpath);
++      kfree(mnt_ctx.leaf_fullpath);
++      mount_put_conns(&mnt_ctx);
+       return rc;
+ }
+ #else
+ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
+ {
+       int rc = 0;
+-      unsigned int xid;
+-      struct cifs_ses *ses;
+-      struct cifs_tcon *tcon;
+-      struct TCP_Server_Info *server;
++      struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
+-      rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
++      rc = mount_get_conns(&mnt_ctx);
+       if (rc)
+               goto error;
+-      if (tcon) {
+-              rc = is_path_remote(cifs_sb, ctx, xid, server, tcon);
++      if (mnt_ctx.tcon) {
++              rc = is_path_remote(&mnt_ctx);
+               if (rc == -EREMOTE)
+                       rc = -EOPNOTSUPP;
+               if (rc)
+                       goto error;
+       }
+-      free_xid(xid);
+-
+-      return mount_setup_tlink(cifs_sb, ses, tcon);
++      free_xid(mnt_ctx.xid);
++      return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
+ error:
+-      mount_put_conns(cifs_sb, xid, server, ses, tcon);
++      mount_put_conns(&mnt_ctx);
+       return rc;
+ }
+ #endif
+@@ -3877,7 +3751,6 @@ cifs_umount(struct cifs_sb_info *cifs_sb)
+       kfree(cifs_sb->prepath);
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+       dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id);
+-      kfree(cifs_sb->origin_fullpath);
+ #endif
+       call_rcu(&cifs_sb->rcu, delayed_free);
+ }
+@@ -4204,104 +4077,249 @@ cifs_prune_tlinks(struct work_struct *work)
+ }
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+-int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
++static void mark_tcon_tcp_ses_for_reconnect(struct cifs_tcon *tcon)
++{
++      int i;
++
++      for (i = 0; i < tcon->ses->chan_count; i++) {
++              spin_lock(&GlobalMid_Lock);
++              if (tcon->ses->chans[i].server->tcpStatus != CifsExiting)
++                      tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect;
++              spin_unlock(&GlobalMid_Lock);
++      }
++}
++
++/* Update dfs referral path of superblock */
++static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb,
++                                const char *target)
++{
++      int rc = 0;
++      size_t len = strlen(target);
++      char *refpath, *npath;
++
++      if (unlikely(len < 2 || *target != '\\'))
++              return -EINVAL;
++
++      if (target[1] == '\\') {
++              len += 1;
++              refpath = kmalloc(len, GFP_KERNEL);
++              if (!refpath)
++                      return -ENOMEM;
++
++              scnprintf(refpath, len, "%s", target);
++      } else {
++              len += sizeof("\\");
++              refpath = kmalloc(len, GFP_KERNEL);
++              if (!refpath)
++                      return -ENOMEM;
++
++              scnprintf(refpath, len, "\\%s", target);
++      }
++
++      npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb));
++      kfree(refpath);
++
++      if (IS_ERR(npath)) {
++              rc = PTR_ERR(npath);
++      } else {
++              mutex_lock(&server->refpath_lock);
++              kfree(server->leaf_fullpath);
++              server->leaf_fullpath = npath;
++              mutex_unlock(&server->refpath_lock);
++              server->current_fullpath = server->leaf_fullpath;
++      }
++      return rc;
++}
++
++static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host,
++                                     size_t tcp_host_len, char *share, bool *target_match)
++{
++      int rc = 0;
++      const char *dfs_host;
++      size_t dfs_host_len;
++
++      *target_match = true;
++      extract_unc_hostname(share, &dfs_host, &dfs_host_len);
++
++      /* Check if hostnames or addresses match */
++      if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
++              cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
++                       dfs_host, (int)tcp_host_len, tcp_host);
++              rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
++              if (rc)
++                      cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
++      }
++      return rc;
++}
++
++int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
++                            struct cifs_sb_info *cifs_sb, char *tree,
++                            struct dfs_cache_tgt_list *tl, struct dfs_info3_param *ref)
+ {
+       int rc;
+       struct TCP_Server_Info *server = tcon->ses->server;
+       const struct smb_version_operations *ops = server->ops;
+-      struct dfs_cache_tgt_list tl;
+-      struct dfs_cache_tgt_iterator *it = NULL;
+-      char *tree;
++      struct cifs_tcon *ipc = tcon->ses->tcon_ipc;
++      bool islink;
++      char *share = NULL, *prefix = NULL;
+       const char *tcp_host;
+       size_t tcp_host_len;
+-      const char *dfs_host;
+-      size_t dfs_host_len;
+-      char *share = NULL, *prefix = NULL;
+-      struct dfs_info3_param ref = {0};
+-      bool isroot;
++      struct dfs_cache_tgt_iterator *tit;
++      bool target_match;
+-      tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
+-      if (!tree)
+-              return -ENOMEM;
++      extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
+-      /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
+-      if (!tcon->dfs_path || dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl)) {
+-              if (tcon->ipc) {
+-                      scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
+-                      rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
+-              } else {
+-                      rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
+-              }
++      islink = ref->server_type == DFS_TYPE_LINK;
++      free_dfs_info_param(ref);
++
++      tit = dfs_cache_get_tgt_iterator(tl);
++      if (!tit) {
++              rc = -ENOENT;
+               goto out;
+       }
+-      isroot = ref.server_type == DFS_TYPE_ROOT;
+-      free_dfs_info_param(&ref);
+-
+-      extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
+-
+-      for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) {
+-              bool target_match;
++      /* Try to tree connect to all dfs targets */
++      for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
++              const char *target = dfs_cache_get_tgt_name(tit);
++              struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl);
+               kfree(share);
+               kfree(prefix);
+-              share = NULL;
+-              prefix = NULL;
+-              rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix);
++              /* Check if share matches with tcp ses */
++              rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix);
+               if (rc) {
+-                      cifs_dbg(VFS, "%s: failed to parse target share %d\n",
+-                               __func__, rc);
+-                      continue;
++                      cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc);
++                      break;
+               }
+-              extract_unc_hostname(share, &dfs_host, &dfs_host_len);
+-
+-              if (dfs_host_len != tcp_host_len
+-                  || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
+-                      cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
+-                               dfs_host, (int)tcp_host_len, tcp_host);
++              rc = target_share_matches_server(server, tcp_host, tcp_host_len, share,
++                                               &target_match);
++              if (rc)
++                      break;
++              if (!target_match) {
++                      rc = -EHOSTUNREACH;
++                      continue;
++              }
+-                      rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match);
+-                      if (rc) {
+-                              cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
++              if (ipc->need_reconnect) {
++                      scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
++                      rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls);
++                      if (rc)
+                               break;
+-                      }
++              }
+-                      if (!target_match) {
+-                              cifs_dbg(FYI, "%s: skipping target\n", __func__);
++              scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
++              if (!islink) {
++                      rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
++                      break;
++              }
++              /*
++               * If no dfs referrals were returned from link target, then just do a TREE_CONNECT
++               * to it.  Otherwise, cache the dfs referral and then mark current tcp ses for
++               * reconnect so either the demultiplex thread or the echo worker will reconnect to
++               * newly resolved target.
++               */
++              if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
++                                 ref, &ntl)) {
++                      rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
++                      if (rc)
+                               continue;
+-                      }
++                      rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit);
++                      if (!rc)
++                              rc = cifs_update_super_prepath(cifs_sb, prefix);
++                      break;
+               }
++              /* Target is another dfs share */
++              rc = update_server_fullpath(server, cifs_sb, target);
++              dfs_cache_free_tgts(tl);
+-              if (tcon->ipc) {
+-                      scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share);
+-                      rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
++              if (!rc) {
++                      rc = -EREMOTE;
++                      list_replace_init(&ntl.tl_list, &tl->tl_list);
+               } else {
+-                      scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
+-                      rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
+-                      /* Only handle prefix paths of DFS link targets */
+-                      if (!rc && !isroot) {
+-                              rc = update_super_prepath(tcon, prefix);
+-                              break;
+-                      }
++                      dfs_cache_free_tgts(&ntl);
++                      free_dfs_info_param(ref);
+               }
+-              if (rc == -EREMOTE)
+-                      break;
++              break;
+       }
++out:
+       kfree(share);
+       kfree(prefix);
+-      if (!rc) {
+-              if (it)
+-                      rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, it);
+-              else
+-                      rc = -ENOENT;
++      return rc;
++}
++
++int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
++                          struct cifs_sb_info *cifs_sb, char *tree,
++                          struct dfs_cache_tgt_list *tl, struct dfs_info3_param *ref)
++{
++      int rc;
++      int num_links = 0;
++      struct TCP_Server_Info *server = tcon->ses->server;
++
++      do {
++              rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, tl, ref);
++              if (!rc || rc != -EREMOTE)
++                      break;
++      } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
++      /*
++       * If we couldn't tree connect to any targets from last referral path, then retry from
++       * original referral path.
++       */
++      if (rc && server->current_fullpath != server->origin_fullpath) {
++              server->current_fullpath = server->origin_fullpath;
++              mark_tcon_tcp_ses_for_reconnect(tcon);
+       }
+-      dfs_cache_free_tgts(&tl);
++
++      dfs_cache_free_tgts(tl);
++      return rc;
++}
++
++int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
++{
++      int rc;
++      struct TCP_Server_Info *server = tcon->ses->server;
++      const struct smb_version_operations *ops = server->ops;
++      struct super_block *sb = NULL;
++      struct cifs_sb_info *cifs_sb;
++      struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
++      char *tree;
++      struct dfs_info3_param ref = {0};
++
++      tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
++      if (!tree)
++              return -ENOMEM;
++
++      if (tcon->ipc) {
++              scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
++              rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
++              goto out;
++      }
++
++      sb = cifs_get_tcp_super(server);
++      if (IS_ERR(sb)) {
++              rc = PTR_ERR(sb);
++              cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc);
++              goto out;
++      }
++
++      cifs_sb = CIFS_SB(sb);
++
++      /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
++      if (!server->current_fullpath ||
++          dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) {
++              rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, cifs_sb->local_nls);
++              goto out;
++      }
++
++      rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, &tl, &ref);
++
+ out:
+       kfree(tree);
++      cifs_put_tcp_super(sb);
++
+       return rc;
+ }
+ #else
+diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c
+index 283745592844..1f3efa7821a0 100644
+--- a/fs/cifs/dfs_cache.c
++++ b/fs/cifs/dfs_cache.c
+@@ -1364,9 +1364,9 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach
+ }
+ /* Refresh dfs referral of tcon and mark it for reconnect if needed */
+-static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh)
++static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon,
++                        bool force_refresh)
+ {
+-      const char *path = tcon->dfs_path + 1;
+       struct cifs_ses *ses;
+       struct cache_entry *ce;
+       struct dfs_info3_param *refs = NULL;
+@@ -1422,6 +1422,20 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool
+       return rc;
+ }
++static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh)
++{
++      struct TCP_Server_Info *server = tcon->ses->server;
++
++      mutex_lock(&server->refpath_lock);
++      if (strcasecmp(server->leaf_fullpath, server->origin_fullpath))
++              __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh);
++      mutex_unlock(&server->refpath_lock);
++
++      __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh);
++
++      return 0;
++}
++
+ /**
+  * dfs_cache_remount_fs - remount a DFS share
+  *
+@@ -1435,6 +1449,7 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool
+ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
+ {
+       struct cifs_tcon *tcon;
++      struct TCP_Server_Info *server;
+       struct mount_group *mg;
+       struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL};
+       int rc;
+@@ -1443,13 +1458,15 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
+               return -EINVAL;
+       tcon = cifs_sb_master_tcon(cifs_sb);
+-      if (!tcon->dfs_path) {
+-              cifs_dbg(FYI, "%s: not a dfs tcon\n", __func__);
++      server = tcon->ses->server;
++
++      if (!server->origin_fullpath) {
++              cifs_dbg(FYI, "%s: not a dfs mount\n", __func__);
+               return 0;
+       }
+       if (uuid_is_null(&cifs_sb->dfs_mount_id)) {
+-              cifs_dbg(FYI, "%s: tcon has no dfs mount group id\n", __func__);
++              cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__);
+               return -EINVAL;
+       }
+@@ -1457,7 +1474,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
+       mg = find_mount_group_locked(&cifs_sb->dfs_mount_id);
+       if (IS_ERR(mg)) {
+               mutex_unlock(&mount_group_list_lock);
+-              cifs_dbg(FYI, "%s: tcon has ipc session to refresh referral\n", __func__);
++              cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__);
+               return PTR_ERR(mg);
+       }
+       kref_get(&mg->refcount);
+@@ -1498,9 +1515,12 @@ static void refresh_mounts(struct cifs_ses **sessions)
+       spin_lock(&cifs_tcp_ses_lock);
+       list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
++              if (!server->is_dfs_conn)
++                      continue;
++
+               list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
+                       list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
+-                              if (tcon->dfs_path) {
++                              if (!tcon->ipc && !tcon->need_reconnect) {
+                                       tcon->tc_count++;
+                                       list_add_tail(&tcon->ulist, &tcons);
+                               }
+@@ -1510,8 +1530,16 @@ static void refresh_mounts(struct cifs_ses **sessions)
+       spin_unlock(&cifs_tcp_ses_lock);
+       list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
++              struct TCP_Server_Info *server = tcon->ses->server;
++
+               list_del_init(&tcon->ulist);
+-              refresh_tcon(sessions, tcon, false);
++
++              mutex_lock(&server->refpath_lock);
++              if (strcasecmp(server->leaf_fullpath, server->origin_fullpath))
++                      __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false);
++              mutex_unlock(&server->refpath_lock);
++
++              __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false);
+               cifs_put_tcon(tcon);
+       }
+ }
+diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c
+index 699f676ded47..94143d7f58c7 100644
+--- a/fs/cifs/misc.c
++++ b/fs/cifs/misc.c
+@@ -139,9 +139,6 @@ tconInfoFree(struct cifs_tcon *buf_to_free)
+       kfree(buf_to_free->nativeFileSystem);
+       kfree_sensitive(buf_to_free->password);
+       kfree(buf_to_free->crfid.fid);
+-#ifdef CONFIG_CIFS_DFS_UPCALL
+-      kfree(buf_to_free->dfs_path);
+-#endif
+       kfree(buf_to_free);
+ }
+@@ -1299,69 +1296,20 @@ int match_target_ip(struct TCP_Server_Info *server,
+       return rc;
+ }
+-static void tcon_super_cb(struct super_block *sb, void *arg)
++int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix)
+ {
+-      struct super_cb_data *sd = arg;
+-      struct cifs_tcon *tcon = sd->data;
+-      struct cifs_sb_info *cifs_sb;
+-
+-      if (sd->sb)
+-              return;
+-
+-      cifs_sb = CIFS_SB(sb);
+-      if (tcon->dfs_path && cifs_sb->origin_fullpath &&
+-          !strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath))
+-              sd->sb = sb;
+-}
+-
+-static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
+-{
+-      return __cifs_get_super(tcon_super_cb, tcon);
+-}
+-
+-static inline void cifs_put_tcon_super(struct super_block *sb)
+-{
+-      __cifs_put_super(sb);
+-}
+-#else
+-static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
+-{
+-      return ERR_PTR(-EOPNOTSUPP);
+-}
+-
+-static inline void cifs_put_tcon_super(struct super_block *sb)
+-{
+-}
+-#endif
+-
+-int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
+-{
+-      struct super_block *sb;
+-      struct cifs_sb_info *cifs_sb;
+-      int rc = 0;
+-
+-      sb = cifs_get_tcon_super(tcon);
+-      if (IS_ERR(sb))
+-              return PTR_ERR(sb);
+-
+-      cifs_sb = CIFS_SB(sb);
+-
+       kfree(cifs_sb->prepath);
+       if (prefix && *prefix) {
+               cifs_sb->prepath = kstrdup(prefix, GFP_ATOMIC);
+-              if (!cifs_sb->prepath) {
+-                      rc = -ENOMEM;
+-                      goto out;
+-              }
++              if (!cifs_sb->prepath)
++                      return -ENOMEM;
+               convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb));
+       } else
+               cifs_sb->prepath = NULL;
+       cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
+-
+-out:
+-      cifs_put_tcon_super(sb);
+-      return rc;
++      return 0;
+ }
++#endif
+diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c
+index 53e87466e3b2..5e6526c201fe 100644
+--- a/fs/cifs/smb2ops.c
++++ b/fs/cifs/smb2ops.c
+@@ -2869,6 +2869,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+       struct fsctl_get_dfs_referral_req *dfs_req = NULL;
+       struct get_dfs_referral_rsp *dfs_rsp = NULL;
+       u32 dfs_req_size = 0, dfs_rsp_size = 0;
++      int retry_count = 0;
+       cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name);
+@@ -2920,11 +2921,14 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
+                               true /* is_fsctl */,
+                               (char *)dfs_req, dfs_req_size, CIFSMaxBufSize,
+                               (char **)&dfs_rsp, &dfs_rsp_size);
+-      } while (rc == -EAGAIN);
++              if (!is_retryable_error(rc))
++                      break;
++              usleep_range(512, 2048);
++      } while (++retry_count < 5);
+       if (rc) {
+-              if ((rc != -ENOENT) && (rc != -EOPNOTSUPP))
+-                      cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc);
++              if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP)
++                      cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc);
+               goto out;
+       }
+diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c
+index 8aa0372141f5..c0ea2813978b 100644
+--- a/fs/cifs/smb2pdu.c
++++ b/fs/cifs/smb2pdu.c
+@@ -156,7 +156,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
+       if (tcon == NULL)
+               return 0;
+-      if (smb2_command == SMB2_TREE_CONNECT)
++      /*
++       * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in
++       * cifs_tree_connect().
++       */
++      if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
+               return 0;
+       if (tcon->tidStatus == CifsExiting) {
+-- 
+2.35.1
+
diff --git a/queue-5.15/drm-display-don-t-assume-dual-mode-adaptors-support-.patch b/queue-5.15/drm-display-don-t-assume-dual-mode-adaptors-support-.patch
new file mode 100644 (file)
index 0000000..df96cc6
--- /dev/null
@@ -0,0 +1,150 @@
+From cee0048a9d79033e208d9bee7446113a2df7f010 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 6 Oct 2022 11:33:14 +0200
+Subject: drm/display: Don't assume dual mode adaptors support i2c
+ sub-addressing
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Simon Rettberg <simon.rettberg@rz.uni-freiburg.de>
+
+[ Upstream commit 5954acbacbd1946b96ce8ee799d309cb0cd3cb9d ]
+
+Current dual mode adaptor ("DP++") detection code assumes that all
+adaptors support i2c sub-addressing for read operations from the
+DP-HDMI adaptor ID buffer.  It has been observed that multiple
+adaptors do not in fact support this, and always return data starting
+at register 0.  On affected adaptors, the code fails to read the proper
+registers that would identify the device as a type 2 adaptor, and
+handles those as type 1, limiting the TMDS clock to 165MHz, even if
+the according register would announce a higher TMDS clock.
+Fix this by always reading the ID buffer starting from offset 0, and
+discarding any bytes before the actual offset of interest.
+
+We tried finding authoritative documentation on whether or not this is
+allowed behaviour, but since all the official VESA docs are paywalled,
+the best we could come up with was the spec sheet for Texas Instruments'
+SNx5DP149 chip family.[1]  It explicitly mentions that sub-addressing is
+supported for register writes, but *not* for reads (See NOTE in
+section 8.5.3).  Unless TI openly decided to violate the VESA spec, one
+could take that as a hint that sub-addressing is in fact not mandated
+by VESA.
+The other two adaptors affected used the PS8409(A) and the LT8611,
+according to the data returned from their ID buffers.
+
+[1] https://www.ti.com/lit/ds/symlink/sn75dp149.pdf
+
+Cc: stable@vger.kernel.org
+Signed-off-by: Simon Rettberg <simon.rettberg@rz.uni-freiburg.de>
+Reviewed-by: Rafael Gieschke <rafael.gieschke@rz.uni-freiburg.de>
+Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
+Link: https://patchwork.freedesktop.org/patch/msgid/20221006113314.41101987@computer
+Acked-by: Jani Nikula <jani.nikula@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_dp_dual_mode_helper.c | 51 +++++++++++++----------
+ 1 file changed, 29 insertions(+), 22 deletions(-)
+
+diff --git a/drivers/gpu/drm/drm_dp_dual_mode_helper.c b/drivers/gpu/drm/drm_dp_dual_mode_helper.c
+index 9faf49354cab..cb52a00ae1b1 100644
+--- a/drivers/gpu/drm/drm_dp_dual_mode_helper.c
++++ b/drivers/gpu/drm/drm_dp_dual_mode_helper.c
+@@ -63,23 +63,45 @@
+ ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter,
+                             u8 offset, void *buffer, size_t size)
+ {
++      u8 zero = 0;
++      char *tmpbuf = NULL;
++      /*
++       * As sub-addressing is not supported by all adaptors,
++       * always explicitly read from the start and discard
++       * any bytes that come before the requested offset.
++       * This way, no matter whether the adaptor supports it
++       * or not, we'll end up reading the proper data.
++       */
+       struct i2c_msg msgs[] = {
+               {
+                       .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
+                       .flags = 0,
+                       .len = 1,
+-                      .buf = &offset,
++                      .buf = &zero,
+               },
+               {
+                       .addr = DP_DUAL_MODE_SLAVE_ADDRESS,
+                       .flags = I2C_M_RD,
+-                      .len = size,
++                      .len = size + offset,
+                       .buf = buffer,
+               },
+       };
+       int ret;
++      if (offset) {
++              tmpbuf = kmalloc(size + offset, GFP_KERNEL);
++              if (!tmpbuf)
++                      return -ENOMEM;
++
++              msgs[1].buf = tmpbuf;
++      }
++
+       ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs));
++      if (tmpbuf)
++              memcpy(buffer, tmpbuf + offset, size);
++
++      kfree(tmpbuf);
++
+       if (ret < 0)
+               return ret;
+       if (ret != ARRAY_SIZE(msgs))
+@@ -208,18 +230,6 @@ enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
+       if (ret)
+               return DRM_DP_DUAL_MODE_UNKNOWN;
+-      /*
+-       * Sigh. Some (maybe all?) type 1 adaptors are broken and ack
+-       * the offset but ignore it, and instead they just always return
+-       * data from the start of the HDMI ID buffer. So for a broken
+-       * type 1 HDMI adaptor a single byte read will always give us
+-       * 0x44, and for a type 1 DVI adaptor it should give 0x00
+-       * (assuming it implements any registers). Fortunately neither
+-       * of those values will match the type 2 signature of the
+-       * DP_DUAL_MODE_ADAPTOR_ID register so we can proceed with
+-       * the type 2 adaptor detection safely even in the presence
+-       * of broken type 1 adaptors.
+-       */
+       ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID,
+                                   &adaptor_id, sizeof(adaptor_id));
+       drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret);
+@@ -233,11 +243,10 @@ enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev,
+                               return DRM_DP_DUAL_MODE_TYPE2_DVI;
+               }
+               /*
+-               * If neither a proper type 1 ID nor a broken type 1 adaptor
+-               * as described above, assume type 1, but let the user know
+-               * that we may have misdetected the type.
++               * If not a proper type 1 ID, still assume type 1, but let
++               * the user know that we may have misdetected the type.
+                */
+-              if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0])
++              if (!is_type1_adaptor(adaptor_id))
+                       drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id);
+       }
+@@ -343,10 +352,8 @@ EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output);
+  * @enable: enable (as opposed to disable) the TMDS output buffers
+  *
+  * Set the state of the TMDS output buffers in the adaptor. For
+- * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. As
+- * some type 1 adaptors have problems with registers (see comments
+- * in drm_dp_dual_mode_detect()) we avoid touching the register,
+- * making this function a no-op on type 1 adaptors.
++ * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register.
++ * Type1 adaptors do not support any register writes.
+  *
+  * Returns:
+  * 0 on success, negative error code on failure
+-- 
+2.35.1
+
diff --git a/queue-5.15/drm-panel-orientation-quirks-add-quirk-for-acer-swit.patch b/queue-5.15/drm-panel-orientation-quirks-add-quirk-for-acer-swit.patch
new file mode 100644 (file)
index 0000000..cb64c3b
--- /dev/null
@@ -0,0 +1,43 @@
+From 32b2843276359867282525062adf43bb79160961 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 6 Nov 2022 22:50:52 +0100
+Subject: drm: panel-orientation-quirks: Add quirk for Acer Switch V 10
+ (SW5-017)
+
+From: Hans de Goede <hdegoede@redhat.com>
+
+[ Upstream commit 653f2d94fcda200b02bd79cea2e0307b26c1b747 ]
+
+Like the Acer Switch One 10 S1003, for which there already is a quirk,
+the Acer Switch V 10 (SW5-017) has a 800x1280 portrait screen mounted
+in the tablet part of a landscape oriented 2-in-1. Add a quirk for this.
+
+Cc: Rudolf Polzer <rpolzer@google.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Acked-by: Simon Ser <contact@emersion.fr>
+Link: https://patchwork.freedesktop.org/patch/msgid/20221106215052.66995-1-hdegoede@redhat.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c
+index 083273736c83..ca0fefeaab20 100644
+--- a/drivers/gpu/drm/drm_panel_orientation_quirks.c
++++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c
+@@ -128,6 +128,12 @@ static const struct dmi_system_id orientation_data[] = {
+                 DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"),
+               },
+               .driver_data = (void *)&lcd800x1280_rightside_up,
++      }, {    /* Acer Switch V 10 (SW5-017) */
++              .matches = {
++                DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"),
++                DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"),
++              },
++              .driver_data = (void *)&lcd800x1280_rightside_up,
+       }, {    /* Anbernic Win600 */
+               .matches = {
+                 DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Anbernic"),
+-- 
+2.35.1
+
diff --git a/queue-5.15/iio-ms5611-simplify-io-callback-parameters.patch b/queue-5.15/iio-ms5611-simplify-io-callback-parameters.patch
new file mode 100644 (file)
index 0000000..b115916
--- /dev/null
@@ -0,0 +1,188 @@
+From 68e124a64d94959ad595d88ce6937af8b2a82a4b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 20 Oct 2021 16:21:10 +0200
+Subject: iio: ms5611: Simplify IO callback parameters
+
+From: Lars-Peter Clausen <lars@metafoo.de>
+
+[ Upstream commit dc19fa63ad80a636fdbc1a02153d1ab140cb901f ]
+
+The ms5611 passes &indio_dev->dev as a parameter to all its IO callbacks
+only to directly cast the struct device back to struct iio_dev. And the
+struct iio_dev is then only used to get the drivers state struct.
+
+Simplify this a bit by passing the state struct directly. This makes it a
+bit easier to follow what the code is doing.
+
+Signed-off-by: Lars-Peter Clausen <lars@metafoo.de>
+Link: https://lore.kernel.org/r/20211020142110.7060-1-lars@metafoo.de
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Stable-dep-of: 17f442e7e475 ("iio: pressure: ms5611: fixed value compensation bug")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iio/pressure/ms5611.h      |  6 +++---
+ drivers/iio/pressure/ms5611_core.c |  7 +++----
+ drivers/iio/pressure/ms5611_i2c.c  | 11 ++++-------
+ drivers/iio/pressure/ms5611_spi.c  | 17 +++++++----------
+ 4 files changed, 17 insertions(+), 24 deletions(-)
+
+diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h
+index bc06271fa38b..345f3902e3e3 100644
+--- a/drivers/iio/pressure/ms5611.h
++++ b/drivers/iio/pressure/ms5611.h
+@@ -50,9 +50,9 @@ struct ms5611_state {
+       const struct ms5611_osr *pressure_osr;
+       const struct ms5611_osr *temp_osr;
+-      int (*reset)(struct device *dev);
+-      int (*read_prom_word)(struct device *dev, int index, u16 *word);
+-      int (*read_adc_temp_and_pressure)(struct device *dev,
++      int (*reset)(struct ms5611_state *st);
++      int (*read_prom_word)(struct ms5611_state *st, int index, u16 *word);
++      int (*read_adc_temp_and_pressure)(struct ms5611_state *st,
+                                         s32 *temp, s32 *pressure);
+       struct ms5611_chip_info *chip_info;
+diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
+index 214b0d25f598..885ccb7914dc 100644
+--- a/drivers/iio/pressure/ms5611_core.c
++++ b/drivers/iio/pressure/ms5611_core.c
+@@ -85,8 +85,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev)
+       struct ms5611_state *st = iio_priv(indio_dev);
+       for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
+-              ret = st->read_prom_word(&indio_dev->dev,
+-                                       i, &st->chip_info->prom[i]);
++              ret = st->read_prom_word(st, i, &st->chip_info->prom[i]);
+               if (ret < 0) {
+                       dev_err(&indio_dev->dev,
+                               "failed to read prom at %d\n", i);
+@@ -108,7 +107,7 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
+       int ret;
+       struct ms5611_state *st = iio_priv(indio_dev);
+-      ret = st->read_adc_temp_and_pressure(&indio_dev->dev, temp, pressure);
++      ret = st->read_adc_temp_and_pressure(st, temp, pressure);
+       if (ret < 0) {
+               dev_err(&indio_dev->dev,
+                       "failed to read temperature and pressure\n");
+@@ -196,7 +195,7 @@ static int ms5611_reset(struct iio_dev *indio_dev)
+       int ret;
+       struct ms5611_state *st = iio_priv(indio_dev);
+-      ret = st->reset(&indio_dev->dev);
++      ret = st->reset(st);
+       if (ret < 0) {
+               dev_err(&indio_dev->dev, "failed to reset device\n");
+               return ret;
+diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c
+index 7c04f730430c..cccc40f7df0b 100644
+--- a/drivers/iio/pressure/ms5611_i2c.c
++++ b/drivers/iio/pressure/ms5611_i2c.c
+@@ -20,17 +20,15 @@
+ #include "ms5611.h"
+-static int ms5611_i2c_reset(struct device *dev)
++static int ms5611_i2c_reset(struct ms5611_state *st)
+ {
+-      struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+-
+       return i2c_smbus_write_byte(st->client, MS5611_RESET);
+ }
+-static int ms5611_i2c_read_prom_word(struct device *dev, int index, u16 *word)
++static int ms5611_i2c_read_prom_word(struct ms5611_state *st, int index,
++                                   u16 *word)
+ {
+       int ret;
+-      struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+       ret = i2c_smbus_read_word_swapped(st->client,
+                       MS5611_READ_PROM_WORD + (index << 1));
+@@ -57,11 +55,10 @@ static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val)
+       return 0;
+ }
+-static int ms5611_i2c_read_adc_temp_and_pressure(struct device *dev,
++static int ms5611_i2c_read_adc_temp_and_pressure(struct ms5611_state *st,
+                                                s32 *temp, s32 *pressure)
+ {
+       int ret;
+-      struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+       const struct ms5611_osr *osr = st->temp_osr;
+       ret = i2c_smbus_write_byte(st->client, osr->cmd);
+diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c
+index f7743ee3318f..3039fe8aa2a2 100644
+--- a/drivers/iio/pressure/ms5611_spi.c
++++ b/drivers/iio/pressure/ms5611_spi.c
+@@ -15,18 +15,17 @@
+ #include "ms5611.h"
+-static int ms5611_spi_reset(struct device *dev)
++static int ms5611_spi_reset(struct ms5611_state *st)
+ {
+       u8 cmd = MS5611_RESET;
+-      struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+       return spi_write_then_read(st->client, &cmd, 1, NULL, 0);
+ }
+-static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word)
++static int ms5611_spi_read_prom_word(struct ms5611_state *st, int index,
++                                   u16 *word)
+ {
+       int ret;
+-      struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+       ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1));
+       if (ret < 0)
+@@ -37,11 +36,10 @@ static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word)
+       return 0;
+ }
+-static int ms5611_spi_read_adc(struct device *dev, s32 *val)
++static int ms5611_spi_read_adc(struct ms5611_state *st, s32 *val)
+ {
+       int ret;
+       u8 buf[3] = { MS5611_READ_ADC };
+-      struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+       ret = spi_write_then_read(st->client, buf, 1, buf, 3);
+       if (ret < 0)
+@@ -52,11 +50,10 @@ static int ms5611_spi_read_adc(struct device *dev, s32 *val)
+       return 0;
+ }
+-static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev,
++static int ms5611_spi_read_adc_temp_and_pressure(struct ms5611_state *st,
+                                                s32 *temp, s32 *pressure)
+ {
+       int ret;
+-      struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev));
+       const struct ms5611_osr *osr = st->temp_osr;
+       /*
+@@ -68,7 +65,7 @@ static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev,
+               return ret;
+       usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL));
+-      ret = ms5611_spi_read_adc(dev, temp);
++      ret = ms5611_spi_read_adc(st, temp);
+       if (ret < 0)
+               return ret;
+@@ -78,7 +75,7 @@ static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev,
+               return ret;
+       usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL));
+-      return ms5611_spi_read_adc(dev, pressure);
++      return ms5611_spi_read_adc(st, pressure);
+ }
+ static int ms5611_spi_probe(struct spi_device *spi)
+-- 
+2.35.1
+
diff --git a/queue-5.15/iio-pressure-ms5611-fixed-value-compensation-bug.patch b/queue-5.15/iio-pressure-ms5611-fixed-value-compensation-bug.patch
new file mode 100644 (file)
index 0000000..542c3e4
--- /dev/null
@@ -0,0 +1,173 @@
+From 0ded399b3fdd59cad1eb3f0ca2cd7db8259ab48d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 21 Oct 2022 15:58:20 +0200
+Subject: iio: pressure: ms5611: fixed value compensation bug
+
+From: Mitja Spes <mitja@lxnav.com>
+
+[ Upstream commit 17f442e7e47579d3881fc4d47354eaef09302e6f ]
+
+When using multiple instances of this driver the compensation PROM was
+overwritten by the last initialized sensor. Now each sensor has own PROM
+storage.
+
+Signed-off-by: Mitja Spes <mitja@lxnav.com>
+Fixes: 9690d81a02dc ("iio: pressure: ms5611: add support for MS5607 temperature and pressure sensor")
+Link: https://lore.kernel.org/r/20221021135827.1444793-2-mitja@lxnav.com
+Cc: <Stable@vger.kernel.org>
+Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/iio/pressure/ms5611.h      | 12 +++----
+ drivers/iio/pressure/ms5611_core.c | 51 ++++++++++++++++--------------
+ 2 files changed, 31 insertions(+), 32 deletions(-)
+
+diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h
+index 345f3902e3e3..5e2d2d4d87b5 100644
+--- a/drivers/iio/pressure/ms5611.h
++++ b/drivers/iio/pressure/ms5611.h
+@@ -25,13 +25,6 @@ enum {
+       MS5607,
+ };
+-struct ms5611_chip_info {
+-      u16 prom[MS5611_PROM_WORDS_NB];
+-
+-      int (*temp_and_pressure_compensate)(struct ms5611_chip_info *chip_info,
+-                                          s32 *temp, s32 *pressure);
+-};
+-
+ /*
+  * OverSampling Rate descriptor.
+  * Warning: cmd MUST be kept aligned on a word boundary (see
+@@ -50,12 +43,15 @@ struct ms5611_state {
+       const struct ms5611_osr *pressure_osr;
+       const struct ms5611_osr *temp_osr;
++      u16 prom[MS5611_PROM_WORDS_NB];
++
+       int (*reset)(struct ms5611_state *st);
+       int (*read_prom_word)(struct ms5611_state *st, int index, u16 *word);
+       int (*read_adc_temp_and_pressure)(struct ms5611_state *st,
+                                         s32 *temp, s32 *pressure);
+-      struct ms5611_chip_info *chip_info;
++      int (*compensate_temp_and_pressure)(struct ms5611_state *st, s32 *temp,
++                                        s32 *pressure);
+       struct regulator *vdd;
+ };
+diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
+index 885ccb7914dc..874a73b3ea9d 100644
+--- a/drivers/iio/pressure/ms5611_core.c
++++ b/drivers/iio/pressure/ms5611_core.c
+@@ -85,7 +85,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev)
+       struct ms5611_state *st = iio_priv(indio_dev);
+       for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
+-              ret = st->read_prom_word(st, i, &st->chip_info->prom[i]);
++              ret = st->read_prom_word(st, i, &st->prom[i]);
+               if (ret < 0) {
+                       dev_err(&indio_dev->dev,
+                               "failed to read prom at %d\n", i);
+@@ -93,7 +93,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev)
+               }
+       }
+-      if (!ms5611_prom_is_valid(st->chip_info->prom, MS5611_PROM_WORDS_NB)) {
++      if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) {
+               dev_err(&indio_dev->dev, "PROM integrity check failed\n");
+               return -ENODEV;
+       }
+@@ -114,21 +114,20 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
+               return ret;
+       }
+-      return st->chip_info->temp_and_pressure_compensate(st->chip_info,
+-                                                         temp, pressure);
++      return st->compensate_temp_and_pressure(st, temp, pressure);
+ }
+-static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info,
++static int ms5611_temp_and_pressure_compensate(struct ms5611_state *st,
+                                              s32 *temp, s32 *pressure)
+ {
+       s32 t = *temp, p = *pressure;
+       s64 off, sens, dt;
+-      dt = t - (chip_info->prom[5] << 8);
+-      off = ((s64)chip_info->prom[2] << 16) + ((chip_info->prom[4] * dt) >> 7);
+-      sens = ((s64)chip_info->prom[1] << 15) + ((chip_info->prom[3] * dt) >> 8);
++      dt = t - (st->prom[5] << 8);
++      off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7);
++      sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8);
+-      t = 2000 + ((chip_info->prom[6] * dt) >> 23);
++      t = 2000 + ((st->prom[6] * dt) >> 23);
+       if (t < 2000) {
+               s64 off2, sens2, t2;
+@@ -154,17 +153,17 @@ static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_inf
+       return 0;
+ }
+-static int ms5607_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info,
++static int ms5607_temp_and_pressure_compensate(struct ms5611_state *st,
+                                              s32 *temp, s32 *pressure)
+ {
+       s32 t = *temp, p = *pressure;
+       s64 off, sens, dt;
+-      dt = t - (chip_info->prom[5] << 8);
+-      off = ((s64)chip_info->prom[2] << 17) + ((chip_info->prom[4] * dt) >> 6);
+-      sens = ((s64)chip_info->prom[1] << 16) + ((chip_info->prom[3] * dt) >> 7);
++      dt = t - (st->prom[5] << 8);
++      off = ((s64)st->prom[2] << 17) + ((st->prom[4] * dt) >> 6);
++      sens = ((s64)st->prom[1] << 16) + ((st->prom[3] * dt) >> 7);
+-      t = 2000 + ((chip_info->prom[6] * dt) >> 23);
++      t = 2000 + ((st->prom[6] * dt) >> 23);
+       if (t < 2000) {
+               s64 off2, sens2, t2, tmp;
+@@ -342,15 +341,6 @@ static int ms5611_write_raw(struct iio_dev *indio_dev,
+ static const unsigned long ms5611_scan_masks[] = {0x3, 0};
+-static struct ms5611_chip_info chip_info_tbl[] = {
+-      [MS5611] = {
+-              .temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate,
+-      },
+-      [MS5607] = {
+-              .temp_and_pressure_compensate = ms5607_temp_and_pressure_compensate,
+-      }
+-};
+-
+ static const struct iio_chan_spec ms5611_channels[] = {
+       {
+               .type = IIO_PRESSURE,
+@@ -433,7 +423,20 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
+       struct ms5611_state *st = iio_priv(indio_dev);
+       mutex_init(&st->lock);
+-      st->chip_info = &chip_info_tbl[type];
++
++      switch (type) {
++      case MS5611:
++              st->compensate_temp_and_pressure =
++                      ms5611_temp_and_pressure_compensate;
++              break;
++      case MS5607:
++              st->compensate_temp_and_pressure =
++                      ms5607_temp_and_pressure_compensate;
++              break;
++      default:
++              return -EINVAL;
++      }
++
+       st->temp_osr =
+               &ms5611_avail_temp_osr[ARRAY_SIZE(ms5611_avail_temp_osr) - 1];
+       st->pressure_osr =
+-- 
+2.35.1
+
diff --git a/queue-5.15/mips-pic32-treat-port-as-signed-integer.patch b/queue-5.15/mips-pic32-treat-port-as-signed-integer.patch
new file mode 100644 (file)
index 0000000..3952b58
--- /dev/null
@@ -0,0 +1,105 @@
+From f8ca0c71226633b8e5041422632ed1b5d03b494b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 28 Oct 2022 15:23:44 +0200
+Subject: MIPS: pic32: treat port as signed integer
+
+From: Jason A. Donenfeld <Jason@zx2c4.com>
+
+[ Upstream commit 648060902aa302331b5d6e4f26d8ee0761d239ab ]
+
+get_port_from_cmdline() returns an int, yet is assigned to a char, which
+is wrong in its own right, but also, with char becoming unsigned, this
+poses problems, because -1 is used as an error value. Further
+complicating things, fw_init_early_console() is only ever called with a
+-1 argument. Fix this up by removing the unused argument from
+fw_init_early_console() and treating port as a proper signed integer.
+
+Cc: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Signed-off-by: Thomas Bogendoerfer <tsbogend@alpha.franken.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/mips/include/asm/fw/fw.h             |  2 +-
+ arch/mips/pic32/pic32mzda/early_console.c | 13 ++++++-------
+ arch/mips/pic32/pic32mzda/init.c          |  2 +-
+ 3 files changed, 8 insertions(+), 9 deletions(-)
+
+diff --git a/arch/mips/include/asm/fw/fw.h b/arch/mips/include/asm/fw/fw.h
+index d0ef8b4892bb..d0494ce4b337 100644
+--- a/arch/mips/include/asm/fw/fw.h
++++ b/arch/mips/include/asm/fw/fw.h
+@@ -26,6 +26,6 @@ extern char *fw_getcmdline(void);
+ extern void fw_meminit(void);
+ extern char *fw_getenv(char *name);
+ extern unsigned long fw_getenvl(char *name);
+-extern void fw_init_early_console(char port);
++extern void fw_init_early_console(void);
+ #endif /* __ASM_FW_H_ */
+diff --git a/arch/mips/pic32/pic32mzda/early_console.c b/arch/mips/pic32/pic32mzda/early_console.c
+index 25372e62783b..3cd1b408fa1c 100644
+--- a/arch/mips/pic32/pic32mzda/early_console.c
++++ b/arch/mips/pic32/pic32mzda/early_console.c
+@@ -27,7 +27,7 @@
+ #define U_BRG(x)      (UART_BASE(x) + 0x40)
+ static void __iomem *uart_base;
+-static char console_port = -1;
++static int console_port = -1;
+ static int __init configure_uart_pins(int port)
+ {
+@@ -47,7 +47,7 @@ static int __init configure_uart_pins(int port)
+       return 0;
+ }
+-static void __init configure_uart(char port, int baud)
++static void __init configure_uart(int port, int baud)
+ {
+       u32 pbclk;
+@@ -60,7 +60,7 @@ static void __init configure_uart(char port, int baud)
+                    uart_base + PIC32_SET(U_STA(port)));
+ }
+-static void __init setup_early_console(char port, int baud)
++static void __init setup_early_console(int port, int baud)
+ {
+       if (configure_uart_pins(port))
+               return;
+@@ -130,16 +130,15 @@ static int __init get_baud_from_cmdline(char *arch_cmdline)
+       return baud;
+ }
+-void __init fw_init_early_console(char port)
++void __init fw_init_early_console(void)
+ {
+       char *arch_cmdline = pic32_getcmdline();
+-      int baud = -1;
++      int baud, port;
+       uart_base = ioremap(PIC32_BASE_UART, 0xc00);
+       baud = get_baud_from_cmdline(arch_cmdline);
+-      if (port == -1)
+-              port = get_port_from_cmdline(arch_cmdline);
++      port = get_port_from_cmdline(arch_cmdline);
+       if (port == -1)
+               port = EARLY_CONSOLE_PORT;
+diff --git a/arch/mips/pic32/pic32mzda/init.c b/arch/mips/pic32/pic32mzda/init.c
+index 764f2d022fae..429830afff54 100644
+--- a/arch/mips/pic32/pic32mzda/init.c
++++ b/arch/mips/pic32/pic32mzda/init.c
+@@ -47,7 +47,7 @@ void __init plat_mem_setup(void)
+               strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE);
+ #ifdef CONFIG_EARLY_PRINTK
+-      fw_init_early_console(-1);
++      fw_init_early_console();
+ #endif
+       pic32_config_init();
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.15/nvme-add-a-bogus-subsystem-nqn-quirk-for-micron-mtfd.patch b/queue-5.15/nvme-add-a-bogus-subsystem-nqn-quirk-for-micron-mtfd.patch
new file mode 100644 (file)
index 0000000..6a8d84c
--- /dev/null
@@ -0,0 +1,37 @@
+From ffa20c963662d5d7832da0332bd385e1e491b7ca Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 22 Jun 2022 12:19:21 +0200
+Subject: nvme: add a bogus subsystem NQN quirk for Micron MTFDKBA2T0TFH
+
+From: Leo Savernik <l.savernik@aon.at>
+
+[ Upstream commit 41f38043f884c66af4114a7109cf540d6222f450 ]
+
+The Micron MTFDKBA2T0TFH device reports the same subsysem NQN for
+all devices.  Add a quick to ignore it.
+
+Signed-off-by: Leo Savernik <l.savernik@aon.at>
+Reviewed-by: Keith Busch <kbusch@kernel.org>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Stable-dep-of: d5ceb4d1c507 ("nvme-pci: add NVME_QUIRK_BOGUS_NID for Micron Nitro")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/pci.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index e9f3701dda3f..4fa2955dbf6c 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -3351,6 +3351,8 @@ static const struct pci_device_id nvme_id_table[] = {
+       { PCI_DEVICE(0x1cc1, 0x8201),   /* ADATA SX8200PNP 512GB */
+               .driver_data = NVME_QUIRK_NO_DEEPEST_PS |
+                               NVME_QUIRK_IGNORE_DEV_SUBNQN, },
++       { PCI_DEVICE(0x1344, 0x5407), /* Micron Technology Inc NVMe SSD */
++              .driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN },
+       { PCI_DEVICE(0x1c5c, 0x1504),   /* SK Hynix PC400 */
+               .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
+       { PCI_DEVICE(0x15b7, 0x2001),   /*  Sandisk Skyhawk */
+-- 
+2.35.1
+
diff --git a/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-micron-nitro.patch b/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-micron-nitro.patch
new file mode 100644 (file)
index 0000000..8c75fcd
--- /dev/null
@@ -0,0 +1,35 @@
+From 28811cf28593e7620fe8ad50b8a481b6ea9d62b0 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 14 Nov 2022 14:48:52 +0100
+Subject: nvme-pci: add NVME_QUIRK_BOGUS_NID for Micron Nitro
+
+From: Bean Huo <beanhuo@micron.com>
+
+[ Upstream commit d5ceb4d1c50786d21de3d4b06c3f43109ec56dd8 ]
+
+Added a quirk to fix Micron Nitro NVMe reporting duplicate NGUIDs.
+
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Bean Huo <beanhuo@micron.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/pci.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index 4fa2955dbf6c..278302771841 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -3353,6 +3353,8 @@ static const struct pci_device_id nvme_id_table[] = {
+                               NVME_QUIRK_IGNORE_DEV_SUBNQN, },
+        { PCI_DEVICE(0x1344, 0x5407), /* Micron Technology Inc NVMe SSD */
+               .driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN },
++       { PCI_DEVICE(0x1344, 0x6001),   /* Micron Nitro NVMe */
++               .driver_data = NVME_QUIRK_BOGUS_NID, },
+       { PCI_DEVICE(0x1c5c, 0x1504),   /* SK Hynix PC400 */
+               .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
+       { PCI_DEVICE(0x15b7, 0x2001),   /*  Sandisk Skyhawk */
+-- 
+2.35.1
+
diff --git a/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-netac-nv7000.patch b/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-netac-nv7000.patch
new file mode 100644 (file)
index 0000000..da60372
--- /dev/null
@@ -0,0 +1,36 @@
+From f0c49f5246a1c3ca0b8ca61c966c56bcaaf3b6aa Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 16 Nov 2022 00:17:56 -0300
+Subject: nvme-pci: add NVME_QUIRK_BOGUS_NID for Netac NV7000
+
+From: Tiago Dias Ferreira <tiagodfer@gmail.com>
+
+[ Upstream commit 8d6e38f636ac063e8062a21e7616f7d9bf0df5d8 ]
+
+Added a quirk to fix the Netac NV7000 SSD reporting duplicate NGUIDs.
+
+Cc: <stable@vger.kernel.org>
+Signed-off-by: Tiago Dias Ferreira <tiagodfer@gmail.com>
+Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/pci.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index a1a803b3105a..772bdc6845fb 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -3375,6 +3375,8 @@ static const struct pci_device_id nvme_id_table[] = {
+               .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
+       { PCI_DEVICE(0x2646, 0x501E),   /* KINGSTON OM3PGP4xxxxQ OS21011 NVMe SSD */
+               .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
++      { PCI_DEVICE(0x1f40, 0x5236),   /* Netac Technologies Co. NV7000 NVMe SSD */
++              .driver_data = NVME_QUIRK_BOGUS_NID, },
+       { PCI_DEVICE(0x1e4B, 0x1001),   /* MAXIO MAP1001 */
+               .driver_data = NVME_QUIRK_BOGUS_NID, },
+       { PCI_DEVICE(0x1e4B, 0x1002),   /* MAXIO MAP1002 */
+-- 
+2.35.1
+
diff --git a/queue-5.15/nvme-pci-disable-namespace-identifiers-for-the-maxio.patch b/queue-5.15/nvme-pci-disable-namespace-identifiers-for-the-maxio.patch
new file mode 100644 (file)
index 0000000..a760850
--- /dev/null
@@ -0,0 +1,39 @@
+From dc3d8853ac253e3562a1abf90ce9fde224d82958 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 27 May 2022 07:32:08 +0200
+Subject: nvme-pci: disable namespace identifiers for the MAXIO MAP1001
+
+From: Christoph Hellwig <hch@lst.de>
+
+[ Upstream commit 70ce3455345d056b5fc427c3bb4a3ff4d126b6d5 ]
+
+The MAXIO MAP1001 controllers reports completely bogus Namespace
+identifiers that even change after suspend cycles.  Disable using
+the Identifiers entirely.
+
+Reported-by: Arman Hajishafieha <arman.hajishafieha@hotmail.com>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Reviewed-by: Chaitanya Kulkarni <kch@nvidia.com>
+Tested-by: Arman Hajishafieha <arman.hajishafieha@hotmail.com>
+Stable-dep-of: 8d6e38f636ac ("nvme-pci: add NVME_QUIRK_BOGUS_NID for Netac NV7000")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/pci.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index 278302771841..66e252af9218 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -3365,6 +3365,8 @@ static const struct pci_device_id nvme_id_table[] = {
+               .driver_data = NVME_QUIRK_NO_DEEPEST_PS, },
+       { PCI_DEVICE(0x2646, 0x2263),   /* KINGSTON A2000 NVMe SSD  */
+               .driver_data = NVME_QUIRK_NO_DEEPEST_PS, },
++      { PCI_DEVICE(0x1e4B, 0x1001),   /* MAXIO MAP1001 */
++              .driver_data = NVME_QUIRK_BOGUS_NID, },
+       { PCI_DEVICE(0x1e4B, 0x1002),   /* MAXIO MAP1002 */
+               .driver_data = NVME_QUIRK_BOGUS_NID, },
+       { PCI_DEVICE(0x1e4B, 0x1202),   /* MAXIO MAP1202 */
+-- 
+2.35.1
+
diff --git a/queue-5.15/nvme-pci-disable-write-zeroes-on-various-kingston-ss.patch b/queue-5.15/nvme-pci-disable-write-zeroes-on-various-kingston-ss.patch
new file mode 100644 (file)
index 0000000..438c8aa
--- /dev/null
@@ -0,0 +1,45 @@
+From ef1b4f28b8ce7ad627d1c4f00e4e0fae3155f338 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 11 Oct 2022 04:06:42 -0700
+Subject: nvme-pci: disable write zeroes on various Kingston SSD
+
+From: Xander Li <xander_li@kingston.com.tw>
+
+[ Upstream commit ac9b57d4e1e3ecf0122e915bbba1bd4c90ec3031 ]
+
+Kingston SSDs do support NVMe Write_Zeroes cmd but take long time to
+process.  The firmware version is locked by these SSDs, we can not expect
+firmware improvement, so disable Write_Zeroes cmd.
+
+Signed-off-by: Xander Li <xander_li@kingston.com.tw>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Stable-dep-of: 8d6e38f636ac ("nvme-pci: add NVME_QUIRK_BOGUS_NID for Netac NV7000")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/host/pci.c | 10 ++++++++++
+ 1 file changed, 10 insertions(+)
+
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index 66e252af9218..a1a803b3105a 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -3365,6 +3365,16 @@ static const struct pci_device_id nvme_id_table[] = {
+               .driver_data = NVME_QUIRK_NO_DEEPEST_PS, },
+       { PCI_DEVICE(0x2646, 0x2263),   /* KINGSTON A2000 NVMe SSD  */
+               .driver_data = NVME_QUIRK_NO_DEEPEST_PS, },
++      { PCI_DEVICE(0x2646, 0x5018),   /* KINGSTON OM8SFP4xxxxP OS21012 NVMe SSD */
++              .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
++      { PCI_DEVICE(0x2646, 0x5016),   /* KINGSTON OM3PGP4xxxxP OS21011 NVMe SSD */
++              .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
++      { PCI_DEVICE(0x2646, 0x501A),   /* KINGSTON OM8PGP4xxxxP OS21005 NVMe SSD */
++              .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
++      { PCI_DEVICE(0x2646, 0x501B),   /* KINGSTON OM8PGP4xxxxQ OS21005 NVMe SSD */
++              .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
++      { PCI_DEVICE(0x2646, 0x501E),   /* KINGSTON OM3PGP4xxxxQ OS21011 NVMe SSD */
++              .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
+       { PCI_DEVICE(0x1e4B, 0x1001),   /* MAXIO MAP1001 */
+               .driver_data = NVME_QUIRK_BOGUS_NID, },
+       { PCI_DEVICE(0x1e4B, 0x1002),   /* MAXIO MAP1002 */
+-- 
+2.35.1
+
diff --git a/queue-5.15/nvmet-fix-memory-leak-in-nvmet_subsys_attr_model_sto.patch b/queue-5.15/nvmet-fix-memory-leak-in-nvmet_subsys_attr_model_sto.patch
new file mode 100644 (file)
index 0000000..32e3102
--- /dev/null
@@ -0,0 +1,51 @@
+From 4ddd30e2e971981985db19122cb2239bdd78eec5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 26 Oct 2022 12:31:33 +0400
+Subject: nvmet: fix memory leak in nvmet_subsys_attr_model_store_locked
+
+From: Aleksandr Miloserdov <a.miloserdov@yadro.com>
+
+[ Upstream commit becc4cac309dc867571f0080fde4426a6c2222e0 ]
+
+Since model_number is allocated before it needs to be freed before
+kmemdump_nul.
+
+Reviewed-by: Konstantin Shelekhin <k.shelekhin@yadro.com>
+Reviewed-by: Dmitriy Bogdanov <d.bogdanov@yadro.com>
+Signed-off-by: Aleksandr Miloserdov <a.miloserdov@yadro.com>
+Reviewed-by: Sagi Grimberg <sagi@grimberg.me>
+Signed-off-by: Christoph Hellwig <hch@lst.de>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/nvme/target/configfs.c | 7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c
+index cea30e4f5053..625038057a76 100644
+--- a/drivers/nvme/target/configfs.c
++++ b/drivers/nvme/target/configfs.c
+@@ -1189,6 +1189,7 @@ static ssize_t nvmet_subsys_attr_model_store_locked(struct nvmet_subsys *subsys,
+               const char *page, size_t count)
+ {
+       int pos = 0, len;
++      char *val;
+       if (subsys->subsys_discovered) {
+               pr_err("Can't set model number. %s is already assigned\n",
+@@ -1211,9 +1212,11 @@ static ssize_t nvmet_subsys_attr_model_store_locked(struct nvmet_subsys *subsys,
+                       return -EINVAL;
+       }
+-      subsys->model_number = kmemdup_nul(page, len, GFP_KERNEL);
+-      if (!subsys->model_number)
++      val = kmemdup_nul(page, len, GFP_KERNEL);
++      if (!val)
+               return -ENOMEM;
++      kfree(subsys->model_number);
++      subsys->model_number = val;
+       return count;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.15/platform-x86-ideapad-laptop-disable-touchpad_switch.patch b/queue-5.15/platform-x86-ideapad-laptop-disable-touchpad_switch.patch
new file mode 100644 (file)
index 0000000..d866f66
--- /dev/null
@@ -0,0 +1,70 @@
+From 1d8c2e23d71bcf52cb50eed12358f4aa7124d010 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 18 Oct 2022 17:53:23 +0800
+Subject: platform/x86: ideapad-laptop: Disable touchpad_switch
+
+From: Manyi Li <limanyi@uniontech.com>
+
+[ Upstream commit a231224a601c1924b9df620281ad04472900d75f ]
+
+Ideapads for "Lenovo Yoga 3 Pro 1370" and "ZhaoYang K4e-IML" do not
+use EC to switch touchpad.
+
+Reading VPCCMD_R_TOUCHPAD will return zero thus touchpad may be blocked
+unexpectedly.
+
+Signed-off-by: Manyi Li <limanyi@uniontech.com>
+Link: https://lore.kernel.org/r/20221018095323.14591-1-limanyi@uniontech.com
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/platform/x86/ideapad-laptop.c | 25 ++++++++++++++++++++++++-
+ 1 file changed, 24 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
+index e7a1299e3776..ab4dfff2174b 100644
+--- a/drivers/platform/x86/ideapad-laptop.c
++++ b/drivers/platform/x86/ideapad-laptop.c
+@@ -1499,6 +1499,24 @@ static const struct dmi_system_id hw_rfkill_list[] = {
+       {}
+ };
++static const struct dmi_system_id no_touchpad_switch_list[] = {
++      {
++      .ident = "Lenovo Yoga 3 Pro 1370",
++      .matches = {
++              DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++              DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3"),
++              },
++      },
++      {
++      .ident = "ZhaoYang K4e-IML",
++      .matches = {
++              DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++              DMI_MATCH(DMI_PRODUCT_VERSION, "ZhaoYang K4e-IML"),
++              },
++      },
++      {}
++};
++
+ static void ideapad_check_features(struct ideapad_private *priv)
+ {
+       acpi_handle handle = priv->adev->handle;
+@@ -1507,7 +1525,12 @@ static void ideapad_check_features(struct ideapad_private *priv)
+       priv->features.hw_rfkill_switch = dmi_check_system(hw_rfkill_list);
+       /* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */
+-      priv->features.touchpad_ctrl_via_ec = !acpi_dev_present("ELAN0634", NULL, -1);
++      if (acpi_dev_present("ELAN0634", NULL, -1))
++              priv->features.touchpad_ctrl_via_ec = 0;
++      else if (dmi_check_system(no_touchpad_switch_list))
++              priv->features.touchpad_ctrl_via_ec = 0;
++      else
++              priv->features.touchpad_ctrl_via_ec = 1;
+       if (!read_ec_data(handle, VPCCMD_R_FAN, &val))
+               priv->features.fan_mode = true;
+-- 
+2.35.1
+
diff --git a/queue-5.15/platform-x86-intel-hid-add-some-acpi-device-ids.patch b/queue-5.15/platform-x86-intel-hid-add-some-acpi-device-ids.patch
new file mode 100644 (file)
index 0000000..6f563d9
--- /dev/null
@@ -0,0 +1,38 @@
+From a6b3107f327b85cf588753a205a8dfcd2c0d0151 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Nov 2022 10:05:48 +0800
+Subject: platform/x86/intel/hid: Add some ACPI device IDs
+
+From: Ivan Hu <ivan.hu@canonical.com>
+
+[ Upstream commit a977ece5773b6746b814aac410da4776023db239 ]
+
+Add INTC1076 (JasonLake), INTC1077 (MeteorLake) and INTC1078 (RaptorLake)
+devices IDs.
+
+Signed-off-by: Ivan Hu <ivan.hu@canonical.com>
+Link: https://lore.kernel.org/r/20221102020548.5225-1-ivan.hu@canonical.com
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/platform/x86/intel/hid.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c
+index d7d6782c40c2..4d1c78635114 100644
+--- a/drivers/platform/x86/intel/hid.c
++++ b/drivers/platform/x86/intel/hid.c
+@@ -27,6 +27,9 @@ static const struct acpi_device_id intel_hid_ids[] = {
+       {"INTC1051", 0},
+       {"INTC1054", 0},
+       {"INTC1070", 0},
++      {"INTC1076", 0},
++      {"INTC1077", 0},
++      {"INTC1078", 0},
+       {"", 0},
+ };
+ MODULE_DEVICE_TABLE(acpi, intel_hid_ids);
+-- 
+2.35.1
+
diff --git a/queue-5.15/platform-x86-intel-pmt-sapphire-rapids-pmt-errata-fi.patch b/queue-5.15/platform-x86-intel-pmt-sapphire-rapids-pmt-errata-fi.patch
new file mode 100644 (file)
index 0000000..703934a
--- /dev/null
@@ -0,0 +1,88 @@
+From 3511949e896e703e0ae13c21bde4441fe9ea5656 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 4 Nov 2022 20:42:28 -0700
+Subject: platform/x86/intel/pmt: Sapphire Rapids PMT errata fix
+
+From: David E. Box <david.e.box@linux.intel.com>
+
+[ Upstream commit bcdfa1f77ea7f67368d20384932a9d1e3047ddd2 ]
+
+On Sapphire Rapids, due to a hardware issue affecting the PUNIT telemetry
+region, reads that are not done in QWORD quantities and alignment may
+return incorrect data. Use a custom 64-bit copy for this region.
+
+Signed-off-by: David E. Box <david.e.box@linux.intel.com>
+Link: https://lore.kernel.org/r/20221105034228.1376677-1-david.e.box@linux.intel.com
+Reviewed-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/platform/x86/intel/pmt/class.c | 31 +++++++++++++++++++++++++-
+ 1 file changed, 30 insertions(+), 1 deletion(-)
+
+diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c
+index 659b1073033c..586a5877422b 100644
+--- a/drivers/platform/x86/intel/pmt/class.c
++++ b/drivers/platform/x86/intel/pmt/class.c
+@@ -9,6 +9,7 @@
+  */
+ #include <linux/kernel.h>
++#include <linux/io-64-nonatomic-lo-hi.h>
+ #include <linux/module.h>
+ #include <linux/mm.h>
+ #include <linux/pci.h>
+@@ -18,6 +19,7 @@
+ #define PMT_XA_START          0
+ #define PMT_XA_MAX            INT_MAX
+ #define PMT_XA_LIMIT          XA_LIMIT(PMT_XA_START, PMT_XA_MAX)
++#define GUID_SPR_PUNIT                0x9956f43f
+ /*
+  * Early implementations of PMT on client platforms have some
+@@ -41,6 +43,29 @@ bool intel_pmt_is_early_client_hw(struct device *dev)
+ }
+ EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw);
++static inline int
++pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count)
++{
++      int i, remain;
++      u64 *buf = to;
++
++      if (!IS_ALIGNED((unsigned long)from, 8))
++              return -EFAULT;
++
++      for (i = 0; i < count/8; i++)
++              buf[i] = readq(&from[i]);
++
++      /* Copy any remaining bytes */
++      remain = count % 8;
++      if (remain) {
++              u64 tmp = readq(&from[i]);
++
++              memcpy(&buf[i], &tmp, remain);
++      }
++
++      return count;
++}
++
+ /*
+  * sysfs
+  */
+@@ -62,7 +87,11 @@ intel_pmt_read(struct file *filp, struct kobject *kobj,
+       if (count > entry->size - off)
+               count = entry->size - off;
+-      memcpy_fromio(buf, entry->base + off, count);
++      if (entry->guid == GUID_SPR_PUNIT)
++              /* PUNIT on SPR only supports aligned 64-bit read */
++              count = pmt_memcpy64_fromio(buf, entry->base + off, count);
++      else
++              memcpy_fromio(buf, entry->base + off, count);
+       return count;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.15/platform-x86-touchscreen_dmi-add-info-for-the-rca-ca.patch b/queue-5.15/platform-x86-touchscreen_dmi-add-info-for-the-rca-ca.patch
new file mode 100644 (file)
index 0000000..39c6c05
--- /dev/null
@@ -0,0 +1,66 @@
+From df50847d3ef978d7e5b937b8f158707924279cad Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 25 Oct 2022 16:11:31 +0200
+Subject: platform/x86: touchscreen_dmi: Add info for the RCA Cambio W101 v2
+ 2-in-1
+
+From: Hans de Goede <hdegoede@redhat.com>
+
+[ Upstream commit 0df044b34bf33e7e35c32b3bf6747fde6279c162 ]
+
+Add touchscreen info for the RCA Cambio W101 v2 2-in-1.
+
+Link: https://github.com/onitake/gsl-firmware/discussions/193
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Link: https://lore.kernel.org/r/20221025141131.509211-1-hdegoede@redhat.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/platform/x86/touchscreen_dmi.c | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c
+index c608078538a7..3d0790263fa7 100644
+--- a/drivers/platform/x86/touchscreen_dmi.c
++++ b/drivers/platform/x86/touchscreen_dmi.c
+@@ -773,6 +773,22 @@ static const struct ts_dmi_data predia_basic_data = {
+       .properties     = predia_basic_props,
+ };
++static const struct property_entry rca_cambio_w101_v2_props[] = {
++      PROPERTY_ENTRY_U32("touchscreen-min-x", 4),
++      PROPERTY_ENTRY_U32("touchscreen-min-y", 20),
++      PROPERTY_ENTRY_U32("touchscreen-size-x", 1644),
++      PROPERTY_ENTRY_U32("touchscreen-size-y", 874),
++      PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"),
++      PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rca-cambio-w101-v2.fw"),
++      PROPERTY_ENTRY_U32("silead,max-fingers", 10),
++      { }
++};
++
++static const struct ts_dmi_data rca_cambio_w101_v2_data = {
++      .acpi_name = "MSSL1680:00",
++      .properties = rca_cambio_w101_v2_props,
++};
++
+ static const struct property_entry rwc_nanote_p8_props[] = {
+       PROPERTY_ENTRY_U32("touchscreen-min-y", 46),
+       PROPERTY_ENTRY_U32("touchscreen-size-x", 1728),
+@@ -1394,6 +1410,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = {
+                       DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"),
+               },
+       },
++      {
++              /* RCA Cambio W101 v2 */
++              /* https://github.com/onitake/gsl-firmware/discussions/193 */
++              .driver_data = (void *)&rca_cambio_w101_v2_data,
++              .matches = {
++                      DMI_MATCH(DMI_SYS_VENDOR, "RCA"),
++                      DMI_MATCH(DMI_PRODUCT_NAME, "W101SA23T1"),
++              },
++      },
+       {
+               /* RWC NANOTE P8 */
+               .driver_data = (void *)&rwc_nanote_p8_data,
+-- 
+2.35.1
+
diff --git a/queue-5.15/revert-drm-amdgpu-revert-drm-amdgpu-getting-fan-spee.patch b/queue-5.15/revert-drm-amdgpu-revert-drm-amdgpu-getting-fan-spee.patch
new file mode 100644 (file)
index 0000000..bf6a9e8
--- /dev/null
@@ -0,0 +1,65 @@
+From f739ed2747d38102a22d721ac279ec7c9196165d Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 3 Nov 2022 18:28:40 +0800
+Subject: Revert "drm/amdgpu: Revert "drm/amdgpu: getting fan speed pwm for
+ vega10 properly""
+
+From: Asher Song <Asher.Song@amd.com>
+
+[ Upstream commit 30b8e7b8ee3be003e0df85c857c5cd0e0bd58b82 ]
+
+This reverts commit 4545ae2ed3f2f7c3f615a53399c9c8460ee5bca7.
+
+The origin patch "drm/amdgpu: getting fan speed pwm for vega10 properly" works fine.
+Test failure is caused by test case self.
+
+Signed-off-by: Asher Song <Asher.Song@amd.com>
+Reviewed-by: Guchun Chen <guchun.chen@amd.com>
+Signed-off-by: Alex Deucher <alexander.deucher@amd.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../amd/pm/powerplay/hwmgr/vega10_thermal.c   | 25 +++++++++----------
+ 1 file changed, 12 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c
+index dad3e3741a4e..190af79f3236 100644
+--- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c
++++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_thermal.c
+@@ -67,22 +67,21 @@ int vega10_fan_ctrl_get_fan_speed_info(struct pp_hwmgr *hwmgr,
+ int vega10_fan_ctrl_get_fan_speed_pwm(struct pp_hwmgr *hwmgr,
+               uint32_t *speed)
+ {
+-      uint32_t current_rpm;
+-      uint32_t percent = 0;
+-
+-      if (hwmgr->thermal_controller.fanInfo.bNoFan)
+-              return 0;
++      struct amdgpu_device *adev = hwmgr->adev;
++      uint32_t duty100, duty;
++      uint64_t tmp64;
+-      if (vega10_get_current_rpm(hwmgr, &current_rpm))
+-              return -1;
++      duty100 = REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_FDO_CTRL1),
++                              CG_FDO_CTRL1, FMAX_DUTY100);
++      duty = REG_GET_FIELD(RREG32_SOC15(THM, 0, mmCG_THERMAL_STATUS),
++                              CG_THERMAL_STATUS, FDO_PWM_DUTY);
+-      if (hwmgr->thermal_controller.
+-                      advanceFanControlParameters.usMaxFanRPM != 0)
+-              percent = current_rpm * 255 /
+-                      hwmgr->thermal_controller.
+-                      advanceFanControlParameters.usMaxFanRPM;
++      if (!duty100)
++              return -EINVAL;
+-      *speed = MIN(percent, 255);
++      tmp64 = (uint64_t)duty * 255;
++      do_div(tmp64, duty100);
++      *speed = MIN((uint32_t)tmp64, 255);
+       return 0;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.15/revert-net-macsec-report-real_dev-features-when-hw-o.patch b/queue-5.15/revert-net-macsec-report-real_dev-features-when-hw-o.patch
new file mode 100644 (file)
index 0000000..c4cff41
--- /dev/null
@@ -0,0 +1,114 @@
+From 019d6087093ef064b31f6e36d9ef1c2621728e87 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Nov 2022 22:33:12 +0100
+Subject: Revert "net: macsec: report real_dev features when HW offloading is
+ enabled"
+
+From: Sabrina Dubroca <sd@queasysnail.net>
+
+[ Upstream commit 8bcd560ae8784da57c610d857118c5d6576b1a8f ]
+
+This reverts commit c850240b6c4132574a00f2da439277ab94265b66.
+
+That commit tried to improve the performance of macsec offload by
+taking advantage of some of the NIC's features, but in doing so, broke
+macsec offload when the lower device supports both macsec and ipsec
+offload, as the ipsec offload feature flags (mainly NETIF_F_HW_ESP)
+were copied from the real device. Since the macsec device doesn't
+provide xdo_* ops, the XFRM core rejects the registration of the new
+macsec device in xfrm_api_check.
+
+Example perf trace when running
+  ip link add link eni1np1 type macsec port 4 offload mac
+
+    ip   737 [003]   795.477676: probe:xfrm_dev_event__REGISTER      name="macsec0" features=0x1c000080014869
+              xfrm_dev_event+0x3a
+              notifier_call_chain+0x47
+              register_netdevice+0x846
+              macsec_newlink+0x25a
+
+    ip   737 [003]   795.477687:   probe:xfrm_dev_event__return      ret=0x8002 (NOTIFY_BAD)
+             notifier_call_chain+0x47
+             register_netdevice+0x846
+             macsec_newlink+0x25a
+
+dev->features includes NETIF_F_HW_ESP (0x04000000000000), so
+xfrm_api_check returns NOTIFY_BAD because we don't have
+dev->xfrmdev_ops on the macsec device.
+
+We could probably propagate GSO and a few other features from the
+lower device, similar to macvlan. This will be done in a future patch.
+
+Signed-off-by: Sabrina Dubroca <sd@queasysnail.net>
+Reviewed-by: Antoine Tenart <atenart@kernel.org>
+Reviewed-by: Leon Romanovsky <leonro@nvidia.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/macsec.c | 27 ++++-----------------------
+ 1 file changed, 4 insertions(+), 23 deletions(-)
+
+diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c
+index 4811bd1f3d74..f1961d7f9db2 100644
+--- a/drivers/net/macsec.c
++++ b/drivers/net/macsec.c
+@@ -2644,11 +2644,6 @@ static int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info)
+       if (ret)
+               goto rollback;
+-      /* Force features update, since they are different for SW MACSec and
+-       * HW offloading cases.
+-       */
+-      netdev_update_features(dev);
+-
+       rtnl_unlock();
+       return 0;
+@@ -3416,16 +3411,9 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb,
+       return ret;
+ }
+-#define SW_MACSEC_FEATURES \
++#define MACSEC_FEATURES \
+       (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST)
+-/* If h/w offloading is enabled, use real device features save for
+- *   VLAN_FEATURES - they require additional ops
+- *   HW_MACSEC - no reason to report it
+- */
+-#define REAL_DEV_FEATURES(dev) \
+-      ((dev)->features & ~(NETIF_F_VLAN_FEATURES | NETIF_F_HW_MACSEC))
+-
+ static int macsec_dev_init(struct net_device *dev)
+ {
+       struct macsec_dev *macsec = macsec_priv(dev);
+@@ -3442,12 +3430,8 @@ static int macsec_dev_init(struct net_device *dev)
+               return err;
+       }
+-      if (macsec_is_offloaded(macsec)) {
+-              dev->features = REAL_DEV_FEATURES(real_dev);
+-      } else {
+-              dev->features = real_dev->features & SW_MACSEC_FEATURES;
+-              dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE;
+-      }
++      dev->features = real_dev->features & MACSEC_FEATURES;
++      dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE;
+       dev->needed_headroom = real_dev->needed_headroom +
+                              MACSEC_NEEDED_HEADROOM;
+@@ -3476,10 +3460,7 @@ static netdev_features_t macsec_fix_features(struct net_device *dev,
+       struct macsec_dev *macsec = macsec_priv(dev);
+       struct net_device *real_dev = macsec->real_dev;
+-      if (macsec_is_offloaded(macsec))
+-              return REAL_DEV_FEATURES(real_dev);
+-
+-      features &= (real_dev->features & SW_MACSEC_FEATURES) |
++      features &= (real_dev->features & MACSEC_FEATURES) |
+                   NETIF_F_GSO_SOFTWARE | NETIF_F_SOFT_FEATURES;
+       features |= NETIF_F_LLTX;
+-- 
+2.35.1
+
diff --git a/queue-5.15/risc-v-vdso-do-not-add-missing-symbols-to-version-se.patch b/queue-5.15/risc-v-vdso-do-not-add-missing-symbols-to-version-se.patch
new file mode 100644 (file)
index 0000000..2c2f290
--- /dev/null
@@ -0,0 +1,69 @@
+From 9585bb8208b22b5da772f30146cc3f4d7f9a73a9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 8 Nov 2022 10:13:23 -0700
+Subject: RISC-V: vdso: Do not add missing symbols to version section in linker
+ script
+
+From: Nathan Chancellor <nathan@kernel.org>
+
+[ Upstream commit fcae44fd36d052e956e69a64642fc03820968d78 ]
+
+Recently, ld.lld moved from '--undefined-version' to
+'--no-undefined-version' as the default, which breaks the compat vDSO
+build:
+
+  ld.lld: error: version script assignment of 'LINUX_4.15' to symbol '__vdso_gettimeofday' failed: symbol not defined
+  ld.lld: error: version script assignment of 'LINUX_4.15' to symbol '__vdso_clock_gettime' failed: symbol not defined
+  ld.lld: error: version script assignment of 'LINUX_4.15' to symbol '__vdso_clock_getres' failed: symbol not defined
+
+These symbols are not present in the compat vDSO or the regular vDSO for
+32-bit but they are unconditionally included in the version section of
+the linker script, which is prohibited with '--no-undefined-version'.
+
+Fix this issue by only including the symbols that are actually exported
+in the version section of the linker script.
+
+Link: https://github.com/ClangBuiltLinux/linux/issues/1756
+Signed-off-by: Nathan Chancellor <nathan@kernel.org>
+Tested-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20221108171324.3377226-1-nathan@kernel.org/
+Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/riscv/kernel/vdso/Makefile   | 3 +++
+ arch/riscv/kernel/vdso/vdso.lds.S | 2 ++
+ 2 files changed, 5 insertions(+)
+
+diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile
+index 84ac0fe612e7..db6548509bb3 100644
+--- a/arch/riscv/kernel/vdso/Makefile
++++ b/arch/riscv/kernel/vdso/Makefile
+@@ -28,6 +28,9 @@ obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
+ obj-y += vdso.o
+ CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
++ifneq ($(filter vgettimeofday, $(vdso-syms)),)
++CPPFLAGS_vdso.lds += -DHAS_VGETTIMEOFDAY
++endif
+ # Disable -pg to prevent insert call site
+ CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE)
+diff --git a/arch/riscv/kernel/vdso/vdso.lds.S b/arch/riscv/kernel/vdso/vdso.lds.S
+index e9111f700af0..3729cb28aac8 100644
+--- a/arch/riscv/kernel/vdso/vdso.lds.S
++++ b/arch/riscv/kernel/vdso/vdso.lds.S
+@@ -65,9 +65,11 @@ VERSION
+       LINUX_4.15 {
+       global:
+               __vdso_rt_sigreturn;
++#ifdef HAS_VGETTIMEOFDAY
+               __vdso_gettimeofday;
+               __vdso_clock_gettime;
+               __vdso_clock_getres;
++#endif
+               __vdso_getcpu;
+               __vdso_flush_icache;
+       local: *;
+-- 
+2.35.1
+
diff --git a/queue-5.15/riscv-dts-sifive-unleashed-add-pwm-controlled-leds.patch b/queue-5.15/riscv-dts-sifive-unleashed-add-pwm-controlled-leds.patch
new file mode 100644 (file)
index 0000000..14c8885
--- /dev/null
@@ -0,0 +1,83 @@
+From 60afe9b3d6150ec7590bb6f4d8476e256fc6aa78 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 12 Oct 2022 13:09:28 +0200
+Subject: riscv: dts: sifive unleashed: Add PWM controlled LEDs
+
+From: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+
+[ Upstream commit 8bc8824d30193eb7755043d5bb65fa7f0d11a595 ]
+
+This adds the 4 PWM controlled green LEDs to the HiFive Unleashed device
+tree. The schematic doesn't specify any special function for the LEDs,
+so they're added here without any default triggers and named d1, d2, d3
+and d4 just like in the schematic.
+
+Signed-off-by: Emil Renner Berthing <emil.renner.berthing@canonical.com>
+Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
+Tested-by: Conor Dooley <conor.dooley@microchip.com>
+Link: https://lore.kernel.org/r/20221012110928.352910-1-emil.renner.berthing@canonical.com
+Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../boot/dts/sifive/hifive-unleashed-a00.dts  | 38 +++++++++++++++++++
+ 1 file changed, 38 insertions(+)
+
+diff --git a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+index 22f971e97161..2f4d677c9c4f 100644
+--- a/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
++++ b/arch/riscv/boot/dts/sifive/hifive-unleashed-a00.dts
+@@ -3,6 +3,8 @@
+ #include "fu540-c000.dtsi"
+ #include <dt-bindings/gpio/gpio.h>
++#include <dt-bindings/leds/common.h>
++#include <dt-bindings/pwm/pwm.h>
+ /* Clock frequency (in Hz) of the PCB crystal for rtcclk */
+ #define RTCCLK_FREQ           1000000
+@@ -46,6 +48,42 @@ gpio-restart {
+               compatible = "gpio-restart";
+               gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
+       };
++
++      led-controller {
++              compatible = "pwm-leds";
++
++              led-d1 {
++                      pwms = <&pwm0 0 7812500 PWM_POLARITY_INVERTED>;
++                      active-low;
++                      color = <LED_COLOR_ID_GREEN>;
++                      max-brightness = <255>;
++                      label = "d1";
++              };
++
++              led-d2 {
++                      pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>;
++                      active-low;
++                      color = <LED_COLOR_ID_GREEN>;
++                      max-brightness = <255>;
++                      label = "d2";
++              };
++
++              led-d3 {
++                      pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>;
++                      active-low;
++                      color = <LED_COLOR_ID_GREEN>;
++                      max-brightness = <255>;
++                      label = "d3";
++              };
++
++              led-d4 {
++                      pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>;
++                      active-low;
++                      color = <LED_COLOR_ID_GREEN>;
++                      max-brightness = <255>;
++                      label = "d4";
++              };
++      };
+ };
+ &uart0 {
+-- 
+2.35.1
+
diff --git a/queue-5.15/scsi-ibmvfc-avoid-path-failures-during-live-migratio.patch b/queue-5.15/scsi-ibmvfc-avoid-path-failures-during-live-migratio.patch
new file mode 100644 (file)
index 0000000..3e11641
--- /dev/null
@@ -0,0 +1,65 @@
+From 58c7ea94eb5e2489cb6e930a5851bc45dcd442f5 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 26 Oct 2022 13:13:56 -0500
+Subject: scsi: ibmvfc: Avoid path failures during live migration
+
+From: Brian King <brking@linux.vnet.ibm.com>
+
+[ Upstream commit 62fa3ce05d5d73c5eccc40b2db493f55fecfc446 ]
+
+Fix an issue reported when performing a live migration when multipath is
+configured with a short fast fail timeout of 5 seconds and also to have
+no_path_retry set to fail. In this scenario, all paths would go into the
+devloss state while the ibmvfc driver went through discovery to log back
+in. On a loaded system, the discovery might take longer than 5 seconds,
+which was resulting in all paths being marked failed, which then resulted
+in a read only filesystem.
+
+This patch changes the migration code in ibmvfc to avoid deleting rports at
+all in this scenario, so we avoid losing all paths.
+
+Signed-off-by: Brian King <brking@linux.vnet.ibm.com>
+Link: https://lore.kernel.org/r/20221026181356.148517-1-brking@linux.vnet.ibm.com
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/ibmvscsi/ibmvfc.c | 14 +++++++++++---
+ 1 file changed, 11 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c
+index b3531065a438..45ef78f388dc 100644
+--- a/drivers/scsi/ibmvscsi/ibmvfc.c
++++ b/drivers/scsi/ibmvscsi/ibmvfc.c
+@@ -708,8 +708,13 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost)
+               memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE);
+               vhost->async_crq.cur = 0;
+-              list_for_each_entry(tgt, &vhost->targets, queue)
+-                      ibmvfc_del_tgt(tgt);
++              list_for_each_entry(tgt, &vhost->targets, queue) {
++                      if (vhost->client_migrated)
++                              tgt->need_login = 1;
++                      else
++                              ibmvfc_del_tgt(tgt);
++              }
++
+               scsi_block_requests(vhost->host);
+               ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT);
+               vhost->job_step = ibmvfc_npiv_login;
+@@ -3235,9 +3240,12 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost,
+                       /* We need to re-setup the interpartition connection */
+                       dev_info(vhost->dev, "Partition migrated, Re-enabling adapter\n");
+                       vhost->client_migrated = 1;
++
++                      scsi_block_requests(vhost->host);
+                       ibmvfc_purge_requests(vhost, DID_REQUEUE);
+-                      ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN);
++                      ibmvfc_set_host_state(vhost, IBMVFC_LINK_DOWN);
+                       ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE);
++                      wake_up(&vhost->work_wait_q);
+               } else if (crq->format == IBMVFC_PARTNER_FAILED || crq->format == IBMVFC_PARTNER_DEREGISTER) {
+                       dev_err(vhost->dev, "Host partner adapter deregistered or failed (rc=%d)\n", crq->format);
+                       ibmvfc_purge_requests(vhost, DID_ERROR);
+-- 
+2.35.1
+
diff --git a/queue-5.15/scsi-scsi_debug-make-the-read-capacity-response-comp.patch b/queue-5.15/scsi-scsi_debug-make-the-read-capacity-response-comp.patch
new file mode 100644 (file)
index 0000000..2016d50
--- /dev/null
@@ -0,0 +1,59 @@
+From 51245182ef450667d67467b394223701ab6d407b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Nov 2022 12:32:48 -0700
+Subject: scsi: scsi_debug: Make the READ CAPACITY response compliant with ZBC
+
+From: Bart Van Assche <bvanassche@acm.org>
+
+[ Upstream commit ecb8c2580d37dbb641451049376d80c8afaa387f ]
+
+From ZBC-1:
+
+ - RC BASIS = 0: The RETURNED LOGICAL BLOCK ADDRESS field indicates the
+   highest LBA of a contiguous range of zones that are not sequential write
+   required zones starting with the first zone.
+
+ - RC BASIS = 1: The RETURNED LOGICAL BLOCK ADDRESS field indicates the LBA
+   of the last logical block on the logical unit.
+
+The current scsi_debug READ CAPACITY response does not comply with the
+above if there are one or more sequential write required zones. SCSI
+initiators need a way to retrieve the largest valid LBA from SCSI
+devices. Reporting the largest valid LBA if there are one or more
+sequential zones requires to set the RC BASIS field in the READ CAPACITY
+response to one. Hence this patch.
+
+Cc: Douglas Gilbert <dgilbert@interlog.com>
+Cc: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Suggested-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Signed-off-by: Bart Van Assche <bvanassche@acm.org>
+Link: https://lore.kernel.org/r/20221102193248.3177608-1-bvanassche@acm.org
+Reviewed-by: Damien Le Moal <damien.lemoal@opensource.wdc.com>
+Acked-by: Douglas Gilbert <dgilbert@interlog.com>
+Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/scsi/scsi_debug.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
+index 2b5e249f5d5b..0b16061d8da8 100644
+--- a/drivers/scsi/scsi_debug.c
++++ b/drivers/scsi/scsi_debug.c
+@@ -1879,6 +1879,13 @@ static int resp_readcap16(struct scsi_cmnd *scp,
+                       arr[14] |= 0x40;
+       }
++      /*
++       * Since the scsi_debug READ CAPACITY implementation always reports the
++       * total disk capacity, set RC BASIS = 1 for host-managed ZBC devices.
++       */
++      if (devip->zmodel == BLK_ZONED_HM)
++              arr[12] |= 1 << 4;
++
+       arr[15] = sdebug_lowest_aligned & 0xff;
+       if (have_dif_prot) {
+-- 
+2.35.1
+
diff --git a/queue-5.15/sctp-clear-out_curr-if-all-frag-chunks-of-current-ms.patch b/queue-5.15/sctp-clear-out_curr-if-all-frag-chunks-of-current-ms.patch
new file mode 100644 (file)
index 0000000..0a7833f
--- /dev/null
@@ -0,0 +1,68 @@
+From 5eb95af3372c58f75e49e8fd9b9fd37a24668d58 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 4 Nov 2022 17:45:16 -0400
+Subject: sctp: clear out_curr if all frag chunks of current msg are pruned
+
+From: Xin Long <lucien.xin@gmail.com>
+
+[ Upstream commit 2f201ae14ae0f91dbf1cffea7bb1e29e81d4d108 ]
+
+A crash was reported by Zhen Chen:
+
+  list_del corruption, ffffa035ddf01c18->next is NULL
+  WARNING: CPU: 1 PID: 250682 at lib/list_debug.c:49 __list_del_entry_valid+0x59/0xe0
+  RIP: 0010:__list_del_entry_valid+0x59/0xe0
+  Call Trace:
+   sctp_sched_dequeue_common+0x17/0x70 [sctp]
+   sctp_sched_fcfs_dequeue+0x37/0x50 [sctp]
+   sctp_outq_flush_data+0x85/0x360 [sctp]
+   sctp_outq_uncork+0x77/0xa0 [sctp]
+   sctp_cmd_interpreter.constprop.0+0x164/0x1450 [sctp]
+   sctp_side_effects+0x37/0xe0 [sctp]
+   sctp_do_sm+0xd0/0x230 [sctp]
+   sctp_primitive_SEND+0x2f/0x40 [sctp]
+   sctp_sendmsg_to_asoc+0x3fa/0x5c0 [sctp]
+   sctp_sendmsg+0x3d5/0x440 [sctp]
+   sock_sendmsg+0x5b/0x70
+
+and in sctp_sched_fcfs_dequeue() it dequeued a chunk from stream
+out_curr outq while this outq was empty.
+
+Normally stream->out_curr must be set to NULL once all frag chunks of
+current msg are dequeued, as we can see in sctp_sched_dequeue_done().
+However, in sctp_prsctp_prune_unsent() as it is not a proper dequeue,
+sctp_sched_dequeue_done() is not called to do this.
+
+This patch is to fix it by simply setting out_curr to NULL when the
+last frag chunk of current msg is dequeued from out_curr stream in
+sctp_prsctp_prune_unsent().
+
+Fixes: 5bbbbe32a431 ("sctp: introduce stream scheduler foundations")
+Reported-by: Zhen Chen <chenzhen126@huawei.com>
+Tested-by: Caowangbao <caowangbao@huawei.com>
+Signed-off-by: Xin Long <lucien.xin@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sctp/outqueue.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
+index 6fcc4ff97f94..dc29ac0f8d3f 100644
+--- a/net/sctp/outqueue.c
++++ b/net/sctp/outqueue.c
+@@ -403,6 +403,11 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
+               sout = SCTP_SO(&asoc->stream, chk->sinfo.sinfo_stream);
+               sout->ext->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
++              /* clear out_curr if all frag chunks are pruned */
++              if (asoc->stream.out_curr == sout &&
++                  list_is_last(&chk->frag_list, &chk->msg->chunks))
++                      asoc->stream.out_curr = NULL;
++
+               msg_len -= chk->skb->truesize + sizeof(struct sctp_chunk);
+               sctp_chunk_free(chk);
+               if (msg_len <= 0)
+-- 
+2.35.1
+
diff --git a/queue-5.15/sctp-remove-the-unnecessary-sinfo_stream-check-in-sc.patch b/queue-5.15/sctp-remove-the-unnecessary-sinfo_stream-check-in-sc.patch
new file mode 100644 (file)
index 0000000..41c6e12
--- /dev/null
@@ -0,0 +1,53 @@
+From b86214767455865eaa3d679413b86761a295439b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 4 Nov 2022 17:45:15 -0400
+Subject: sctp: remove the unnecessary sinfo_stream check in
+ sctp_prsctp_prune_unsent
+
+From: Xin Long <lucien.xin@gmail.com>
+
+[ Upstream commit 9f0b773210c27a8f5d98ddb2fc4ba60a42a3285f ]
+
+Since commit 5bbbbe32a431 ("sctp: introduce stream scheduler foundations"),
+sctp_stream_outq_migrate() has been called in sctp_stream_init/update to
+removes those chunks to streams higher than the new max. There is no longer
+need to do such check in sctp_prsctp_prune_unsent().
+
+Signed-off-by: Xin Long <lucien.xin@gmail.com>
+Signed-off-by: Jakub Kicinski <kuba@kernel.org>
+Stable-dep-of: 2f201ae14ae0 ("sctp: clear out_curr if all frag chunks of current msg are pruned")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/sctp/outqueue.c | 8 +++-----
+ 1 file changed, 3 insertions(+), 5 deletions(-)
+
+diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
+index b3950963fc8f..6fcc4ff97f94 100644
+--- a/net/sctp/outqueue.c
++++ b/net/sctp/outqueue.c
+@@ -384,6 +384,7 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
+ {
+       struct sctp_outq *q = &asoc->outqueue;
+       struct sctp_chunk *chk, *temp;
++      struct sctp_stream_out *sout;
+       q->sched->unsched_all(&asoc->stream);
+@@ -398,12 +399,9 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
+               sctp_sched_dequeue_common(q, chk);
+               asoc->sent_cnt_removable--;
+               asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
+-              if (chk->sinfo.sinfo_stream < asoc->stream.outcnt) {
+-                      struct sctp_stream_out *streamout =
+-                              SCTP_SO(&asoc->stream, chk->sinfo.sinfo_stream);
+-                      streamout->ext->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
+-              }
++              sout = SCTP_SO(&asoc->stream, chk->sinfo.sinfo_stream);
++              sout->ext->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
+               msg_len -= chk->skb->truesize + sizeof(struct sctp_chunk);
+               sctp_chunk_free(chk);
+-- 
+2.35.1
+
diff --git a/queue-5.15/selftests-bpf-add-verifier-test-for-release_referenc.patch b/queue-5.15/selftests-bpf-add-verifier-test-for-release_referenc.patch
new file mode 100644 (file)
index 0000000..dabcf5b
--- /dev/null
@@ -0,0 +1,86 @@
+From 2bd99369a8aa8987fe96655461c0bbceacb3086c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 3 Nov 2022 17:34:40 +0800
+Subject: selftests/bpf: Add verifier test for release_reference()
+
+From: Youlin Li <liulin063@gmail.com>
+
+[ Upstream commit 475244f5e06beeda7b557d9dde46a5f439bf3379 ]
+
+Add a test case to ensure that released pointer registers will not be
+leaked into the map.
+
+Before fix:
+
+  ./test_verifier 984
+    984/u reference tracking: try to leak released ptr reg FAIL
+    Unexpected success to load!
+    verification time 67 usec
+    stack depth 4
+    processed 23 insns (limit 1000000) max_states_per_insn 0 total_states 2
+    peak_states 2 mark_read 1
+    984/p reference tracking: try to leak released ptr reg OK
+    Summary: 1 PASSED, 0 SKIPPED, 1 FAILED
+
+After fix:
+
+  ./test_verifier 984
+    984/u reference tracking: try to leak released ptr reg OK
+    984/p reference tracking: try to leak released ptr reg OK
+    Summary: 2 PASSED, 0 SKIPPED, 0 FAILED
+
+Signed-off-by: Youlin Li <liulin063@gmail.com>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Link: https://lore.kernel.org/bpf/20221103093440.3161-2-liulin063@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ .../selftests/bpf/verifier/ref_tracking.c     | 36 +++++++++++++++++++
+ 1 file changed, 36 insertions(+)
+
+diff --git a/tools/testing/selftests/bpf/verifier/ref_tracking.c b/tools/testing/selftests/bpf/verifier/ref_tracking.c
+index 3b6ee009c00b..4a768b130d61 100644
+--- a/tools/testing/selftests/bpf/verifier/ref_tracking.c
++++ b/tools/testing/selftests/bpf/verifier/ref_tracking.c
+@@ -905,3 +905,39 @@
+       .result_unpriv = REJECT,
+       .errstr_unpriv = "unknown func",
+ },
++{
++      "reference tracking: try to leak released ptr reg",
++      .insns = {
++              BPF_MOV64_IMM(BPF_REG_0, 0),
++              BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4),
++              BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
++              BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
++              BPF_LD_MAP_FD(BPF_REG_1, 0),
++              BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem),
++              BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
++              BPF_EXIT_INSN(),
++              BPF_MOV64_REG(BPF_REG_9, BPF_REG_0),
++
++              BPF_MOV64_IMM(BPF_REG_0, 0),
++              BPF_LD_MAP_FD(BPF_REG_1, 0),
++              BPF_MOV64_IMM(BPF_REG_2, 8),
++              BPF_MOV64_IMM(BPF_REG_3, 0),
++              BPF_EMIT_CALL(BPF_FUNC_ringbuf_reserve),
++              BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
++              BPF_EXIT_INSN(),
++              BPF_MOV64_REG(BPF_REG_8, BPF_REG_0),
++
++              BPF_MOV64_REG(BPF_REG_1, BPF_REG_8),
++              BPF_MOV64_IMM(BPF_REG_2, 0),
++              BPF_EMIT_CALL(BPF_FUNC_ringbuf_discard),
++              BPF_MOV64_IMM(BPF_REG_0, 0),
++
++              BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_8, 0),
++              BPF_EXIT_INSN()
++      },
++      .fixup_map_array_48b = { 4 },
++      .fixup_map_ringbuf = { 11 },
++      .result = ACCEPT,
++      .result_unpriv = REJECT,
++      .errstr_unpriv = "R8 !read_ok"
++},
+-- 
+2.35.1
+
diff --git a/queue-5.15/serial-add-rs485_supported-to-uart_port.patch b/queue-5.15/serial-add-rs485_supported-to-uart_port.patch
new file mode 100644 (file)
index 0000000..8945a99
--- /dev/null
@@ -0,0 +1,53 @@
+From 5272dc772f299d74ee79c2d44d7d65885e7db48e Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Jun 2022 13:04:00 +0300
+Subject: serial: Add rs485_supported to uart_port
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 8925c31c1ac2f1e05da988581f2a70a2a8c4d638 ]
+
+Preparing to move serial_rs485 struct sanitization into serial core,
+each driver has to provide what fields/flags it supports. This
+information is pointed into by rs485_supported.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Link: https://lore.kernel.org/r/20220606100433.13793-4-ilpo.jarvinen@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 76bad3f88750 ("tty: serial: fsl_lpuart: don't break the on-going transfer when global reset")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/8250/8250_core.c | 1 +
+ include/linux/serial_core.h         | 1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c
+index c3348d5af922..f3bfaa1a794b 100644
+--- a/drivers/tty/serial/8250/8250_core.c
++++ b/drivers/tty/serial/8250/8250_core.c
+@@ -1016,6 +1016,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up)
+               uart->port.throttle     = up->port.throttle;
+               uart->port.unthrottle   = up->port.unthrottle;
+               uart->port.rs485_config = up->port.rs485_config;
++              uart->port.rs485_supported = up->port.rs485_supported;
+               uart->port.rs485        = up->port.rs485;
+               uart->rs485_start_tx    = up->rs485_start_tx;
+               uart->rs485_stop_tx     = up->rs485_stop_tx;
+diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h
+index 86fa187f6d65..d5b6b1550d59 100644
+--- a/include/linux/serial_core.h
++++ b/include/linux/serial_core.h
+@@ -254,6 +254,7 @@ struct uart_port {
+       struct attribute_group  *attr_group;            /* port specific attributes */
+       const struct attribute_group **tty_groups;      /* all attributes (serial core use only) */
+       struct serial_rs485     rs485;
++      const struct serial_rs485       *rs485_supported;       /* Supported mask for serial_rs485 */
+       struct gpio_desc        *rs485_term_gpio;       /* enable RS485 bus termination */
+       struct serial_iso7816   iso7816;
+       void                    *private_data;          /* generic platform data pointer */
+-- 
+2.35.1
+
diff --git a/queue-5.15/serial-fsl_lpuart-fill-in-rs485_supported.patch b/queue-5.15/serial-fsl_lpuart-fill-in-rs485_supported.patch
new file mode 100644 (file)
index 0000000..58581ec
--- /dev/null
@@ -0,0 +1,50 @@
+From c12622640ab97a9adeda65431ad1162821f173ce Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 6 Jun 2022 13:04:12 +0300
+Subject: serial: fsl_lpuart: Fill in rs485_supported
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+
+[ Upstream commit 07481f448b635d7cebb92d5940f5bea5c4395a26 ]
+
+Add information on supported serial_rs485 features.
+
+Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
+Link: https://lore.kernel.org/r/20220606100433.13793-16-ilpo.jarvinen@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 76bad3f88750 ("tty: serial: fsl_lpuart: don't break the on-going transfer when global reset")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/fsl_lpuart.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
+index 44ed4285e1ef..1d13d88ea363 100644
+--- a/drivers/tty/serial/fsl_lpuart.c
++++ b/drivers/tty/serial/fsl_lpuart.c
+@@ -2644,6 +2644,11 @@ static struct uart_driver lpuart_reg = {
+       .cons           = LPUART_CONSOLE,
+ };
++static const struct serial_rs485 lpuart_rs485_supported = {
++      .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND,
++      /* delay_rts_* and RX_DURING_TX are not supported */
++};
++
+ static int lpuart_probe(struct platform_device *pdev)
+ {
+       const struct lpuart_soc_data *sdata = of_device_get_match_data(&pdev->dev);
+@@ -2683,6 +2688,7 @@ static int lpuart_probe(struct platform_device *pdev)
+               sport->port.rs485_config = lpuart32_config_rs485;
+       else
+               sport->port.rs485_config = lpuart_config_rs485;
++      sport->port.rs485_supported = &lpuart_rs485_supported;
+       sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg");
+       if (IS_ERR(sport->ipg_clk)) {
+-- 
+2.35.1
+
diff --git a/queue-5.15/series b/queue-5.15/series
new file mode 100644 (file)
index 0000000..cf245bc
--- /dev/null
@@ -0,0 +1,54 @@
+asoc-fsl_sai-use-local-device-pointer.patch
+asoc-fsl_asrc-fsl_esai-fsl_sai-allow-config_pm-n.patch
+serial-add-rs485_supported-to-uart_port.patch
+serial-fsl_lpuart-fill-in-rs485_supported.patch
+tty-serial-fsl_lpuart-don-t-break-the-on-going-trans.patch
+sctp-remove-the-unnecessary-sinfo_stream-check-in-sc.patch
+sctp-clear-out_curr-if-all-frag-chunks-of-current-ms.patch
+cifs-introduce-new-helper-for-cifs_reconnect.patch
+cifs-split-out-dfs-code-from-cifs_reconnect.patch
+cifs-support-nested-dfs-links-over-reconnect.patch
+cifs-fix-connections-leak-when-tlink-setup-failed.patch
+ata-libata-scsi-simplify-__ata_scsi_queuecmd.patch
+ata-libata-core-do-not-issue-non-internal-commands-o.patch
+drm-display-don-t-assume-dual-mode-adaptors-support-.patch
+nvme-add-a-bogus-subsystem-nqn-quirk-for-micron-mtfd.patch
+nvme-pci-add-nvme_quirk_bogus_nid-for-micron-nitro.patch
+nvme-pci-disable-namespace-identifiers-for-the-maxio.patch
+nvme-pci-disable-write-zeroes-on-various-kingston-ss.patch
+nvme-pci-add-nvme_quirk_bogus_nid-for-netac-nv7000.patch
+speakup-generate-speakupmap.h-automatically.patch
+speakup-replace-utils-u_char-with-unsigned-char.patch
+iio-ms5611-simplify-io-callback-parameters.patch
+iio-pressure-ms5611-fixed-value-compensation-bug.patch
+ceph-do-not-update-snapshot-context-when-there-is-no.patch
+ceph-avoid-putting-the-realm-twice-when-decoding-sna.patch
+x86-sgx-create-utility-to-validate-user-provided-off.patch
+x86-sgx-add-overflow-check-in-sgx_validate_offset_le.patch
+binder-validate-alloc-mm-in-mmap-handler.patch
+ceph-use-kcalloc-for-allocating-multiple-elements.patch
+ceph-fix-null-pointer-dereference-for-req-r_session.patch
+wifi-mac80211-fix-memory-free-error-when-registering.patch
+wifi-mac80211_hwsim-fix-debugfs-attribute-ps-with-rc.patch
+riscv-dts-sifive-unleashed-add-pwm-controlled-leds.patch
+audit-fix-undefined-behavior-in-bit-shift-for-audit_.patch
+wifi-airo-do-not-assign-1-to-unsigned-char.patch
+wifi-mac80211-fix-ack-frame-idr-leak-when-mesh-has-n.patch
+wifi-ath11k-fix-qcn9074-firmware-boot-on-x86.patch
+spi-stm32-fix-stm32_spi_prepare_mbr-that-halves-spi-.patch
+selftests-bpf-add-verifier-test-for-release_referenc.patch
+revert-net-macsec-report-real_dev-features-when-hw-o.patch
+platform-x86-ideapad-laptop-disable-touchpad_switch.patch
+platform-x86-touchscreen_dmi-add-info-for-the-rca-ca.patch
+platform-x86-intel-pmt-sapphire-rapids-pmt-errata-fi.patch
+platform-x86-intel-hid-add-some-acpi-device-ids.patch
+scsi-ibmvfc-avoid-path-failures-during-live-migratio.patch
+scsi-scsi_debug-make-the-read-capacity-response-comp.patch
+drm-panel-orientation-quirks-add-quirk-for-acer-swit.patch
+block-bfq-fix-null-pointer-dereference-in-bfq_bio_bf.patch
+arm64-syscall-include-asm-ptrace.h-in-syscall_wrappe.patch
+nvmet-fix-memory-leak-in-nvmet_subsys_attr_model_sto.patch
+revert-drm-amdgpu-revert-drm-amdgpu-getting-fan-spee.patch
+alsa-usb-audio-add-quirk-to-fix-hamedal-c20-disconne.patch
+risc-v-vdso-do-not-add-missing-symbols-to-version-se.patch
+mips-pic32-treat-port-as-signed-integer.patch
diff --git a/queue-5.15/speakup-generate-speakupmap.h-automatically.patch b/queue-5.15/speakup-generate-speakupmap.h-automatically.patch
new file mode 100644 (file)
index 0000000..002f792
--- /dev/null
@@ -0,0 +1,563 @@
+From df1e815b9659494d9dd19bf4b2f22be3e22b99c9 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sun, 12 Jun 2022 19:22:44 +0200
+Subject: speakup: Generate speakupmap.h automatically
+
+From: Samuel Thibault <samuel.thibault@ens-lyon.org>
+
+[ Upstream commit 6646b95aab5f62c049f1416a3801dec5432c348b ]
+
+speakupmap.h was not actually intended to be source code, speakupmap.map
+is.
+
+This resurrects the makemapdata.c and genmap.c tools to generate
+speakupmap.h automatically from the input and speakup headers, and the
+speakupmap.map keyboard mapping source file.
+
+Signed-off-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Link: https://lore.kernel.org/r/20220515230358.ikwt2kspiwvv5cf4@begin
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Stable-dep-of: 92ca969ff881 ("speakup: replace utils' u_char with unsigned char")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/accessibility/speakup/.gitignore    |   4 +
+ drivers/accessibility/speakup/Makefile      |  28 ++++
+ drivers/accessibility/speakup/genmap.c      | 162 ++++++++++++++++++++
+ drivers/accessibility/speakup/makemapdata.c | 125 +++++++++++++++
+ drivers/accessibility/speakup/speakupmap.h  |  66 --------
+ drivers/accessibility/speakup/utils.h       | 102 ++++++++++++
+ 6 files changed, 421 insertions(+), 66 deletions(-)
+ create mode 100644 drivers/accessibility/speakup/.gitignore
+ create mode 100644 drivers/accessibility/speakup/genmap.c
+ create mode 100644 drivers/accessibility/speakup/makemapdata.c
+ delete mode 100644 drivers/accessibility/speakup/speakupmap.h
+ create mode 100644 drivers/accessibility/speakup/utils.h
+
+diff --git a/drivers/accessibility/speakup/.gitignore b/drivers/accessibility/speakup/.gitignore
+new file mode 100644
+index 000000000000..ac084679fea7
+--- /dev/null
++++ b/drivers/accessibility/speakup/.gitignore
+@@ -0,0 +1,4 @@
++/makemapdata
++/mapdata.h
++/genmap
++/speakupmap.h
+diff --git a/drivers/accessibility/speakup/Makefile b/drivers/accessibility/speakup/Makefile
+index 6e4bfac8af65..ba69b0803d42 100644
+--- a/drivers/accessibility/speakup/Makefile
++++ b/drivers/accessibility/speakup/Makefile
+@@ -30,3 +30,31 @@ speakup-y := \
+       thread.o \
+       varhandlers.o
+ speakup-$(CONFIG_SPEAKUP_SERIALIO) += serialio.o
++
++
++clean-files := mapdata.h speakupmap.h
++
++
++# Generate mapdata.h from headers
++hostprogs += makemapdata
++makemapdata-objs := makemapdata.o
++
++quiet_cmd_mkmap = MKMAP   $@
++      cmd_mkmap = TOPDIR=$(srctree) $(obj)/makemapdata > $@
++
++$(obj)/mapdata.h: $(obj)/makemapdata
++      $(call cmd,mkmap)
++
++
++# Generate speakupmap.h from mapdata.h
++hostprogs += genmap
++genmap-objs := genmap.o
++$(obj)/genmap.o: $(obj)/mapdata.h
++
++quiet_cmd_genmap = GENMAP  $@
++      cmd_genmap = $(obj)/genmap $< > $@
++
++$(obj)/speakupmap.h: $(src)/speakupmap.map $(obj)/genmap
++      $(call cmd,genmap)
++
++$(obj)/main.o: $(obj)/speakupmap.h
+diff --git a/drivers/accessibility/speakup/genmap.c b/drivers/accessibility/speakup/genmap.c
+new file mode 100644
+index 000000000000..0125000e00d9
+--- /dev/null
++++ b/drivers/accessibility/speakup/genmap.c
+@@ -0,0 +1,162 @@
++// SPDX-License-Identifier: GPL-2.0+
++/* genmap.c
++ * originally written by: Kirk Reiser.
++ *
++ ** Copyright (C) 2002  Kirk Reiser.
++ *  Copyright (C) 2003  David Borowski.
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <libgen.h>
++#include <string.h>
++#include <linux/version.h>
++#include <ctype.h>
++#include "utils.h"
++
++struct st_key_init {
++      char *name;
++      int value, shift;
++};
++
++static unsigned char key_data[MAXKEYVAL][16], *kp;
++
++#include "mapdata.h"
++
++static const char delims[] = "\t\n ";
++static char *cp;
++static int map_ver = 119; /* an arbitrary number so speakup can check */
++static int shift_table[17];
++static int max_states = 1, flags;
++/* flags reserved for later, maybe for individual console maps */
++
++static int get_shift_value(int state)
++{
++      int i;
++
++      for (i = 0; shift_table[i] != state; i++) {
++              if (shift_table[i] == -1) {
++                      if (i >= 16)
++                              oops("too many shift states", NULL);
++                      shift_table[i] = state;
++                      max_states = i+1;
++              break;
++      }
++      }
++      return i;
++}
++
++int
++main(int argc, char *argv[])
++{
++      int value, shift_state, i, spk_val = 0, lock_val = 0;
++      int max_key_used = 0, num_keys_used = 0;
++      struct st_key *this;
++      struct st_key_init *p_init;
++      char buffer[256];
++
++      bzero(key_table, sizeof(key_table));
++      bzero(key_data, sizeof(key_data));
++
++      shift_table[0] = 0;
++      for (i = 1; i <= 16; i++)
++              shift_table[i] = -1;
++
++      if (argc < 2) {
++              fputs("usage: genmap filename\n", stderr);
++              exit(1);
++      }
++
++      for (p_init = init_key_data; p_init->name[0] != '.'; p_init++)
++              add_key(p_init->name, p_init->value, p_init->shift);
++
++      open_input(NULL, argv[1]);
++      while (fgets(buffer, sizeof(buffer), infile)) {
++              lc++;
++              value = shift_state = 0;
++
++              cp = strtok(buffer, delims);
++              if (*cp == '#')
++                      continue;
++
++              while (cp) {
++                      if (*cp == '=')
++                              break;
++                      this = find_key(cp);
++                      if (this == NULL)
++                              oops("unknown key/modifier", cp);
++                      if (this->shift == is_shift) {
++                              if (value)
++                                      oops("modifiers must come first", cp);
++                              shift_state += this->value;
++                      } else if (this->shift == is_input)
++                              value = this->value;
++                      else
++                              oops("bad modifier or key", cp);
++                      cp = strtok(0, delims);
++              }
++              if (!cp)
++                      oops("no = found", NULL);
++
++              cp = strtok(0, delims);
++              if (!cp)
++                      oops("no speakup function after =", NULL);
++
++              this = find_key(cp);
++              if (this == NULL || this->shift != is_spk)
++                      oops("invalid speakup function", cp);
++
++              i = get_shift_value(shift_state);
++              if (key_data[value][i]) {
++                      while (--cp > buffer)
++                              if (!*cp)
++                                      *cp = ' ';
++                      oops("two functions on same key combination", cp);
++              }
++              key_data[value][i] = (char)this->value;
++              if (value > max_key_used)
++                      max_key_used = value;
++      }
++      fclose(infile);
++
++      this = find_key("spk_key");
++      if (this)
++              spk_val = this->value;
++
++      this = find_key("spk_lock");
++      if (this)
++              lock_val = this->value;
++
++      for (lc = 1; lc <= max_key_used; lc++) {
++              kp = key_data[lc];
++              if (!memcmp(key_data[0], kp, 16))
++                      continue;
++              num_keys_used++;
++              for (i = 0; i < max_states; i++) {
++                      if (kp[i] != spk_val && kp[i] != lock_val)
++                              continue;
++                      shift_state = shift_table[i];
++                      if (shift_state&16)
++                              continue;
++                      shift_state = get_shift_value(shift_state+16);
++                      kp[shift_state] = kp[i];
++                      /* fill in so we can process the key up, as spk bit will be set */
++              }
++      }
++
++      printf("\t%d, %d, %d,\n\t", map_ver, num_keys_used, max_states);
++      for (i = 0; i < max_states; i++)
++              printf("%d, ", shift_table[i]);
++      printf("%d,", flags);
++      for (lc = 1; lc <= max_key_used; lc++) {
++              kp = key_data[lc];
++              if (!memcmp(key_data[0], kp, 16))
++                      continue;
++              printf("\n\t%d,", lc);
++              for (i = 0; i < max_states; i++)
++                      printf(" %d,", (unsigned int)kp[i]);
++      }
++      printf("\n\t0, %d\n", map_ver);
++
++      exit(0);
++}
+diff --git a/drivers/accessibility/speakup/makemapdata.c b/drivers/accessibility/speakup/makemapdata.c
+new file mode 100644
+index 000000000000..81db9ebf1fff
+--- /dev/null
++++ b/drivers/accessibility/speakup/makemapdata.c
+@@ -0,0 +1,125 @@
++// SPDX-License-Identifier: GPL-2.0+
++/* makemapdata.c
++ * originally written by: Kirk Reiser.
++ *
++ ** Copyright (C) 2002  Kirk Reiser.
++ *  Copyright (C) 2003  David Borowski.
++ */
++
++#include <stdlib.h>
++#include <stdio.h>
++#include <libgen.h>
++#include <string.h>
++#include <linux/version.h>
++#include <ctype.h>
++#include "utils.h"
++
++static char buffer[256];
++
++static int get_define(void)
++{
++      char *c;
++
++      while (fgets(buffer, sizeof(buffer)-1, infile)) {
++              lc++;
++              if (strncmp(buffer, "#define", 7))
++                      continue;
++              c = buffer + 7;
++              while (*c == ' ' || *c == '\t')
++                      c++;
++              def_name = c;
++              while (*c && *c != ' ' && *c != '\t' && *c != '\n')
++                      c++;
++              if (!*c || *c == '\n')
++                      continue;
++              *c++ = '\0';
++              while (*c == ' ' || *c == '\t' || *c == '(')
++                      c++;
++              def_val = c;
++              while (*c && *c != '\n' && *c != ')')
++                      c++;
++              *c++ = '\0';
++              return 1;
++      }
++      fclose(infile);
++      infile = 0;
++      return 0;
++}
++
++int
++main(int argc, char *argv[])
++{
++      int value, i;
++      struct st_key *this;
++      const char *dir_name;
++      char *cp;
++
++      dir_name = getenv("TOPDIR");
++      if (!dir_name)
++              dir_name = ".";
++      bzero(key_table, sizeof(key_table));
++      add_key("shift",        1, is_shift);
++      add_key("altgr",        2, is_shift);
++      add_key("ctrl", 4, is_shift);
++      add_key("alt",  8, is_shift);
++      add_key("spk", 16, is_shift);
++      add_key("double", 32, is_shift);
++
++      open_input(dir_name, "include/linux/input.h");
++      while (get_define()) {
++              if (strncmp(def_name, "KEY_", 4))
++                      continue;
++              value = atoi(def_val);
++              if (value > 0 && value < MAXKEYVAL)
++                      add_key(def_name, value, is_input);
++      }
++
++      open_input(dir_name, "include/uapi/linux/input-event-codes.h");
++      while (get_define()) {
++              if (strncmp(def_name, "KEY_", 4))
++                      continue;
++              value = atoi(def_val);
++              if (value > 0 && value < MAXKEYVAL)
++                      add_key(def_name, value, is_input);
++      }
++
++      open_input(dir_name, "drivers/accessibility/speakup/spk_priv_keyinfo.h");
++      while (get_define()) {
++              if (strlen(def_val) > 5) {
++                      //if (def_val[0] == '(')
++                      //      def_val++;
++                      cp = strchr(def_val, '+');
++                      if (!cp)
++                              continue;
++                      if (cp[-1] == ' ')
++                              cp[-1] = '\0';
++                      *cp++ = '\0';
++                      this = find_key(def_val);
++                      while (*cp == ' ')
++                              cp++;
++                      if (!this || *cp < '0' || *cp > '9')
++                              continue;
++                      value = this->value+atoi(cp);
++              } else if (!strncmp(def_val, "0x", 2))
++                      sscanf(def_val+2, "%x", &value);
++              else if (*def_val >= '0' && *def_val <= '9')
++                      value = atoi(def_val);
++              else
++                      continue;
++              add_key(def_name, value, is_spk);
++      }
++
++      printf("struct st_key_init init_key_data[] = {\n");
++      for (i = 0; i < HASHSIZE; i++) {
++              this = &key_table[i];
++              if (!this->name)
++                      continue;
++              do {
++                      printf("\t{ \"%s\", %d, %d, },\n", this->name, this->value, this->shift);
++                      this = this->next;
++              } while (this);
++      }
++      printf("\t{ \".\", 0, 0 }\n};\n");
++
++      exit(0);
++}
+diff --git a/drivers/accessibility/speakup/speakupmap.h b/drivers/accessibility/speakup/speakupmap.h
+deleted file mode 100644
+index c60d7339b89a..000000000000
+--- a/drivers/accessibility/speakup/speakupmap.h
++++ /dev/null
+@@ -1,66 +0,0 @@
+-/* SPDX-License-Identifier: GPL-2.0 */
+-      119, 62, 6,
+-      0, 16, 20, 17, 32, 48, 0,
+-      2, 0, 78, 0, 0, 0, 0,
+-      3, 0, 79, 0, 0, 0, 0,
+-      4, 0, 76, 0, 0, 0, 0,
+-      5, 0, 77, 0, 0, 0, 0,
+-      6, 0, 74, 0, 0, 0, 0,
+-      7, 0, 75, 0, 0, 0, 0,
+-      9, 0, 5, 46, 0, 0, 0,
+-      10, 0, 4, 0, 0, 0, 0,
+-      11, 0, 0, 1, 0, 0, 0,
+-      12, 0, 27, 0, 33, 0, 0,
+-      19, 0, 47, 0, 0, 0, 0,
+-      21, 0, 29, 17, 0, 0, 0,
+-      22, 0, 15, 0, 0, 0, 0,
+-      23, 0, 14, 0, 0, 0, 28,
+-      24, 0, 16, 0, 0, 0, 0,
+-      25, 0, 30, 18, 0, 0, 0,
+-      28, 0, 3, 26, 0, 0, 0,
+-      35, 0, 31, 0, 0, 0, 0,
+-      36, 0, 12, 0, 0, 0, 0,
+-      37, 0, 11, 0, 0, 0, 22,
+-      38, 0, 13, 0, 0, 0, 0,
+-      39, 0, 32, 7, 0, 0, 0,
+-      40, 0, 23, 0, 0, 0, 0,
+-      44, 0, 44, 0, 0, 0, 0,
+-      49, 0, 24, 0, 0, 0, 0,
+-      50, 0, 9, 19, 6, 0, 0,
+-      51, 0, 8, 0, 0, 0, 36,
+-      52, 0, 10, 20, 0, 0, 0,
+-      53, 0, 25, 0, 0, 0, 0,
+-      55, 46, 1, 0, 0, 0, 0,
+-      58, 128, 128, 0, 0, 0, 0,
+-      59, 0, 45, 0, 0, 0, 0,
+-      60, 0, 40, 0, 0, 0, 0,
+-      61, 0, 41, 0, 0, 0, 0,
+-      62, 0, 42, 0, 0, 0, 0,
+-      63, 0, 34, 0, 0, 0, 0,
+-      64, 0, 35, 0, 0, 0, 0,
+-      65, 0, 37, 0, 0, 0, 0,
+-      66, 0, 38, 0, 0, 0, 0,
+-      67, 0, 66, 0, 39, 0, 0,
+-      68, 0, 67, 0, 0, 0, 0,
+-      71, 15, 19, 0, 0, 0, 0,
+-      72, 14, 29, 0, 0, 28, 0,
+-      73, 16, 17, 0, 0, 0, 0,
+-      74, 27, 33, 0, 0, 0, 0,
+-      75, 12, 31, 0, 0, 0, 0,
+-      76, 11, 21, 0, 0, 22, 0,
+-      77, 13, 32, 0, 0, 0, 0,
+-      78, 23, 43, 0, 0, 0, 0,
+-      79, 9, 20, 0, 0, 0, 0,
+-      80, 8, 30, 0, 0, 36, 0,
+-      81, 10, 18, 0, 0, 0, 0,
+-      82, 128, 128, 0, 0, 0, 0,
+-      83, 24, 25, 0, 0, 0, 0,
+-      87, 0, 68, 0, 0, 0, 0,
+-      88, 0, 69, 0, 0, 0, 0,
+-      96, 3, 26, 0, 0, 0, 0,
+-      98, 4, 5, 0, 0, 0, 0,
+-      99, 2, 0, 0, 0, 0, 0,
+-      104, 0, 6, 0, 0, 0, 0,
+-      109, 0, 7, 0, 0, 0, 0,
+-      125, 128, 128, 0, 0, 0, 0,
+-      0, 119
+diff --git a/drivers/accessibility/speakup/utils.h b/drivers/accessibility/speakup/utils.h
+new file mode 100644
+index 000000000000..4bf2ee8ac246
+--- /dev/null
++++ b/drivers/accessibility/speakup/utils.h
+@@ -0,0 +1,102 @@
++/* SPDX-License-Identifier: GPL-2.0+ */
++/* utils.h
++ * originally written by: Kirk Reiser.
++ *
++ ** Copyright (C) 2002  Kirk Reiser.
++ *  Copyright (C) 2003  David Borowski.
++ */
++
++#include <stdio.h>
++
++#define MAXKEYS 512
++#define MAXKEYVAL 160
++#define HASHSIZE 101
++#define is_shift -3
++#define is_spk -2
++#define is_input -1
++
++struct st_key {
++      char *name;
++      struct st_key *next;
++      int value, shift;
++};
++
++struct st_key key_table[MAXKEYS];
++struct st_key *extra_keys = key_table+HASHSIZE;
++char *def_name, *def_val;
++FILE *infile;
++int lc;
++
++char filename[256];
++
++static inline void open_input(const char *dir_name, const char *name)
++{
++      if (dir_name)
++              snprintf(filename, sizeof(filename), "%s/%s", dir_name, name);
++      else
++              snprintf(filename, sizeof(filename), "%s", name);
++      infile = fopen(filename, "r");
++      if (infile == 0) {
++              fprintf(stderr, "can't open %s\n", filename);
++              exit(1);
++      }
++      lc = 0;
++}
++
++static inline int oops(const char *msg, const char *info)
++{
++      if (info == NULL)
++              info = "";
++      fprintf(stderr, "error: file %s line %d\n", filename, lc);
++      fprintf(stderr, "%s %s\n", msg, info);
++      exit(1);
++}
++
++static inline struct st_key *hash_name(char *name)
++{
++      u_char *pn = (u_char *)name;
++      int hash = 0;
++
++      while (*pn) {
++              hash = (hash * 17) & 0xfffffff;
++              if (isupper(*pn))
++                      *pn = tolower(*pn);
++              hash += (int)*pn;
++              pn++;
++      }
++      hash %= HASHSIZE;
++      return &key_table[hash];
++}
++
++static inline struct st_key *find_key(char *name)
++{
++      struct st_key *this = hash_name(name);
++
++      while (this) {
++              if (this->name && !strcmp(name, this->name))
++                      return this;
++              this = this->next;
++      }
++      return this;
++}
++
++static inline struct st_key *add_key(char *name, int value, int shift)
++{
++      struct st_key *this = hash_name(name);
++
++      if (extra_keys-key_table >= MAXKEYS)
++              oops("out of key table space, enlarge MAXKEYS", NULL);
++      if (this->name != NULL) {
++              while (this->next) {
++                      if (!strcmp(name, this->name))
++                              oops("attempt to add duplicate key", name);
++                      this = this->next;
++              }
++              this->next = extra_keys++;
++              this = this->next;
++      }
++      this->name = strdup(name);
++      this->value = value;
++      this->shift = shift;
++      return this;
++}
+-- 
+2.35.1
+
diff --git a/queue-5.15/speakup-replace-utils-u_char-with-unsigned-char.patch b/queue-5.15/speakup-replace-utils-u_char-with-unsigned-char.patch
new file mode 100644 (file)
index 0000000..ed9647d
--- /dev/null
@@ -0,0 +1,46 @@
+From 0a1e1c44176ae95beec745ee2f6b3bcec2862716 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 17 Oct 2022 15:09:36 +0700
+Subject: speakup: replace utils' u_char with unsigned char
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Đoàn Trần Công Danh <congdanhqx@gmail.com>
+
+[ Upstream commit 92ca969ff8815f3feef2645199bd39bf594e5eeb ]
+
+drivers/accessibility/speakup/utils.h will be used to compile host tool
+to generate metadata.
+
+"u_char" is a non-standard type, which is defined to "unsigned char"
+on glibc but not defined by some libc, e.g. musl.
+
+Let's replace "u_char" with "unsigned char"
+
+Signed-off-by: Đoàn Trần Công Danh <congdanhqx@gmail.com>
+Reviewed-by: Samuel Thibault <samuel.thibault@ens-lyon.org>
+Cc: stable <stable@kernel.org>
+Link: https://lore.kernel.org/r/b75743026aaee2d81efe3d7f2e8fa47f7d0b8ea7.1665736571.git.congdanhqx@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/accessibility/speakup/utils.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/accessibility/speakup/utils.h b/drivers/accessibility/speakup/utils.h
+index 4bf2ee8ac246..4ce9a12f7664 100644
+--- a/drivers/accessibility/speakup/utils.h
++++ b/drivers/accessibility/speakup/utils.h
+@@ -54,7 +54,7 @@ static inline int oops(const char *msg, const char *info)
+ static inline struct st_key *hash_name(char *name)
+ {
+-      u_char *pn = (u_char *)name;
++      unsigned char *pn = (unsigned char *)name;
+       int hash = 0;
+       while (*pn) {
+-- 
+2.35.1
+
diff --git a/queue-5.15/spi-stm32-fix-stm32_spi_prepare_mbr-that-halves-spi-.patch b/queue-5.15/spi-stm32-fix-stm32_spi_prepare_mbr-that-halves-spi-.patch
new file mode 100644 (file)
index 0000000..cc04bb0
--- /dev/null
@@ -0,0 +1,51 @@
+From f8e8311c96dcf5d08dd6bb7d54fca81bce837ee2 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 3 Nov 2022 09:00:42 +0100
+Subject: spi: stm32: fix stm32_spi_prepare_mbr() that halves spi clk for every
+ run
+
+From: Sean Nyekjaer <sean@geanix.com>
+
+[ Upstream commit 62aa1a344b0904549f6de7af958e8a1136fd5228 ]
+
+When this driver is used with a driver that uses preallocated spi_transfer
+structs. The speed_hz is halved by every run. This results in:
+
+spi_stm32 44004000.spi: SPI transfer setup failed
+ads7846 spi0.0: SPI transfer failed: -22
+
+Example when running with DIV_ROUND_UP():
+- First run; speed_hz = 1000000, spi->clk_rate 125000000
+  div 125 -> mbrdiv = 7, cur_speed = 976562
+- Second run; speed_hz = 976562
+  div 128,00007 (roundup to 129) -> mbrdiv = 8, cur_speed = 488281
+- Third run; speed_hz = 488281
+  div 256,000131072067109 (roundup to 257) and then -EINVAL is returned.
+
+Use DIV_ROUND_CLOSEST to allow to round down and allow us to keep the
+set speed.
+
+Signed-off-by: Sean Nyekjaer <sean@geanix.com>
+Link: https://lore.kernel.org/r/20221103080043.3033414-1-sean@geanix.com
+Signed-off-by: Mark Brown <broonie@kernel.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/spi/spi-stm32.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c
+index 96a73f9e2677..3c6f201b5dd8 100644
+--- a/drivers/spi/spi-stm32.c
++++ b/drivers/spi/spi-stm32.c
+@@ -434,7 +434,7 @@ static int stm32_spi_prepare_mbr(struct stm32_spi *spi, u32 speed_hz,
+       u32 div, mbrdiv;
+       /* Ensure spi->clk_rate is even */
+-      div = DIV_ROUND_UP(spi->clk_rate & ~0x1, speed_hz);
++      div = DIV_ROUND_CLOSEST(spi->clk_rate & ~0x1, speed_hz);
+       /*
+        * SPI framework set xfer->speed_hz to master->max_speed_hz if
+-- 
+2.35.1
+
diff --git a/queue-5.15/tty-serial-fsl_lpuart-don-t-break-the-on-going-trans.patch b/queue-5.15/tty-serial-fsl_lpuart-don-t-break-the-on-going-trans.patch
new file mode 100644 (file)
index 0000000..7b94750
--- /dev/null
@@ -0,0 +1,137 @@
+From ec677f4d3be029442a04224599c10156f79ebb74 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Oct 2022 16:58:44 +0800
+Subject: tty: serial: fsl_lpuart: don't break the on-going transfer when
+ global reset
+
+From: Sherry Sun <sherry.sun@nxp.com>
+
+[ Upstream commit 76bad3f88750f8cc465c489e6846249e0bc3d8f5 ]
+
+lpuart_global_reset() shouldn't break the on-going transmit engine, need
+to recover the on-going data transfer after reset.
+
+This can help earlycon here, since commit 60f361722ad2 ("serial:
+fsl_lpuart: Reset prior to registration") moved lpuart_global_reset()
+before uart_add_one_port(), earlycon is writing during global reset,
+as global reset will disable the TX and clear the baud rate register,
+which caused the earlycon cannot work any more after reset, needs to
+restore the baud rate and re-enable the transmitter to recover the
+earlycon write.
+
+Also move the lpuart_global_reset() down, then we can reuse the
+lpuart32_tx_empty() without declaration.
+
+Fixes: bd5305dcabbc ("tty: serial: fsl_lpuart: do software reset for imx7ulp and imx8qxp")
+Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
+Link: https://lore.kernel.org/r/20221024085844.22786-1-sherry.sun@nxp.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/tty/serial/fsl_lpuart.c | 76 +++++++++++++++++++++------------
+ 1 file changed, 49 insertions(+), 27 deletions(-)
+
+diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
+index 1d13d88ea363..595430aedc0d 100644
+--- a/drivers/tty/serial/fsl_lpuart.c
++++ b/drivers/tty/serial/fsl_lpuart.c
+@@ -12,6 +12,7 @@
+ #include <linux/dmaengine.h>
+ #include <linux/dmapool.h>
+ #include <linux/io.h>
++#include <linux/iopoll.h>
+ #include <linux/irq.h>
+ #include <linux/module.h>
+ #include <linux/of.h>
+@@ -395,33 +396,6 @@ static unsigned int lpuart_get_baud_clk_rate(struct lpuart_port *sport)
+ #define lpuart_enable_clks(x) __lpuart_enable_clks(x, true)
+ #define lpuart_disable_clks(x)        __lpuart_enable_clks(x, false)
+-static int lpuart_global_reset(struct lpuart_port *sport)
+-{
+-      struct uart_port *port = &sport->port;
+-      void __iomem *global_addr;
+-      int ret;
+-
+-      if (uart_console(port))
+-              return 0;
+-
+-      ret = clk_prepare_enable(sport->ipg_clk);
+-      if (ret) {
+-              dev_err(sport->port.dev, "failed to enable uart ipg clk: %d\n", ret);
+-              return ret;
+-      }
+-
+-      if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) {
+-              global_addr = port->membase + UART_GLOBAL - IMX_REG_OFF;
+-              writel(UART_GLOBAL_RST, global_addr);
+-              usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US);
+-              writel(0, global_addr);
+-              usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US);
+-      }
+-
+-      clk_disable_unprepare(sport->ipg_clk);
+-      return 0;
+-}
+-
+ static void lpuart_stop_tx(struct uart_port *port)
+ {
+       unsigned char temp;
+@@ -2649,6 +2623,54 @@ static const struct serial_rs485 lpuart_rs485_supported = {
+       /* delay_rts_* and RX_DURING_TX are not supported */
+ };
++static int lpuart_global_reset(struct lpuart_port *sport)
++{
++      struct uart_port *port = &sport->port;
++      void __iomem *global_addr;
++      unsigned long ctrl, bd;
++      unsigned int val = 0;
++      int ret;
++
++      ret = clk_prepare_enable(sport->ipg_clk);
++      if (ret) {
++              dev_err(sport->port.dev, "failed to enable uart ipg clk: %d\n", ret);
++              return ret;
++      }
++
++      if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) {
++              /*
++               * If the transmitter is used by earlycon, wait for transmit engine to
++               * complete and then reset.
++               */
++              ctrl = lpuart32_read(port, UARTCTRL);
++              if (ctrl & UARTCTRL_TE) {
++                      bd = lpuart32_read(&sport->port, UARTBAUD);
++                      if (read_poll_timeout(lpuart32_tx_empty, val, val, 1, 100000, false,
++                                            port)) {
++                              dev_warn(sport->port.dev,
++                                       "timeout waiting for transmit engine to complete\n");
++                              clk_disable_unprepare(sport->ipg_clk);
++                              return 0;
++                      }
++              }
++
++              global_addr = port->membase + UART_GLOBAL - IMX_REG_OFF;
++              writel(UART_GLOBAL_RST, global_addr);
++              usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US);
++              writel(0, global_addr);
++              usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US);
++
++              /* Recover the transmitter for earlycon. */
++              if (ctrl & UARTCTRL_TE) {
++                      lpuart32_write(port, bd, UARTBAUD);
++                      lpuart32_write(port, ctrl, UARTCTRL);
++              }
++      }
++
++      clk_disable_unprepare(sport->ipg_clk);
++      return 0;
++}
++
+ static int lpuart_probe(struct platform_device *pdev)
+ {
+       const struct lpuart_soc_data *sdata = of_device_get_match_data(&pdev->dev);
+-- 
+2.35.1
+
diff --git a/queue-5.15/wifi-airo-do-not-assign-1-to-unsigned-char.patch b/queue-5.15/wifi-airo-do-not-assign-1-to-unsigned-char.patch
new file mode 100644 (file)
index 0000000..ea5cc17
--- /dev/null
@@ -0,0 +1,75 @@
+From 5f84ab9f8c179fe4f1105af3c07f20177dced941 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Mon, 24 Oct 2022 18:28:43 +0200
+Subject: wifi: airo: do not assign -1 to unsigned char
+
+From: Jason A. Donenfeld <Jason@zx2c4.com>
+
+[ Upstream commit e6cb8769452e8236b52134e5cb4a18b8f5986932 ]
+
+With char becoming unsigned by default, and with `char` alone being
+ambiguous and based on architecture, we get a warning when assigning the
+unchecked output of hex_to_bin() to that unsigned char. Mark `key` as a
+`u8`, which matches the struct's type, and then check each call to
+hex_to_bin() before casting.
+
+Cc: Kalle Valo <kvalo@kernel.org>
+Cc: linux-wireless@vger.kernel.org
+Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
+Signed-off-by: Kalle Valo <kvalo@kernel.org>
+Link: https://lore.kernel.org/r/20221024162843.535921-1-Jason@zx2c4.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/cisco/airo.c | 18 ++++++++++++++----
+ 1 file changed, 14 insertions(+), 4 deletions(-)
+
+diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c
+index 65dd8cff1b01..fc19ecbc4c08 100644
+--- a/drivers/net/wireless/cisco/airo.c
++++ b/drivers/net/wireless/cisco/airo.c
+@@ -5233,7 +5233,7 @@ static int get_wep_tx_idx(struct airo_info *ai)
+       return -1;
+ }
+-static int set_wep_key(struct airo_info *ai, u16 index, const char *key,
++static int set_wep_key(struct airo_info *ai, u16 index, const u8 *key,
+                      u16 keylen, int perm, int lock)
+ {
+       static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 };
+@@ -5284,7 +5284,7 @@ static void proc_wepkey_on_close(struct inode *inode, struct file *file)
+       struct net_device *dev = PDE_DATA(inode);
+       struct airo_info *ai = dev->ml_priv;
+       int i, rc;
+-      char key[16];
++      u8 key[16];
+       u16 index = 0;
+       int j = 0;
+@@ -5312,12 +5312,22 @@ static void proc_wepkey_on_close(struct inode *inode, struct file *file)
+       }
+       for (i = 0; i < 16*3 && data->wbuffer[i+j]; i++) {
++              int val;
++
++              if (i % 3 == 2)
++                      continue;
++
++              val = hex_to_bin(data->wbuffer[i+j]);
++              if (val < 0) {
++                      airo_print_err(ai->dev->name, "WebKey passed invalid key hex");
++                      return;
++              }
+               switch(i%3) {
+               case 0:
+-                      key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4;
++                      key[i/3] = (u8)val << 4;
+                       break;
+               case 1:
+-                      key[i/3] |= hex_to_bin(data->wbuffer[i+j]);
++                      key[i/3] |= (u8)val;
+                       break;
+               }
+       }
+-- 
+2.35.1
+
diff --git a/queue-5.15/wifi-ath11k-fix-qcn9074-firmware-boot-on-x86.patch b/queue-5.15/wifi-ath11k-fix-qcn9074-firmware-boot-on-x86.patch
new file mode 100644 (file)
index 0000000..d9cf19f
--- /dev/null
@@ -0,0 +1,50 @@
+From 0288d08d7b1d6441671b53218a76f81ed93a5a0c Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 2 Nov 2022 18:56:39 +0200
+Subject: wifi: ath11k: Fix QCN9074 firmware boot on x86
+
+From: Tyler J. Stachecki <stachecki.tyler@gmail.com>
+
+[ Upstream commit 3a89b6dec9920026eaa90fe8457f4348d3388a98 ]
+
+The 2.7.0 series of QCN9074's firmware requests 5 segments
+of memory instead of 3 (as in the 2.5.0 series).
+
+The first segment (11M) is too large to be kalloc'd in one
+go on x86 and requires piecemeal 1MB allocations, as was
+the case with the prior public firmware (2.5.0, 15M).
+
+Since f6f92968e1e5, ath11k will break the memory requests,
+but only if there were fewer than 3 segments requested by
+the firmware. It seems that 5 segments works fine and
+allows QCN9074 to boot on x86 with firmware 2.7.0, so
+change things accordingly.
+
+Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
+Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01208-QCAHKSWPL_SILICONZ-1
+Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.16
+
+Signed-off-by: Tyler J. Stachecki <stachecki.tyler@gmail.com>
+Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
+Link: https://lore.kernel.org/r/20221022042728.43015-1-stachecki.tyler@gmail.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/ath/ath11k/qmi.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h
+index 3d5930330703..25940b683ea4 100644
+--- a/drivers/net/wireless/ath/ath11k/qmi.h
++++ b/drivers/net/wireless/ath/ath11k/qmi.h
+@@ -27,7 +27,7 @@
+ #define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52
+ #define ATH11K_QMI_CALDB_SIZE                 0x480000
+ #define ATH11K_QMI_BDF_EXT_STR_LENGTH         0x20
+-#define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT     3
++#define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT     5
+ #define QMI_WLFW_REQUEST_MEM_IND_V01          0x0035
+ #define QMI_WLFW_FW_MEM_READY_IND_V01         0x0037
+-- 
+2.35.1
+
diff --git a/queue-5.15/wifi-mac80211-fix-ack-frame-idr-leak-when-mesh-has-n.patch b/queue-5.15/wifi-mac80211-fix-ack-frame-idr-leak-when-mesh-has-n.patch
new file mode 100644 (file)
index 0000000..71b06b1
--- /dev/null
@@ -0,0 +1,42 @@
+From f905afbf5f132c40940b4b10324ebe9f2a7d1b2f Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Thu, 27 Oct 2022 16:01:33 +0200
+Subject: wifi: mac80211: Fix ack frame idr leak when mesh has no route
+
+From: Nicolas Cavallari <nicolas.cavallari@green-communications.fr>
+
+[ Upstream commit 39e7b5de9853bd92ddbfa4b14165babacd7da0ba ]
+
+When trying to transmit an data frame with tx_status to a destination
+that have no route in the mesh, then it is dropped without recrediting
+the ack_status_frames idr.
+
+Once it is exhausted, wpa_supplicant starts failing to do SAE with
+NL80211_CMD_FRAME and logs "nl80211: Frame command failed".
+
+Use ieee80211_free_txskb() instead of kfree_skb() to fix it.
+
+Signed-off-by: Nicolas Cavallari <nicolas.cavallari@green-communications.fr>
+Link: https://lore.kernel.org/r/20221027140133.1504-1-nicolas.cavallari@green-communications.fr
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/mesh_pathtbl.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
+index acc1c299f1ae..69d5e1ec6ede 100644
+--- a/net/mac80211/mesh_pathtbl.c
++++ b/net/mac80211/mesh_pathtbl.c
+@@ -710,7 +710,7 @@ int mesh_path_send_to_gates(struct mesh_path *mpath)
+ void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata,
+                            struct sk_buff *skb)
+ {
+-      kfree_skb(skb);
++      ieee80211_free_txskb(&sdata->local->hw, skb);
+       sdata->u.mesh.mshstats.dropped_frames_no_route++;
+ }
+-- 
+2.35.1
+
diff --git a/queue-5.15/wifi-mac80211-fix-memory-free-error-when-registering.patch b/queue-5.15/wifi-mac80211-fix-memory-free-error-when-registering.patch
new file mode 100644 (file)
index 0000000..42cb988
--- /dev/null
@@ -0,0 +1,53 @@
+From 29c20b1ea29dfb6eba4b07192e6e67918435f464 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Sat, 15 Oct 2022 17:38:31 +0800
+Subject: wifi: mac80211: fix memory free error when registering wiphy fail
+
+From: taozhang <taozhang@bestechnic.com>
+
+[ Upstream commit 50b2e8711462409cd368c41067405aa446dfa2af ]
+
+ieee80211_register_hw free the allocated cipher suites when
+registering wiphy fail, and ieee80211_free_hw will re-free it.
+
+set wiphy_ciphers_allocated to false after freeing allocated
+cipher suites.
+
+Signed-off-by: taozhang <taozhang@bestechnic.com>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ net/mac80211/main.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/net/mac80211/main.c b/net/mac80211/main.c
+index 5311c3cd3050..9617ff8e2714 100644
+--- a/net/mac80211/main.c
++++ b/net/mac80211/main.c
+@@ -1357,8 +1357,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
+       ieee80211_led_exit(local);
+       destroy_workqueue(local->workqueue);
+  fail_workqueue:
+-      if (local->wiphy_ciphers_allocated)
++      if (local->wiphy_ciphers_allocated) {
+               kfree(local->hw.wiphy->cipher_suites);
++              local->wiphy_ciphers_allocated = false;
++      }
+       kfree(local->int_scan_req);
+       return result;
+ }
+@@ -1426,8 +1428,10 @@ void ieee80211_free_hw(struct ieee80211_hw *hw)
+       mutex_destroy(&local->iflist_mtx);
+       mutex_destroy(&local->mtx);
+-      if (local->wiphy_ciphers_allocated)
++      if (local->wiphy_ciphers_allocated) {
+               kfree(local->hw.wiphy->cipher_suites);
++              local->wiphy_ciphers_allocated = false;
++      }
+       idr_for_each(&local->ack_status_frames,
+                    ieee80211_free_ack_frame, NULL);
+-- 
+2.35.1
+
diff --git a/queue-5.15/wifi-mac80211_hwsim-fix-debugfs-attribute-ps-with-rc.patch b/queue-5.15/wifi-mac80211_hwsim-fix-debugfs-attribute-ps-with-rc.patch
new file mode 100644 (file)
index 0000000..28922ca
--- /dev/null
@@ -0,0 +1,57 @@
+From 0fbc0e08ec64160f54639e70525f7507c8826d8b Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Fri, 14 Oct 2022 16:54:39 +0200
+Subject: wifi: mac80211_hwsim: fix debugfs attribute ps with rc table support
+
+From: Jonas Jelonek <jelonek.jonas@gmail.com>
+
+[ Upstream commit 69188df5f6e4cecc6b76b958979ba363cd5240e8 ]
+
+Fixes a warning that occurs when rc table support is enabled
+(IEEE80211_HW_SUPPORTS_RC_TABLE) in mac80211_hwsim and the PS mode
+is changed via the exported debugfs attribute.
+
+When the PS mode is changed, a packet is broadcasted via
+hwsim_send_nullfunc by creating and transmitting a plain skb with only
+header initialized. The ieee80211 rate array in the control buffer is
+zero-initialized. When ratetbl support is enabled, ieee80211_get_tx_rates
+is called for the skb with sta parameter set to NULL and thus no
+ratetbl can be used. The final rate array then looks like
+[-1,0; 0,0; 0,0; 0,0] which causes the warning in ieee80211_get_tx_rate.
+
+The issue is fixed by setting the count of the first rate with idx '0'
+to 1 and hence ieee80211_get_tx_rates won't overwrite it with idx '-1'.
+
+Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ drivers/net/wireless/mac80211_hwsim.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c
+index b228567b2a73..c3c3b5aa87b0 100644
+--- a/drivers/net/wireless/mac80211_hwsim.c
++++ b/drivers/net/wireless/mac80211_hwsim.c
+@@ -845,6 +845,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
+       struct hwsim_vif_priv *vp = (void *)vif->drv_priv;
+       struct sk_buff *skb;
+       struct ieee80211_hdr *hdr;
++      struct ieee80211_tx_info *cb;
+       if (!vp->assoc)
+               return;
+@@ -866,6 +867,10 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac,
+       memcpy(hdr->addr2, mac, ETH_ALEN);
+       memcpy(hdr->addr3, vp->bssid, ETH_ALEN);
++      cb = IEEE80211_SKB_CB(skb);
++      cb->control.rates[0].count = 1;
++      cb->control.rates[1].idx = -1;
++
+       rcu_read_lock();
+       mac80211_hwsim_tx_frame(data->hw, skb,
+                               rcu_dereference(vif->chanctx_conf)->def.chan);
+-- 
+2.35.1
+
diff --git a/queue-5.15/x86-sgx-add-overflow-check-in-sgx_validate_offset_le.patch b/queue-5.15/x86-sgx-add-overflow-check-in-sgx_validate_offset_le.patch
new file mode 100644 (file)
index 0000000..55c8b18
--- /dev/null
@@ -0,0 +1,44 @@
+From 9042251bf9339a9670adcf93fbbc7007ba499cee Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Wed, 5 Oct 2022 00:59:03 +0200
+Subject: x86/sgx: Add overflow check in sgx_validate_offset_length()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Borys Popławski <borysp@invisiblethingslab.com>
+
+[ Upstream commit f0861f49bd946ff94fce4f82509c45e167f63690 ]
+
+sgx_validate_offset_length() function verifies "offset" and "length"
+arguments provided by userspace, but was missing an overflow check on
+their addition. Add it.
+
+Fixes: c6d26d370767 ("x86/sgx: Add SGX_IOC_ENCLAVE_ADD_PAGES")
+Signed-off-by: Borys Popławski <borysp@invisiblethingslab.com>
+Signed-off-by: Borislav Petkov <bp@suse.de>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Cc: stable@vger.kernel.org # v5.11+
+Link: https://lore.kernel.org/r/0d91ac79-6d84-abed-5821-4dbe59fa1a38@invisiblethingslab.com
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/cpu/sgx/ioctl.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c
+index a66795e0b685..217777c029ee 100644
+--- a/arch/x86/kernel/cpu/sgx/ioctl.c
++++ b/arch/x86/kernel/cpu/sgx/ioctl.c
+@@ -386,6 +386,9 @@ static int sgx_validate_offset_length(struct sgx_encl *encl,
+       if (!length || !IS_ALIGNED(length, PAGE_SIZE))
+               return -EINVAL;
++      if (offset + length < offset)
++              return -EINVAL;
++
+       if (offset + length - PAGE_SIZE >= encl->size)
+               return -EINVAL;
+-- 
+2.35.1
+
diff --git a/queue-5.15/x86-sgx-create-utility-to-validate-user-provided-off.patch b/queue-5.15/x86-sgx-create-utility-to-validate-user-provided-off.patch
new file mode 100644 (file)
index 0000000..cc4c060
--- /dev/null
@@ -0,0 +1,75 @@
+From 3ec706786be50139df3b038f444f2c751a278cc4 Mon Sep 17 00:00:00 2001
+From: Sasha Levin <sashal@kernel.org>
+Date: Tue, 10 May 2022 11:08:46 -0700
+Subject: x86/sgx: Create utility to validate user provided offset and length
+
+From: Reinette Chatre <reinette.chatre@intel.com>
+
+[ Upstream commit dda03e2c331b9fc7bbc8fc0de12a6d92d8c18661 ]
+
+User provided offset and length is validated when parsing the parameters
+of the SGX_IOC_ENCLAVE_ADD_PAGES ioctl(). Extract this validation
+(with consistent use of IS_ALIGNED) into a utility that can be used
+by the SGX2 ioctl()s that will also provide these values.
+
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
+Reviewed-by: Jarkko Sakkinen <jarkko@kernel.org>
+Link: https://lkml.kernel.org/r/767147bc100047abed47fe27c592901adfbb93a2.1652137848.git.reinette.chatre@intel.com
+Stable-dep-of: f0861f49bd94 ("x86/sgx: Add overflow check in sgx_validate_offset_length()")
+Signed-off-by: Sasha Levin <sashal@kernel.org>
+---
+ arch/x86/kernel/cpu/sgx/ioctl.c | 28 ++++++++++++++++++++++------
+ 1 file changed, 22 insertions(+), 6 deletions(-)
+
+diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c
+index 83df20e3e633..a66795e0b685 100644
+--- a/arch/x86/kernel/cpu/sgx/ioctl.c
++++ b/arch/x86/kernel/cpu/sgx/ioctl.c
+@@ -372,6 +372,26 @@ static int sgx_encl_add_page(struct sgx_encl *encl, unsigned long src,
+       return ret;
+ }
++/*
++ * Ensure user provided offset and length values are valid for
++ * an enclave.
++ */
++static int sgx_validate_offset_length(struct sgx_encl *encl,
++                                    unsigned long offset,
++                                    unsigned long length)
++{
++      if (!IS_ALIGNED(offset, PAGE_SIZE))
++              return -EINVAL;
++
++      if (!length || !IS_ALIGNED(length, PAGE_SIZE))
++              return -EINVAL;
++
++      if (offset + length - PAGE_SIZE >= encl->size)
++              return -EINVAL;
++
++      return 0;
++}
++
+ /**
+  * sgx_ioc_enclave_add_pages() - The handler for %SGX_IOC_ENCLAVE_ADD_PAGES
+  * @encl:       an enclave pointer
+@@ -425,14 +445,10 @@ static long sgx_ioc_enclave_add_pages(struct sgx_encl *encl, void __user *arg)
+       if (copy_from_user(&add_arg, arg, sizeof(add_arg)))
+               return -EFAULT;
+-      if (!IS_ALIGNED(add_arg.offset, PAGE_SIZE) ||
+-          !IS_ALIGNED(add_arg.src, PAGE_SIZE))
+-              return -EINVAL;
+-
+-      if (!add_arg.length || add_arg.length & (PAGE_SIZE - 1))
++      if (!IS_ALIGNED(add_arg.src, PAGE_SIZE))
+               return -EINVAL;
+-      if (add_arg.offset + add_arg.length - PAGE_SIZE >= encl->size)
++      if (sgx_validate_offset_length(encl, add_arg.offset, add_arg.length))
+               return -EINVAL;
+       if (copy_from_user(&secinfo, (void __user *)add_arg.secinfo,
+-- 
+2.35.1
+