--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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, ¤t_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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+
--- /dev/null
+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
+