From: Sasha Levin Date: Sat, 26 Nov 2022 14:00:49 +0000 (-0500) Subject: Fixes for 5.15 X-Git-Tag: v5.10.157~98 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=549ee77544ab12c235250e1eb8f9274b8229c67c;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.15 Signed-off-by: Sasha Levin --- diff --git a/queue-5.15/alsa-usb-audio-add-quirk-to-fix-hamedal-c20-disconne.patch b/queue-5.15/alsa-usb-audio-add-quirk-to-fix-hamedal-c20-disconne.patch new file mode 100644 index 00000000000..9f01ba65524 --- /dev/null +++ b/queue-5.15/alsa-usb-audio-add-quirk-to-fix-hamedal-c20-disconne.patch @@ -0,0 +1,74 @@ +From 209b5fe05434f962da516216c36a7b9af9a48d57 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 10 Nov 2022 14:34:52 +0800 +Subject: ALSA: usb-audio: add quirk to fix Hamedal C20 disconnect issue + +From: Ai Chao + +[ 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 +Link: https://lore.kernel.org/r/20221110063452.295110-1-aichao@kylinos.cn +Signed-off-by: Takashi Iwai +Signed-off-by: Sasha Levin +--- + sound/usb/endpoint.c | 3 ++- + sound/usb/quirks.c | 2 ++ + sound/usb/usbaudio.h | 3 +++ + 3 files changed, 7 insertions(+), 1 deletion(-) + +diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c +index 3bbc227769d0..092350eb5f4e 100644 +--- a/sound/usb/endpoint.c ++++ b/sound/usb/endpoint.c +@@ -887,7 +887,8 @@ void snd_usb_endpoint_close(struct snd_usb_audio *chip, + usb_audio_dbg(chip, "Closing EP 0x%x (count %d)\n", + ep->ep_num, ep->opened); + +- if (!--ep->iface_ref->opened) ++ if (!--ep->iface_ref->opened && ++ !(chip->quirk_flags & QUIRK_FLAG_IFACE_SKIP_CLOSE)) + endpoint_set_interface(chip, ep, false); + + if (!--ep->opened) { +diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c +index 879d8b1f301c..2ae9ad993ff4 100644 +--- a/sound/usb/quirks.c ++++ b/sound/usb/quirks.c +@@ -1884,6 +1884,8 @@ static const struct usb_audio_quirk_flags_table quirk_flags_table[] = { + QUIRK_FLAG_GENERIC_IMPLICIT_FB), + DEVICE_FLG(0x2b53, 0x0031, /* Fiero SC-01 (firmware v1.1.0) */ + QUIRK_FLAG_GENERIC_IMPLICIT_FB), ++ DEVICE_FLG(0x0525, 0xa4ad, /* Hamedal C20 usb camero */ ++ QUIRK_FLAG_IFACE_SKIP_CLOSE), + + /* Vendor matches */ + VENDOR_FLG(0x045e, /* MS Lifecam */ +diff --git a/sound/usb/usbaudio.h b/sound/usb/usbaudio.h +index 39c3c61a7e49..ec06f441e890 100644 +--- a/sound/usb/usbaudio.h ++++ b/sound/usb/usbaudio.h +@@ -169,6 +169,8 @@ extern bool snd_usb_skip_validation; + * Apply the generic implicit feedback sync mode (same as implicit_fb=1 option) + * QUIRK_FLAG_SKIP_IMPLICIT_FB + * Don't apply implicit feedback sync mode ++ * QUIRK_FLAG_IFACE_SKIP_CLOSE ++ * Don't closed interface during setting sample rate + */ + + #define QUIRK_FLAG_GET_SAMPLE_RATE (1U << 0) +@@ -190,5 +192,6 @@ extern bool snd_usb_skip_validation; + #define QUIRK_FLAG_SET_IFACE_FIRST (1U << 16) + #define QUIRK_FLAG_GENERIC_IMPLICIT_FB (1U << 17) + #define QUIRK_FLAG_SKIP_IMPLICIT_FB (1U << 18) ++#define QUIRK_FLAG_IFACE_SKIP_CLOSE (1U << 19) + + #endif /* __USBAUDIO_H */ +-- +2.35.1 + diff --git a/queue-5.15/arm64-syscall-include-asm-ptrace.h-in-syscall_wrappe.patch b/queue-5.15/arm64-syscall-include-asm-ptrace.h-in-syscall_wrappe.patch new file mode 100644 index 00000000000..e1fe12d585f --- /dev/null +++ b/queue-5.15/arm64-syscall-include-asm-ptrace.h-in-syscall_wrappe.patch @@ -0,0 +1,51 @@ +From 42e69f15bdb040630eb9a3081eea6878c7fd560d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 31 Oct 2022 14:57:28 -0700 +Subject: arm64/syscall: Include asm/ptrace.h in syscall_wrapper header. + +From: Kuniyuki Iwashima + +[ 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 +Acked-by: Andrii Nakryiko +Link: https://lore.kernel.org/r/20221031215728.50389-1-kuniyu@amazon.com +Signed-off-by: Catalin Marinas +Signed-off-by: Sasha Levin +--- + 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 + + #define SC_ARM64_REGS_TO_ARGS(x, ...) \ + __MAP(x,__SC_ARGS \ +-- +2.35.1 + diff --git a/queue-5.15/asoc-fsl_asrc-fsl_esai-fsl_sai-allow-config_pm-n.patch b/queue-5.15/asoc-fsl_asrc-fsl_esai-fsl_sai-allow-config_pm-n.patch new file mode 100644 index 00000000000..311d9330618 --- /dev/null +++ b/queue-5.15/asoc-fsl_asrc-fsl_esai-fsl_sai-allow-config_pm-n.patch @@ -0,0 +1,74 @@ +From deff5af75c7951e8e3b3d57d91136c85dd6943ec Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 28 Oct 2022 16:11:28 +0200 +Subject: ASoC: fsl_asrc fsl_esai fsl_sai: allow CONFIG_PM=N + +From: Maarten Zanders + +[ 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 +Reviewed-by: Daniel Baluta +Link: https://lore.kernel.org/r/20221028141129.100702-1-maarten.zanders@mind.be +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/fsl/fsl_asrc.c | 2 +- + sound/soc/fsl/fsl_esai.c | 2 +- + sound/soc/fsl/fsl_sai.c | 2 +- + 3 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c +index 44dcbf49456c..08ca410ef551 100644 +--- a/sound/soc/fsl/fsl_asrc.c ++++ b/sound/soc/fsl/fsl_asrc.c +@@ -1226,7 +1226,7 @@ static int fsl_asrc_probe(struct platform_device *pdev) + } + + ret = pm_runtime_put_sync(&pdev->dev); +- if (ret < 0) ++ if (ret < 0 && ret != -ENOSYS) + goto err_pm_get_sync; + + ret = devm_snd_soc_register_component(&pdev->dev, &fsl_asrc_component, +diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c +index bda66b30e063..763f5f0592af 100644 +--- a/sound/soc/fsl/fsl_esai.c ++++ b/sound/soc/fsl/fsl_esai.c +@@ -1070,7 +1070,7 @@ static int fsl_esai_probe(struct platform_device *pdev) + regmap_write(esai_priv->regmap, REG_ESAI_RSMB, 0); + + ret = pm_runtime_put_sync(&pdev->dev); +- if (ret < 0) ++ if (ret < 0 && ret != -ENOSYS) + goto err_pm_get_sync; + + /* +diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c +index bcf6b66a5ac0..5ba06df2ace5 100644 +--- a/sound/soc/fsl/fsl_sai.c ++++ b/sound/soc/fsl/fsl_sai.c +@@ -1150,7 +1150,7 @@ static int fsl_sai_probe(struct platform_device *pdev) + } + + ret = pm_runtime_put_sync(dev); +- if (ret < 0) ++ if (ret < 0 && ret != -ENOSYS) + goto err_pm_get_sync; + + /* +-- +2.35.1 + diff --git a/queue-5.15/asoc-fsl_sai-use-local-device-pointer.patch b/queue-5.15/asoc-fsl_sai-use-local-device-pointer.patch new file mode 100644 index 00000000000..41c701a00c9 --- /dev/null +++ b/queue-5.15/asoc-fsl_sai-use-local-device-pointer.patch @@ -0,0 +1,188 @@ +From 0ee3f8e183d270916f47d73144aa738363e6f86a Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 1 Jun 2022 11:23:40 +0200 +Subject: ASoC: fsl_sai: use local device pointer + +From: Marco Felsch + +[ 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 +Acked-by: Shengjiu Wang +Link: https://lore.kernel.org/r/20220601092342.3328644-1-m.felsch@pengutronix.de +Signed-off-by: Mark Brown +Stable-dep-of: 6a564338a23c ("ASoC: fsl_asrc fsl_esai fsl_sai: allow CONFIG_PM=N") +Signed-off-by: Sasha Levin +--- + sound/soc/fsl/fsl_sai.c | 53 +++++++++++++++++++++-------------------- + 1 file changed, 27 insertions(+), 26 deletions(-) + +diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c +index 38f6362099d5..bcf6b66a5ac0 100644 +--- a/sound/soc/fsl/fsl_sai.c ++++ b/sound/soc/fsl/fsl_sai.c +@@ -1000,6 +1000,7 @@ static int fsl_sai_runtime_resume(struct device *dev); + static int fsl_sai_probe(struct platform_device *pdev) + { + struct device_node *np = pdev->dev.of_node; ++ struct device *dev = &pdev->dev; + struct fsl_sai *sai; + struct regmap *gpr; + struct resource *res; +@@ -1008,12 +1009,12 @@ static int fsl_sai_probe(struct platform_device *pdev) + int irq, ret, i; + int index; + +- sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL); ++ sai = devm_kzalloc(dev, sizeof(*sai), GFP_KERNEL); + if (!sai) + return -ENOMEM; + + sai->pdev = pdev; +- sai->soc_data = of_device_get_match_data(&pdev->dev); ++ sai->soc_data = of_device_get_match_data(dev); + + sai->is_lsb_first = of_property_read_bool(np, "lsb-first"); + +@@ -1028,18 +1029,18 @@ static int fsl_sai_probe(struct platform_device *pdev) + ARRAY_SIZE(fsl_sai_reg_defaults_ofs8); + } + +- sai->regmap = devm_regmap_init_mmio(&pdev->dev, base, &fsl_sai_regmap_config); ++ sai->regmap = devm_regmap_init_mmio(dev, base, &fsl_sai_regmap_config); + if (IS_ERR(sai->regmap)) { +- dev_err(&pdev->dev, "regmap init failed\n"); ++ dev_err(dev, "regmap init failed\n"); + return PTR_ERR(sai->regmap); + } + +- sai->bus_clk = devm_clk_get(&pdev->dev, "bus"); ++ sai->bus_clk = devm_clk_get(dev, "bus"); + /* Compatible with old DTB cases */ + if (IS_ERR(sai->bus_clk) && PTR_ERR(sai->bus_clk) != -EPROBE_DEFER) +- sai->bus_clk = devm_clk_get(&pdev->dev, "sai"); ++ sai->bus_clk = devm_clk_get(dev, "sai"); + if (IS_ERR(sai->bus_clk)) { +- dev_err(&pdev->dev, "failed to get bus clock: %ld\n", ++ dev_err(dev, "failed to get bus clock: %ld\n", + PTR_ERR(sai->bus_clk)); + /* -EPROBE_DEFER */ + return PTR_ERR(sai->bus_clk); +@@ -1047,9 +1048,9 @@ static int fsl_sai_probe(struct platform_device *pdev) + + for (i = 1; i < FSL_SAI_MCLK_MAX; i++) { + sprintf(tmp, "mclk%d", i); +- sai->mclk_clk[i] = devm_clk_get(&pdev->dev, tmp); ++ sai->mclk_clk[i] = devm_clk_get(dev, tmp); + if (IS_ERR(sai->mclk_clk[i])) { +- dev_err(&pdev->dev, "failed to get mclk%d clock: %ld\n", ++ dev_err(dev, "failed to get mclk%d clock: %ld\n", + i + 1, PTR_ERR(sai->mclk_clk[i])); + sai->mclk_clk[i] = NULL; + } +@@ -1064,10 +1065,10 @@ static int fsl_sai_probe(struct platform_device *pdev) + if (irq < 0) + return irq; + +- ret = devm_request_irq(&pdev->dev, irq, fsl_sai_isr, IRQF_SHARED, ++ ret = devm_request_irq(dev, irq, fsl_sai_isr, IRQF_SHARED, + np->name, sai); + if (ret) { +- dev_err(&pdev->dev, "failed to claim irq %u\n", irq); ++ dev_err(dev, "failed to claim irq %u\n", irq); + return ret; + } + +@@ -1084,7 +1085,7 @@ static int fsl_sai_probe(struct platform_device *pdev) + if (of_find_property(np, "fsl,sai-synchronous-rx", NULL) && + of_find_property(np, "fsl,sai-asynchronous", NULL)) { + /* error out if both synchronous and asynchronous are present */ +- dev_err(&pdev->dev, "invalid binding for synchronous mode\n"); ++ dev_err(dev, "invalid binding for synchronous mode\n"); + return -EINVAL; + } + +@@ -1105,7 +1106,7 @@ static int fsl_sai_probe(struct platform_device *pdev) + of_device_is_compatible(np, "fsl,imx6ul-sai")) { + gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr"); + if (IS_ERR(gpr)) { +- dev_err(&pdev->dev, "cannot find iomuxc registers\n"); ++ dev_err(dev, "cannot find iomuxc registers\n"); + return PTR_ERR(gpr); + } + +@@ -1123,23 +1124,23 @@ static int fsl_sai_probe(struct platform_device *pdev) + sai->dma_params_tx.maxburst = FSL_SAI_MAXBURST_TX; + + platform_set_drvdata(pdev, sai); +- pm_runtime_enable(&pdev->dev); +- if (!pm_runtime_enabled(&pdev->dev)) { +- ret = fsl_sai_runtime_resume(&pdev->dev); ++ pm_runtime_enable(dev); ++ if (!pm_runtime_enabled(dev)) { ++ ret = fsl_sai_runtime_resume(dev); + if (ret) + goto err_pm_disable; + } + +- ret = pm_runtime_get_sync(&pdev->dev); ++ ret = pm_runtime_get_sync(dev); + if (ret < 0) { +- pm_runtime_put_noidle(&pdev->dev); ++ pm_runtime_put_noidle(dev); + goto err_pm_get_sync; + } + + /* Get sai version */ +- ret = fsl_sai_check_version(&pdev->dev); ++ ret = fsl_sai_check_version(dev); + if (ret < 0) +- dev_warn(&pdev->dev, "Error reading SAI version: %d\n", ret); ++ dev_warn(dev, "Error reading SAI version: %d\n", ret); + + /* Select MCLK direction */ + if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) && +@@ -1148,7 +1149,7 @@ static int fsl_sai_probe(struct platform_device *pdev) + FSL_SAI_MCTL_MCLK_EN, FSL_SAI_MCTL_MCLK_EN); + } + +- ret = pm_runtime_put_sync(&pdev->dev); ++ ret = pm_runtime_put_sync(dev); + if (ret < 0) + goto err_pm_get_sync; + +@@ -1161,12 +1162,12 @@ static int fsl_sai_probe(struct platform_device *pdev) + if (ret) + goto err_pm_get_sync; + } else { +- ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); ++ ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0); + if (ret) + goto err_pm_get_sync; + } + +- ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component, ++ ret = devm_snd_soc_register_component(dev, &fsl_component, + &sai->cpu_dai_drv, 1); + if (ret) + goto err_pm_get_sync; +@@ -1174,10 +1175,10 @@ static int fsl_sai_probe(struct platform_device *pdev) + return ret; + + err_pm_get_sync: +- if (!pm_runtime_status_suspended(&pdev->dev)) +- fsl_sai_runtime_suspend(&pdev->dev); ++ if (!pm_runtime_status_suspended(dev)) ++ fsl_sai_runtime_suspend(dev); + err_pm_disable: +- pm_runtime_disable(&pdev->dev); ++ pm_runtime_disable(dev); + + return ret; + } +-- +2.35.1 + diff --git a/queue-5.15/ata-libata-core-do-not-issue-non-internal-commands-o.patch b/queue-5.15/ata-libata-core-do-not-issue-non-internal-commands-o.patch new file mode 100644 index 00000000000..4b6acf378f2 --- /dev/null +++ b/queue-5.15/ata-libata-core-do-not-issue-non-internal-commands-o.patch @@ -0,0 +1,87 @@ +From 64b10491e240fb3024aa8cf84a52e034011901bb Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Tested-by: John Garry +Signed-off-by: Damien Le Moal +Signed-off-by: Sasha Levin +--- + drivers/ata/libata-scsi.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c +index 61b7e0b0bbf6..061d2f8feeb5 100644 +--- a/drivers/ata/libata-scsi.c ++++ b/drivers/ata/libata-scsi.c +@@ -3988,9 +3988,19 @@ void ata_scsi_dump_cdb(struct ata_port *ap, struct scsi_cmnd *cmd) + + int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev) + { ++ struct ata_port *ap = dev->link->ap; + u8 scsi_op = scmd->cmnd[0]; + ata_xlat_func_t xlat_func; + ++ /* ++ * scsi_queue_rq() will defer commands if scsi_host_in_recovery(). ++ * However, this check is done without holding the ap->lock (a libata ++ * specific lock), so we can have received an error irq since then, ++ * therefore we must check if EH is pending, while holding ap->lock. ++ */ ++ if (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) ++ return SCSI_MLQUEUE_DEVICE_BUSY; ++ + if (unlikely(!scmd->cmd_len)) + goto bad_cdb_len; + +-- +2.35.1 + diff --git a/queue-5.15/ata-libata-scsi-simplify-__ata_scsi_queuecmd.patch b/queue-5.15/ata-libata-scsi-simplify-__ata_scsi_queuecmd.patch new file mode 100644 index 00000000000..266368da03f --- /dev/null +++ b/queue-5.15/ata-libata-scsi-simplify-__ata_scsi_queuecmd.patch @@ -0,0 +1,98 @@ +From 65f099cdb989809ccb8fa2a6d5ed6111e503dbac Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 5 Jan 2022 19:13:54 -0500 +Subject: ata: libata-scsi: simplify __ata_scsi_queuecmd() + +From: Wenchao Hao + +[ 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 +Signed-off-by: Damien Le Moal +Stable-dep-of: e20e81a24a4d ("ata: libata-core: do not issue non-internal commands once EH is pending") +Signed-off-by: Sasha Levin +--- + drivers/ata/libata-scsi.c | 45 ++++++++++++++++++--------------------- + 1 file changed, 21 insertions(+), 24 deletions(-) + +diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c +index ef41cb385a0d..61b7e0b0bbf6 100644 +--- a/drivers/ata/libata-scsi.c ++++ b/drivers/ata/libata-scsi.c +@@ -3990,42 +3990,39 @@ int __ata_scsi_queuecmd(struct scsi_cmnd *scmd, struct ata_device *dev) + { + u8 scsi_op = scmd->cmnd[0]; + ata_xlat_func_t xlat_func; +- int rc = 0; ++ ++ if (unlikely(!scmd->cmd_len)) ++ goto bad_cdb_len; + + if (dev->class == ATA_DEV_ATA || dev->class == ATA_DEV_ZAC) { +- if (unlikely(!scmd->cmd_len || scmd->cmd_len > dev->cdb_len)) ++ if (unlikely(scmd->cmd_len > dev->cdb_len)) + goto bad_cdb_len; + + xlat_func = ata_get_xlat_func(dev, scsi_op); +- } else { +- if (unlikely(!scmd->cmd_len)) +- goto bad_cdb_len; ++ } else if (likely((scsi_op != ATA_16) || !atapi_passthru16)) { ++ /* relay SCSI command to ATAPI device */ ++ int len = COMMAND_SIZE(scsi_op); + +- xlat_func = NULL; +- if (likely((scsi_op != ATA_16) || !atapi_passthru16)) { +- /* relay SCSI command to ATAPI device */ +- int len = COMMAND_SIZE(scsi_op); +- if (unlikely(len > scmd->cmd_len || +- len > dev->cdb_len || +- scmd->cmd_len > ATAPI_CDB_LEN)) +- goto bad_cdb_len; ++ if (unlikely(len > scmd->cmd_len || ++ len > dev->cdb_len || ++ scmd->cmd_len > ATAPI_CDB_LEN)) ++ goto bad_cdb_len; + +- xlat_func = atapi_xlat; +- } else { +- /* ATA_16 passthru, treat as an ATA command */ +- if (unlikely(scmd->cmd_len > 16)) +- goto bad_cdb_len; ++ xlat_func = atapi_xlat; ++ } else { ++ /* ATA_16 passthru, treat as an ATA command */ ++ if (unlikely(scmd->cmd_len > 16)) ++ goto bad_cdb_len; + +- xlat_func = ata_get_xlat_func(dev, scsi_op); +- } ++ xlat_func = ata_get_xlat_func(dev, scsi_op); + } + + if (xlat_func) +- rc = ata_scsi_translate(dev, scmd, xlat_func); +- else +- ata_scsi_simulate(dev, scmd); ++ return ata_scsi_translate(dev, scmd, xlat_func); + +- return rc; ++ ata_scsi_simulate(dev, scmd); ++ ++ return 0; + + bad_cdb_len: + DPRINTK("bad CDB len=%u, scsi_op=0x%02x, max=%u\n", +-- +2.35.1 + diff --git a/queue-5.15/audit-fix-undefined-behavior-in-bit-shift-for-audit_.patch b/queue-5.15/audit-fix-undefined-behavior-in-bit-shift-for-audit_.patch new file mode 100644 index 00000000000..ed370d58c5a --- /dev/null +++ b/queue-5.15/audit-fix-undefined-behavior-in-bit-shift-for-audit_.patch @@ -0,0 +1,52 @@ +From 34ff33e8d8f92bcd794dbe71cd91dcda313de021 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 31 Oct 2022 10:10:21 +0800 +Subject: audit: fix undefined behavior in bit shift for AUDIT_BIT + +From: Gaosheng Cui + +[ 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: + + 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 + + +Signed-off-by: Gaosheng Cui +[PM: remove bad 'Fixes' tag as issue predates git, added in v2.6.6-rc1] +Signed-off-by: Paul Moore +Signed-off-by: Sasha Levin +--- + include/uapi/linux/audit.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h +index daa481729e9b..27799acd0e5e 100644 +--- a/include/uapi/linux/audit.h ++++ b/include/uapi/linux/audit.h +@@ -182,7 +182,7 @@ + #define AUDIT_MAX_KEY_LEN 256 + #define AUDIT_BITMASK_SIZE 64 + #define AUDIT_WORD(nr) ((__u32)((nr)/32)) +-#define AUDIT_BIT(nr) (1 << ((nr) - AUDIT_WORD(nr)*32)) ++#define AUDIT_BIT(nr) (1U << ((nr) - AUDIT_WORD(nr)*32)) + + #define AUDIT_SYSCALL_CLASSES 16 + #define AUDIT_CLASS_DIR_WRITE 0 +-- +2.35.1 + diff --git a/queue-5.15/binder-validate-alloc-mm-in-mmap-handler.patch b/queue-5.15/binder-validate-alloc-mm-in-mmap-handler.patch new file mode 100644 index 00000000000..eca996eec66 --- /dev/null +++ b/queue-5.15/binder-validate-alloc-mm-in-mmap-handler.patch @@ -0,0 +1,66 @@ +From fb3a616383db1095d3df8b5122410d5006464e79 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 4 Nov 2022 23:12:35 +0000 +Subject: binder: validate alloc->mm in ->mmap() handler + +From: Carlos Llamas + +[ 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 +Cc: # 5.15+ +Signed-off-by: Carlos Llamas +Acked-by: Todd Kjos +Link: https://lore.kernel.org/r/20221104231235.348958-1-cmllamas@google.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/android/binder_alloc.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/android/binder_alloc.c b/drivers/android/binder_alloc.c +index 8ed450125c92..6acfb896b2e5 100644 +--- a/drivers/android/binder_alloc.c ++++ b/drivers/android/binder_alloc.c +@@ -753,6 +753,12 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, + const char *failure_string; + struct binder_buffer *buffer; + ++ if (unlikely(vma->vm_mm != alloc->vma_vm_mm)) { ++ ret = -EINVAL; ++ failure_string = "invalid vma->vm_mm"; ++ goto err_invalid_mm; ++ } ++ + mutex_lock(&binder_alloc_mmap_lock); + if (alloc->buffer_size) { + ret = -EBUSY; +@@ -799,6 +805,7 @@ int binder_alloc_mmap_handler(struct binder_alloc *alloc, + alloc->buffer_size = 0; + err_already_mapped: + mutex_unlock(&binder_alloc_mmap_lock); ++err_invalid_mm: + binder_alloc_debug(BINDER_DEBUG_USER_ERROR, + "%s: %d %lx-%lx %s failed %d\n", __func__, + alloc->pid, vma->vm_start, vma->vm_end, +-- +2.35.1 + diff --git a/queue-5.15/block-bfq-fix-null-pointer-dereference-in-bfq_bio_bf.patch b/queue-5.15/block-bfq-fix-null-pointer-dereference-in-bfq_bio_bf.patch new file mode 100644 index 00000000000..223e1840416 --- /dev/null +++ b/queue-5.15/block-bfq-fix-null-pointer-dereference-in-bfq_bio_bf.patch @@ -0,0 +1,151 @@ +From 54ef109871876f87838011d206e62211b277ff68 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 8 Nov 2022 18:34:34 +0800 +Subject: block, bfq: fix null pointer dereference in bfq_bio_bfqg() + +From: Yu Kuai + +[ 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 +Reviewed-by: Jan Kara +Link: https://lore.kernel.org/r/20221108103434.2853269-1-yukuai1@huaweicloud.com +Signed-off-by: Jens Axboe +Signed-off-by: Sasha Levin +--- + block/bfq-cgroup.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/block/bfq-cgroup.c b/block/bfq-cgroup.c +index e2e765a54fe9..a8d0b4c71b05 100644 +--- a/block/bfq-cgroup.c ++++ b/block/bfq-cgroup.c +@@ -613,6 +613,10 @@ struct bfq_group *bfq_bio_bfqg(struct bfq_data *bfqd, struct bio *bio) + struct bfq_group *bfqg; + + while (blkg) { ++ if (!blkg->online) { ++ blkg = blkg->parent; ++ continue; ++ } + bfqg = blkg_to_bfqg(blkg); + if (bfqg->online) { + bio_associate_blkg_from_css(bio, &blkg->blkcg->css); +-- +2.35.1 + diff --git a/queue-5.15/ceph-avoid-putting-the-realm-twice-when-decoding-sna.patch b/queue-5.15/ceph-avoid-putting-the-realm-twice-when-decoding-sna.patch new file mode 100644 index 00000000000..11b3d81711c --- /dev/null +++ b/queue-5.15/ceph-avoid-putting-the-realm-twice-when-decoding-sna.patch @@ -0,0 +1,48 @@ +From a2d20ab9390fdcbac68704359db4ef153ebf89f3 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 9 Nov 2022 11:00:39 +0800 +Subject: ceph: avoid putting the realm twice when decoding snaps fails + +From: Xiubo Li + +[ 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 +Reviewed-by: Ilya Dryomov +Signed-off-by: Ilya Dryomov +Signed-off-by: Sasha Levin +--- + fs/ceph/snap.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c +index ae9cf37374e3..b512c82f9ccd 100644 +--- a/fs/ceph/snap.c ++++ b/fs/ceph/snap.c +@@ -705,7 +705,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + struct ceph_mds_snap_realm *ri; /* encoded */ + __le64 *snaps; /* encoded */ + __le64 *prior_parent_snaps; /* encoded */ +- struct ceph_snap_realm *realm = NULL; ++ struct ceph_snap_realm *realm; + struct ceph_snap_realm *first_realm = NULL; + struct ceph_snap_realm *realm_to_rebuild = NULL; + int rebuild_snapcs; +@@ -716,6 +716,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + + dout("update_snap_trace deletion=%d\n", deletion); + more: ++ realm = NULL; + rebuild_snapcs = 0; + ceph_decode_need(&p, e, sizeof(*ri), bad); + ri = p; +-- +2.35.1 + diff --git a/queue-5.15/ceph-do-not-update-snapshot-context-when-there-is-no.patch b/queue-5.15/ceph-do-not-update-snapshot-context-when-there-is-no.patch new file mode 100644 index 00000000000..6fa613edaf8 --- /dev/null +++ b/queue-5.15/ceph-do-not-update-snapshot-context-when-there-is-no.patch @@ -0,0 +1,129 @@ +From e655095b18a368ff27abdd11a2bcdcad03e5599e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Jeff Layton +Signed-off-by: Ilya Dryomov +Stable-dep-of: 51884d153f7e ("ceph: avoid putting the realm twice when decoding snaps fails") +Signed-off-by: Sasha Levin +--- + fs/ceph/snap.c | 28 +++++++++++++++++++--------- + 1 file changed, 19 insertions(+), 9 deletions(-) + +diff --git a/fs/ceph/snap.c b/fs/ceph/snap.c +index b41e6724c591..ae9cf37374e3 100644 +--- a/fs/ceph/snap.c ++++ b/fs/ceph/snap.c +@@ -707,7 +707,8 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + __le64 *prior_parent_snaps; /* encoded */ + struct ceph_snap_realm *realm = NULL; + struct ceph_snap_realm *first_realm = NULL; +- int invalidate = 0; ++ struct ceph_snap_realm *realm_to_rebuild = NULL; ++ int rebuild_snapcs; + int err = -ENOMEM; + LIST_HEAD(dirty_realms); + +@@ -715,6 +716,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + + dout("update_snap_trace deletion=%d\n", deletion); + more: ++ rebuild_snapcs = 0; + ceph_decode_need(&p, e, sizeof(*ri), bad); + ri = p; + p += sizeof(*ri); +@@ -738,7 +740,7 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + err = adjust_snap_realm_parent(mdsc, realm, le64_to_cpu(ri->parent)); + if (err < 0) + goto fail; +- invalidate += err; ++ rebuild_snapcs += err; + + if (le64_to_cpu(ri->seq) > realm->seq) { + dout("update_snap_trace updating %llx %p %lld -> %lld\n", +@@ -763,22 +765,30 @@ int ceph_update_snap_trace(struct ceph_mds_client *mdsc, + if (realm->seq > mdsc->last_snap_seq) + mdsc->last_snap_seq = realm->seq; + +- invalidate = 1; ++ rebuild_snapcs = 1; + } else if (!realm->cached_context) { + dout("update_snap_trace %llx %p seq %lld new\n", + realm->ino, realm, realm->seq); +- invalidate = 1; ++ rebuild_snapcs = 1; + } else { + dout("update_snap_trace %llx %p seq %lld unchanged\n", + realm->ino, realm, realm->seq); + } + +- dout("done with %llx %p, invalidated=%d, %p %p\n", realm->ino, +- realm, invalidate, p, e); ++ dout("done with %llx %p, rebuild_snapcs=%d, %p %p\n", realm->ino, ++ realm, rebuild_snapcs, p, e); + +- /* invalidate when we reach the _end_ (root) of the trace */ +- if (invalidate && p >= e) +- rebuild_snap_realms(realm, &dirty_realms); ++ /* ++ * this will always track the uppest parent realm from which ++ * we need to rebuild the snapshot contexts _downward_ in ++ * hierarchy. ++ */ ++ if (rebuild_snapcs) ++ realm_to_rebuild = realm; ++ ++ /* rebuild_snapcs when we reach the _end_ (root) of the trace */ ++ if (realm_to_rebuild && p >= e) ++ rebuild_snap_realms(realm_to_rebuild, &dirty_realms); + + if (!first_realm) + first_realm = realm; +-- +2.35.1 + diff --git a/queue-5.15/ceph-fix-null-pointer-dereference-for-req-r_session.patch b/queue-5.15/ceph-fix-null-pointer-dereference-for-req-r_session.patch new file mode 100644 index 00000000000..e5c332e612e --- /dev/null +++ b/queue-5.15/ceph-fix-null-pointer-dereference-for-req-r_session.patch @@ -0,0 +1,125 @@ +From f1c2e878b7cca9505dbf66af76fa8fc2b3273a6f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 10 Nov 2022 21:01:59 +0800 +Subject: ceph: fix NULL pointer dereference for req->r_session + +From: Xiubo Li + +[ 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 +Reviewed-by: Ilya Dryomov +Signed-off-by: Ilya Dryomov +Signed-off-by: Sasha Levin +--- + fs/ceph/caps.c | 48 ++++++++++++------------------------------------ + 1 file changed, 12 insertions(+), 36 deletions(-) + +diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c +index a6e2aaff17dd..be96fe615bec 100644 +--- a/fs/ceph/caps.c ++++ b/fs/ceph/caps.c +@@ -2217,7 +2217,6 @@ static int unsafe_request_wait(struct inode *inode) + struct ceph_mds_client *mdsc = ceph_sb_to_client(inode->i_sb)->mdsc; + struct ceph_inode_info *ci = ceph_inode(inode); + struct ceph_mds_request *req1 = NULL, *req2 = NULL; +- unsigned int max_sessions; + int ret, err = 0; + + spin_lock(&ci->i_unsafe_lock); +@@ -2235,28 +2234,24 @@ static int unsafe_request_wait(struct inode *inode) + } + spin_unlock(&ci->i_unsafe_lock); + +- /* +- * The mdsc->max_sessions is unlikely to be changed +- * mostly, here we will retry it by reallocating the +- * sessions array memory to get rid of the mdsc->mutex +- * lock. +- */ +-retry: +- max_sessions = mdsc->max_sessions; +- + /* + * Trigger to flush the journal logs in all the relevant MDSes + * manually, or in the worst case we must wait at most 5 seconds + * to wait the journal logs to be flushed by the MDSes periodically. + */ +- if ((req1 || req2) && likely(max_sessions)) { +- struct ceph_mds_session **sessions = NULL; +- struct ceph_mds_session *s; ++ if (req1 || req2) { + struct ceph_mds_request *req; ++ struct ceph_mds_session **sessions; ++ struct ceph_mds_session *s; ++ unsigned int max_sessions; + int i; + ++ mutex_lock(&mdsc->mutex); ++ max_sessions = mdsc->max_sessions; ++ + sessions = kcalloc(max_sessions, sizeof(s), GFP_KERNEL); + if (!sessions) { ++ mutex_unlock(&mdsc->mutex); + err = -ENOMEM; + goto out; + } +@@ -2268,16 +2263,6 @@ static int unsafe_request_wait(struct inode *inode) + s = req->r_session; + if (!s) + continue; +- if (unlikely(s->s_mds >= max_sessions)) { +- spin_unlock(&ci->i_unsafe_lock); +- for (i = 0; i < max_sessions; i++) { +- s = sessions[i]; +- if (s) +- ceph_put_mds_session(s); +- } +- kfree(sessions); +- goto retry; +- } + if (!sessions[s->s_mds]) { + s = ceph_get_mds_session(s); + sessions[s->s_mds] = s; +@@ -2290,16 +2275,6 @@ static int unsafe_request_wait(struct inode *inode) + s = req->r_session; + if (!s) + continue; +- if (unlikely(s->s_mds >= max_sessions)) { +- spin_unlock(&ci->i_unsafe_lock); +- for (i = 0; i < max_sessions; i++) { +- s = sessions[i]; +- if (s) +- ceph_put_mds_session(s); +- } +- kfree(sessions); +- goto retry; +- } + if (!sessions[s->s_mds]) { + s = ceph_get_mds_session(s); + sessions[s->s_mds] = s; +@@ -2311,11 +2286,12 @@ static int unsafe_request_wait(struct inode *inode) + /* the auth MDS */ + spin_lock(&ci->i_ceph_lock); + if (ci->i_auth_cap) { +- s = ci->i_auth_cap->session; +- if (!sessions[s->s_mds]) +- sessions[s->s_mds] = ceph_get_mds_session(s); ++ s = ci->i_auth_cap->session; ++ if (!sessions[s->s_mds]) ++ sessions[s->s_mds] = ceph_get_mds_session(s); + } + spin_unlock(&ci->i_ceph_lock); ++ mutex_unlock(&mdsc->mutex); + + /* send flush mdlog request to MDSes */ + for (i = 0; i < max_sessions; i++) { +-- +2.35.1 + diff --git a/queue-5.15/ceph-use-kcalloc-for-allocating-multiple-elements.patch b/queue-5.15/ceph-use-kcalloc-for-allocating-multiple-elements.patch new file mode 100644 index 00000000000..d032fa25000 --- /dev/null +++ b/queue-5.15/ceph-use-kcalloc-for-allocating-multiple-elements.patch @@ -0,0 +1,37 @@ +From 0f58d7c37861a931d5c3a321f41409c073609c34 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 18 Aug 2022 22:42:55 -0700 +Subject: ceph: Use kcalloc for allocating multiple elements + +From: Kenneth Lee + +[ 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 +Reviewed-by: Xiubo Li +Signed-off-by: Ilya Dryomov +Stable-dep-of: 5bd76b8de5b7 ("ceph: fix NULL pointer dereference for req->r_session") +Signed-off-by: Sasha Levin +--- + fs/ceph/caps.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/ceph/caps.c b/fs/ceph/caps.c +index 883bb91ee257..a6e2aaff17dd 100644 +--- a/fs/ceph/caps.c ++++ b/fs/ceph/caps.c +@@ -2255,7 +2255,7 @@ static int unsafe_request_wait(struct inode *inode) + struct ceph_mds_request *req; + int i; + +- sessions = kzalloc(max_sessions * sizeof(s), GFP_KERNEL); ++ sessions = kcalloc(max_sessions, sizeof(s), GFP_KERNEL); + if (!sessions) { + err = -ENOMEM; + goto out; +-- +2.35.1 + diff --git a/queue-5.15/cifs-fix-connections-leak-when-tlink-setup-failed.patch b/queue-5.15/cifs-fix-connections-leak-when-tlink-setup-failed.patch new file mode 100644 index 00000000000..1b98e1f4c75 --- /dev/null +++ b/queue-5.15/cifs-fix-connections-leak-when-tlink-setup-failed.patch @@ -0,0 +1,64 @@ +From 0c3d07dc46d1809c84602e13e534b3781e2b0d81 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 11 Nov 2022 15:12:12 +0800 +Subject: cifs: Fix connections leak when tlink setup failed + +From: Zhang Xiaoxu + +[ 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) +Signed-off-by: Zhang Xiaoxu +Signed-off-by: Steve French +Signed-off-by: Sasha Levin +--- + fs/cifs/connect.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c +index 902eb8a5afd2..839059b8a9c9 100644 +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -3550,9 +3550,13 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) + uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id); + + out: +- free_xid(mnt_ctx.xid); + cifs_try_adding_channels(cifs_sb, mnt_ctx.ses); +- return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); ++ rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); ++ if (rc) ++ goto error; ++ ++ free_xid(mnt_ctx.xid); ++ return rc; + + error: + dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id); +@@ -3579,8 +3583,12 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) + goto error; + } + ++ rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); ++ if (rc) ++ goto error; ++ + free_xid(mnt_ctx.xid); +- return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); ++ return rc; + + error: + mount_put_conns(&mnt_ctx); +-- +2.35.1 + diff --git a/queue-5.15/cifs-introduce-new-helper-for-cifs_reconnect.patch b/queue-5.15/cifs-introduce-new-helper-for-cifs_reconnect.patch new file mode 100644 index 00000000000..811ee5c2699 --- /dev/null +++ b/queue-5.15/cifs-introduce-new-helper-for-cifs_reconnect.patch @@ -0,0 +1,203 @@ +From 0872775701ce78034efa93b0673e58561c272230 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 Oct 2021 13:57:21 -0300 +Subject: cifs: introduce new helper for cifs_reconnect() + +From: Paulo Alcantara + +[ 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) +Reviewed-by: Shyam Prasad N +Signed-off-by: Steve French +Stable-dep-of: 1dcdf5f5b213 ("cifs: Fix connections leak when tlink setup failed") +Signed-off-by: Sasha Levin +--- + fs/cifs/connect.c | 139 +++++++++++++++++++++++++--------------------- + 1 file changed, 75 insertions(+), 64 deletions(-) + +diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c +index ad5c935f7f06..ed429a442808 100644 +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -199,80 +199,29 @@ static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb, + } + #endif + +-/* +- * cifs tcp session reconnection ++/** ++ * Mark all sessions and tcons for reconnect. + * +- * mark tcp session as reconnecting so temporarily locked +- * mark all smb sessions as reconnecting for tcp session +- * reconnect tcp session +- * wake up waiters on reconnection? - (not needed currently) ++ * @server needs to be previously set to CifsNeedReconnect. + */ +-int +-cifs_reconnect(struct TCP_Server_Info *server) ++static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server) + { +- int rc = 0; + struct list_head *tmp, *tmp2; + struct cifs_ses *ses; + struct cifs_tcon *tcon; + struct mid_q_entry *mid_entry; + struct list_head retry_list; +-#ifdef CONFIG_CIFS_DFS_UPCALL +- struct super_block *sb = NULL; +- struct cifs_sb_info *cifs_sb = NULL; +- struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list); +- struct dfs_cache_tgt_iterator *tgt_it = NULL; +-#endif + +- spin_lock(&GlobalMid_Lock); +- server->nr_targets = 1; +-#ifdef CONFIG_CIFS_DFS_UPCALL +- spin_unlock(&GlobalMid_Lock); +- sb = cifs_get_tcp_super(server); +- if (IS_ERR(sb)) { +- rc = PTR_ERR(sb); +- cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n", +- __func__, rc); +- sb = NULL; +- } else { +- cifs_sb = CIFS_SB(sb); +- rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list); +- if (rc) { +- cifs_sb = NULL; +- if (rc != -EOPNOTSUPP) { +- cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n", +- __func__); +- } +- } else { +- server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list); +- } +- } +- cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__, +- server->nr_targets); +- spin_lock(&GlobalMid_Lock); +-#endif +- if (server->tcpStatus == CifsExiting) { +- /* the demux thread will exit normally +- next time through the loop */ +- spin_unlock(&GlobalMid_Lock); +-#ifdef CONFIG_CIFS_DFS_UPCALL +- dfs_cache_free_tgts(&tgt_list); +- cifs_put_tcp_super(sb); +-#endif +- wake_up(&server->response_q); +- return rc; +- } else +- server->tcpStatus = CifsNeedReconnect; +- spin_unlock(&GlobalMid_Lock); + server->maxBuf = 0; + server->max_read = 0; + + cifs_dbg(FYI, "Mark tcp session as need reconnect\n"); + trace_smb3_reconnect(server->CurrentMid, server->conn_id, server->hostname); +- +- /* before reconnecting the tcp session, mark the smb session (uid) +- and the tid bad so they are not used until reconnected */ +- cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", +- __func__); ++ /* ++ * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they ++ * are not used until reconnected. ++ */ ++ cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n", __func__); + spin_lock(&cifs_tcp_ses_lock); + list_for_each(tmp, &server->smb_ses_list) { + ses = list_entry(tmp, struct cifs_ses, smb_ses_list); +@@ -290,11 +239,11 @@ cifs_reconnect(struct TCP_Server_Info *server) + cifs_dbg(FYI, "%s: tearing down socket\n", __func__); + mutex_lock(&server->srv_mutex); + if (server->ssocket) { +- cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", +- server->ssocket->state, server->ssocket->flags); ++ cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state, ++ server->ssocket->flags); + kernel_sock_shutdown(server->ssocket, SHUT_WR); +- cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", +- server->ssocket->state, server->ssocket->flags); ++ cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", server->ssocket->state, ++ server->ssocket->flags); + sock_release(server->ssocket); + server->ssocket = NULL; + } +@@ -333,6 +282,68 @@ cifs_reconnect(struct TCP_Server_Info *server) + smbd_destroy(server); + mutex_unlock(&server->srv_mutex); + } ++} ++ ++/* ++ * cifs tcp session reconnection ++ * ++ * mark tcp session as reconnecting so temporarily locked ++ * mark all smb sessions as reconnecting for tcp session ++ * reconnect tcp session ++ * wake up waiters on reconnection? - (not needed currently) ++ */ ++int ++cifs_reconnect(struct TCP_Server_Info *server) ++{ ++ int rc = 0; ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ struct super_block *sb = NULL; ++ struct cifs_sb_info *cifs_sb = NULL; ++ struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list); ++ struct dfs_cache_tgt_iterator *tgt_it = NULL; ++#endif ++ ++ spin_lock(&GlobalMid_Lock); ++ server->nr_targets = 1; ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ spin_unlock(&GlobalMid_Lock); ++ sb = cifs_get_tcp_super(server); ++ if (IS_ERR(sb)) { ++ rc = PTR_ERR(sb); ++ cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n", ++ __func__, rc); ++ sb = NULL; ++ } else { ++ cifs_sb = CIFS_SB(sb); ++ rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list); ++ if (rc) { ++ cifs_sb = NULL; ++ if (rc != -EOPNOTSUPP) { ++ cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n", ++ __func__); ++ } ++ } else { ++ server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list); ++ } ++ } ++ cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__, ++ server->nr_targets); ++ spin_lock(&GlobalMid_Lock); ++#endif ++ if (server->tcpStatus == CifsExiting) { ++ /* the demux thread will exit normally next time through the loop */ ++ spin_unlock(&GlobalMid_Lock); ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ dfs_cache_free_tgts(&tgt_list); ++ cifs_put_tcp_super(sb); ++#endif ++ wake_up(&server->response_q); ++ return rc; ++ } else ++ server->tcpStatus = CifsNeedReconnect; ++ spin_unlock(&GlobalMid_Lock); ++ ++ cifs_mark_tcp_ses_conns_for_reconnect(server); + + do { + try_to_freeze(); +-- +2.35.1 + diff --git a/queue-5.15/cifs-split-out-dfs-code-from-cifs_reconnect.patch b/queue-5.15/cifs-split-out-dfs-code-from-cifs_reconnect.patch new file mode 100644 index 00000000000..839b31bb2c1 --- /dev/null +++ b/queue-5.15/cifs-split-out-dfs-code-from-cifs_reconnect.patch @@ -0,0 +1,383 @@ +From 8a24fd41b19a31b160998ad0fe52a095013acddd Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 14 Oct 2021 17:49:54 -0300 +Subject: cifs: split out dfs code from cifs_reconnect() + +From: Paulo Alcantara + +[ 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) +Reviewed-by: Shyam Prasad N +Signed-off-by: Steve French +Stable-dep-of: 1dcdf5f5b213 ("cifs: Fix connections leak when tlink setup failed") +Signed-off-by: Sasha Levin +--- + fs/cifs/connect.c | 295 +++++++++++++++++++++++++--------------------- + 1 file changed, 162 insertions(+), 133 deletions(-) + +diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c +index ed429a442808..5d87d5c01762 100644 +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -148,57 +148,6 @@ static void cifs_resolve_server(struct work_struct *work) + mutex_unlock(&server->srv_mutex); + } + +-#ifdef CONFIG_CIFS_DFS_UPCALL +-/* These functions must be called with server->srv_mutex held */ +-static void reconn_set_next_dfs_target(struct TCP_Server_Info *server, +- struct cifs_sb_info *cifs_sb, +- struct dfs_cache_tgt_list *tgt_list, +- struct dfs_cache_tgt_iterator **tgt_it) +-{ +- const char *name; +- int rc; +- +- if (!cifs_sb || !cifs_sb->origin_fullpath) +- return; +- +- if (!*tgt_it) { +- *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); +- } else { +- *tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it); +- if (!*tgt_it) +- *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); +- } +- +- cifs_dbg(FYI, "%s: UNC: %s\n", __func__, cifs_sb->origin_fullpath); +- +- name = dfs_cache_get_tgt_name(*tgt_it); +- +- kfree(server->hostname); +- +- server->hostname = extract_hostname(name); +- if (IS_ERR(server->hostname)) { +- cifs_dbg(FYI, +- "%s: failed to extract hostname from target: %ld\n", +- __func__, PTR_ERR(server->hostname)); +- return; +- } +- +- rc = reconn_set_ipaddr_from_hostname(server); +- if (rc) { +- cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", +- __func__, rc); +- } +-} +- +-static inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb, +- struct dfs_cache_tgt_list *tl) +-{ +- if (!cifs_sb->origin_fullpath) +- return -EOPNOTSUPP; +- return dfs_cache_noreq_find(cifs_sb->origin_fullpath + 1, NULL, tl); +-} +-#endif +- + /** + * Mark all sessions and tcons for reconnect. + * +@@ -284,6 +233,21 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server + } + } + ++static bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets) ++{ ++ spin_lock(&GlobalMid_Lock); ++ server->nr_targets = num_targets; ++ if (server->tcpStatus == CifsExiting) { ++ /* the demux thread will exit normally next time through the loop */ ++ spin_unlock(&GlobalMid_Lock); ++ wake_up(&server->response_q); ++ return false; ++ } ++ server->tcpStatus = CifsNeedReconnect; ++ spin_unlock(&GlobalMid_Lock); ++ return true; ++} ++ + /* + * cifs tcp session reconnection + * +@@ -292,90 +256,23 @@ static void cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server + * reconnect tcp session + * wake up waiters on reconnection? - (not needed currently) + */ +-int +-cifs_reconnect(struct TCP_Server_Info *server) ++static int __cifs_reconnect(struct TCP_Server_Info *server) + { + int rc = 0; +-#ifdef CONFIG_CIFS_DFS_UPCALL +- struct super_block *sb = NULL; +- struct cifs_sb_info *cifs_sb = NULL; +- struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list); +- struct dfs_cache_tgt_iterator *tgt_it = NULL; +-#endif + +- spin_lock(&GlobalMid_Lock); +- server->nr_targets = 1; +-#ifdef CONFIG_CIFS_DFS_UPCALL +- spin_unlock(&GlobalMid_Lock); +- sb = cifs_get_tcp_super(server); +- if (IS_ERR(sb)) { +- rc = PTR_ERR(sb); +- cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n", +- __func__, rc); +- sb = NULL; +- } else { +- cifs_sb = CIFS_SB(sb); +- rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list); +- if (rc) { +- cifs_sb = NULL; +- if (rc != -EOPNOTSUPP) { +- cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n", +- __func__); +- } +- } else { +- server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list); +- } +- } +- cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__, +- server->nr_targets); +- spin_lock(&GlobalMid_Lock); +-#endif +- if (server->tcpStatus == CifsExiting) { +- /* the demux thread will exit normally next time through the loop */ +- spin_unlock(&GlobalMid_Lock); +-#ifdef CONFIG_CIFS_DFS_UPCALL +- dfs_cache_free_tgts(&tgt_list); +- cifs_put_tcp_super(sb); +-#endif +- wake_up(&server->response_q); +- return rc; +- } else +- server->tcpStatus = CifsNeedReconnect; +- spin_unlock(&GlobalMid_Lock); ++ if (!cifs_tcp_ses_needs_reconnect(server, 1)) ++ return 0; + + cifs_mark_tcp_ses_conns_for_reconnect(server); + + do { + try_to_freeze(); +- + mutex_lock(&server->srv_mutex); + +- + if (!cifs_swn_set_server_dstaddr(server)) { +-#ifdef CONFIG_CIFS_DFS_UPCALL +- if (cifs_sb && cifs_sb->origin_fullpath) +- /* +- * Set up next DFS target server (if any) for reconnect. If DFS +- * feature is disabled, then we will retry last server we +- * connected to before. +- */ +- reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it); +- else { +-#endif +- /* +- * Resolve the hostname again to make sure that IP address is up-to-date. +- */ ++ /* resolve the hostname again to make sure that IP address is up-to-date */ + rc = reconn_set_ipaddr_from_hostname(server); +- if (rc) { +- cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n", +- __func__, rc); +- } +- +-#ifdef CONFIG_CIFS_DFS_UPCALL +- } +-#endif +- +- ++ cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); + } + + if (cifs_rdma_enabled(server)) +@@ -383,8 +280,8 @@ cifs_reconnect(struct TCP_Server_Info *server) + else + rc = generic_ip_connect(server); + if (rc) { +- cifs_dbg(FYI, "reconnect error %d\n", rc); + mutex_unlock(&server->srv_mutex); ++ cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); + msleep(3000); + } else { + atomic_inc(&tcpSesReconnectCount); +@@ -398,19 +295,109 @@ cifs_reconnect(struct TCP_Server_Info *server) + } + } while (server->tcpStatus == CifsNeedReconnect); + ++ if (server->tcpStatus == CifsNeedNegotiate) ++ mod_delayed_work(cifsiod_wq, &server->echo, 0); ++ ++ wake_up(&server->response_q); ++ return rc; ++} ++ + #ifdef CONFIG_CIFS_DFS_UPCALL +- if (tgt_it) { +- rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1, +- tgt_it); ++static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb) ++{ ++ int rc = 0; ++ const char *refpath = cifs_sb->origin_fullpath + 1; ++ struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); ++ struct dfs_cache_tgt_iterator *tit = NULL; ++ int num_targets = 1; ++ char *hostname; ++ ++ /* ++ * Determine the number of dfs targets the referral path in @cifs_sb resolves to. ++ * ++ * smb2_reconnect() needs to know how long it should wait based upon the number of dfs ++ * targets (server->nr_targets). It's also possible that the cached referral was cleared ++ * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after ++ * refreshing the referral, so, in this case, default it to 1. ++ */ ++ if (!dfs_cache_noreq_find(refpath, NULL, &tl)) { ++ num_targets = dfs_cache_get_nr_tgts(&tl); ++ if (!num_targets) ++ num_targets = 1; ++ } ++ ++ if (!cifs_tcp_ses_needs_reconnect(server, num_targets)) ++ return 0; ++ ++ cifs_mark_tcp_ses_conns_for_reconnect(server); ++ ++ do { ++ /* Get next dfs target from target list (if any) */ ++ if (!tit) ++ tit = dfs_cache_get_tgt_iterator(&tl); ++ else ++ tit = dfs_cache_get_next_tgt(&tl, tit); ++ ++ try_to_freeze(); ++ mutex_lock(&server->srv_mutex); ++ ++ if (!cifs_swn_set_server_dstaddr(server)) { ++ /* ++ * If any dfs target was selected, then update @server with either a ++ * hostname or an address. ++ */ ++ if (tit) { ++ hostname = extract_hostname(dfs_cache_get_tgt_name(tit)); ++ if (!IS_ERR(hostname)) { ++ kfree(server->hostname); ++ server->hostname = hostname; ++ } else { ++ cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", ++ __func__, PTR_ERR(hostname)); ++ cifs_dbg(FYI, "%s: default to last target server: %s\n", ++ __func__, server->hostname); ++ } ++ } ++ /* resolve the hostname again to make sure that IP address is up-to-date. */ ++ rc = reconn_set_ipaddr_from_hostname(server); ++ cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); ++ } ++ ++ /* Reconnect the socket */ ++ if (cifs_rdma_enabled(server)) ++ rc = smbd_reconnect(server); ++ else ++ rc = generic_ip_connect(server); ++ + if (rc) { +- cifs_server_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n", +- __func__, rc); ++ /* Failed to reconnect socket. Retry next dfs target. */ ++ mutex_unlock(&server->srv_mutex); ++ cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); ++ msleep(3000); ++ continue; + } +- dfs_cache_free_tgts(&tgt_list); +- } + +- cifs_put_tcp_super(sb); +-#endif ++ /* ++ * Socket was created. Update tcp session status to CifsNeedNegotiate so that a ++ * process waiting for reconnect will know it needs to re-establish session and tcon ++ * through the reconnected target server. ++ */ ++ atomic_inc(&tcpSesReconnectCount); ++ set_credits(server, 1); ++ spin_lock(&GlobalMid_Lock); ++ if (server->tcpStatus != CifsExiting) ++ server->tcpStatus = CifsNeedNegotiate; ++ spin_unlock(&GlobalMid_Lock); ++ cifs_swn_reset_server_dstaddr(server); ++ mutex_unlock(&server->srv_mutex); ++ } while (server->tcpStatus == CifsNeedReconnect); ++ ++ if (tit) ++ dfs_cache_noreq_update_tgthint(refpath, tit); ++ ++ dfs_cache_free_tgts(&tl); ++ ++ /* Need to set up echo worker again once connection has been established */ + if (server->tcpStatus == CifsNeedNegotiate) + mod_delayed_work(cifsiod_wq, &server->echo, 0); + +@@ -418,6 +405,48 @@ cifs_reconnect(struct TCP_Server_Info *server) + return rc; + } + ++int cifs_reconnect(struct TCP_Server_Info *server) ++{ ++ int rc; ++ struct super_block *sb; ++ struct cifs_sb_info *cifs_sb; ++ ++ /* ++ * If tcp session is not an dfs connection or it is a channel, then reconnect to last target ++ * server. ++ */ ++ spin_lock(&cifs_tcp_ses_lock); ++ if (!server->is_dfs_conn || server->is_channel) { ++ spin_unlock(&cifs_tcp_ses_lock); ++ return __cifs_reconnect(server); ++ } ++ spin_unlock(&cifs_tcp_ses_lock); ++ ++ /* If no superblock, then it might be an ipc connection */ ++ sb = cifs_get_tcp_super(server); ++ if (IS_ERR(sb)) ++ return __cifs_reconnect(server); ++ ++ /* ++ * Check for a referral path to look up in superblock. If unset, then simply reconnect to ++ * last target server. ++ */ ++ cifs_sb = CIFS_SB(sb); ++ if (!cifs_sb->origin_fullpath || !cifs_sb->origin_fullpath[0]) ++ rc = __cifs_reconnect(server); ++ else ++ rc = reconnect_dfs_server(server, cifs_sb); ++ ++ cifs_put_tcp_super(sb); ++ return rc; ++} ++#else ++int cifs_reconnect(struct TCP_Server_Info *server) ++{ ++ return __cifs_reconnect(server); ++} ++#endif ++ + static void + cifs_echo_request(struct work_struct *work) + { +-- +2.35.1 + diff --git a/queue-5.15/cifs-support-nested-dfs-links-over-reconnect.patch b/queue-5.15/cifs-support-nested-dfs-links-over-reconnect.patch new file mode 100644 index 00000000000..6e2c540bb37 --- /dev/null +++ b/queue-5.15/cifs-support-nested-dfs-links-over-reconnect.patch @@ -0,0 +1,1918 @@ +From d917a89ec903aa8dd5735ecc50dd3dfc52204dc0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 3 Nov 2021 13:53:29 -0300 +Subject: cifs: support nested dfs links over reconnect + +From: Paulo Alcantara + +[ 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) +Signed-off-by: Steve French +Stable-dep-of: 1dcdf5f5b213 ("cifs: Fix connections leak when tlink setup failed") +Signed-off-by: Sasha Levin +--- + fs/cifs/cifs_dfs_ref.c | 59 +-- + fs/cifs/cifs_fs_sb.h | 5 - + fs/cifs/cifsglob.h | 24 +- + fs/cifs/cifsproto.h | 5 +- + fs/cifs/connect.c | 1138 ++++++++++++++++++++-------------------- + fs/cifs/dfs_cache.c | 44 +- + fs/cifs/misc.c | 62 +-- + fs/cifs/smb2ops.c | 10 +- + fs/cifs/smb2pdu.c | 6 +- + 9 files changed, 660 insertions(+), 693 deletions(-) + +diff --git a/fs/cifs/cifs_dfs_ref.c b/fs/cifs/cifs_dfs_ref.c +index 007427ba75e5..b0864da9ef43 100644 +--- a/fs/cifs/cifs_dfs_ref.c ++++ b/fs/cifs/cifs_dfs_ref.c +@@ -307,12 +307,8 @@ static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt, + static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) + { + struct cifs_sb_info *cifs_sb; +- struct cifs_ses *ses; +- struct cifs_tcon *tcon; + void *page; +- char *full_path, *root_path; +- unsigned int xid; +- int rc; ++ char *full_path; + struct vfsmount *mnt; + + cifs_dbg(FYI, "in %s\n", __func__); +@@ -324,8 +320,6 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) + * the double backslashes usually used in the UNC. This function + * gives us the latter, so we must adjust the result. + */ +- mnt = ERR_PTR(-ENOMEM); +- + cifs_sb = CIFS_SB(mntpt->d_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) { + mnt = ERR_PTR(-EREMOTE); +@@ -341,60 +335,11 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt) + } + + convert_delimiter(full_path, '\\'); +- + cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path); + +- if (!cifs_sb_master_tlink(cifs_sb)) { +- cifs_dbg(FYI, "%s: master tlink is NULL\n", __func__); +- goto free_full_path; +- } +- +- tcon = cifs_sb_master_tcon(cifs_sb); +- if (!tcon) { +- cifs_dbg(FYI, "%s: master tcon is NULL\n", __func__); +- goto free_full_path; +- } +- +- root_path = kstrdup(tcon->treeName, GFP_KERNEL); +- if (!root_path) { +- mnt = ERR_PTR(-ENOMEM); +- goto free_full_path; +- } +- cifs_dbg(FYI, "%s: root path: %s\n", __func__, root_path); +- +- ses = tcon->ses; +- xid = get_xid(); +- +- /* +- * If DFS root has been expired, then unconditionally fetch it again to +- * refresh DFS referral cache. +- */ +- rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), +- root_path + 1, NULL, NULL); +- if (!rc) { +- rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, +- cifs_remap(cifs_sb), full_path + 1, +- NULL, NULL); +- } +- +- free_xid(xid); +- +- if (rc) { +- mnt = ERR_PTR(rc); +- goto free_root_path; +- } +- /* +- * OK - we were able to get and cache a referral for @full_path. +- * +- * Now, pass it down to cifs_mount() and it will retry every available +- * node server in case of failures - no need to do it here. +- */ + mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path); +- cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, +- full_path + 1, mnt); ++ cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__, full_path + 1, mnt); + +-free_root_path: +- kfree(root_path); + free_full_path: + free_dentry_path(page); + cdda_exit: +diff --git a/fs/cifs/cifs_fs_sb.h b/fs/cifs/cifs_fs_sb.h +index f97407520ea1..013a4bd65280 100644 +--- a/fs/cifs/cifs_fs_sb.h ++++ b/fs/cifs/cifs_fs_sb.h +@@ -61,11 +61,6 @@ struct cifs_sb_info { + /* only used when CIFS_MOUNT_USE_PREFIX_PATH is set */ + char *prepath; + +- /* +- * Canonical DFS path initially provided by the mount call. We might connect to something +- * different via DFS but we want to keep it to do failover properly. +- */ +- char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */ + /* randomly generated 128-bit number for indexing dfs mount groups in referral cache */ + uuid_t dfs_mount_id; + /* +diff --git a/fs/cifs/cifsglob.h b/fs/cifs/cifsglob.h +index a97ed30843cf..1ab72c3d0bff 100644 +--- a/fs/cifs/cifsglob.h ++++ b/fs/cifs/cifsglob.h +@@ -693,6 +693,19 @@ struct TCP_Server_Info { + #endif + #ifdef CONFIG_CIFS_DFS_UPCALL + bool is_dfs_conn; /* if a dfs connection */ ++ struct mutex refpath_lock; /* protects leaf_fullpath */ ++ /* ++ * Canonical DFS full paths that were used to chase referrals in mount and reconnect. ++ * ++ * origin_fullpath: first or original referral path ++ * leaf_fullpath: last referral path (might be changed due to nested links in reconnect) ++ * ++ * current_fullpath: pointer to either origin_fullpath or leaf_fullpath ++ * NOTE: cannot be accessed outside cifs_reconnect() and smb2_reconnect() ++ * ++ * format: \\HOST\SHARE\[OPTIONAL PATH] ++ */ ++ char *origin_fullpath, *leaf_fullpath, *current_fullpath; + #endif + }; + +@@ -1097,7 +1110,6 @@ struct cifs_tcon { + struct cached_fid crfid; /* Cached root fid */ + /* BB add field for back pointer to sb struct(s)? */ + #ifdef CONFIG_CIFS_DFS_UPCALL +- char *dfs_path; /* canonical DFS path */ + struct list_head ulist; /* cache update list */ + #endif + }; +@@ -1950,4 +1962,14 @@ static inline bool is_tcon_dfs(struct cifs_tcon *tcon) + tcon->share_flags & (SHI1005_FLAGS_DFS | SHI1005_FLAGS_DFS_ROOT); + } + ++static inline bool cifs_is_referral_server(struct cifs_tcon *tcon, ++ const struct dfs_info3_param *ref) ++{ ++ /* ++ * Check if all targets are capable of handling DFS referrals as per ++ * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL. ++ */ ++ return is_tcon_dfs(tcon) || (ref && (ref->flags & DFSREF_REFERRAL_SERVER)); ++} ++ + #endif /* _CIFS_GLOB_H */ +diff --git a/fs/cifs/cifsproto.h b/fs/cifs/cifsproto.h +index d0f85b666662..b2697356b5e7 100644 +--- a/fs/cifs/cifsproto.h ++++ b/fs/cifs/cifsproto.h +@@ -607,7 +607,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov, + + struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server); + void cifs_put_tcp_super(struct super_block *sb); +-int update_super_prepath(struct cifs_tcon *tcon, char *prefix); ++int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix); + char *extract_hostname(const char *unc); + char *extract_sharename(const char *unc); + +@@ -634,4 +634,7 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options) + return options; + } + ++struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon); ++void cifs_put_tcon_super(struct super_block *sb); ++ + #endif /* _CIFSPROTO_H */ +diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c +index 5d87d5c01762..902eb8a5afd2 100644 +--- a/fs/cifs/connect.c ++++ b/fs/cifs/connect.c +@@ -61,6 +61,20 @@ extern bool disable_legacy_dialects; + /* Drop the connection to not overload the server */ + #define NUM_STATUS_IO_TIMEOUT 5 + ++struct mount_ctx { ++ struct cifs_sb_info *cifs_sb; ++ struct smb3_fs_context *fs_ctx; ++ unsigned int xid; ++ struct TCP_Server_Info *server; ++ struct cifs_ses *ses; ++ struct cifs_tcon *tcon; ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ struct cifs_ses *root_ses; ++ uuid_t mount_id; ++ char *origin_fullpath, *leaf_fullpath; ++#endif ++}; ++ + static int ip_connect(struct TCP_Server_Info *server); + static int generic_ip_connect(struct TCP_Server_Info *server); + static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink); +@@ -303,14 +317,68 @@ static int __cifs_reconnect(struct TCP_Server_Info *server) + } + + #ifdef CONFIG_CIFS_DFS_UPCALL +-static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb) ++static int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target) ++{ ++ int rc; ++ char *hostname; ++ ++ if (!cifs_swn_set_server_dstaddr(server)) { ++ if (server->hostname != target) { ++ hostname = extract_hostname(target); ++ if (!IS_ERR(hostname)) { ++ kfree(server->hostname); ++ server->hostname = hostname; ++ } else { ++ cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", ++ __func__, PTR_ERR(hostname)); ++ cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__, ++ server->hostname); ++ } ++ } ++ /* resolve the hostname again to make sure that IP address is up-to-date. */ ++ rc = reconn_set_ipaddr_from_hostname(server); ++ cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); ++ } ++ /* Reconnect the socket */ ++ if (cifs_rdma_enabled(server)) ++ rc = smbd_reconnect(server); ++ else ++ rc = generic_ip_connect(server); ++ ++ return rc; ++} ++ ++static int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl, ++ struct dfs_cache_tgt_iterator **target_hint) ++{ ++ int rc; ++ struct dfs_cache_tgt_iterator *tit; ++ ++ *target_hint = NULL; ++ ++ /* If dfs target list is empty, then reconnect to last server */ ++ tit = dfs_cache_get_tgt_iterator(tl); ++ if (!tit) ++ return __reconnect_target_unlocked(server, server->hostname); ++ ++ /* Otherwise, try every dfs target in @tl */ ++ for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { ++ rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit)); ++ if (!rc) { ++ *target_hint = tit; ++ break; ++ } ++ } ++ return rc; ++} ++ ++static int reconnect_dfs_server(struct TCP_Server_Info *server) + { + int rc = 0; +- const char *refpath = cifs_sb->origin_fullpath + 1; ++ const char *refpath = server->current_fullpath + 1; + struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); +- struct dfs_cache_tgt_iterator *tit = NULL; +- int num_targets = 1; +- char *hostname; ++ struct dfs_cache_tgt_iterator *target_hint = NULL; ++ int num_targets = 0; + + /* + * Determine the number of dfs targets the referral path in @cifs_sb resolves to. +@@ -320,11 +388,10 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i + * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after + * refreshing the referral, so, in this case, default it to 1. + */ +- if (!dfs_cache_noreq_find(refpath, NULL, &tl)) { ++ if (!dfs_cache_noreq_find(refpath, NULL, &tl)) + num_targets = dfs_cache_get_nr_tgts(&tl); +- if (!num_targets) +- num_targets = 1; +- } ++ if (!num_targets) ++ num_targets = 1; + + if (!cifs_tcp_ses_needs_reconnect(server, num_targets)) + return 0; +@@ -332,51 +399,17 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i + cifs_mark_tcp_ses_conns_for_reconnect(server); + + do { +- /* Get next dfs target from target list (if any) */ +- if (!tit) +- tit = dfs_cache_get_tgt_iterator(&tl); +- else +- tit = dfs_cache_get_next_tgt(&tl, tit); +- + try_to_freeze(); + mutex_lock(&server->srv_mutex); + +- if (!cifs_swn_set_server_dstaddr(server)) { +- /* +- * If any dfs target was selected, then update @server with either a +- * hostname or an address. +- */ +- if (tit) { +- hostname = extract_hostname(dfs_cache_get_tgt_name(tit)); +- if (!IS_ERR(hostname)) { +- kfree(server->hostname); +- server->hostname = hostname; +- } else { +- cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n", +- __func__, PTR_ERR(hostname)); +- cifs_dbg(FYI, "%s: default to last target server: %s\n", +- __func__, server->hostname); +- } +- } +- /* resolve the hostname again to make sure that IP address is up-to-date. */ +- rc = reconn_set_ipaddr_from_hostname(server); +- cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc); +- } +- +- /* Reconnect the socket */ +- if (cifs_rdma_enabled(server)) +- rc = smbd_reconnect(server); +- else +- rc = generic_ip_connect(server); +- ++ rc = reconnect_target_unlocked(server, &tl, &target_hint); + if (rc) { +- /* Failed to reconnect socket. Retry next dfs target. */ ++ /* Failed to reconnect socket */ + mutex_unlock(&server->srv_mutex); + cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc); + msleep(3000); + continue; + } +- + /* + * Socket was created. Update tcp session status to CifsNeedNegotiate so that a + * process waiting for reconnect will know it needs to re-establish session and tcon +@@ -392,8 +425,8 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i + mutex_unlock(&server->srv_mutex); + } while (server->tcpStatus == CifsNeedReconnect); + +- if (tit) +- dfs_cache_noreq_update_tgthint(refpath, tit); ++ if (target_hint) ++ dfs_cache_noreq_update_tgthint(refpath, target_hint); + + dfs_cache_free_tgts(&tl); + +@@ -407,38 +440,15 @@ static int reconnect_dfs_server(struct TCP_Server_Info *server, struct cifs_sb_i + + int cifs_reconnect(struct TCP_Server_Info *server) + { +- int rc; +- struct super_block *sb; +- struct cifs_sb_info *cifs_sb; +- +- /* +- * If tcp session is not an dfs connection or it is a channel, then reconnect to last target +- * server. +- */ ++ /* If tcp session is not an dfs connection, then reconnect to last target server */ + spin_lock(&cifs_tcp_ses_lock); +- if (!server->is_dfs_conn || server->is_channel) { ++ if (!server->is_dfs_conn || !server->origin_fullpath || !server->leaf_fullpath) { + spin_unlock(&cifs_tcp_ses_lock); + return __cifs_reconnect(server); + } + spin_unlock(&cifs_tcp_ses_lock); + +- /* If no superblock, then it might be an ipc connection */ +- sb = cifs_get_tcp_super(server); +- if (IS_ERR(sb)) +- return __cifs_reconnect(server); +- +- /* +- * Check for a referral path to look up in superblock. If unset, then simply reconnect to +- * last target server. +- */ +- cifs_sb = CIFS_SB(sb); +- if (!cifs_sb->origin_fullpath || !cifs_sb->origin_fullpath[0]) +- rc = __cifs_reconnect(server); +- else +- rc = reconnect_dfs_server(server, cifs_sb); +- +- cifs_put_tcp_super(sb); +- return rc; ++ return reconnect_dfs_server(server); + } + #else + int cifs_reconnect(struct TCP_Server_Info *server) +@@ -829,6 +839,10 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server) + */ + } + ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ kfree(server->origin_fullpath); ++ kfree(server->leaf_fullpath); ++#endif + kfree(server); + + length = atomic_dec_return(&tcpSesAllocCount); +@@ -1445,6 +1459,9 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx) + INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server); + INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server); + mutex_init(&tcp_ses->reconnect_mutex); ++#ifdef CONFIG_CIFS_DFS_UPCALL ++ mutex_init(&tcp_ses->refpath_lock); ++#endif + memcpy(&tcp_ses->srcaddr, &ctx->srcaddr, + sizeof(tcp_ses->srcaddr)); + memcpy(&tcp_ses->dstaddr, &ctx->dstaddr, +@@ -2909,73 +2926,64 @@ int cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb) + } + + /* Release all succeed connections */ +-static inline void mount_put_conns(struct cifs_sb_info *cifs_sb, +- unsigned int xid, +- struct TCP_Server_Info *server, +- struct cifs_ses *ses, struct cifs_tcon *tcon) ++static inline void mount_put_conns(struct mount_ctx *mnt_ctx) + { + int rc = 0; + +- if (tcon) +- cifs_put_tcon(tcon); +- else if (ses) +- cifs_put_smb_ses(ses); +- else if (server) +- cifs_put_tcp_session(server, 0); +- cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; +- free_xid(xid); ++ if (mnt_ctx->tcon) ++ cifs_put_tcon(mnt_ctx->tcon); ++ else if (mnt_ctx->ses) ++ cifs_put_smb_ses(mnt_ctx->ses); ++ else if (mnt_ctx->server) ++ cifs_put_tcp_session(mnt_ctx->server, 0); ++ mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS; ++ free_xid(mnt_ctx->xid); + } + + /* Get connections for tcp, ses and tcon */ +-static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, +- unsigned int *xid, +- struct TCP_Server_Info **nserver, +- struct cifs_ses **nses, struct cifs_tcon **ntcon) ++static int mount_get_conns(struct mount_ctx *mnt_ctx) + { + int rc = 0; +- struct TCP_Server_Info *server; +- struct cifs_ses *ses; +- struct cifs_tcon *tcon; +- +- *nserver = NULL; +- *nses = NULL; +- *ntcon = NULL; ++ struct TCP_Server_Info *server = NULL; ++ struct cifs_ses *ses = NULL; ++ struct cifs_tcon *tcon = NULL; ++ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; ++ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ unsigned int xid; + +- *xid = get_xid(); ++ xid = get_xid(); + + /* get a reference to a tcp session */ + server = cifs_get_tcp_session(ctx); + if (IS_ERR(server)) { + rc = PTR_ERR(server); +- return rc; ++ server = NULL; ++ goto out; + } + +- *nserver = server; +- + /* get a reference to a SMB session */ + ses = cifs_get_smb_ses(server, ctx); + if (IS_ERR(ses)) { + rc = PTR_ERR(ses); +- return rc; ++ ses = NULL; ++ goto out; + } + +- *nses = ses; +- + if ((ctx->persistent == true) && (!(ses->server->capabilities & + SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) { + cifs_server_dbg(VFS, "persistent handles not supported by server\n"); +- return -EOPNOTSUPP; ++ rc = -EOPNOTSUPP; ++ goto out; + } + + /* search for existing tcon to this server share */ + tcon = cifs_get_tcon(ses, ctx); + if (IS_ERR(tcon)) { + rc = PTR_ERR(tcon); +- return rc; ++ tcon = NULL; ++ goto out; + } + +- *ntcon = tcon; +- + /* if new SMB3.11 POSIX extensions are supported do not remap / and \ */ + if (tcon->posix_extensions) + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS; +@@ -2986,17 +2994,19 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif + * reset of caps checks mount to see if unix extensions disabled + * for just this mount. + */ +- reset_cifs_unix_caps(*xid, tcon, cifs_sb, ctx); ++ reset_cifs_unix_caps(xid, tcon, cifs_sb, ctx); + if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) && + (le64_to_cpu(tcon->fsUnixInfo.Capability) & +- CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) +- return -EACCES; ++ CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) { ++ rc = -EACCES; ++ goto out; ++ } + } else + tcon->unix_ext = 0; /* server does not support them */ + + /* do not care if a following call succeed - informational */ + if (!tcon->pipe && server->ops->qfs_tcon) { +- server->ops->qfs_tcon(*xid, tcon, cifs_sb); ++ server->ops->qfs_tcon(xid, tcon, cifs_sb); + if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) { + if (tcon->fsDevInfo.DeviceCharacteristics & + cpu_to_le32(FILE_READ_ONLY_DEVICE)) +@@ -3020,7 +3030,13 @@ static int mount_get_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cif + (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx))) + cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx); + +- return 0; ++out: ++ mnt_ctx->server = server; ++ mnt_ctx->ses = ses; ++ mnt_ctx->tcon = tcon; ++ mnt_ctx->xid = xid; ++ ++ return rc; + } + + static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, +@@ -3050,18 +3066,17 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses, + } + + #ifdef CONFIG_CIFS_DFS_UPCALL +-static int mount_get_dfs_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, +- unsigned int *xid, struct TCP_Server_Info **nserver, +- struct cifs_ses **nses, struct cifs_tcon **ntcon) ++/* Get unique dfs connections */ ++static int mount_get_dfs_conns(struct mount_ctx *mnt_ctx) + { + int rc; + +- ctx->nosharesock = true; +- rc = mount_get_conns(ctx, cifs_sb, xid, nserver, nses, ntcon); +- if (*nserver) { ++ mnt_ctx->fs_ctx->nosharesock = true; ++ rc = mount_get_conns(mnt_ctx); ++ if (mnt_ctx->server) { + cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__); + spin_lock(&cifs_tcp_ses_lock); +- (*nserver)->is_dfs_conn = true; ++ mnt_ctx->server->is_dfs_conn = true; + spin_unlock(&cifs_tcp_ses_lock); + } + return rc; +@@ -3103,190 +3118,38 @@ build_unc_path_to_root(const struct smb3_fs_context *ctx, + } + + /* +- * expand_dfs_referral - Perform a dfs referral query and update the cifs_sb ++ * expand_dfs_referral - Update cifs_sb from dfs referral path + * +- * If a referral is found, cifs_sb->ctx->mount_options will be (re-)allocated +- * to a string containing updated options for the submount. Otherwise it +- * will be left untouched. +- * +- * Returns the rc from get_dfs_path to the caller, which can be used to +- * determine whether there were referrals. ++ * cifs_sb->ctx->mount_options will be (re-)allocated to a string containing updated options for the ++ * submount. Otherwise it will be left untouched. + */ +-static int +-expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses, +- struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb, +- char *ref_path) ++static int expand_dfs_referral(struct mount_ctx *mnt_ctx, const char *full_path, ++ struct dfs_info3_param *referral) + { + int rc; +- struct dfs_info3_param referral = {0}; +- char *full_path = NULL, *mdata = NULL; +- +- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) +- return -EREMOTE; +- +- full_path = build_unc_path_to_root(ctx, cifs_sb, true); +- if (IS_ERR(full_path)) +- return PTR_ERR(full_path); +- +- rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), +- ref_path, &referral, NULL); +- if (!rc) { +- char *fake_devname = NULL; +- +- mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, +- full_path + 1, &referral, +- &fake_devname); +- free_dfs_info_param(&referral); +- +- if (IS_ERR(mdata)) { +- rc = PTR_ERR(mdata); +- mdata = NULL; +- } else { +- /* +- * We can not clear out the whole structure since we +- * no longer have an explicit function to parse +- * a mount-string. Instead we need to clear out the +- * individual fields that are no longer valid. +- */ +- kfree(ctx->prepath); +- ctx->prepath = NULL; +- rc = cifs_setup_volume_info(ctx, mdata, fake_devname); +- } +- kfree(fake_devname); +- kfree(cifs_sb->ctx->mount_options); +- cifs_sb->ctx->mount_options = mdata; +- } +- kfree(full_path); +- return rc; +-} +- +-static int get_next_dfs_tgt(struct dfs_cache_tgt_list *tgt_list, +- struct dfs_cache_tgt_iterator **tgt_it) +-{ +- if (!*tgt_it) +- *tgt_it = dfs_cache_get_tgt_iterator(tgt_list); +- else +- *tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it); +- return !*tgt_it ? -EHOSTDOWN : 0; +-} +- +-static int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it, +- struct smb3_fs_context *fake_ctx, struct smb3_fs_context *ctx) +-{ +- const char *tgt = dfs_cache_get_tgt_name(tgt_it); +- int len = strlen(tgt) + 2; +- char *new_unc; +- +- new_unc = kmalloc(len, GFP_KERNEL); +- if (!new_unc) +- return -ENOMEM; +- scnprintf(new_unc, len, "\\%s", tgt); +- +- kfree(ctx->UNC); +- ctx->UNC = new_unc; +- +- if (fake_ctx->prepath) { +- kfree(ctx->prepath); +- ctx->prepath = fake_ctx->prepath; +- fake_ctx->prepath = NULL; +- } +- memcpy(&ctx->dstaddr, &fake_ctx->dstaddr, sizeof(ctx->dstaddr)); +- +- return 0; +-} +- +-static int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb, +- struct smb3_fs_context *ctx, struct cifs_ses *root_ses, +- unsigned int *xid, struct TCP_Server_Info **server, +- struct cifs_ses **ses, struct cifs_tcon **tcon) +-{ +- int rc; +- char *npath = NULL; +- struct dfs_cache_tgt_list tgt_list = DFS_CACHE_TGT_LIST_INIT(tgt_list); +- struct dfs_cache_tgt_iterator *tgt_it = NULL; +- struct smb3_fs_context tmp_ctx = {NULL}; +- +- if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) +- return -EOPNOTSUPP; +- +- npath = dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb)); +- if (IS_ERR(npath)) +- return PTR_ERR(npath); +- +- cifs_dbg(FYI, "%s: path=%s full_path=%s\n", __func__, npath, full_path); +- +- rc = dfs_cache_noreq_find(npath, NULL, &tgt_list); +- if (rc) +- goto out; +- /* +- * We use a 'tmp_ctx' here because we need pass it down to the mount_{get,put} functions to +- * test connection against new DFS targets. +- */ +- rc = smb3_fs_context_dup(&tmp_ctx, ctx); +- if (rc) +- goto out; +- +- for (;;) { +- struct dfs_info3_param ref = {0}; +- char *fake_devname = NULL, *mdata = NULL; +- +- /* Get next DFS target server - if any */ +- rc = get_next_dfs_tgt(&tgt_list, &tgt_it); +- if (rc) +- break; +- +- rc = dfs_cache_get_tgt_referral(npath, tgt_it, &ref); +- if (rc) +- break; +- +- cifs_dbg(FYI, "%s: old ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC, +- tmp_ctx.prepath); +- +- mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, &ref, +- &fake_devname); +- free_dfs_info_param(&ref); +- +- if (IS_ERR(mdata)) { +- rc = PTR_ERR(mdata); +- mdata = NULL; +- } else +- rc = cifs_setup_volume_info(&tmp_ctx, mdata, fake_devname); +- +- kfree(mdata); +- kfree(fake_devname); +- +- if (rc) +- break; +- +- cifs_dbg(FYI, "%s: new ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC, +- tmp_ctx.prepath); +- +- mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon); +- rc = mount_get_dfs_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon); +- if (!rc || (*server && *ses)) { +- /* +- * We were able to connect to new target server. Update current context with +- * new target server. +- */ +- rc = update_vol_info(tgt_it, &tmp_ctx, ctx); +- break; +- } +- } +- if (!rc) { +- cifs_dbg(FYI, "%s: final ctx: UNC=%s prepath=%s\n", __func__, tmp_ctx.UNC, +- tmp_ctx.prepath); ++ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; ++ char *fake_devname = NULL, *mdata = NULL; ++ ++ mdata = cifs_compose_mount_options(cifs_sb->ctx->mount_options, full_path + 1, referral, ++ &fake_devname); ++ if (IS_ERR(mdata)) { ++ rc = PTR_ERR(mdata); ++ mdata = NULL; ++ } else { + /* +- * Update DFS target hint in DFS referral cache with the target server we +- * successfully reconnected to. ++ * We can not clear out the whole structure since we no longer have an explicit ++ * function to parse a mount-string. Instead we need to clear out the individual ++ * fields that are no longer valid. + */ +- rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses, cifs_sb->local_nls, +- cifs_remap(cifs_sb), path, tgt_it); ++ kfree(ctx->prepath); ++ ctx->prepath = NULL; ++ rc = cifs_setup_volume_info(ctx, mdata, fake_devname); + } ++ kfree(fake_devname); ++ kfree(cifs_sb->ctx->mount_options); ++ cifs_sb->ctx->mount_options = mdata; + +-out: +- kfree(npath); +- smb3_cleanup_fs_context_contents(&tmp_ctx); +- dfs_cache_free_tgts(&tgt_list); + return rc; + } + #endif +@@ -3393,12 +3256,14 @@ cifs_are_all_path_components_accessible(struct TCP_Server_Info *server, + * Check if path is remote (e.g. a DFS share). Return -EREMOTE if it is, + * otherwise 0. + */ +-static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx, +- const unsigned int xid, +- struct TCP_Server_Info *server, +- struct cifs_tcon *tcon) ++static int is_path_remote(struct mount_ctx *mnt_ctx) + { + int rc; ++ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ struct TCP_Server_Info *server = mnt_ctx->server; ++ unsigned int xid = mnt_ctx->xid; ++ struct cifs_tcon *tcon = mnt_ctx->tcon; ++ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + char *full_path; + + if (!server->ops->is_path_accessible) +@@ -3436,280 +3301,289 @@ static int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb3_fs_context * + } + + #ifdef CONFIG_CIFS_DFS_UPCALL +-static void set_root_ses(struct cifs_sb_info *cifs_sb, const uuid_t *mount_id, struct cifs_ses *ses, +- struct cifs_ses **root_ses) ++static void set_root_ses(struct mount_ctx *mnt_ctx) + { +- if (ses) { ++ if (mnt_ctx->ses) { + spin_lock(&cifs_tcp_ses_lock); +- ses->ses_count++; ++ mnt_ctx->ses->ses_count++; + spin_unlock(&cifs_tcp_ses_lock); +- dfs_cache_add_refsrv_session(mount_id, ses); ++ dfs_cache_add_refsrv_session(&mnt_ctx->mount_id, mnt_ctx->ses); + } +- *root_ses = ses; ++ mnt_ctx->root_ses = mnt_ctx->ses; + } + +-/* Set up next dfs prefix path in @dfs_path */ +-static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx, +- const unsigned int xid, struct TCP_Server_Info *server, +- struct cifs_tcon *tcon, char **dfs_path) ++static int is_dfs_mount(struct mount_ctx *mnt_ctx, bool *isdfs, struct dfs_cache_tgt_list *root_tl) + { +- char *path, *npath; +- int added_treename = is_tcon_dfs(tcon); + int rc; ++ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; + +- path = cifs_build_path_to_root(ctx, cifs_sb, tcon, added_treename); +- if (!path) +- return -ENOMEM; ++ *isdfs = true; + +- rc = is_path_remote(cifs_sb, ctx, xid, server, tcon); +- if (rc == -EREMOTE) { +- struct smb3_fs_context v = {NULL}; +- /* if @path contains a tree name, skip it in the prefix path */ +- if (added_treename) { +- rc = smb3_parse_devname(path, &v); +- if (rc) +- goto out; +- npath = build_unc_path_to_root(&v, cifs_sb, true); +- smb3_cleanup_fs_context_contents(&v); +- } else { +- v.UNC = ctx->UNC; +- v.prepath = path + 1; +- npath = build_unc_path_to_root(&v, cifs_sb, true); +- } ++ rc = mount_get_conns(mnt_ctx); ++ /* ++ * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally ++ * try to get an DFS referral (even cached) to determine whether it is an DFS mount. ++ * ++ * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem ++ * to respond with PATH_NOT_COVERED to requests that include the prefix. ++ */ ++ if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || ++ dfs_cache_find(mnt_ctx->xid, mnt_ctx->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ++ ctx->UNC + 1, NULL, root_tl)) { ++ if (rc) ++ return rc; ++ /* Check if it is fully accessible and then mount it */ ++ rc = is_path_remote(mnt_ctx); ++ if (!rc) ++ *isdfs = false; ++ else if (rc != -EREMOTE) ++ return rc; ++ } ++ return 0; ++} + +- if (IS_ERR(npath)) { +- rc = PTR_ERR(npath); +- goto out; +- } ++static int connect_dfs_target(struct mount_ctx *mnt_ctx, const char *full_path, ++ const char *ref_path, struct dfs_cache_tgt_iterator *tit) ++{ ++ int rc; ++ struct dfs_info3_param ref = {}; ++ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ char *oldmnt = cifs_sb->ctx->mount_options; + +- kfree(*dfs_path); +- *dfs_path = npath; +- rc = -EREMOTE; ++ rc = dfs_cache_get_tgt_referral(ref_path, tit, &ref); ++ if (rc) ++ goto out; ++ ++ rc = expand_dfs_referral(mnt_ctx, full_path, &ref); ++ if (rc) ++ goto out; ++ ++ /* Connect to new target only if we were redirected (e.g. mount options changed) */ ++ if (oldmnt != cifs_sb->ctx->mount_options) { ++ mount_put_conns(mnt_ctx); ++ rc = mount_get_dfs_conns(mnt_ctx); ++ } ++ if (!rc) { ++ if (cifs_is_referral_server(mnt_ctx->tcon, &ref)) ++ set_root_ses(mnt_ctx); ++ rc = dfs_cache_update_tgthint(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, ++ cifs_remap(cifs_sb), ref_path, tit); + } + + out: +- kfree(path); ++ free_dfs_info_param(&ref); + return rc; + } + +-/* Check if resolved targets can handle any DFS referrals */ +-static int is_referral_server(const char *ref_path, struct cifs_sb_info *cifs_sb, +- struct cifs_tcon *tcon, bool *ref_server) ++static int connect_dfs_root(struct mount_ctx *mnt_ctx, struct dfs_cache_tgt_list *root_tl) + { + int rc; +- struct dfs_info3_param ref = {0}; ++ char *full_path; ++ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; ++ struct dfs_cache_tgt_iterator *tit; + +- cifs_dbg(FYI, "%s: ref_path=%s\n", __func__, ref_path); ++ /* Put initial connections as they might be shared with other mounts. We need unique dfs ++ * connections per mount to properly failover, so mount_get_dfs_conns() must be used from ++ * now on. ++ */ ++ mount_put_conns(mnt_ctx); ++ mount_get_dfs_conns(mnt_ctx); + +- if (is_tcon_dfs(tcon)) { +- *ref_server = true; +- } else { +- char *npath; ++ full_path = build_unc_path_to_root(ctx, cifs_sb, true); ++ if (IS_ERR(full_path)) ++ return PTR_ERR(full_path); + +- npath = dfs_cache_canonical_path(ref_path, cifs_sb->local_nls, cifs_remap(cifs_sb)); +- if (IS_ERR(npath)) +- return PTR_ERR(npath); ++ mnt_ctx->origin_fullpath = dfs_cache_canonical_path(ctx->UNC, cifs_sb->local_nls, ++ cifs_remap(cifs_sb)); ++ if (IS_ERR(mnt_ctx->origin_fullpath)) { ++ rc = PTR_ERR(mnt_ctx->origin_fullpath); ++ mnt_ctx->origin_fullpath = NULL; ++ goto out; ++ } + +- rc = dfs_cache_noreq_find(npath, &ref, NULL); +- kfree(npath); +- if (rc) { +- cifs_dbg(VFS, "%s: dfs_cache_noreq_find: failed (rc=%d)\n", __func__, rc); +- return rc; ++ /* Try all dfs root targets */ ++ for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(root_tl); ++ tit; tit = dfs_cache_get_next_tgt(root_tl, tit)) { ++ rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->origin_fullpath + 1, tit); ++ if (!rc) { ++ mnt_ctx->leaf_fullpath = kstrdup(mnt_ctx->origin_fullpath, GFP_KERNEL); ++ if (!mnt_ctx->leaf_fullpath) ++ rc = -ENOMEM; ++ break; + } +- cifs_dbg(FYI, "%s: ref.flags=0x%x\n", __func__, ref.flags); +- /* +- * Check if all targets are capable of handling DFS referrals as per +- * MS-DFSC 2.2.4 RESP_GET_DFS_REFERRAL. +- */ +- *ref_server = !!(ref.flags & DFSREF_REFERRAL_SERVER); +- free_dfs_info_param(&ref); + } +- return 0; ++ ++out: ++ kfree(full_path); ++ return rc; + } + +-int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) ++static int __follow_dfs_link(struct mount_ctx *mnt_ctx) + { +- int rc = 0; +- unsigned int xid; +- struct TCP_Server_Info *server = NULL; +- struct cifs_ses *ses = NULL, *root_ses = NULL; +- struct cifs_tcon *tcon = NULL; +- int count = 0; +- uuid_t mount_id = {0}; +- char *ref_path = NULL, *full_path = NULL; +- char *oldmnt = NULL; +- bool ref_server = false; ++ int rc; ++ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; ++ char *full_path; ++ struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); ++ struct dfs_cache_tgt_iterator *tit; + +- rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); +- /* +- * If called with 'nodfs' mount option, then skip DFS resolving. Otherwise unconditionally +- * try to get an DFS referral (even cached) to determine whether it is an DFS mount. +- * +- * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem +- * to respond with PATH_NOT_COVERED to requests that include the prefix. +- */ +- if ((cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS) || +- dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), ctx->UNC + 1, NULL, +- NULL)) { +- if (rc) +- goto error; +- /* Check if it is fully accessible and then mount it */ +- rc = is_path_remote(cifs_sb, ctx, xid, server, tcon); +- if (!rc) +- goto out; +- if (rc != -EREMOTE) +- goto error; ++ full_path = build_unc_path_to_root(ctx, cifs_sb, true); ++ if (IS_ERR(full_path)) ++ return PTR_ERR(full_path); ++ ++ kfree(mnt_ctx->leaf_fullpath); ++ mnt_ctx->leaf_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, ++ cifs_remap(cifs_sb)); ++ if (IS_ERR(mnt_ctx->leaf_fullpath)) { ++ rc = PTR_ERR(mnt_ctx->leaf_fullpath); ++ mnt_ctx->leaf_fullpath = NULL; ++ goto out; + } + +- mount_put_conns(cifs_sb, xid, server, ses, tcon); +- /* +- * Ignore error check here because we may failover to other targets from cached a +- * referral. +- */ +- (void)mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); ++ /* Get referral from dfs link */ ++ rc = dfs_cache_find(mnt_ctx->xid, mnt_ctx->root_ses, cifs_sb->local_nls, ++ cifs_remap(cifs_sb), mnt_ctx->leaf_fullpath + 1, NULL, &tl); ++ if (rc) ++ goto out; + +- /* Get path of DFS root */ +- ref_path = build_unc_path_to_root(ctx, cifs_sb, false); +- if (IS_ERR(ref_path)) { +- rc = PTR_ERR(ref_path); +- ref_path = NULL; +- goto error; ++ /* Try all dfs link targets */ ++ for (rc = -ENOENT, tit = dfs_cache_get_tgt_iterator(&tl); ++ tit; tit = dfs_cache_get_next_tgt(&tl, tit)) { ++ rc = connect_dfs_target(mnt_ctx, full_path, mnt_ctx->leaf_fullpath + 1, tit); ++ if (!rc) { ++ rc = is_path_remote(mnt_ctx); ++ break; ++ } ++ } ++ ++out: ++ kfree(full_path); ++ dfs_cache_free_tgts(&tl); ++ return rc; ++} ++ ++static int follow_dfs_link(struct mount_ctx *mnt_ctx) ++{ ++ int rc; ++ struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb; ++ struct smb3_fs_context *ctx = mnt_ctx->fs_ctx; ++ char *full_path; ++ int num_links = 0; ++ ++ full_path = build_unc_path_to_root(ctx, cifs_sb, true); ++ if (IS_ERR(full_path)) ++ return PTR_ERR(full_path); ++ ++ kfree(mnt_ctx->origin_fullpath); ++ mnt_ctx->origin_fullpath = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, ++ cifs_remap(cifs_sb)); ++ kfree(full_path); ++ ++ if (IS_ERR(mnt_ctx->origin_fullpath)) { ++ rc = PTR_ERR(mnt_ctx->origin_fullpath); ++ mnt_ctx->origin_fullpath = NULL; ++ return rc; + } + +- uuid_gen(&mount_id); +- set_root_ses(cifs_sb, &mount_id, ses, &root_ses); + do { +- /* Save full path of last DFS path we used to resolve final target server */ +- kfree(full_path); +- full_path = build_unc_path_to_root(ctx, cifs_sb, !!count); +- if (IS_ERR(full_path)) { +- rc = PTR_ERR(full_path); +- full_path = NULL; +- break; +- } +- /* Chase referral */ +- oldmnt = cifs_sb->ctx->mount_options; +- rc = expand_dfs_referral(xid, root_ses, ctx, cifs_sb, ref_path + 1); +- if (rc) ++ rc = __follow_dfs_link(mnt_ctx); ++ if (!rc || rc != -EREMOTE) + break; +- /* Connect to new DFS target only if we were redirected */ +- if (oldmnt != cifs_sb->ctx->mount_options) { +- mount_put_conns(cifs_sb, xid, server, ses, tcon); +- rc = mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); +- } +- if (rc && !server && !ses) { +- /* Failed to connect. Try to connect to other targets in the referral. */ +- rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, ctx, root_ses, &xid, +- &server, &ses, &tcon); +- } +- if (rc == -EACCES || rc == -EOPNOTSUPP || !server || !ses) +- break; +- if (!tcon) +- continue; ++ } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); + +- /* Make sure that requests go through new root servers */ +- rc = is_referral_server(ref_path + 1, cifs_sb, tcon, &ref_server); +- if (rc) +- break; +- if (ref_server) +- set_root_ses(cifs_sb, &mount_id, ses, &root_ses); ++ return rc; ++} + +- /* Get next dfs path and then continue chasing them if -EREMOTE */ +- rc = next_dfs_prepath(cifs_sb, ctx, xid, server, tcon, &ref_path); +- /* Prevent recursion on broken link referrals */ +- if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS) +- rc = -ELOOP; +- } while (rc == -EREMOTE); ++/* Set up DFS referral paths for failover */ ++static void setup_server_referral_paths(struct mount_ctx *mnt_ctx) ++{ ++ struct TCP_Server_Info *server = mnt_ctx->server; ++ ++ server->origin_fullpath = mnt_ctx->origin_fullpath; ++ server->leaf_fullpath = mnt_ctx->leaf_fullpath; ++ server->current_fullpath = mnt_ctx->leaf_fullpath; ++ mnt_ctx->origin_fullpath = mnt_ctx->leaf_fullpath = NULL; ++} + +- if (rc || !tcon || !ses) ++int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) ++{ ++ int rc; ++ struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; ++ struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); ++ bool isdfs; ++ ++ rc = is_dfs_mount(&mnt_ctx, &isdfs, &tl); ++ if (rc) + goto error; ++ if (!isdfs) ++ goto out; + +- kfree(ref_path); +- /* +- * Store DFS full path in both superblock and tree connect structures. +- * +- * For DFS root mounts, the prefix path (cifs_sb->prepath) is preserved during reconnect so +- * only the root path is set in cifs_sb->origin_fullpath and tcon->dfs_path. And for DFS +- * links, the prefix path is included in both and may be changed during reconnect. See +- * cifs_tree_connect(). +- */ +- ref_path = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, cifs_remap(cifs_sb)); +- kfree(full_path); +- full_path = NULL; ++ uuid_gen(&mnt_ctx.mount_id); ++ rc = connect_dfs_root(&mnt_ctx, &tl); ++ dfs_cache_free_tgts(&tl); + +- if (IS_ERR(ref_path)) { +- rc = PTR_ERR(ref_path); +- ref_path = NULL; ++ if (rc) + goto error; +- } +- cifs_sb->origin_fullpath = ref_path; + +- ref_path = kstrdup(cifs_sb->origin_fullpath, GFP_KERNEL); +- if (!ref_path) { +- rc = -ENOMEM; ++ rc = is_path_remote(&mnt_ctx); ++ if (rc == -EREMOTE) ++ rc = follow_dfs_link(&mnt_ctx); ++ if (rc) + goto error; +- } +- spin_lock(&cifs_tcp_ses_lock); +- tcon->dfs_path = ref_path; +- ref_path = NULL; +- spin_unlock(&cifs_tcp_ses_lock); + ++ setup_server_referral_paths(&mnt_ctx); + /* +- * After reconnecting to a different server, unique ids won't +- * match anymore, so we disable serverino. This prevents +- * dentry revalidation to think the dentry are stale (ESTALE). ++ * After reconnecting to a different server, unique ids won't match anymore, so we disable ++ * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). + */ + cifs_autodisable_serverino(cifs_sb); + /* +- * Force the use of prefix path to support failover on DFS paths that +- * resolve to targets that have different prefix paths. ++ * Force the use of prefix path to support failover on DFS paths that resolve to targets ++ * that have different prefix paths. + */ + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; + kfree(cifs_sb->prepath); + cifs_sb->prepath = ctx->prepath; + ctx->prepath = NULL; +- uuid_copy(&cifs_sb->dfs_mount_id, &mount_id); ++ uuid_copy(&cifs_sb->dfs_mount_id, &mnt_ctx.mount_id); + + out: +- free_xid(xid); +- cifs_try_adding_channels(cifs_sb, ses); +- return mount_setup_tlink(cifs_sb, ses, tcon); ++ free_xid(mnt_ctx.xid); ++ cifs_try_adding_channels(cifs_sb, mnt_ctx.ses); ++ return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); + + error: +- kfree(ref_path); +- kfree(full_path); +- kfree(cifs_sb->origin_fullpath); +- dfs_cache_put_refsrv_sessions(&mount_id); +- mount_put_conns(cifs_sb, xid, server, ses, tcon); ++ dfs_cache_put_refsrv_sessions(&mnt_ctx.mount_id); ++ kfree(mnt_ctx.origin_fullpath); ++ kfree(mnt_ctx.leaf_fullpath); ++ mount_put_conns(&mnt_ctx); + return rc; + } + #else + int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx) + { + int rc = 0; +- unsigned int xid; +- struct cifs_ses *ses; +- struct cifs_tcon *tcon; +- struct TCP_Server_Info *server; ++ struct mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, }; + +- rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon); ++ rc = mount_get_conns(&mnt_ctx); + if (rc) + goto error; + +- if (tcon) { +- rc = is_path_remote(cifs_sb, ctx, xid, server, tcon); ++ if (mnt_ctx.tcon) { ++ rc = is_path_remote(&mnt_ctx); + if (rc == -EREMOTE) + rc = -EOPNOTSUPP; + if (rc) + goto error; + } + +- free_xid(xid); +- +- return mount_setup_tlink(cifs_sb, ses, tcon); ++ free_xid(mnt_ctx.xid); ++ return mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon); + + error: +- mount_put_conns(cifs_sb, xid, server, ses, tcon); ++ mount_put_conns(&mnt_ctx); + return rc; + } + #endif +@@ -3877,7 +3751,6 @@ cifs_umount(struct cifs_sb_info *cifs_sb) + kfree(cifs_sb->prepath); + #ifdef CONFIG_CIFS_DFS_UPCALL + dfs_cache_put_refsrv_sessions(&cifs_sb->dfs_mount_id); +- kfree(cifs_sb->origin_fullpath); + #endif + call_rcu(&cifs_sb->rcu, delayed_free); + } +@@ -4204,104 +4077,249 @@ cifs_prune_tlinks(struct work_struct *work) + } + + #ifdef CONFIG_CIFS_DFS_UPCALL +-int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) ++static void mark_tcon_tcp_ses_for_reconnect(struct cifs_tcon *tcon) ++{ ++ int i; ++ ++ for (i = 0; i < tcon->ses->chan_count; i++) { ++ spin_lock(&GlobalMid_Lock); ++ if (tcon->ses->chans[i].server->tcpStatus != CifsExiting) ++ tcon->ses->chans[i].server->tcpStatus = CifsNeedReconnect; ++ spin_unlock(&GlobalMid_Lock); ++ } ++} ++ ++/* Update dfs referral path of superblock */ ++static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb, ++ const char *target) ++{ ++ int rc = 0; ++ size_t len = strlen(target); ++ char *refpath, *npath; ++ ++ if (unlikely(len < 2 || *target != '\\')) ++ return -EINVAL; ++ ++ if (target[1] == '\\') { ++ len += 1; ++ refpath = kmalloc(len, GFP_KERNEL); ++ if (!refpath) ++ return -ENOMEM; ++ ++ scnprintf(refpath, len, "%s", target); ++ } else { ++ len += sizeof("\\"); ++ refpath = kmalloc(len, GFP_KERNEL); ++ if (!refpath) ++ return -ENOMEM; ++ ++ scnprintf(refpath, len, "\\%s", target); ++ } ++ ++ npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb)); ++ kfree(refpath); ++ ++ if (IS_ERR(npath)) { ++ rc = PTR_ERR(npath); ++ } else { ++ mutex_lock(&server->refpath_lock); ++ kfree(server->leaf_fullpath); ++ server->leaf_fullpath = npath; ++ mutex_unlock(&server->refpath_lock); ++ server->current_fullpath = server->leaf_fullpath; ++ } ++ return rc; ++} ++ ++static int target_share_matches_server(struct TCP_Server_Info *server, const char *tcp_host, ++ size_t tcp_host_len, char *share, bool *target_match) ++{ ++ int rc = 0; ++ const char *dfs_host; ++ size_t dfs_host_len; ++ ++ *target_match = true; ++ extract_unc_hostname(share, &dfs_host, &dfs_host_len); ++ ++ /* Check if hostnames or addresses match */ ++ if (dfs_host_len != tcp_host_len || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { ++ cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, ++ dfs_host, (int)tcp_host_len, tcp_host); ++ rc = match_target_ip(server, dfs_host, dfs_host_len, target_match); ++ if (rc) ++ cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); ++ } ++ return rc; ++} ++ ++int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, ++ struct cifs_sb_info *cifs_sb, char *tree, ++ struct dfs_cache_tgt_list *tl, struct dfs_info3_param *ref) + { + int rc; + struct TCP_Server_Info *server = tcon->ses->server; + const struct smb_version_operations *ops = server->ops; +- struct dfs_cache_tgt_list tl; +- struct dfs_cache_tgt_iterator *it = NULL; +- char *tree; ++ struct cifs_tcon *ipc = tcon->ses->tcon_ipc; ++ bool islink; ++ char *share = NULL, *prefix = NULL; + const char *tcp_host; + size_t tcp_host_len; +- const char *dfs_host; +- size_t dfs_host_len; +- char *share = NULL, *prefix = NULL; +- struct dfs_info3_param ref = {0}; +- bool isroot; ++ struct dfs_cache_tgt_iterator *tit; ++ bool target_match; + +- tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); +- if (!tree) +- return -ENOMEM; ++ extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); + +- /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ +- if (!tcon->dfs_path || dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl)) { +- if (tcon->ipc) { +- scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); +- rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); +- } else { +- rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc); +- } ++ islink = ref->server_type == DFS_TYPE_LINK; ++ free_dfs_info_param(ref); ++ ++ tit = dfs_cache_get_tgt_iterator(tl); ++ if (!tit) { ++ rc = -ENOENT; + goto out; + } + +- isroot = ref.server_type == DFS_TYPE_ROOT; +- free_dfs_info_param(&ref); +- +- extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len); +- +- for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) { +- bool target_match; ++ /* Try to tree connect to all dfs targets */ ++ for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) { ++ const char *target = dfs_cache_get_tgt_name(tit); ++ struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl); + + kfree(share); + kfree(prefix); +- share = NULL; +- prefix = NULL; + +- rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix); ++ /* Check if share matches with tcp ses */ ++ rc = dfs_cache_get_tgt_share(server->current_fullpath + 1, tit, &share, &prefix); + if (rc) { +- cifs_dbg(VFS, "%s: failed to parse target share %d\n", +- __func__, rc); +- continue; ++ cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc); ++ break; + } + +- extract_unc_hostname(share, &dfs_host, &dfs_host_len); +- +- if (dfs_host_len != tcp_host_len +- || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) { +- cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len, +- dfs_host, (int)tcp_host_len, tcp_host); ++ rc = target_share_matches_server(server, tcp_host, tcp_host_len, share, ++ &target_match); ++ if (rc) ++ break; ++ if (!target_match) { ++ rc = -EHOSTUNREACH; ++ continue; ++ } + +- rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match); +- if (rc) { +- cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc); ++ if (ipc->need_reconnect) { ++ scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); ++ rc = ops->tree_connect(xid, ipc->ses, tree, ipc, cifs_sb->local_nls); ++ if (rc) + break; +- } ++ } + +- if (!target_match) { +- cifs_dbg(FYI, "%s: skipping target\n", __func__); ++ scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); ++ if (!islink) { ++ rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); ++ break; ++ } ++ /* ++ * If no dfs referrals were returned from link target, then just do a TREE_CONNECT ++ * to it. Otherwise, cache the dfs referral and then mark current tcp ses for ++ * reconnect so either the demultiplex thread or the echo worker will reconnect to ++ * newly resolved target. ++ */ ++ if (dfs_cache_find(xid, tcon->ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target, ++ ref, &ntl)) { ++ rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls); ++ if (rc) + continue; +- } ++ rc = dfs_cache_noreq_update_tgthint(server->current_fullpath + 1, tit); ++ if (!rc) ++ rc = cifs_update_super_prepath(cifs_sb, prefix); ++ break; + } ++ /* Target is another dfs share */ ++ rc = update_server_fullpath(server, cifs_sb, target); ++ dfs_cache_free_tgts(tl); + +- if (tcon->ipc) { +- scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share); +- rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); ++ if (!rc) { ++ rc = -EREMOTE; ++ list_replace_init(&ntl.tl_list, &tl->tl_list); + } else { +- scnprintf(tree, MAX_TREE_SIZE, "\\%s", share); +- rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); +- /* Only handle prefix paths of DFS link targets */ +- if (!rc && !isroot) { +- rc = update_super_prepath(tcon, prefix); +- break; +- } ++ dfs_cache_free_tgts(&ntl); ++ free_dfs_info_param(ref); + } +- if (rc == -EREMOTE) +- break; ++ break; + } + ++out: + kfree(share); + kfree(prefix); + +- if (!rc) { +- if (it) +- rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, it); +- else +- rc = -ENOENT; ++ return rc; ++} ++ ++int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon, ++ struct cifs_sb_info *cifs_sb, char *tree, ++ struct dfs_cache_tgt_list *tl, struct dfs_info3_param *ref) ++{ ++ int rc; ++ int num_links = 0; ++ struct TCP_Server_Info *server = tcon->ses->server; ++ ++ do { ++ rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, tl, ref); ++ if (!rc || rc != -EREMOTE) ++ break; ++ } while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS); ++ /* ++ * If we couldn't tree connect to any targets from last referral path, then retry from ++ * original referral path. ++ */ ++ if (rc && server->current_fullpath != server->origin_fullpath) { ++ server->current_fullpath = server->origin_fullpath; ++ mark_tcon_tcp_ses_for_reconnect(tcon); + } +- dfs_cache_free_tgts(&tl); ++ ++ dfs_cache_free_tgts(tl); ++ return rc; ++} ++ ++int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc) ++{ ++ int rc; ++ struct TCP_Server_Info *server = tcon->ses->server; ++ const struct smb_version_operations *ops = server->ops; ++ struct super_block *sb = NULL; ++ struct cifs_sb_info *cifs_sb; ++ struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl); ++ char *tree; ++ struct dfs_info3_param ref = {0}; ++ ++ tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL); ++ if (!tree) ++ return -ENOMEM; ++ ++ if (tcon->ipc) { ++ scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname); ++ rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc); ++ goto out; ++ } ++ ++ sb = cifs_get_tcp_super(server); ++ if (IS_ERR(sb)) { ++ rc = PTR_ERR(sb); ++ cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc); ++ goto out; ++ } ++ ++ cifs_sb = CIFS_SB(sb); ++ ++ /* If it is not dfs or there was no cached dfs referral, then reconnect to same share */ ++ if (!server->current_fullpath || ++ dfs_cache_noreq_find(server->current_fullpath + 1, &ref, &tl)) { ++ rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, cifs_sb->local_nls); ++ goto out; ++ } ++ ++ rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, &tl, &ref); ++ + out: + kfree(tree); ++ cifs_put_tcp_super(sb); ++ + return rc; + } + #else +diff --git a/fs/cifs/dfs_cache.c b/fs/cifs/dfs_cache.c +index 283745592844..1f3efa7821a0 100644 +--- a/fs/cifs/dfs_cache.c ++++ b/fs/cifs/dfs_cache.c +@@ -1364,9 +1364,9 @@ static void mark_for_reconnect_if_needed(struct cifs_tcon *tcon, struct dfs_cach + } + + /* Refresh dfs referral of tcon and mark it for reconnect if needed */ +-static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) ++static int __refresh_tcon(const char *path, struct cifs_ses **sessions, struct cifs_tcon *tcon, ++ bool force_refresh) + { +- const char *path = tcon->dfs_path + 1; + struct cifs_ses *ses; + struct cache_entry *ce; + struct dfs_info3_param *refs = NULL; +@@ -1422,6 +1422,20 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool + return rc; + } + ++static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool force_refresh) ++{ ++ struct TCP_Server_Info *server = tcon->ses->server; ++ ++ mutex_lock(&server->refpath_lock); ++ if (strcasecmp(server->leaf_fullpath, server->origin_fullpath)) ++ __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, force_refresh); ++ mutex_unlock(&server->refpath_lock); ++ ++ __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, force_refresh); ++ ++ return 0; ++} ++ + /** + * dfs_cache_remount_fs - remount a DFS share + * +@@ -1435,6 +1449,7 @@ static int refresh_tcon(struct cifs_ses **sessions, struct cifs_tcon *tcon, bool + int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) + { + struct cifs_tcon *tcon; ++ struct TCP_Server_Info *server; + struct mount_group *mg; + struct cifs_ses *sessions[CACHE_MAX_ENTRIES + 1] = {NULL}; + int rc; +@@ -1443,13 +1458,15 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) + return -EINVAL; + + tcon = cifs_sb_master_tcon(cifs_sb); +- if (!tcon->dfs_path) { +- cifs_dbg(FYI, "%s: not a dfs tcon\n", __func__); ++ server = tcon->ses->server; ++ ++ if (!server->origin_fullpath) { ++ cifs_dbg(FYI, "%s: not a dfs mount\n", __func__); + return 0; + } + + if (uuid_is_null(&cifs_sb->dfs_mount_id)) { +- cifs_dbg(FYI, "%s: tcon has no dfs mount group id\n", __func__); ++ cifs_dbg(FYI, "%s: no dfs mount group id\n", __func__); + return -EINVAL; + } + +@@ -1457,7 +1474,7 @@ int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) + mg = find_mount_group_locked(&cifs_sb->dfs_mount_id); + if (IS_ERR(mg)) { + mutex_unlock(&mount_group_list_lock); +- cifs_dbg(FYI, "%s: tcon has ipc session to refresh referral\n", __func__); ++ cifs_dbg(FYI, "%s: no ipc session for refreshing referral\n", __func__); + return PTR_ERR(mg); + } + kref_get(&mg->refcount); +@@ -1498,9 +1515,12 @@ static void refresh_mounts(struct cifs_ses **sessions) + + spin_lock(&cifs_tcp_ses_lock); + list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) { ++ if (!server->is_dfs_conn) ++ continue; ++ + list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { + list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { +- if (tcon->dfs_path) { ++ if (!tcon->ipc && !tcon->need_reconnect) { + tcon->tc_count++; + list_add_tail(&tcon->ulist, &tcons); + } +@@ -1510,8 +1530,16 @@ static void refresh_mounts(struct cifs_ses **sessions) + spin_unlock(&cifs_tcp_ses_lock); + + list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) { ++ struct TCP_Server_Info *server = tcon->ses->server; ++ + list_del_init(&tcon->ulist); +- refresh_tcon(sessions, tcon, false); ++ ++ mutex_lock(&server->refpath_lock); ++ if (strcasecmp(server->leaf_fullpath, server->origin_fullpath)) ++ __refresh_tcon(server->leaf_fullpath + 1, sessions, tcon, false); ++ mutex_unlock(&server->refpath_lock); ++ ++ __refresh_tcon(server->origin_fullpath + 1, sessions, tcon, false); + cifs_put_tcon(tcon); + } + } +diff --git a/fs/cifs/misc.c b/fs/cifs/misc.c +index 699f676ded47..94143d7f58c7 100644 +--- a/fs/cifs/misc.c ++++ b/fs/cifs/misc.c +@@ -139,9 +139,6 @@ tconInfoFree(struct cifs_tcon *buf_to_free) + kfree(buf_to_free->nativeFileSystem); + kfree_sensitive(buf_to_free->password); + kfree(buf_to_free->crfid.fid); +-#ifdef CONFIG_CIFS_DFS_UPCALL +- kfree(buf_to_free->dfs_path); +-#endif + kfree(buf_to_free); + } + +@@ -1299,69 +1296,20 @@ int match_target_ip(struct TCP_Server_Info *server, + return rc; + } + +-static void tcon_super_cb(struct super_block *sb, void *arg) ++int cifs_update_super_prepath(struct cifs_sb_info *cifs_sb, char *prefix) + { +- struct super_cb_data *sd = arg; +- struct cifs_tcon *tcon = sd->data; +- struct cifs_sb_info *cifs_sb; +- +- if (sd->sb) +- return; +- +- cifs_sb = CIFS_SB(sb); +- if (tcon->dfs_path && cifs_sb->origin_fullpath && +- !strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath)) +- sd->sb = sb; +-} +- +-static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) +-{ +- return __cifs_get_super(tcon_super_cb, tcon); +-} +- +-static inline void cifs_put_tcon_super(struct super_block *sb) +-{ +- __cifs_put_super(sb); +-} +-#else +-static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon) +-{ +- return ERR_PTR(-EOPNOTSUPP); +-} +- +-static inline void cifs_put_tcon_super(struct super_block *sb) +-{ +-} +-#endif +- +-int update_super_prepath(struct cifs_tcon *tcon, char *prefix) +-{ +- struct super_block *sb; +- struct cifs_sb_info *cifs_sb; +- int rc = 0; +- +- sb = cifs_get_tcon_super(tcon); +- if (IS_ERR(sb)) +- return PTR_ERR(sb); +- +- cifs_sb = CIFS_SB(sb); +- + kfree(cifs_sb->prepath); + + if (prefix && *prefix) { + cifs_sb->prepath = kstrdup(prefix, GFP_ATOMIC); +- if (!cifs_sb->prepath) { +- rc = -ENOMEM; +- goto out; +- } ++ if (!cifs_sb->prepath) ++ return -ENOMEM; + + convert_delimiter(cifs_sb->prepath, CIFS_DIR_SEP(cifs_sb)); + } else + cifs_sb->prepath = NULL; + + cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; +- +-out: +- cifs_put_tcon_super(sb); +- return rc; ++ return 0; + } ++#endif +diff --git a/fs/cifs/smb2ops.c b/fs/cifs/smb2ops.c +index 53e87466e3b2..5e6526c201fe 100644 +--- a/fs/cifs/smb2ops.c ++++ b/fs/cifs/smb2ops.c +@@ -2869,6 +2869,7 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, + struct fsctl_get_dfs_referral_req *dfs_req = NULL; + struct get_dfs_referral_rsp *dfs_rsp = NULL; + u32 dfs_req_size = 0, dfs_rsp_size = 0; ++ int retry_count = 0; + + cifs_dbg(FYI, "%s: path: %s\n", __func__, search_name); + +@@ -2920,11 +2921,14 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses, + true /* is_fsctl */, + (char *)dfs_req, dfs_req_size, CIFSMaxBufSize, + (char **)&dfs_rsp, &dfs_rsp_size); +- } while (rc == -EAGAIN); ++ if (!is_retryable_error(rc)) ++ break; ++ usleep_range(512, 2048); ++ } while (++retry_count < 5); + + if (rc) { +- if ((rc != -ENOENT) && (rc != -EOPNOTSUPP)) +- cifs_tcon_dbg(VFS, "ioctl error in %s rc=%d\n", __func__, rc); ++ if (!is_retryable_error(rc) && rc != -ENOENT && rc != -EOPNOTSUPP) ++ cifs_tcon_dbg(VFS, "%s: ioctl error: rc=%d\n", __func__, rc); + goto out; + } + +diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c +index 8aa0372141f5..c0ea2813978b 100644 +--- a/fs/cifs/smb2pdu.c ++++ b/fs/cifs/smb2pdu.c +@@ -156,7 +156,11 @@ smb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon, + if (tcon == NULL) + return 0; + +- if (smb2_command == SMB2_TREE_CONNECT) ++ /* ++ * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in ++ * cifs_tree_connect(). ++ */ ++ if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL) + return 0; + + if (tcon->tidStatus == CifsExiting) { +-- +2.35.1 + diff --git a/queue-5.15/drm-display-don-t-assume-dual-mode-adaptors-support-.patch b/queue-5.15/drm-display-don-t-assume-dual-mode-adaptors-support-.patch new file mode 100644 index 00000000000..df96cc65f6b --- /dev/null +++ b/queue-5.15/drm-display-don-t-assume-dual-mode-adaptors-support-.patch @@ -0,0 +1,150 @@ +From cee0048a9d79033e208d9bee7446113a2df7f010 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Rafael Gieschke +Signed-off-by: Ville Syrjälä +Link: https://patchwork.freedesktop.org/patch/msgid/20221006113314.41101987@computer +Acked-by: Jani Nikula +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/drm_dp_dual_mode_helper.c | 51 +++++++++++++---------- + 1 file changed, 29 insertions(+), 22 deletions(-) + +diff --git a/drivers/gpu/drm/drm_dp_dual_mode_helper.c b/drivers/gpu/drm/drm_dp_dual_mode_helper.c +index 9faf49354cab..cb52a00ae1b1 100644 +--- a/drivers/gpu/drm/drm_dp_dual_mode_helper.c ++++ b/drivers/gpu/drm/drm_dp_dual_mode_helper.c +@@ -63,23 +63,45 @@ + ssize_t drm_dp_dual_mode_read(struct i2c_adapter *adapter, + u8 offset, void *buffer, size_t size) + { ++ u8 zero = 0; ++ char *tmpbuf = NULL; ++ /* ++ * As sub-addressing is not supported by all adaptors, ++ * always explicitly read from the start and discard ++ * any bytes that come before the requested offset. ++ * This way, no matter whether the adaptor supports it ++ * or not, we'll end up reading the proper data. ++ */ + struct i2c_msg msgs[] = { + { + .addr = DP_DUAL_MODE_SLAVE_ADDRESS, + .flags = 0, + .len = 1, +- .buf = &offset, ++ .buf = &zero, + }, + { + .addr = DP_DUAL_MODE_SLAVE_ADDRESS, + .flags = I2C_M_RD, +- .len = size, ++ .len = size + offset, + .buf = buffer, + }, + }; + int ret; + ++ if (offset) { ++ tmpbuf = kmalloc(size + offset, GFP_KERNEL); ++ if (!tmpbuf) ++ return -ENOMEM; ++ ++ msgs[1].buf = tmpbuf; ++ } ++ + ret = i2c_transfer(adapter, msgs, ARRAY_SIZE(msgs)); ++ if (tmpbuf) ++ memcpy(buffer, tmpbuf + offset, size); ++ ++ kfree(tmpbuf); ++ + if (ret < 0) + return ret; + if (ret != ARRAY_SIZE(msgs)) +@@ -208,18 +230,6 @@ enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev, + if (ret) + return DRM_DP_DUAL_MODE_UNKNOWN; + +- /* +- * Sigh. Some (maybe all?) type 1 adaptors are broken and ack +- * the offset but ignore it, and instead they just always return +- * data from the start of the HDMI ID buffer. So for a broken +- * type 1 HDMI adaptor a single byte read will always give us +- * 0x44, and for a type 1 DVI adaptor it should give 0x00 +- * (assuming it implements any registers). Fortunately neither +- * of those values will match the type 2 signature of the +- * DP_DUAL_MODE_ADAPTOR_ID register so we can proceed with +- * the type 2 adaptor detection safely even in the presence +- * of broken type 1 adaptors. +- */ + ret = drm_dp_dual_mode_read(adapter, DP_DUAL_MODE_ADAPTOR_ID, + &adaptor_id, sizeof(adaptor_id)); + drm_dbg_kms(dev, "DP dual mode adaptor ID: %02x (err %zd)\n", adaptor_id, ret); +@@ -233,11 +243,10 @@ enum drm_dp_dual_mode_type drm_dp_dual_mode_detect(const struct drm_device *dev, + return DRM_DP_DUAL_MODE_TYPE2_DVI; + } + /* +- * If neither a proper type 1 ID nor a broken type 1 adaptor +- * as described above, assume type 1, but let the user know +- * that we may have misdetected the type. ++ * If not a proper type 1 ID, still assume type 1, but let ++ * the user know that we may have misdetected the type. + */ +- if (!is_type1_adaptor(adaptor_id) && adaptor_id != hdmi_id[0]) ++ if (!is_type1_adaptor(adaptor_id)) + drm_err(dev, "Unexpected DP dual mode adaptor ID %02x\n", adaptor_id); + + } +@@ -343,10 +352,8 @@ EXPORT_SYMBOL(drm_dp_dual_mode_get_tmds_output); + * @enable: enable (as opposed to disable) the TMDS output buffers + * + * Set the state of the TMDS output buffers in the adaptor. For +- * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. As +- * some type 1 adaptors have problems with registers (see comments +- * in drm_dp_dual_mode_detect()) we avoid touching the register, +- * making this function a no-op on type 1 adaptors. ++ * type2 this is set via the DP_DUAL_MODE_TMDS_OEN register. ++ * Type1 adaptors do not support any register writes. + * + * Returns: + * 0 on success, negative error code on failure +-- +2.35.1 + diff --git a/queue-5.15/drm-panel-orientation-quirks-add-quirk-for-acer-swit.patch b/queue-5.15/drm-panel-orientation-quirks-add-quirk-for-acer-swit.patch new file mode 100644 index 00000000000..cb64c3b8e05 --- /dev/null +++ b/queue-5.15/drm-panel-orientation-quirks-add-quirk-for-acer-swit.patch @@ -0,0 +1,43 @@ +From 32b2843276359867282525062adf43bb79160961 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Hans de Goede +Acked-by: Simon Ser +Link: https://patchwork.freedesktop.org/patch/msgid/20221106215052.66995-1-hdegoede@redhat.com +Signed-off-by: Sasha Levin +--- + drivers/gpu/drm/drm_panel_orientation_quirks.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/gpu/drm/drm_panel_orientation_quirks.c b/drivers/gpu/drm/drm_panel_orientation_quirks.c +index 083273736c83..ca0fefeaab20 100644 +--- a/drivers/gpu/drm/drm_panel_orientation_quirks.c ++++ b/drivers/gpu/drm/drm_panel_orientation_quirks.c +@@ -128,6 +128,12 @@ static const struct dmi_system_id orientation_data[] = { + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "One S1003"), + }, + .driver_data = (void *)&lcd800x1280_rightside_up, ++ }, { /* Acer Switch V 10 (SW5-017) */ ++ .matches = { ++ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Acer"), ++ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "SW5-017"), ++ }, ++ .driver_data = (void *)&lcd800x1280_rightside_up, + }, { /* Anbernic Win600 */ + .matches = { + DMI_EXACT_MATCH(DMI_BOARD_VENDOR, "Anbernic"), +-- +2.35.1 + diff --git a/queue-5.15/iio-ms5611-simplify-io-callback-parameters.patch b/queue-5.15/iio-ms5611-simplify-io-callback-parameters.patch new file mode 100644 index 00000000000..b1159163d6d --- /dev/null +++ b/queue-5.15/iio-ms5611-simplify-io-callback-parameters.patch @@ -0,0 +1,188 @@ +From 68e124a64d94959ad595d88ce6937af8b2a82a4b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 20 Oct 2021 16:21:10 +0200 +Subject: iio: ms5611: Simplify IO callback parameters + +From: Lars-Peter Clausen + +[ 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 +Link: https://lore.kernel.org/r/20211020142110.7060-1-lars@metafoo.de +Signed-off-by: Jonathan Cameron +Stable-dep-of: 17f442e7e475 ("iio: pressure: ms5611: fixed value compensation bug") +Signed-off-by: Sasha Levin +--- + drivers/iio/pressure/ms5611.h | 6 +++--- + drivers/iio/pressure/ms5611_core.c | 7 +++---- + drivers/iio/pressure/ms5611_i2c.c | 11 ++++------- + drivers/iio/pressure/ms5611_spi.c | 17 +++++++---------- + 4 files changed, 17 insertions(+), 24 deletions(-) + +diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h +index bc06271fa38b..345f3902e3e3 100644 +--- a/drivers/iio/pressure/ms5611.h ++++ b/drivers/iio/pressure/ms5611.h +@@ -50,9 +50,9 @@ struct ms5611_state { + const struct ms5611_osr *pressure_osr; + const struct ms5611_osr *temp_osr; + +- int (*reset)(struct device *dev); +- int (*read_prom_word)(struct device *dev, int index, u16 *word); +- int (*read_adc_temp_and_pressure)(struct device *dev, ++ int (*reset)(struct ms5611_state *st); ++ int (*read_prom_word)(struct ms5611_state *st, int index, u16 *word); ++ int (*read_adc_temp_and_pressure)(struct ms5611_state *st, + s32 *temp, s32 *pressure); + + struct ms5611_chip_info *chip_info; +diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c +index 214b0d25f598..885ccb7914dc 100644 +--- a/drivers/iio/pressure/ms5611_core.c ++++ b/drivers/iio/pressure/ms5611_core.c +@@ -85,8 +85,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev) + struct ms5611_state *st = iio_priv(indio_dev); + + for (i = 0; i < MS5611_PROM_WORDS_NB; i++) { +- ret = st->read_prom_word(&indio_dev->dev, +- i, &st->chip_info->prom[i]); ++ ret = st->read_prom_word(st, i, &st->chip_info->prom[i]); + if (ret < 0) { + dev_err(&indio_dev->dev, + "failed to read prom at %d\n", i); +@@ -108,7 +107,7 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev, + int ret; + struct ms5611_state *st = iio_priv(indio_dev); + +- ret = st->read_adc_temp_and_pressure(&indio_dev->dev, temp, pressure); ++ ret = st->read_adc_temp_and_pressure(st, temp, pressure); + if (ret < 0) { + dev_err(&indio_dev->dev, + "failed to read temperature and pressure\n"); +@@ -196,7 +195,7 @@ static int ms5611_reset(struct iio_dev *indio_dev) + int ret; + struct ms5611_state *st = iio_priv(indio_dev); + +- ret = st->reset(&indio_dev->dev); ++ ret = st->reset(st); + if (ret < 0) { + dev_err(&indio_dev->dev, "failed to reset device\n"); + return ret; +diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c +index 7c04f730430c..cccc40f7df0b 100644 +--- a/drivers/iio/pressure/ms5611_i2c.c ++++ b/drivers/iio/pressure/ms5611_i2c.c +@@ -20,17 +20,15 @@ + + #include "ms5611.h" + +-static int ms5611_i2c_reset(struct device *dev) ++static int ms5611_i2c_reset(struct ms5611_state *st) + { +- struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); +- + return i2c_smbus_write_byte(st->client, MS5611_RESET); + } + +-static int ms5611_i2c_read_prom_word(struct device *dev, int index, u16 *word) ++static int ms5611_i2c_read_prom_word(struct ms5611_state *st, int index, ++ u16 *word) + { + int ret; +- struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + ret = i2c_smbus_read_word_swapped(st->client, + MS5611_READ_PROM_WORD + (index << 1)); +@@ -57,11 +55,10 @@ static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val) + return 0; + } + +-static int ms5611_i2c_read_adc_temp_and_pressure(struct device *dev, ++static int ms5611_i2c_read_adc_temp_and_pressure(struct ms5611_state *st, + s32 *temp, s32 *pressure) + { + int ret; +- struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + const struct ms5611_osr *osr = st->temp_osr; + + ret = i2c_smbus_write_byte(st->client, osr->cmd); +diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c +index f7743ee3318f..3039fe8aa2a2 100644 +--- a/drivers/iio/pressure/ms5611_spi.c ++++ b/drivers/iio/pressure/ms5611_spi.c +@@ -15,18 +15,17 @@ + + #include "ms5611.h" + +-static int ms5611_spi_reset(struct device *dev) ++static int ms5611_spi_reset(struct ms5611_state *st) + { + u8 cmd = MS5611_RESET; +- struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + return spi_write_then_read(st->client, &cmd, 1, NULL, 0); + } + +-static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word) ++static int ms5611_spi_read_prom_word(struct ms5611_state *st, int index, ++ u16 *word) + { + int ret; +- struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1)); + if (ret < 0) +@@ -37,11 +36,10 @@ static int ms5611_spi_read_prom_word(struct device *dev, int index, u16 *word) + return 0; + } + +-static int ms5611_spi_read_adc(struct device *dev, s32 *val) ++static int ms5611_spi_read_adc(struct ms5611_state *st, s32 *val) + { + int ret; + u8 buf[3] = { MS5611_READ_ADC }; +- struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + + ret = spi_write_then_read(st->client, buf, 1, buf, 3); + if (ret < 0) +@@ -52,11 +50,10 @@ static int ms5611_spi_read_adc(struct device *dev, s32 *val) + return 0; + } + +-static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, ++static int ms5611_spi_read_adc_temp_and_pressure(struct ms5611_state *st, + s32 *temp, s32 *pressure) + { + int ret; +- struct ms5611_state *st = iio_priv(dev_to_iio_dev(dev)); + const struct ms5611_osr *osr = st->temp_osr; + + /* +@@ -68,7 +65,7 @@ static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, + return ret; + + usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL)); +- ret = ms5611_spi_read_adc(dev, temp); ++ ret = ms5611_spi_read_adc(st, temp); + if (ret < 0) + return ret; + +@@ -78,7 +75,7 @@ static int ms5611_spi_read_adc_temp_and_pressure(struct device *dev, + return ret; + + usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL)); +- return ms5611_spi_read_adc(dev, pressure); ++ return ms5611_spi_read_adc(st, pressure); + } + + static int ms5611_spi_probe(struct spi_device *spi) +-- +2.35.1 + diff --git a/queue-5.15/iio-pressure-ms5611-fixed-value-compensation-bug.patch b/queue-5.15/iio-pressure-ms5611-fixed-value-compensation-bug.patch new file mode 100644 index 00000000000..542c3e4cc56 --- /dev/null +++ b/queue-5.15/iio-pressure-ms5611-fixed-value-compensation-bug.patch @@ -0,0 +1,173 @@ +From 0ded399b3fdd59cad1eb3f0ca2cd7db8259ab48d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 21 Oct 2022 15:58:20 +0200 +Subject: iio: pressure: ms5611: fixed value compensation bug + +From: Mitja Spes + +[ 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 +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: +Signed-off-by: Jonathan Cameron +Signed-off-by: Sasha Levin +--- + drivers/iio/pressure/ms5611.h | 12 +++---- + drivers/iio/pressure/ms5611_core.c | 51 ++++++++++++++++-------------- + 2 files changed, 31 insertions(+), 32 deletions(-) + +diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h +index 345f3902e3e3..5e2d2d4d87b5 100644 +--- a/drivers/iio/pressure/ms5611.h ++++ b/drivers/iio/pressure/ms5611.h +@@ -25,13 +25,6 @@ enum { + MS5607, + }; + +-struct ms5611_chip_info { +- u16 prom[MS5611_PROM_WORDS_NB]; +- +- int (*temp_and_pressure_compensate)(struct ms5611_chip_info *chip_info, +- s32 *temp, s32 *pressure); +-}; +- + /* + * OverSampling Rate descriptor. + * Warning: cmd MUST be kept aligned on a word boundary (see +@@ -50,12 +43,15 @@ struct ms5611_state { + const struct ms5611_osr *pressure_osr; + const struct ms5611_osr *temp_osr; + ++ u16 prom[MS5611_PROM_WORDS_NB]; ++ + int (*reset)(struct ms5611_state *st); + int (*read_prom_word)(struct ms5611_state *st, int index, u16 *word); + int (*read_adc_temp_and_pressure)(struct ms5611_state *st, + s32 *temp, s32 *pressure); + +- struct ms5611_chip_info *chip_info; ++ int (*compensate_temp_and_pressure)(struct ms5611_state *st, s32 *temp, ++ s32 *pressure); + struct regulator *vdd; + }; + +diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c +index 885ccb7914dc..874a73b3ea9d 100644 +--- a/drivers/iio/pressure/ms5611_core.c ++++ b/drivers/iio/pressure/ms5611_core.c +@@ -85,7 +85,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev) + struct ms5611_state *st = iio_priv(indio_dev); + + for (i = 0; i < MS5611_PROM_WORDS_NB; i++) { +- ret = st->read_prom_word(st, i, &st->chip_info->prom[i]); ++ ret = st->read_prom_word(st, i, &st->prom[i]); + if (ret < 0) { + dev_err(&indio_dev->dev, + "failed to read prom at %d\n", i); +@@ -93,7 +93,7 @@ static int ms5611_read_prom(struct iio_dev *indio_dev) + } + } + +- if (!ms5611_prom_is_valid(st->chip_info->prom, MS5611_PROM_WORDS_NB)) { ++ if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) { + dev_err(&indio_dev->dev, "PROM integrity check failed\n"); + return -ENODEV; + } +@@ -114,21 +114,20 @@ static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev, + return ret; + } + +- return st->chip_info->temp_and_pressure_compensate(st->chip_info, +- temp, pressure); ++ return st->compensate_temp_and_pressure(st, temp, pressure); + } + +-static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info, ++static int ms5611_temp_and_pressure_compensate(struct ms5611_state *st, + s32 *temp, s32 *pressure) + { + s32 t = *temp, p = *pressure; + s64 off, sens, dt; + +- dt = t - (chip_info->prom[5] << 8); +- off = ((s64)chip_info->prom[2] << 16) + ((chip_info->prom[4] * dt) >> 7); +- sens = ((s64)chip_info->prom[1] << 15) + ((chip_info->prom[3] * dt) >> 8); ++ dt = t - (st->prom[5] << 8); ++ off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7); ++ sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8); + +- t = 2000 + ((chip_info->prom[6] * dt) >> 23); ++ t = 2000 + ((st->prom[6] * dt) >> 23); + if (t < 2000) { + s64 off2, sens2, t2; + +@@ -154,17 +153,17 @@ static int ms5611_temp_and_pressure_compensate(struct ms5611_chip_info *chip_inf + return 0; + } + +-static int ms5607_temp_and_pressure_compensate(struct ms5611_chip_info *chip_info, ++static int ms5607_temp_and_pressure_compensate(struct ms5611_state *st, + s32 *temp, s32 *pressure) + { + s32 t = *temp, p = *pressure; + s64 off, sens, dt; + +- dt = t - (chip_info->prom[5] << 8); +- off = ((s64)chip_info->prom[2] << 17) + ((chip_info->prom[4] * dt) >> 6); +- sens = ((s64)chip_info->prom[1] << 16) + ((chip_info->prom[3] * dt) >> 7); ++ dt = t - (st->prom[5] << 8); ++ off = ((s64)st->prom[2] << 17) + ((st->prom[4] * dt) >> 6); ++ sens = ((s64)st->prom[1] << 16) + ((st->prom[3] * dt) >> 7); + +- t = 2000 + ((chip_info->prom[6] * dt) >> 23); ++ t = 2000 + ((st->prom[6] * dt) >> 23); + if (t < 2000) { + s64 off2, sens2, t2, tmp; + +@@ -342,15 +341,6 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, + + static const unsigned long ms5611_scan_masks[] = {0x3, 0}; + +-static struct ms5611_chip_info chip_info_tbl[] = { +- [MS5611] = { +- .temp_and_pressure_compensate = ms5611_temp_and_pressure_compensate, +- }, +- [MS5607] = { +- .temp_and_pressure_compensate = ms5607_temp_and_pressure_compensate, +- } +-}; +- + static const struct iio_chan_spec ms5611_channels[] = { + { + .type = IIO_PRESSURE, +@@ -433,7 +423,20 @@ int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, + struct ms5611_state *st = iio_priv(indio_dev); + + mutex_init(&st->lock); +- st->chip_info = &chip_info_tbl[type]; ++ ++ switch (type) { ++ case MS5611: ++ st->compensate_temp_and_pressure = ++ ms5611_temp_and_pressure_compensate; ++ break; ++ case MS5607: ++ st->compensate_temp_and_pressure = ++ ms5607_temp_and_pressure_compensate; ++ break; ++ default: ++ return -EINVAL; ++ } ++ + st->temp_osr = + &ms5611_avail_temp_osr[ARRAY_SIZE(ms5611_avail_temp_osr) - 1]; + st->pressure_osr = +-- +2.35.1 + diff --git a/queue-5.15/mips-pic32-treat-port-as-signed-integer.patch b/queue-5.15/mips-pic32-treat-port-as-signed-integer.patch new file mode 100644 index 00000000000..3952b585328 --- /dev/null +++ b/queue-5.15/mips-pic32-treat-port-as-signed-integer.patch @@ -0,0 +1,105 @@ +From f8ca0c71226633b8e5041422632ed1b5d03b494b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 28 Oct 2022 15:23:44 +0200 +Subject: MIPS: pic32: treat port as signed integer + +From: Jason A. Donenfeld + +[ 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 +Signed-off-by: Jason A. Donenfeld +Signed-off-by: Thomas Bogendoerfer +Signed-off-by: Sasha Levin +--- + arch/mips/include/asm/fw/fw.h | 2 +- + arch/mips/pic32/pic32mzda/early_console.c | 13 ++++++------- + arch/mips/pic32/pic32mzda/init.c | 2 +- + 3 files changed, 8 insertions(+), 9 deletions(-) + +diff --git a/arch/mips/include/asm/fw/fw.h b/arch/mips/include/asm/fw/fw.h +index d0ef8b4892bb..d0494ce4b337 100644 +--- a/arch/mips/include/asm/fw/fw.h ++++ b/arch/mips/include/asm/fw/fw.h +@@ -26,6 +26,6 @@ extern char *fw_getcmdline(void); + extern void fw_meminit(void); + extern char *fw_getenv(char *name); + extern unsigned long fw_getenvl(char *name); +-extern void fw_init_early_console(char port); ++extern void fw_init_early_console(void); + + #endif /* __ASM_FW_H_ */ +diff --git a/arch/mips/pic32/pic32mzda/early_console.c b/arch/mips/pic32/pic32mzda/early_console.c +index 25372e62783b..3cd1b408fa1c 100644 +--- a/arch/mips/pic32/pic32mzda/early_console.c ++++ b/arch/mips/pic32/pic32mzda/early_console.c +@@ -27,7 +27,7 @@ + #define U_BRG(x) (UART_BASE(x) + 0x40) + + static void __iomem *uart_base; +-static char console_port = -1; ++static int console_port = -1; + + static int __init configure_uart_pins(int port) + { +@@ -47,7 +47,7 @@ static int __init configure_uart_pins(int port) + return 0; + } + +-static void __init configure_uart(char port, int baud) ++static void __init configure_uart(int port, int baud) + { + u32 pbclk; + +@@ -60,7 +60,7 @@ static void __init configure_uart(char port, int baud) + uart_base + PIC32_SET(U_STA(port))); + } + +-static void __init setup_early_console(char port, int baud) ++static void __init setup_early_console(int port, int baud) + { + if (configure_uart_pins(port)) + return; +@@ -130,16 +130,15 @@ static int __init get_baud_from_cmdline(char *arch_cmdline) + return baud; + } + +-void __init fw_init_early_console(char port) ++void __init fw_init_early_console(void) + { + char *arch_cmdline = pic32_getcmdline(); +- int baud = -1; ++ int baud, port; + + uart_base = ioremap(PIC32_BASE_UART, 0xc00); + + baud = get_baud_from_cmdline(arch_cmdline); +- if (port == -1) +- port = get_port_from_cmdline(arch_cmdline); ++ port = get_port_from_cmdline(arch_cmdline); + + if (port == -1) + port = EARLY_CONSOLE_PORT; +diff --git a/arch/mips/pic32/pic32mzda/init.c b/arch/mips/pic32/pic32mzda/init.c +index 764f2d022fae..429830afff54 100644 +--- a/arch/mips/pic32/pic32mzda/init.c ++++ b/arch/mips/pic32/pic32mzda/init.c +@@ -47,7 +47,7 @@ void __init plat_mem_setup(void) + strlcpy(arcs_cmdline, boot_command_line, COMMAND_LINE_SIZE); + + #ifdef CONFIG_EARLY_PRINTK +- fw_init_early_console(-1); ++ fw_init_early_console(); + #endif + pic32_config_init(); + } +-- +2.35.1 + diff --git a/queue-5.15/nvme-add-a-bogus-subsystem-nqn-quirk-for-micron-mtfd.patch b/queue-5.15/nvme-add-a-bogus-subsystem-nqn-quirk-for-micron-mtfd.patch new file mode 100644 index 00000000000..6a8d84c5a36 --- /dev/null +++ b/queue-5.15/nvme-add-a-bogus-subsystem-nqn-quirk-for-micron-mtfd.patch @@ -0,0 +1,37 @@ +From ffa20c963662d5d7832da0332bd385e1e491b7ca Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 22 Jun 2022 12:19:21 +0200 +Subject: nvme: add a bogus subsystem NQN quirk for Micron MTFDKBA2T0TFH + +From: Leo Savernik + +[ 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 +Reviewed-by: Keith Busch +Signed-off-by: Christoph Hellwig +Stable-dep-of: d5ceb4d1c507 ("nvme-pci: add NVME_QUIRK_BOGUS_NID for Micron Nitro") +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index e9f3701dda3f..4fa2955dbf6c 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -3351,6 +3351,8 @@ static const struct pci_device_id nvme_id_table[] = { + { PCI_DEVICE(0x1cc1, 0x8201), /* ADATA SX8200PNP 512GB */ + .driver_data = NVME_QUIRK_NO_DEEPEST_PS | + NVME_QUIRK_IGNORE_DEV_SUBNQN, }, ++ { PCI_DEVICE(0x1344, 0x5407), /* Micron Technology Inc NVMe SSD */ ++ .driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN }, + { PCI_DEVICE(0x1c5c, 0x1504), /* SK Hynix PC400 */ + .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, + { PCI_DEVICE(0x15b7, 0x2001), /* Sandisk Skyhawk */ +-- +2.35.1 + diff --git a/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-micron-nitro.patch b/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-micron-nitro.patch new file mode 100644 index 00000000000..8c75fcded41 --- /dev/null +++ b/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-micron-nitro.patch @@ -0,0 +1,35 @@ +From 28811cf28593e7620fe8ad50b8a481b6ea9d62b0 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 14 Nov 2022 14:48:52 +0100 +Subject: nvme-pci: add NVME_QUIRK_BOGUS_NID for Micron Nitro + +From: Bean Huo + +[ Upstream commit d5ceb4d1c50786d21de3d4b06c3f43109ec56dd8 ] + +Added a quirk to fix Micron Nitro NVMe reporting duplicate NGUIDs. + +Cc: +Signed-off-by: Bean Huo +Signed-off-by: Christoph Hellwig +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 4fa2955dbf6c..278302771841 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -3353,6 +3353,8 @@ static const struct pci_device_id nvme_id_table[] = { + NVME_QUIRK_IGNORE_DEV_SUBNQN, }, + { PCI_DEVICE(0x1344, 0x5407), /* Micron Technology Inc NVMe SSD */ + .driver_data = NVME_QUIRK_IGNORE_DEV_SUBNQN }, ++ { PCI_DEVICE(0x1344, 0x6001), /* Micron Nitro NVMe */ ++ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1c5c, 0x1504), /* SK Hynix PC400 */ + .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, + { PCI_DEVICE(0x15b7, 0x2001), /* Sandisk Skyhawk */ +-- +2.35.1 + diff --git a/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-netac-nv7000.patch b/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-netac-nv7000.patch new file mode 100644 index 00000000000..da603727666 --- /dev/null +++ b/queue-5.15/nvme-pci-add-nvme_quirk_bogus_nid-for-netac-nv7000.patch @@ -0,0 +1,36 @@ +From f0c49f5246a1c3ca0b8ca61c966c56bcaaf3b6aa Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 16 Nov 2022 00:17:56 -0300 +Subject: nvme-pci: add NVME_QUIRK_BOGUS_NID for Netac NV7000 + +From: Tiago Dias Ferreira + +[ Upstream commit 8d6e38f636ac063e8062a21e7616f7d9bf0df5d8 ] + +Added a quirk to fix the Netac NV7000 SSD reporting duplicate NGUIDs. + +Cc: +Signed-off-by: Tiago Dias Ferreira +Reviewed-by: Chaitanya Kulkarni +Signed-off-by: Christoph Hellwig +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index a1a803b3105a..772bdc6845fb 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -3375,6 +3375,8 @@ static const struct pci_device_id nvme_id_table[] = { + .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, + { PCI_DEVICE(0x2646, 0x501E), /* KINGSTON OM3PGP4xxxxQ OS21011 NVMe SSD */ + .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, ++ { PCI_DEVICE(0x1f40, 0x5236), /* Netac Technologies Co. NV7000 NVMe SSD */ ++ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1e4B, 0x1001), /* MAXIO MAP1001 */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1e4B, 0x1002), /* MAXIO MAP1002 */ +-- +2.35.1 + diff --git a/queue-5.15/nvme-pci-disable-namespace-identifiers-for-the-maxio.patch b/queue-5.15/nvme-pci-disable-namespace-identifiers-for-the-maxio.patch new file mode 100644 index 00000000000..a760850b7b7 --- /dev/null +++ b/queue-5.15/nvme-pci-disable-namespace-identifiers-for-the-maxio.patch @@ -0,0 +1,39 @@ +From dc3d8853ac253e3562a1abf90ce9fde224d82958 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 27 May 2022 07:32:08 +0200 +Subject: nvme-pci: disable namespace identifiers for the MAXIO MAP1001 + +From: Christoph Hellwig + +[ 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 +Signed-off-by: Christoph Hellwig +Reviewed-by: Chaitanya Kulkarni +Tested-by: Arman Hajishafieha +Stable-dep-of: 8d6e38f636ac ("nvme-pci: add NVME_QUIRK_BOGUS_NID for Netac NV7000") +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 278302771841..66e252af9218 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -3365,6 +3365,8 @@ static const struct pci_device_id nvme_id_table[] = { + .driver_data = NVME_QUIRK_NO_DEEPEST_PS, }, + { PCI_DEVICE(0x2646, 0x2263), /* KINGSTON A2000 NVMe SSD */ + .driver_data = NVME_QUIRK_NO_DEEPEST_PS, }, ++ { PCI_DEVICE(0x1e4B, 0x1001), /* MAXIO MAP1001 */ ++ .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1e4B, 0x1002), /* MAXIO MAP1002 */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1e4B, 0x1202), /* MAXIO MAP1202 */ +-- +2.35.1 + diff --git a/queue-5.15/nvme-pci-disable-write-zeroes-on-various-kingston-ss.patch b/queue-5.15/nvme-pci-disable-write-zeroes-on-various-kingston-ss.patch new file mode 100644 index 00000000000..438c8aaa617 --- /dev/null +++ b/queue-5.15/nvme-pci-disable-write-zeroes-on-various-kingston-ss.patch @@ -0,0 +1,45 @@ +From ef1b4f28b8ce7ad627d1c4f00e4e0fae3155f338 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 11 Oct 2022 04:06:42 -0700 +Subject: nvme-pci: disable write zeroes on various Kingston SSD + +From: Xander Li + +[ 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 +Signed-off-by: Christoph Hellwig +Stable-dep-of: 8d6e38f636ac ("nvme-pci: add NVME_QUIRK_BOGUS_NID for Netac NV7000") +Signed-off-by: Sasha Levin +--- + drivers/nvme/host/pci.c | 10 ++++++++++ + 1 file changed, 10 insertions(+) + +diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c +index 66e252af9218..a1a803b3105a 100644 +--- a/drivers/nvme/host/pci.c ++++ b/drivers/nvme/host/pci.c +@@ -3365,6 +3365,16 @@ static const struct pci_device_id nvme_id_table[] = { + .driver_data = NVME_QUIRK_NO_DEEPEST_PS, }, + { PCI_DEVICE(0x2646, 0x2263), /* KINGSTON A2000 NVMe SSD */ + .driver_data = NVME_QUIRK_NO_DEEPEST_PS, }, ++ { PCI_DEVICE(0x2646, 0x5018), /* KINGSTON OM8SFP4xxxxP OS21012 NVMe SSD */ ++ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, ++ { PCI_DEVICE(0x2646, 0x5016), /* KINGSTON OM3PGP4xxxxP OS21011 NVMe SSD */ ++ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, ++ { PCI_DEVICE(0x2646, 0x501A), /* KINGSTON OM8PGP4xxxxP OS21005 NVMe SSD */ ++ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, ++ { PCI_DEVICE(0x2646, 0x501B), /* KINGSTON OM8PGP4xxxxQ OS21005 NVMe SSD */ ++ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, ++ { PCI_DEVICE(0x2646, 0x501E), /* KINGSTON OM3PGP4xxxxQ OS21011 NVMe SSD */ ++ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, }, + { PCI_DEVICE(0x1e4B, 0x1001), /* MAXIO MAP1001 */ + .driver_data = NVME_QUIRK_BOGUS_NID, }, + { PCI_DEVICE(0x1e4B, 0x1002), /* MAXIO MAP1002 */ +-- +2.35.1 + diff --git a/queue-5.15/nvmet-fix-memory-leak-in-nvmet_subsys_attr_model_sto.patch b/queue-5.15/nvmet-fix-memory-leak-in-nvmet_subsys_attr_model_sto.patch new file mode 100644 index 00000000000..32e3102b249 --- /dev/null +++ b/queue-5.15/nvmet-fix-memory-leak-in-nvmet_subsys_attr_model_sto.patch @@ -0,0 +1,51 @@ +From 4ddd30e2e971981985db19122cb2239bdd78eec5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 26 Oct 2022 12:31:33 +0400 +Subject: nvmet: fix memory leak in nvmet_subsys_attr_model_store_locked + +From: Aleksandr Miloserdov + +[ Upstream commit becc4cac309dc867571f0080fde4426a6c2222e0 ] + +Since model_number is allocated before it needs to be freed before +kmemdump_nul. + +Reviewed-by: Konstantin Shelekhin +Reviewed-by: Dmitriy Bogdanov +Signed-off-by: Aleksandr Miloserdov +Reviewed-by: Sagi Grimberg +Signed-off-by: Christoph Hellwig +Signed-off-by: Sasha Levin +--- + drivers/nvme/target/configfs.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c +index cea30e4f5053..625038057a76 100644 +--- a/drivers/nvme/target/configfs.c ++++ b/drivers/nvme/target/configfs.c +@@ -1189,6 +1189,7 @@ static ssize_t nvmet_subsys_attr_model_store_locked(struct nvmet_subsys *subsys, + const char *page, size_t count) + { + int pos = 0, len; ++ char *val; + + if (subsys->subsys_discovered) { + pr_err("Can't set model number. %s is already assigned\n", +@@ -1211,9 +1212,11 @@ static ssize_t nvmet_subsys_attr_model_store_locked(struct nvmet_subsys *subsys, + return -EINVAL; + } + +- subsys->model_number = kmemdup_nul(page, len, GFP_KERNEL); +- if (!subsys->model_number) ++ val = kmemdup_nul(page, len, GFP_KERNEL); ++ if (!val) + return -ENOMEM; ++ kfree(subsys->model_number); ++ subsys->model_number = val; + return count; + } + +-- +2.35.1 + diff --git a/queue-5.15/platform-x86-ideapad-laptop-disable-touchpad_switch.patch b/queue-5.15/platform-x86-ideapad-laptop-disable-touchpad_switch.patch new file mode 100644 index 00000000000..d866f668b2e --- /dev/null +++ b/queue-5.15/platform-x86-ideapad-laptop-disable-touchpad_switch.patch @@ -0,0 +1,70 @@ +From 1d8c2e23d71bcf52cb50eed12358f4aa7124d010 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 18 Oct 2022 17:53:23 +0800 +Subject: platform/x86: ideapad-laptop: Disable touchpad_switch + +From: Manyi Li + +[ 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 +Link: https://lore.kernel.org/r/20221018095323.14591-1-limanyi@uniontech.com +Reviewed-by: Hans de Goede +Signed-off-by: Hans de Goede +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/ideapad-laptop.c | 25 ++++++++++++++++++++++++- + 1 file changed, 24 insertions(+), 1 deletion(-) + +diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c +index e7a1299e3776..ab4dfff2174b 100644 +--- a/drivers/platform/x86/ideapad-laptop.c ++++ b/drivers/platform/x86/ideapad-laptop.c +@@ -1499,6 +1499,24 @@ static const struct dmi_system_id hw_rfkill_list[] = { + {} + }; + ++static const struct dmi_system_id no_touchpad_switch_list[] = { ++ { ++ .ident = "Lenovo Yoga 3 Pro 1370", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo YOGA 3"), ++ }, ++ }, ++ { ++ .ident = "ZhaoYang K4e-IML", ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), ++ DMI_MATCH(DMI_PRODUCT_VERSION, "ZhaoYang K4e-IML"), ++ }, ++ }, ++ {} ++}; ++ + static void ideapad_check_features(struct ideapad_private *priv) + { + acpi_handle handle = priv->adev->handle; +@@ -1507,7 +1525,12 @@ static void ideapad_check_features(struct ideapad_private *priv) + priv->features.hw_rfkill_switch = dmi_check_system(hw_rfkill_list); + + /* Most ideapads with ELAN0634 touchpad don't use EC touchpad switch */ +- priv->features.touchpad_ctrl_via_ec = !acpi_dev_present("ELAN0634", NULL, -1); ++ if (acpi_dev_present("ELAN0634", NULL, -1)) ++ priv->features.touchpad_ctrl_via_ec = 0; ++ else if (dmi_check_system(no_touchpad_switch_list)) ++ priv->features.touchpad_ctrl_via_ec = 0; ++ else ++ priv->features.touchpad_ctrl_via_ec = 1; + + if (!read_ec_data(handle, VPCCMD_R_FAN, &val)) + priv->features.fan_mode = true; +-- +2.35.1 + diff --git a/queue-5.15/platform-x86-intel-hid-add-some-acpi-device-ids.patch b/queue-5.15/platform-x86-intel-hid-add-some-acpi-device-ids.patch new file mode 100644 index 00000000000..6f563d95cca --- /dev/null +++ b/queue-5.15/platform-x86-intel-hid-add-some-acpi-device-ids.patch @@ -0,0 +1,38 @@ +From a6b3107f327b85cf588753a205a8dfcd2c0d0151 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 2 Nov 2022 10:05:48 +0800 +Subject: platform/x86/intel/hid: Add some ACPI device IDs + +From: Ivan Hu + +[ Upstream commit a977ece5773b6746b814aac410da4776023db239 ] + +Add INTC1076 (JasonLake), INTC1077 (MeteorLake) and INTC1078 (RaptorLake) +devices IDs. + +Signed-off-by: Ivan Hu +Link: https://lore.kernel.org/r/20221102020548.5225-1-ivan.hu@canonical.com +Reviewed-by: Hans de Goede +Signed-off-by: Hans de Goede +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/intel/hid.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/drivers/platform/x86/intel/hid.c b/drivers/platform/x86/intel/hid.c +index d7d6782c40c2..4d1c78635114 100644 +--- a/drivers/platform/x86/intel/hid.c ++++ b/drivers/platform/x86/intel/hid.c +@@ -27,6 +27,9 @@ static const struct acpi_device_id intel_hid_ids[] = { + {"INTC1051", 0}, + {"INTC1054", 0}, + {"INTC1070", 0}, ++ {"INTC1076", 0}, ++ {"INTC1077", 0}, ++ {"INTC1078", 0}, + {"", 0}, + }; + MODULE_DEVICE_TABLE(acpi, intel_hid_ids); +-- +2.35.1 + diff --git a/queue-5.15/platform-x86-intel-pmt-sapphire-rapids-pmt-errata-fi.patch b/queue-5.15/platform-x86-intel-pmt-sapphire-rapids-pmt-errata-fi.patch new file mode 100644 index 00000000000..703934a8855 --- /dev/null +++ b/queue-5.15/platform-x86-intel-pmt-sapphire-rapids-pmt-errata-fi.patch @@ -0,0 +1,88 @@ +From 3511949e896e703e0ae13c21bde4441fe9ea5656 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 4 Nov 2022 20:42:28 -0700 +Subject: platform/x86/intel/pmt: Sapphire Rapids PMT errata fix + +From: David E. Box + +[ 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 +Link: https://lore.kernel.org/r/20221105034228.1376677-1-david.e.box@linux.intel.com +Reviewed-by: Hans de Goede +Signed-off-by: Hans de Goede +Signed-off-by: Sasha Levin +--- + 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 ++#include + #include + #include + #include +@@ -18,6 +19,7 @@ + #define PMT_XA_START 0 + #define PMT_XA_MAX INT_MAX + #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) ++#define GUID_SPR_PUNIT 0x9956f43f + + /* + * Early implementations of PMT on client platforms have some +@@ -41,6 +43,29 @@ bool intel_pmt_is_early_client_hw(struct device *dev) + } + EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw); + ++static inline int ++pmt_memcpy64_fromio(void *to, const u64 __iomem *from, size_t count) ++{ ++ int i, remain; ++ u64 *buf = to; ++ ++ if (!IS_ALIGNED((unsigned long)from, 8)) ++ return -EFAULT; ++ ++ for (i = 0; i < count/8; i++) ++ buf[i] = readq(&from[i]); ++ ++ /* Copy any remaining bytes */ ++ remain = count % 8; ++ if (remain) { ++ u64 tmp = readq(&from[i]); ++ ++ memcpy(&buf[i], &tmp, remain); ++ } ++ ++ return count; ++} ++ + /* + * sysfs + */ +@@ -62,7 +87,11 @@ intel_pmt_read(struct file *filp, struct kobject *kobj, + if (count > entry->size - off) + count = entry->size - off; + +- memcpy_fromio(buf, entry->base + off, count); ++ if (entry->guid == GUID_SPR_PUNIT) ++ /* PUNIT on SPR only supports aligned 64-bit read */ ++ count = pmt_memcpy64_fromio(buf, entry->base + off, count); ++ else ++ memcpy_fromio(buf, entry->base + off, count); + + return count; + } +-- +2.35.1 + diff --git a/queue-5.15/platform-x86-touchscreen_dmi-add-info-for-the-rca-ca.patch b/queue-5.15/platform-x86-touchscreen_dmi-add-info-for-the-rca-ca.patch new file mode 100644 index 00000000000..39c6c051d3f --- /dev/null +++ b/queue-5.15/platform-x86-touchscreen_dmi-add-info-for-the-rca-ca.patch @@ -0,0 +1,66 @@ +From df50847d3ef978d7e5b937b8f158707924279cad Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://lore.kernel.org/r/20221025141131.509211-1-hdegoede@redhat.com +Signed-off-by: Sasha Levin +--- + drivers/platform/x86/touchscreen_dmi.c | 25 +++++++++++++++++++++++++ + 1 file changed, 25 insertions(+) + +diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c +index c608078538a7..3d0790263fa7 100644 +--- a/drivers/platform/x86/touchscreen_dmi.c ++++ b/drivers/platform/x86/touchscreen_dmi.c +@@ -773,6 +773,22 @@ static const struct ts_dmi_data predia_basic_data = { + .properties = predia_basic_props, + }; + ++static const struct property_entry rca_cambio_w101_v2_props[] = { ++ PROPERTY_ENTRY_U32("touchscreen-min-x", 4), ++ PROPERTY_ENTRY_U32("touchscreen-min-y", 20), ++ PROPERTY_ENTRY_U32("touchscreen-size-x", 1644), ++ PROPERTY_ENTRY_U32("touchscreen-size-y", 874), ++ PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), ++ PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-rca-cambio-w101-v2.fw"), ++ PROPERTY_ENTRY_U32("silead,max-fingers", 10), ++ { } ++}; ++ ++static const struct ts_dmi_data rca_cambio_w101_v2_data = { ++ .acpi_name = "MSSL1680:00", ++ .properties = rca_cambio_w101_v2_props, ++}; ++ + static const struct property_entry rwc_nanote_p8_props[] = { + PROPERTY_ENTRY_U32("touchscreen-min-y", 46), + PROPERTY_ENTRY_U32("touchscreen-size-x", 1728), +@@ -1394,6 +1410,15 @@ const struct dmi_system_id touchscreen_dmi_table[] = { + DMI_EXACT_MATCH(DMI_BOARD_NAME, "0E57"), + }, + }, ++ { ++ /* RCA Cambio W101 v2 */ ++ /* https://github.com/onitake/gsl-firmware/discussions/193 */ ++ .driver_data = (void *)&rca_cambio_w101_v2_data, ++ .matches = { ++ DMI_MATCH(DMI_SYS_VENDOR, "RCA"), ++ DMI_MATCH(DMI_PRODUCT_NAME, "W101SA23T1"), ++ }, ++ }, + { + /* RWC NANOTE P8 */ + .driver_data = (void *)&rwc_nanote_p8_data, +-- +2.35.1 + diff --git a/queue-5.15/revert-drm-amdgpu-revert-drm-amdgpu-getting-fan-spee.patch b/queue-5.15/revert-drm-amdgpu-revert-drm-amdgpu-getting-fan-spee.patch new file mode 100644 index 00000000000..bf6a9e82f13 --- /dev/null +++ b/queue-5.15/revert-drm-amdgpu-revert-drm-amdgpu-getting-fan-spee.patch @@ -0,0 +1,65 @@ +From f739ed2747d38102a22d721ac279ec7c9196165d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Guchun Chen +Signed-off-by: Alex Deucher +Signed-off-by: Sasha Levin +--- + .../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 + diff --git a/queue-5.15/revert-net-macsec-report-real_dev-features-when-hw-o.patch b/queue-5.15/revert-net-macsec-report-real_dev-features-when-hw-o.patch new file mode 100644 index 00000000000..c4cff41df6c --- /dev/null +++ b/queue-5.15/revert-net-macsec-report-real_dev-features-when-hw-o.patch @@ -0,0 +1,114 @@ +From 019d6087093ef064b31f6e36d9ef1c2621728e87 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Antoine Tenart +Reviewed-by: Leon Romanovsky +Signed-off-by: David S. Miller +Signed-off-by: Sasha Levin +--- + drivers/net/macsec.c | 27 ++++----------------------- + 1 file changed, 4 insertions(+), 23 deletions(-) + +diff --git a/drivers/net/macsec.c b/drivers/net/macsec.c +index 4811bd1f3d74..f1961d7f9db2 100644 +--- a/drivers/net/macsec.c ++++ b/drivers/net/macsec.c +@@ -2644,11 +2644,6 @@ static int macsec_upd_offload(struct sk_buff *skb, struct genl_info *info) + if (ret) + goto rollback; + +- /* Force features update, since they are different for SW MACSec and +- * HW offloading cases. +- */ +- netdev_update_features(dev); +- + rtnl_unlock(); + return 0; + +@@ -3416,16 +3411,9 @@ static netdev_tx_t macsec_start_xmit(struct sk_buff *skb, + return ret; + } + +-#define SW_MACSEC_FEATURES \ ++#define MACSEC_FEATURES \ + (NETIF_F_SG | NETIF_F_HIGHDMA | NETIF_F_FRAGLIST) + +-/* If h/w offloading is enabled, use real device features save for +- * VLAN_FEATURES - they require additional ops +- * HW_MACSEC - no reason to report it +- */ +-#define REAL_DEV_FEATURES(dev) \ +- ((dev)->features & ~(NETIF_F_VLAN_FEATURES | NETIF_F_HW_MACSEC)) +- + static int macsec_dev_init(struct net_device *dev) + { + struct macsec_dev *macsec = macsec_priv(dev); +@@ -3442,12 +3430,8 @@ static int macsec_dev_init(struct net_device *dev) + return err; + } + +- if (macsec_is_offloaded(macsec)) { +- dev->features = REAL_DEV_FEATURES(real_dev); +- } else { +- dev->features = real_dev->features & SW_MACSEC_FEATURES; +- dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE; +- } ++ dev->features = real_dev->features & MACSEC_FEATURES; ++ dev->features |= NETIF_F_LLTX | NETIF_F_GSO_SOFTWARE; + + dev->needed_headroom = real_dev->needed_headroom + + MACSEC_NEEDED_HEADROOM; +@@ -3476,10 +3460,7 @@ static netdev_features_t macsec_fix_features(struct net_device *dev, + struct macsec_dev *macsec = macsec_priv(dev); + struct net_device *real_dev = macsec->real_dev; + +- if (macsec_is_offloaded(macsec)) +- return REAL_DEV_FEATURES(real_dev); +- +- features &= (real_dev->features & SW_MACSEC_FEATURES) | ++ features &= (real_dev->features & MACSEC_FEATURES) | + NETIF_F_GSO_SOFTWARE | NETIF_F_SOFT_FEATURES; + features |= NETIF_F_LLTX; + +-- +2.35.1 + diff --git a/queue-5.15/risc-v-vdso-do-not-add-missing-symbols-to-version-se.patch b/queue-5.15/risc-v-vdso-do-not-add-missing-symbols-to-version-se.patch new file mode 100644 index 00000000000..2c2f29076a6 --- /dev/null +++ b/queue-5.15/risc-v-vdso-do-not-add-missing-symbols-to-version-se.patch @@ -0,0 +1,69 @@ +From 9585bb8208b22b5da772f30146cc3f4d7f9a73a9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Tested-by: Conor Dooley +Link: https://lore.kernel.org/r/20221108171324.3377226-1-nathan@kernel.org/ +Signed-off-by: Palmer Dabbelt +Signed-off-by: Sasha Levin +--- + arch/riscv/kernel/vdso/Makefile | 3 +++ + arch/riscv/kernel/vdso/vdso.lds.S | 2 ++ + 2 files changed, 5 insertions(+) + +diff --git a/arch/riscv/kernel/vdso/Makefile b/arch/riscv/kernel/vdso/Makefile +index 84ac0fe612e7..db6548509bb3 100644 +--- a/arch/riscv/kernel/vdso/Makefile ++++ b/arch/riscv/kernel/vdso/Makefile +@@ -28,6 +28,9 @@ obj-vdso := $(addprefix $(obj)/, $(obj-vdso)) + + obj-y += vdso.o + CPPFLAGS_vdso.lds += -P -C -U$(ARCH) ++ifneq ($(filter vgettimeofday, $(vdso-syms)),) ++CPPFLAGS_vdso.lds += -DHAS_VGETTIMEOFDAY ++endif + + # Disable -pg to prevent insert call site + CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) +diff --git a/arch/riscv/kernel/vdso/vdso.lds.S b/arch/riscv/kernel/vdso/vdso.lds.S +index e9111f700af0..3729cb28aac8 100644 +--- a/arch/riscv/kernel/vdso/vdso.lds.S ++++ b/arch/riscv/kernel/vdso/vdso.lds.S +@@ -65,9 +65,11 @@ VERSION + LINUX_4.15 { + global: + __vdso_rt_sigreturn; ++#ifdef HAS_VGETTIMEOFDAY + __vdso_gettimeofday; + __vdso_clock_gettime; + __vdso_clock_getres; ++#endif + __vdso_getcpu; + __vdso_flush_icache; + local: *; +-- +2.35.1 + diff --git a/queue-5.15/riscv-dts-sifive-unleashed-add-pwm-controlled-leds.patch b/queue-5.15/riscv-dts-sifive-unleashed-add-pwm-controlled-leds.patch new file mode 100644 index 00000000000..14c8885b0be --- /dev/null +++ b/queue-5.15/riscv-dts-sifive-unleashed-add-pwm-controlled-leds.patch @@ -0,0 +1,83 @@ +From 60afe9b3d6150ec7590bb6f4d8476e256fc6aa78 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 12 Oct 2022 13:09:28 +0200 +Subject: riscv: dts: sifive unleashed: Add PWM controlled LEDs + +From: Emil Renner Berthing + +[ 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 +Reviewed-by: Conor Dooley +Tested-by: Conor Dooley +Link: https://lore.kernel.org/r/20221012110928.352910-1-emil.renner.berthing@canonical.com +Signed-off-by: Palmer Dabbelt +Signed-off-by: Sasha Levin +--- + .../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 ++#include ++#include + + /* 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 = ; ++ max-brightness = <255>; ++ label = "d1"; ++ }; ++ ++ led-d2 { ++ pwms = <&pwm0 1 7812500 PWM_POLARITY_INVERTED>; ++ active-low; ++ color = ; ++ max-brightness = <255>; ++ label = "d2"; ++ }; ++ ++ led-d3 { ++ pwms = <&pwm0 2 7812500 PWM_POLARITY_INVERTED>; ++ active-low; ++ color = ; ++ max-brightness = <255>; ++ label = "d3"; ++ }; ++ ++ led-d4 { ++ pwms = <&pwm0 3 7812500 PWM_POLARITY_INVERTED>; ++ active-low; ++ color = ; ++ max-brightness = <255>; ++ label = "d4"; ++ }; ++ }; + }; + + &uart0 { +-- +2.35.1 + diff --git a/queue-5.15/scsi-ibmvfc-avoid-path-failures-during-live-migratio.patch b/queue-5.15/scsi-ibmvfc-avoid-path-failures-during-live-migratio.patch new file mode 100644 index 00000000000..3e1164116aa --- /dev/null +++ b/queue-5.15/scsi-ibmvfc-avoid-path-failures-during-live-migratio.patch @@ -0,0 +1,65 @@ +From 58c7ea94eb5e2489cb6e930a5851bc45dcd442f5 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 26 Oct 2022 13:13:56 -0500 +Subject: scsi: ibmvfc: Avoid path failures during live migration + +From: Brian King + +[ 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 +Link: https://lore.kernel.org/r/20221026181356.148517-1-brking@linux.vnet.ibm.com +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/ibmvscsi/ibmvfc.c | 14 +++++++++++--- + 1 file changed, 11 insertions(+), 3 deletions(-) + +diff --git a/drivers/scsi/ibmvscsi/ibmvfc.c b/drivers/scsi/ibmvscsi/ibmvfc.c +index b3531065a438..45ef78f388dc 100644 +--- a/drivers/scsi/ibmvscsi/ibmvfc.c ++++ b/drivers/scsi/ibmvscsi/ibmvfc.c +@@ -708,8 +708,13 @@ static void ibmvfc_init_host(struct ibmvfc_host *vhost) + memset(vhost->async_crq.msgs.async, 0, PAGE_SIZE); + vhost->async_crq.cur = 0; + +- list_for_each_entry(tgt, &vhost->targets, queue) +- ibmvfc_del_tgt(tgt); ++ list_for_each_entry(tgt, &vhost->targets, queue) { ++ if (vhost->client_migrated) ++ tgt->need_login = 1; ++ else ++ ibmvfc_del_tgt(tgt); ++ } ++ + scsi_block_requests(vhost->host); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_INIT); + vhost->job_step = ibmvfc_npiv_login; +@@ -3235,9 +3240,12 @@ static void ibmvfc_handle_crq(struct ibmvfc_crq *crq, struct ibmvfc_host *vhost, + /* We need to re-setup the interpartition connection */ + dev_info(vhost->dev, "Partition migrated, Re-enabling adapter\n"); + vhost->client_migrated = 1; ++ ++ scsi_block_requests(vhost->host); + ibmvfc_purge_requests(vhost, DID_REQUEUE); +- ibmvfc_link_down(vhost, IBMVFC_LINK_DOWN); ++ ibmvfc_set_host_state(vhost, IBMVFC_LINK_DOWN); + ibmvfc_set_host_action(vhost, IBMVFC_HOST_ACTION_REENABLE); ++ wake_up(&vhost->work_wait_q); + } else if (crq->format == IBMVFC_PARTNER_FAILED || crq->format == IBMVFC_PARTNER_DEREGISTER) { + dev_err(vhost->dev, "Host partner adapter deregistered or failed (rc=%d)\n", crq->format); + ibmvfc_purge_requests(vhost, DID_ERROR); +-- +2.35.1 + diff --git a/queue-5.15/scsi-scsi_debug-make-the-read-capacity-response-comp.patch b/queue-5.15/scsi-scsi_debug-make-the-read-capacity-response-comp.patch new file mode 100644 index 00000000000..2016d50afdb --- /dev/null +++ b/queue-5.15/scsi-scsi_debug-make-the-read-capacity-response-comp.patch @@ -0,0 +1,59 @@ +From 51245182ef450667d67467b394223701ab6d407b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Cc: Damien Le Moal +Suggested-by: Damien Le Moal +Signed-off-by: Bart Van Assche +Link: https://lore.kernel.org/r/20221102193248.3177608-1-bvanassche@acm.org +Reviewed-by: Damien Le Moal +Acked-by: Douglas Gilbert +Signed-off-by: Martin K. Petersen +Signed-off-by: Sasha Levin +--- + drivers/scsi/scsi_debug.c | 7 +++++++ + 1 file changed, 7 insertions(+) + +diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c +index 2b5e249f5d5b..0b16061d8da8 100644 +--- a/drivers/scsi/scsi_debug.c ++++ b/drivers/scsi/scsi_debug.c +@@ -1879,6 +1879,13 @@ static int resp_readcap16(struct scsi_cmnd *scp, + arr[14] |= 0x40; + } + ++ /* ++ * Since the scsi_debug READ CAPACITY implementation always reports the ++ * total disk capacity, set RC BASIS = 1 for host-managed ZBC devices. ++ */ ++ if (devip->zmodel == BLK_ZONED_HM) ++ arr[12] |= 1 << 4; ++ + arr[15] = sdebug_lowest_aligned & 0xff; + + if (have_dif_prot) { +-- +2.35.1 + diff --git a/queue-5.15/sctp-clear-out_curr-if-all-frag-chunks-of-current-ms.patch b/queue-5.15/sctp-clear-out_curr-if-all-frag-chunks-of-current-ms.patch new file mode 100644 index 00000000000..0a7833f493a --- /dev/null +++ b/queue-5.15/sctp-clear-out_curr-if-all-frag-chunks-of-current-ms.patch @@ -0,0 +1,68 @@ +From 5eb95af3372c58f75e49e8fd9b9fd37a24668d58 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Tested-by: Caowangbao +Signed-off-by: Xin Long +Signed-off-by: Jakub Kicinski +Signed-off-by: Sasha Levin +--- + net/sctp/outqueue.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c +index 6fcc4ff97f94..dc29ac0f8d3f 100644 +--- a/net/sctp/outqueue.c ++++ b/net/sctp/outqueue.c +@@ -403,6 +403,11 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc, + sout = SCTP_SO(&asoc->stream, chk->sinfo.sinfo_stream); + sout->ext->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; + ++ /* clear out_curr if all frag chunks are pruned */ ++ if (asoc->stream.out_curr == sout && ++ list_is_last(&chk->frag_list, &chk->msg->chunks)) ++ asoc->stream.out_curr = NULL; ++ + msg_len -= chk->skb->truesize + sizeof(struct sctp_chunk); + sctp_chunk_free(chk); + if (msg_len <= 0) +-- +2.35.1 + diff --git a/queue-5.15/sctp-remove-the-unnecessary-sinfo_stream-check-in-sc.patch b/queue-5.15/sctp-remove-the-unnecessary-sinfo_stream-check-in-sc.patch new file mode 100644 index 00000000000..41c6e12a056 --- /dev/null +++ b/queue-5.15/sctp-remove-the-unnecessary-sinfo_stream-check-in-sc.patch @@ -0,0 +1,53 @@ +From b86214767455865eaa3d679413b86761a295439b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Jakub Kicinski +Stable-dep-of: 2f201ae14ae0 ("sctp: clear out_curr if all frag chunks of current msg are pruned") +Signed-off-by: Sasha Levin +--- + net/sctp/outqueue.c | 8 +++----- + 1 file changed, 3 insertions(+), 5 deletions(-) + +diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c +index b3950963fc8f..6fcc4ff97f94 100644 +--- a/net/sctp/outqueue.c ++++ b/net/sctp/outqueue.c +@@ -384,6 +384,7 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc, + { + struct sctp_outq *q = &asoc->outqueue; + struct sctp_chunk *chk, *temp; ++ struct sctp_stream_out *sout; + + q->sched->unsched_all(&asoc->stream); + +@@ -398,12 +399,9 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc, + sctp_sched_dequeue_common(q, chk); + asoc->sent_cnt_removable--; + asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; +- if (chk->sinfo.sinfo_stream < asoc->stream.outcnt) { +- struct sctp_stream_out *streamout = +- SCTP_SO(&asoc->stream, chk->sinfo.sinfo_stream); + +- streamout->ext->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; +- } ++ sout = SCTP_SO(&asoc->stream, chk->sinfo.sinfo_stream); ++ sout->ext->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++; + + msg_len -= chk->skb->truesize + sizeof(struct sctp_chunk); + sctp_chunk_free(chk); +-- +2.35.1 + diff --git a/queue-5.15/selftests-bpf-add-verifier-test-for-release_referenc.patch b/queue-5.15/selftests-bpf-add-verifier-test-for-release_referenc.patch new file mode 100644 index 00000000000..dabcf5b67f3 --- /dev/null +++ b/queue-5.15/selftests-bpf-add-verifier-test-for-release_referenc.patch @@ -0,0 +1,86 @@ +From 2bd99369a8aa8987fe96655461c0bbceacb3086c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Thu, 3 Nov 2022 17:34:40 +0800 +Subject: selftests/bpf: Add verifier test for release_reference() + +From: Youlin Li + +[ 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 +Signed-off-by: Daniel Borkmann +Link: https://lore.kernel.org/bpf/20221103093440.3161-2-liulin063@gmail.com +Signed-off-by: Sasha Levin +--- + .../selftests/bpf/verifier/ref_tracking.c | 36 +++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/tools/testing/selftests/bpf/verifier/ref_tracking.c b/tools/testing/selftests/bpf/verifier/ref_tracking.c +index 3b6ee009c00b..4a768b130d61 100644 +--- a/tools/testing/selftests/bpf/verifier/ref_tracking.c ++++ b/tools/testing/selftests/bpf/verifier/ref_tracking.c +@@ -905,3 +905,39 @@ + .result_unpriv = REJECT, + .errstr_unpriv = "unknown func", + }, ++{ ++ "reference tracking: try to leak released ptr reg", ++ .insns = { ++ BPF_MOV64_IMM(BPF_REG_0, 0), ++ BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_0, -4), ++ BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), ++ BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), ++ BPF_LD_MAP_FD(BPF_REG_1, 0), ++ BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), ++ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), ++ BPF_EXIT_INSN(), ++ BPF_MOV64_REG(BPF_REG_9, BPF_REG_0), ++ ++ BPF_MOV64_IMM(BPF_REG_0, 0), ++ BPF_LD_MAP_FD(BPF_REG_1, 0), ++ BPF_MOV64_IMM(BPF_REG_2, 8), ++ BPF_MOV64_IMM(BPF_REG_3, 0), ++ BPF_EMIT_CALL(BPF_FUNC_ringbuf_reserve), ++ BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1), ++ BPF_EXIT_INSN(), ++ BPF_MOV64_REG(BPF_REG_8, BPF_REG_0), ++ ++ BPF_MOV64_REG(BPF_REG_1, BPF_REG_8), ++ BPF_MOV64_IMM(BPF_REG_2, 0), ++ BPF_EMIT_CALL(BPF_FUNC_ringbuf_discard), ++ BPF_MOV64_IMM(BPF_REG_0, 0), ++ ++ BPF_STX_MEM(BPF_DW, BPF_REG_9, BPF_REG_8, 0), ++ BPF_EXIT_INSN() ++ }, ++ .fixup_map_array_48b = { 4 }, ++ .fixup_map_ringbuf = { 11 }, ++ .result = ACCEPT, ++ .result_unpriv = REJECT, ++ .errstr_unpriv = "R8 !read_ok" ++}, +-- +2.35.1 + diff --git a/queue-5.15/serial-add-rs485_supported-to-uart_port.patch b/queue-5.15/serial-add-rs485_supported-to-uart_port.patch new file mode 100644 index 00000000000..8945a99bb3b --- /dev/null +++ b/queue-5.15/serial-add-rs485_supported-to-uart_port.patch @@ -0,0 +1,53 @@ +From 5272dc772f299d74ee79c2d44d7d65885e7db48e Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://lore.kernel.org/r/20220606100433.13793-4-ilpo.jarvinen@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 76bad3f88750 ("tty: serial: fsl_lpuart: don't break the on-going transfer when global reset") +Signed-off-by: Sasha Levin +--- + drivers/tty/serial/8250/8250_core.c | 1 + + include/linux/serial_core.h | 1 + + 2 files changed, 2 insertions(+) + +diff --git a/drivers/tty/serial/8250/8250_core.c b/drivers/tty/serial/8250/8250_core.c +index c3348d5af922..f3bfaa1a794b 100644 +--- a/drivers/tty/serial/8250/8250_core.c ++++ b/drivers/tty/serial/8250/8250_core.c +@@ -1016,6 +1016,7 @@ int serial8250_register_8250_port(const struct uart_8250_port *up) + uart->port.throttle = up->port.throttle; + uart->port.unthrottle = up->port.unthrottle; + uart->port.rs485_config = up->port.rs485_config; ++ uart->port.rs485_supported = up->port.rs485_supported; + uart->port.rs485 = up->port.rs485; + uart->rs485_start_tx = up->rs485_start_tx; + uart->rs485_stop_tx = up->rs485_stop_tx; +diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h +index 86fa187f6d65..d5b6b1550d59 100644 +--- a/include/linux/serial_core.h ++++ b/include/linux/serial_core.h +@@ -254,6 +254,7 @@ struct uart_port { + struct attribute_group *attr_group; /* port specific attributes */ + const struct attribute_group **tty_groups; /* all attributes (serial core use only) */ + struct serial_rs485 rs485; ++ const struct serial_rs485 *rs485_supported; /* Supported mask for serial_rs485 */ + struct gpio_desc *rs485_term_gpio; /* enable RS485 bus termination */ + struct serial_iso7816 iso7816; + void *private_data; /* generic platform data pointer */ +-- +2.35.1 + diff --git a/queue-5.15/serial-fsl_lpuart-fill-in-rs485_supported.patch b/queue-5.15/serial-fsl_lpuart-fill-in-rs485_supported.patch new file mode 100644 index 00000000000..58581ec08e6 --- /dev/null +++ b/queue-5.15/serial-fsl_lpuart-fill-in-rs485_supported.patch @@ -0,0 +1,50 @@ +From c12622640ab97a9adeda65431ad1162821f173ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ Upstream commit 07481f448b635d7cebb92d5940f5bea5c4395a26 ] + +Add information on supported serial_rs485 features. + +Signed-off-by: Ilpo Järvinen +Link: https://lore.kernel.org/r/20220606100433.13793-16-ilpo.jarvinen@linux.intel.com +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 76bad3f88750 ("tty: serial: fsl_lpuart: don't break the on-going transfer when global reset") +Signed-off-by: Sasha Levin +--- + drivers/tty/serial/fsl_lpuart.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c +index 44ed4285e1ef..1d13d88ea363 100644 +--- a/drivers/tty/serial/fsl_lpuart.c ++++ b/drivers/tty/serial/fsl_lpuart.c +@@ -2644,6 +2644,11 @@ static struct uart_driver lpuart_reg = { + .cons = LPUART_CONSOLE, + }; + ++static const struct serial_rs485 lpuart_rs485_supported = { ++ .flags = SER_RS485_ENABLED | SER_RS485_RTS_ON_SEND | SER_RS485_RTS_AFTER_SEND, ++ /* delay_rts_* and RX_DURING_TX are not supported */ ++}; ++ + static int lpuart_probe(struct platform_device *pdev) + { + const struct lpuart_soc_data *sdata = of_device_get_match_data(&pdev->dev); +@@ -2683,6 +2688,7 @@ static int lpuart_probe(struct platform_device *pdev) + sport->port.rs485_config = lpuart32_config_rs485; + else + sport->port.rs485_config = lpuart_config_rs485; ++ sport->port.rs485_supported = &lpuart_rs485_supported; + + sport->ipg_clk = devm_clk_get(&pdev->dev, "ipg"); + if (IS_ERR(sport->ipg_clk)) { +-- +2.35.1 + diff --git a/queue-5.15/series b/queue-5.15/series new file mode 100644 index 00000000000..cf245bc364a --- /dev/null +++ b/queue-5.15/series @@ -0,0 +1,54 @@ +asoc-fsl_sai-use-local-device-pointer.patch +asoc-fsl_asrc-fsl_esai-fsl_sai-allow-config_pm-n.patch +serial-add-rs485_supported-to-uart_port.patch +serial-fsl_lpuart-fill-in-rs485_supported.patch +tty-serial-fsl_lpuart-don-t-break-the-on-going-trans.patch +sctp-remove-the-unnecessary-sinfo_stream-check-in-sc.patch +sctp-clear-out_curr-if-all-frag-chunks-of-current-ms.patch +cifs-introduce-new-helper-for-cifs_reconnect.patch +cifs-split-out-dfs-code-from-cifs_reconnect.patch +cifs-support-nested-dfs-links-over-reconnect.patch +cifs-fix-connections-leak-when-tlink-setup-failed.patch +ata-libata-scsi-simplify-__ata_scsi_queuecmd.patch +ata-libata-core-do-not-issue-non-internal-commands-o.patch +drm-display-don-t-assume-dual-mode-adaptors-support-.patch +nvme-add-a-bogus-subsystem-nqn-quirk-for-micron-mtfd.patch +nvme-pci-add-nvme_quirk_bogus_nid-for-micron-nitro.patch +nvme-pci-disable-namespace-identifiers-for-the-maxio.patch +nvme-pci-disable-write-zeroes-on-various-kingston-ss.patch +nvme-pci-add-nvme_quirk_bogus_nid-for-netac-nv7000.patch +speakup-generate-speakupmap.h-automatically.patch +speakup-replace-utils-u_char-with-unsigned-char.patch +iio-ms5611-simplify-io-callback-parameters.patch +iio-pressure-ms5611-fixed-value-compensation-bug.patch +ceph-do-not-update-snapshot-context-when-there-is-no.patch +ceph-avoid-putting-the-realm-twice-when-decoding-sna.patch +x86-sgx-create-utility-to-validate-user-provided-off.patch +x86-sgx-add-overflow-check-in-sgx_validate_offset_le.patch +binder-validate-alloc-mm-in-mmap-handler.patch +ceph-use-kcalloc-for-allocating-multiple-elements.patch +ceph-fix-null-pointer-dereference-for-req-r_session.patch +wifi-mac80211-fix-memory-free-error-when-registering.patch +wifi-mac80211_hwsim-fix-debugfs-attribute-ps-with-rc.patch +riscv-dts-sifive-unleashed-add-pwm-controlled-leds.patch +audit-fix-undefined-behavior-in-bit-shift-for-audit_.patch +wifi-airo-do-not-assign-1-to-unsigned-char.patch +wifi-mac80211-fix-ack-frame-idr-leak-when-mesh-has-n.patch +wifi-ath11k-fix-qcn9074-firmware-boot-on-x86.patch +spi-stm32-fix-stm32_spi_prepare_mbr-that-halves-spi-.patch +selftests-bpf-add-verifier-test-for-release_referenc.patch +revert-net-macsec-report-real_dev-features-when-hw-o.patch +platform-x86-ideapad-laptop-disable-touchpad_switch.patch +platform-x86-touchscreen_dmi-add-info-for-the-rca-ca.patch +platform-x86-intel-pmt-sapphire-rapids-pmt-errata-fi.patch +platform-x86-intel-hid-add-some-acpi-device-ids.patch +scsi-ibmvfc-avoid-path-failures-during-live-migratio.patch +scsi-scsi_debug-make-the-read-capacity-response-comp.patch +drm-panel-orientation-quirks-add-quirk-for-acer-swit.patch +block-bfq-fix-null-pointer-dereference-in-bfq_bio_bf.patch +arm64-syscall-include-asm-ptrace.h-in-syscall_wrappe.patch +nvmet-fix-memory-leak-in-nvmet_subsys_attr_model_sto.patch +revert-drm-amdgpu-revert-drm-amdgpu-getting-fan-spee.patch +alsa-usb-audio-add-quirk-to-fix-hamedal-c20-disconne.patch +risc-v-vdso-do-not-add-missing-symbols-to-version-se.patch +mips-pic32-treat-port-as-signed-integer.patch diff --git a/queue-5.15/speakup-generate-speakupmap.h-automatically.patch b/queue-5.15/speakup-generate-speakupmap.h-automatically.patch new file mode 100644 index 00000000000..002f79200a1 --- /dev/null +++ b/queue-5.15/speakup-generate-speakupmap.h-automatically.patch @@ -0,0 +1,563 @@ +From df1e815b9659494d9dd19bf4b2f22be3e22b99c9 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sun, 12 Jun 2022 19:22:44 +0200 +Subject: speakup: Generate speakupmap.h automatically + +From: Samuel Thibault + +[ 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 +Link: https://lore.kernel.org/r/20220515230358.ikwt2kspiwvv5cf4@begin +Signed-off-by: Greg Kroah-Hartman +Stable-dep-of: 92ca969ff881 ("speakup: replace utils' u_char with unsigned char") +Signed-off-by: Sasha Levin +--- + 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 ++#include ++#include ++#include ++#include ++#include ++#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 ++#include ++#include ++#include ++#include ++#include ++#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 ++ ++#define MAXKEYS 512 ++#define MAXKEYVAL 160 ++#define HASHSIZE 101 ++#define is_shift -3 ++#define is_spk -2 ++#define is_input -1 ++ ++struct st_key { ++ char *name; ++ struct st_key *next; ++ int value, shift; ++}; ++ ++struct st_key key_table[MAXKEYS]; ++struct st_key *extra_keys = key_table+HASHSIZE; ++char *def_name, *def_val; ++FILE *infile; ++int lc; ++ ++char filename[256]; ++ ++static inline void open_input(const char *dir_name, const char *name) ++{ ++ if (dir_name) ++ snprintf(filename, sizeof(filename), "%s/%s", dir_name, name); ++ else ++ snprintf(filename, sizeof(filename), "%s", name); ++ infile = fopen(filename, "r"); ++ if (infile == 0) { ++ fprintf(stderr, "can't open %s\n", filename); ++ exit(1); ++ } ++ lc = 0; ++} ++ ++static inline int oops(const char *msg, const char *info) ++{ ++ if (info == NULL) ++ info = ""; ++ fprintf(stderr, "error: file %s line %d\n", filename, lc); ++ fprintf(stderr, "%s %s\n", msg, info); ++ exit(1); ++} ++ ++static inline struct st_key *hash_name(char *name) ++{ ++ u_char *pn = (u_char *)name; ++ int hash = 0; ++ ++ while (*pn) { ++ hash = (hash * 17) & 0xfffffff; ++ if (isupper(*pn)) ++ *pn = tolower(*pn); ++ hash += (int)*pn; ++ pn++; ++ } ++ hash %= HASHSIZE; ++ return &key_table[hash]; ++} ++ ++static inline struct st_key *find_key(char *name) ++{ ++ struct st_key *this = hash_name(name); ++ ++ while (this) { ++ if (this->name && !strcmp(name, this->name)) ++ return this; ++ this = this->next; ++ } ++ return this; ++} ++ ++static inline struct st_key *add_key(char *name, int value, int shift) ++{ ++ struct st_key *this = hash_name(name); ++ ++ if (extra_keys-key_table >= MAXKEYS) ++ oops("out of key table space, enlarge MAXKEYS", NULL); ++ if (this->name != NULL) { ++ while (this->next) { ++ if (!strcmp(name, this->name)) ++ oops("attempt to add duplicate key", name); ++ this = this->next; ++ } ++ this->next = extra_keys++; ++ this = this->next; ++ } ++ this->name = strdup(name); ++ this->value = value; ++ this->shift = shift; ++ return this; ++} +-- +2.35.1 + diff --git a/queue-5.15/speakup-replace-utils-u_char-with-unsigned-char.patch b/queue-5.15/speakup-replace-utils-u_char-with-unsigned-char.patch new file mode 100644 index 00000000000..ed9647d5948 --- /dev/null +++ b/queue-5.15/speakup-replace-utils-u_char-with-unsigned-char.patch @@ -0,0 +1,46 @@ +From 0a1e1c44176ae95beec745ee2f6b3bcec2862716 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Reviewed-by: Samuel Thibault +Cc: stable +Link: https://lore.kernel.org/r/b75743026aaee2d81efe3d7f2e8fa47f7d0b8ea7.1665736571.git.congdanhqx@gmail.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + drivers/accessibility/speakup/utils.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/accessibility/speakup/utils.h b/drivers/accessibility/speakup/utils.h +index 4bf2ee8ac246..4ce9a12f7664 100644 +--- a/drivers/accessibility/speakup/utils.h ++++ b/drivers/accessibility/speakup/utils.h +@@ -54,7 +54,7 @@ static inline int oops(const char *msg, const char *info) + + static inline struct st_key *hash_name(char *name) + { +- u_char *pn = (u_char *)name; ++ unsigned char *pn = (unsigned char *)name; + int hash = 0; + + while (*pn) { +-- +2.35.1 + diff --git a/queue-5.15/spi-stm32-fix-stm32_spi_prepare_mbr-that-halves-spi-.patch b/queue-5.15/spi-stm32-fix-stm32_spi_prepare_mbr-that-halves-spi-.patch new file mode 100644 index 00000000000..cc04bb0dbc3 --- /dev/null +++ b/queue-5.15/spi-stm32-fix-stm32_spi_prepare_mbr-that-halves-spi-.patch @@ -0,0 +1,51 @@ +From f8e8311c96dcf5d08dd6bb7d54fca81bce837ee2 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://lore.kernel.org/r/20221103080043.3033414-1-sean@geanix.com +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + drivers/spi/spi-stm32.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/spi/spi-stm32.c b/drivers/spi/spi-stm32.c +index 96a73f9e2677..3c6f201b5dd8 100644 +--- a/drivers/spi/spi-stm32.c ++++ b/drivers/spi/spi-stm32.c +@@ -434,7 +434,7 @@ static int stm32_spi_prepare_mbr(struct stm32_spi *spi, u32 speed_hz, + u32 div, mbrdiv; + + /* Ensure spi->clk_rate is even */ +- div = DIV_ROUND_UP(spi->clk_rate & ~0x1, speed_hz); ++ div = DIV_ROUND_CLOSEST(spi->clk_rate & ~0x1, speed_hz); + + /* + * SPI framework set xfer->speed_hz to master->max_speed_hz if +-- +2.35.1 + diff --git a/queue-5.15/tty-serial-fsl_lpuart-don-t-break-the-on-going-trans.patch b/queue-5.15/tty-serial-fsl_lpuart-don-t-break-the-on-going-trans.patch new file mode 100644 index 00000000000..7b94750a7f7 --- /dev/null +++ b/queue-5.15/tty-serial-fsl_lpuart-don-t-break-the-on-going-trans.patch @@ -0,0 +1,137 @@ +From ec677f4d3be029442a04224599c10156f79ebb74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://lore.kernel.org/r/20221024085844.22786-1-sherry.sun@nxp.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Sasha Levin +--- + 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 + #include + #include ++#include + #include + #include + #include +@@ -395,33 +396,6 @@ static unsigned int lpuart_get_baud_clk_rate(struct lpuart_port *sport) + #define lpuart_enable_clks(x) __lpuart_enable_clks(x, true) + #define lpuart_disable_clks(x) __lpuart_enable_clks(x, false) + +-static int lpuart_global_reset(struct lpuart_port *sport) +-{ +- struct uart_port *port = &sport->port; +- void __iomem *global_addr; +- int ret; +- +- if (uart_console(port)) +- return 0; +- +- ret = clk_prepare_enable(sport->ipg_clk); +- if (ret) { +- dev_err(sport->port.dev, "failed to enable uart ipg clk: %d\n", ret); +- return ret; +- } +- +- if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) { +- global_addr = port->membase + UART_GLOBAL - IMX_REG_OFF; +- writel(UART_GLOBAL_RST, global_addr); +- usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US); +- writel(0, global_addr); +- usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US); +- } +- +- clk_disable_unprepare(sport->ipg_clk); +- return 0; +-} +- + static void lpuart_stop_tx(struct uart_port *port) + { + unsigned char temp; +@@ -2649,6 +2623,54 @@ static const struct serial_rs485 lpuart_rs485_supported = { + /* delay_rts_* and RX_DURING_TX are not supported */ + }; + ++static int lpuart_global_reset(struct lpuart_port *sport) ++{ ++ struct uart_port *port = &sport->port; ++ void __iomem *global_addr; ++ unsigned long ctrl, bd; ++ unsigned int val = 0; ++ int ret; ++ ++ ret = clk_prepare_enable(sport->ipg_clk); ++ if (ret) { ++ dev_err(sport->port.dev, "failed to enable uart ipg clk: %d\n", ret); ++ return ret; ++ } ++ ++ if (is_imx7ulp_lpuart(sport) || is_imx8qxp_lpuart(sport)) { ++ /* ++ * If the transmitter is used by earlycon, wait for transmit engine to ++ * complete and then reset. ++ */ ++ ctrl = lpuart32_read(port, UARTCTRL); ++ if (ctrl & UARTCTRL_TE) { ++ bd = lpuart32_read(&sport->port, UARTBAUD); ++ if (read_poll_timeout(lpuart32_tx_empty, val, val, 1, 100000, false, ++ port)) { ++ dev_warn(sport->port.dev, ++ "timeout waiting for transmit engine to complete\n"); ++ clk_disable_unprepare(sport->ipg_clk); ++ return 0; ++ } ++ } ++ ++ global_addr = port->membase + UART_GLOBAL - IMX_REG_OFF; ++ writel(UART_GLOBAL_RST, global_addr); ++ usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US); ++ writel(0, global_addr); ++ usleep_range(GLOBAL_RST_MIN_US, GLOBAL_RST_MAX_US); ++ ++ /* Recover the transmitter for earlycon. */ ++ if (ctrl & UARTCTRL_TE) { ++ lpuart32_write(port, bd, UARTBAUD); ++ lpuart32_write(port, ctrl, UARTCTRL); ++ } ++ } ++ ++ clk_disable_unprepare(sport->ipg_clk); ++ return 0; ++} ++ + static int lpuart_probe(struct platform_device *pdev) + { + const struct lpuart_soc_data *sdata = of_device_get_match_data(&pdev->dev); +-- +2.35.1 + diff --git a/queue-5.15/wifi-airo-do-not-assign-1-to-unsigned-char.patch b/queue-5.15/wifi-airo-do-not-assign-1-to-unsigned-char.patch new file mode 100644 index 00000000000..ea5cc17caf3 --- /dev/null +++ b/queue-5.15/wifi-airo-do-not-assign-1-to-unsigned-char.patch @@ -0,0 +1,75 @@ +From 5f84ab9f8c179fe4f1105af3c07f20177dced941 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 24 Oct 2022 18:28:43 +0200 +Subject: wifi: airo: do not assign -1 to unsigned char + +From: Jason A. Donenfeld + +[ 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 +Cc: linux-wireless@vger.kernel.org +Signed-off-by: Jason A. Donenfeld +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221024162843.535921-1-Jason@zx2c4.com +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/cisco/airo.c | 18 ++++++++++++++---- + 1 file changed, 14 insertions(+), 4 deletions(-) + +diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c +index 65dd8cff1b01..fc19ecbc4c08 100644 +--- a/drivers/net/wireless/cisco/airo.c ++++ b/drivers/net/wireless/cisco/airo.c +@@ -5233,7 +5233,7 @@ static int get_wep_tx_idx(struct airo_info *ai) + return -1; + } + +-static int set_wep_key(struct airo_info *ai, u16 index, const char *key, ++static int set_wep_key(struct airo_info *ai, u16 index, const u8 *key, + u16 keylen, int perm, int lock) + { + static const unsigned char macaddr[ETH_ALEN] = { 0x01, 0, 0, 0, 0, 0 }; +@@ -5284,7 +5284,7 @@ static void proc_wepkey_on_close(struct inode *inode, struct file *file) + struct net_device *dev = PDE_DATA(inode); + struct airo_info *ai = dev->ml_priv; + int i, rc; +- char key[16]; ++ u8 key[16]; + u16 index = 0; + int j = 0; + +@@ -5312,12 +5312,22 @@ static void proc_wepkey_on_close(struct inode *inode, struct file *file) + } + + for (i = 0; i < 16*3 && data->wbuffer[i+j]; i++) { ++ int val; ++ ++ if (i % 3 == 2) ++ continue; ++ ++ val = hex_to_bin(data->wbuffer[i+j]); ++ if (val < 0) { ++ airo_print_err(ai->dev->name, "WebKey passed invalid key hex"); ++ return; ++ } + switch(i%3) { + case 0: +- key[i/3] = hex_to_bin(data->wbuffer[i+j])<<4; ++ key[i/3] = (u8)val << 4; + break; + case 1: +- key[i/3] |= hex_to_bin(data->wbuffer[i+j]); ++ key[i/3] |= (u8)val; + break; + } + } +-- +2.35.1 + diff --git a/queue-5.15/wifi-ath11k-fix-qcn9074-firmware-boot-on-x86.patch b/queue-5.15/wifi-ath11k-fix-qcn9074-firmware-boot-on-x86.patch new file mode 100644 index 00000000000..d9cf19f682b --- /dev/null +++ b/queue-5.15/wifi-ath11k-fix-qcn9074-firmware-boot-on-x86.patch @@ -0,0 +1,50 @@ +From 0288d08d7b1d6441671b53218a76f81ed93a5a0c Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 2 Nov 2022 18:56:39 +0200 +Subject: wifi: ath11k: Fix QCN9074 firmware boot on x86 + +From: Tyler J. Stachecki + +[ 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 +Signed-off-by: Kalle Valo +Link: https://lore.kernel.org/r/20221022042728.43015-1-stachecki.tyler@gmail.com +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/ath/ath11k/qmi.h | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h +index 3d5930330703..25940b683ea4 100644 +--- a/drivers/net/wireless/ath/ath11k/qmi.h ++++ b/drivers/net/wireless/ath/ath11k/qmi.h +@@ -27,7 +27,7 @@ + #define ATH11K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52 + #define ATH11K_QMI_CALDB_SIZE 0x480000 + #define ATH11K_QMI_BDF_EXT_STR_LENGTH 0x20 +-#define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT 3 ++#define ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT 5 + + #define QMI_WLFW_REQUEST_MEM_IND_V01 0x0035 + #define QMI_WLFW_FW_MEM_READY_IND_V01 0x0037 +-- +2.35.1 + diff --git a/queue-5.15/wifi-mac80211-fix-ack-frame-idr-leak-when-mesh-has-n.patch b/queue-5.15/wifi-mac80211-fix-ack-frame-idr-leak-when-mesh-has-n.patch new file mode 100644 index 00000000000..71b06b11387 --- /dev/null +++ b/queue-5.15/wifi-mac80211-fix-ack-frame-idr-leak-when-mesh-has-n.patch @@ -0,0 +1,42 @@ +From f905afbf5f132c40940b4b10324ebe9f2a7d1b2f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Link: https://lore.kernel.org/r/20221027140133.1504-1-nicolas.cavallari@green-communications.fr +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/mesh_pathtbl.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c +index acc1c299f1ae..69d5e1ec6ede 100644 +--- a/net/mac80211/mesh_pathtbl.c ++++ b/net/mac80211/mesh_pathtbl.c +@@ -710,7 +710,7 @@ int mesh_path_send_to_gates(struct mesh_path *mpath) + void mesh_path_discard_frame(struct ieee80211_sub_if_data *sdata, + struct sk_buff *skb) + { +- kfree_skb(skb); ++ ieee80211_free_txskb(&sdata->local->hw, skb); + sdata->u.mesh.mshstats.dropped_frames_no_route++; + } + +-- +2.35.1 + diff --git a/queue-5.15/wifi-mac80211-fix-memory-free-error-when-registering.patch b/queue-5.15/wifi-mac80211-fix-memory-free-error-when-registering.patch new file mode 100644 index 00000000000..42cb988f0e6 --- /dev/null +++ b/queue-5.15/wifi-mac80211-fix-memory-free-error-when-registering.patch @@ -0,0 +1,53 @@ +From 29c20b1ea29dfb6eba4b07192e6e67918435f464 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Sat, 15 Oct 2022 17:38:31 +0800 +Subject: wifi: mac80211: fix memory free error when registering wiphy fail + +From: taozhang + +[ 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 +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + net/mac80211/main.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/net/mac80211/main.c b/net/mac80211/main.c +index 5311c3cd3050..9617ff8e2714 100644 +--- a/net/mac80211/main.c ++++ b/net/mac80211/main.c +@@ -1357,8 +1357,10 @@ int ieee80211_register_hw(struct ieee80211_hw *hw) + ieee80211_led_exit(local); + destroy_workqueue(local->workqueue); + fail_workqueue: +- if (local->wiphy_ciphers_allocated) ++ if (local->wiphy_ciphers_allocated) { + kfree(local->hw.wiphy->cipher_suites); ++ local->wiphy_ciphers_allocated = false; ++ } + kfree(local->int_scan_req); + return result; + } +@@ -1426,8 +1428,10 @@ void ieee80211_free_hw(struct ieee80211_hw *hw) + mutex_destroy(&local->iflist_mtx); + mutex_destroy(&local->mtx); + +- if (local->wiphy_ciphers_allocated) ++ if (local->wiphy_ciphers_allocated) { + kfree(local->hw.wiphy->cipher_suites); ++ local->wiphy_ciphers_allocated = false; ++ } + + idr_for_each(&local->ack_status_frames, + ieee80211_free_ack_frame, NULL); +-- +2.35.1 + diff --git a/queue-5.15/wifi-mac80211_hwsim-fix-debugfs-attribute-ps-with-rc.patch b/queue-5.15/wifi-mac80211_hwsim-fix-debugfs-attribute-ps-with-rc.patch new file mode 100644 index 00000000000..28922ca2d95 --- /dev/null +++ b/queue-5.15/wifi-mac80211_hwsim-fix-debugfs-attribute-ps-with-rc.patch @@ -0,0 +1,57 @@ +From 0fbc0e08ec64160f54639e70525f7507c8826d8b Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 14 Oct 2022 16:54:39 +0200 +Subject: wifi: mac80211_hwsim: fix debugfs attribute ps with rc table support + +From: Jonas Jelonek + +[ 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 +Signed-off-by: Johannes Berg +Signed-off-by: Sasha Levin +--- + drivers/net/wireless/mac80211_hwsim.c | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/drivers/net/wireless/mac80211_hwsim.c b/drivers/net/wireless/mac80211_hwsim.c +index b228567b2a73..c3c3b5aa87b0 100644 +--- a/drivers/net/wireless/mac80211_hwsim.c ++++ b/drivers/net/wireless/mac80211_hwsim.c +@@ -845,6 +845,7 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, + struct hwsim_vif_priv *vp = (void *)vif->drv_priv; + struct sk_buff *skb; + struct ieee80211_hdr *hdr; ++ struct ieee80211_tx_info *cb; + + if (!vp->assoc) + return; +@@ -866,6 +867,10 @@ static void hwsim_send_nullfunc(struct mac80211_hwsim_data *data, u8 *mac, + memcpy(hdr->addr2, mac, ETH_ALEN); + memcpy(hdr->addr3, vp->bssid, ETH_ALEN); + ++ cb = IEEE80211_SKB_CB(skb); ++ cb->control.rates[0].count = 1; ++ cb->control.rates[1].idx = -1; ++ + rcu_read_lock(); + mac80211_hwsim_tx_frame(data->hw, skb, + rcu_dereference(vif->chanctx_conf)->def.chan); +-- +2.35.1 + diff --git a/queue-5.15/x86-sgx-add-overflow-check-in-sgx_validate_offset_le.patch b/queue-5.15/x86-sgx-add-overflow-check-in-sgx_validate_offset_le.patch new file mode 100644 index 00000000000..55c8b1842e3 --- /dev/null +++ b/queue-5.15/x86-sgx-add-overflow-check-in-sgx_validate_offset_le.patch @@ -0,0 +1,44 @@ +From 9042251bf9339a9670adcf93fbbc7007ba499cee Mon Sep 17 00:00:00 2001 +From: Sasha Levin +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 + +[ 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 +Signed-off-by: Borislav Petkov +Reviewed-by: Jarkko Sakkinen +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 +--- + arch/x86/kernel/cpu/sgx/ioctl.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c +index a66795e0b685..217777c029ee 100644 +--- a/arch/x86/kernel/cpu/sgx/ioctl.c ++++ b/arch/x86/kernel/cpu/sgx/ioctl.c +@@ -386,6 +386,9 @@ static int sgx_validate_offset_length(struct sgx_encl *encl, + if (!length || !IS_ALIGNED(length, PAGE_SIZE)) + return -EINVAL; + ++ if (offset + length < offset) ++ return -EINVAL; ++ + if (offset + length - PAGE_SIZE >= encl->size) + return -EINVAL; + +-- +2.35.1 + diff --git a/queue-5.15/x86-sgx-create-utility-to-validate-user-provided-off.patch b/queue-5.15/x86-sgx-create-utility-to-validate-user-provided-off.patch new file mode 100644 index 00000000000..cc4c060d842 --- /dev/null +++ b/queue-5.15/x86-sgx-create-utility-to-validate-user-provided-off.patch @@ -0,0 +1,75 @@ +From 3ec706786be50139df3b038f444f2c751a278cc4 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 10 May 2022 11:08:46 -0700 +Subject: x86/sgx: Create utility to validate user provided offset and length + +From: Reinette Chatre + +[ 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 +Signed-off-by: Dave Hansen +Reviewed-by: Jarkko Sakkinen +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 +--- + 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 +