summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlice Ferrazzi <alicef@gentoo.org>2023-02-22 23:23:27 +0900
committerAlice Ferrazzi <alicef@gentoo.org>2023-02-22 23:23:27 +0900
commitfdccf4e2c07614957cab709f9e796d5667d6c6f3 (patch)
tree44957f4cc1989e4251da3ef998bb985e46541690
parentLinux poatch 5.4.231 (diff)
downloadlinux-patches-fdccf4e2c07614957cab709f9e796d5667d6c6f3.tar.gz
linux-patches-fdccf4e2c07614957cab709f9e796d5667d6c6f3.tar.bz2
linux-patches-fdccf4e2c07614957cab709f9e796d5667d6c6f3.zip
Linux patch 5.4.2325.4-237
Signed-off-by: Alice Ferrazzi <alicef@gentoo.org>
-rw-r--r--0000_README4
-rw-r--r--1231_linux-5.4.232.patch9585
2 files changed, 9589 insertions, 0 deletions
diff --git a/0000_README b/0000_README
index f1efbc1b..19b51d14 100644
--- a/0000_README
+++ b/0000_README
@@ -967,6 +967,10 @@ Patch: 1230_linux-5.4.231.patch
From: http://www.kernel.org
Desc: Linux 5.4.231
+Patch: 1231_linux-5.4.232.patch
+From: http://www.kernel.org
+Desc: Linux 5.4.232
+
Patch: 1500_XATTR_USER_PREFIX.patch
From: https://bugs.gentoo.org/show_bug.cgi?id=470644
Desc: Support for namespace user.pax.* on tmpfs.
diff --git a/1231_linux-5.4.232.patch b/1231_linux-5.4.232.patch
new file mode 100644
index 00000000..a876dddd
--- /dev/null
+++ b/1231_linux-5.4.232.patch
@@ -0,0 +1,9585 @@
+diff --git a/Makefile b/Makefile
+index 3cbbc82e2ddf2..87828284236e3 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,7 +1,7 @@
+ # SPDX-License-Identifier: GPL-2.0
+ VERSION = 5
+ PATCHLEVEL = 4
+-SUBLEVEL = 231
++SUBLEVEL = 232
+ EXTRAVERSION =
+ NAME = Kleptomaniac Octopus
+
+diff --git a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
+index 502c4ac45c29e..8732229f0588c 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-axg.dtsi
+@@ -1705,7 +1705,7 @@
+ sd_emmc_b: sd@5000 {
+ compatible = "amlogic,meson-axg-mmc";
+ reg = <0x0 0x5000 0x0 0x800>;
+- interrupts = <GIC_SPI 217 IRQ_TYPE_EDGE_RISING>;
++ interrupts = <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clkc CLKID_SD_EMMC_B>,
+ <&clkc CLKID_SD_EMMC_B_CLK0>,
+@@ -1717,7 +1717,7 @@
+ sd_emmc_c: mmc@7000 {
+ compatible = "amlogic,meson-axg-mmc";
+ reg = <0x0 0x7000 0x0 0x800>;
+- interrupts = <GIC_SPI 218 IRQ_TYPE_EDGE_RISING>;
++ interrupts = <GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clkc CLKID_SD_EMMC_C>,
+ <&clkc CLKID_SD_EMMC_C_CLK0>,
+diff --git a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
+index d2d255a988a81..6b495587eee2d 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-g12-common.dtsi
+@@ -2317,7 +2317,7 @@
+ sd_emmc_a: sd@ffe03000 {
+ compatible = "amlogic,meson-axg-mmc";
+ reg = <0x0 0xffe03000 0x0 0x800>;
+- interrupts = <GIC_SPI 189 IRQ_TYPE_EDGE_RISING>;
++ interrupts = <GIC_SPI 189 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clkc CLKID_SD_EMMC_A>,
+ <&clkc CLKID_SD_EMMC_A_CLK0>,
+@@ -2329,7 +2329,7 @@
+ sd_emmc_b: sd@ffe05000 {
+ compatible = "amlogic,meson-axg-mmc";
+ reg = <0x0 0xffe05000 0x0 0x800>;
+- interrupts = <GIC_SPI 190 IRQ_TYPE_EDGE_RISING>;
++ interrupts = <GIC_SPI 190 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clkc CLKID_SD_EMMC_B>,
+ <&clkc CLKID_SD_EMMC_B_CLK0>,
+@@ -2341,7 +2341,7 @@
+ sd_emmc_c: mmc@ffe07000 {
+ compatible = "amlogic,meson-axg-mmc";
+ reg = <0x0 0xffe07000 0x0 0x800>;
+- interrupts = <GIC_SPI 191 IRQ_TYPE_EDGE_RISING>;
++ interrupts = <GIC_SPI 191 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ clocks = <&clkc CLKID_SD_EMMC_C>,
+ <&clkc CLKID_SD_EMMC_C_CLK0>,
+diff --git a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+index ad7bc0eec6682..0c667ec15f8cf 100644
+--- a/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
++++ b/arch/arm64/boot/dts/amlogic/meson-gx.dtsi
+@@ -528,21 +528,21 @@
+ sd_emmc_a: mmc@70000 {
+ compatible = "amlogic,meson-gx-mmc", "amlogic,meson-gxbb-mmc";
+ reg = <0x0 0x70000 0x0 0x800>;
+- interrupts = <GIC_SPI 216 IRQ_TYPE_EDGE_RISING>;
++ interrupts = <GIC_SPI 216 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ sd_emmc_b: mmc@72000 {
+ compatible = "amlogic,meson-gx-mmc", "amlogic,meson-gxbb-mmc";
+ reg = <0x0 0x72000 0x0 0x800>;
+- interrupts = <GIC_SPI 217 IRQ_TYPE_EDGE_RISING>;
++ interrupts = <GIC_SPI 217 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ sd_emmc_c: mmc@74000 {
+ compatible = "amlogic,meson-gx-mmc", "amlogic,meson-gxbb-mmc";
+ reg = <0x0 0x74000 0x0 0x800>;
+- interrupts = <GIC_SPI 218 IRQ_TYPE_EDGE_RISING>;
++ interrupts = <GIC_SPI 218 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+ };
+diff --git a/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h b/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h
+index 93b44efdbc527..35a60b0d3a4f0 100644
+--- a/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h
++++ b/arch/arm64/boot/dts/freescale/imx8mm-pinfunc.h
+@@ -585,7 +585,7 @@
+ #define MX8MM_IOMUXC_UART1_RXD_GPIO5_IO22 0x234 0x49C 0x000 0x5 0x0
+ #define MX8MM_IOMUXC_UART1_RXD_TPSMP_HDATA24 0x234 0x49C 0x000 0x7 0x0
+ #define MX8MM_IOMUXC_UART1_TXD_UART1_DCE_TX 0x238 0x4A0 0x000 0x0 0x0
+-#define MX8MM_IOMUXC_UART1_TXD_UART1_DTE_RX 0x238 0x4A0 0x4F4 0x0 0x0
++#define MX8MM_IOMUXC_UART1_TXD_UART1_DTE_RX 0x238 0x4A0 0x4F4 0x0 0x1
+ #define MX8MM_IOMUXC_UART1_TXD_ECSPI3_MOSI 0x238 0x4A0 0x000 0x1 0x0
+ #define MX8MM_IOMUXC_UART1_TXD_GPIO5_IO23 0x238 0x4A0 0x000 0x5 0x0
+ #define MX8MM_IOMUXC_UART1_TXD_TPSMP_HDATA25 0x238 0x4A0 0x000 0x7 0x0
+diff --git a/arch/parisc/kernel/firmware.c b/arch/parisc/kernel/firmware.c
+index 1d976f2ebff0f..2496cb2c55511 100644
+--- a/arch/parisc/kernel/firmware.c
++++ b/arch/parisc/kernel/firmware.c
+@@ -1229,7 +1229,7 @@ static char __attribute__((aligned(64))) iodc_dbuf[4096];
+ */
+ int pdc_iodc_print(const unsigned char *str, unsigned count)
+ {
+- unsigned int i;
++ unsigned int i, found = 0;
+ unsigned long flags;
+
+ for (i = 0; i < count;) {
+@@ -1238,6 +1238,7 @@ int pdc_iodc_print(const unsigned char *str, unsigned count)
+ iodc_dbuf[i+0] = '\r';
+ iodc_dbuf[i+1] = '\n';
+ i += 2;
++ found = 1;
+ goto print;
+ default:
+ iodc_dbuf[i] = str[i];
+@@ -1254,7 +1255,7 @@ print:
+ __pa(iodc_retbuf), 0, __pa(iodc_dbuf), i, 0);
+ spin_unlock_irqrestore(&pdc_lock, flags);
+
+- return i;
++ return i - found;
+ }
+
+ #if !defined(BOOTLOADER)
+diff --git a/arch/parisc/kernel/ptrace.c b/arch/parisc/kernel/ptrace.c
+index 9f6ff7bc06f9a..7407fee7d8182 100644
+--- a/arch/parisc/kernel/ptrace.c
++++ b/arch/parisc/kernel/ptrace.c
+@@ -128,6 +128,12 @@ long arch_ptrace(struct task_struct *child, long request,
+ unsigned long tmp;
+ long ret = -EIO;
+
++ unsigned long user_regs_struct_size = sizeof(struct user_regs_struct);
++#ifdef CONFIG_64BIT
++ if (is_compat_task())
++ user_regs_struct_size /= 2;
++#endif
++
+ switch (request) {
+
+ /* Read the word at location addr in the USER area. For ptraced
+@@ -183,14 +189,14 @@ long arch_ptrace(struct task_struct *child, long request,
+ return copy_regset_to_user(child,
+ task_user_regset_view(current),
+ REGSET_GENERAL,
+- 0, sizeof(struct user_regs_struct),
++ 0, user_regs_struct_size,
+ datap);
+
+ case PTRACE_SETREGS: /* Set all gp regs in the child. */
+ return copy_regset_from_user(child,
+ task_user_regset_view(current),
+ REGSET_GENERAL,
+- 0, sizeof(struct user_regs_struct),
++ 0, user_regs_struct_size,
+ datap);
+
+ case PTRACE_GETFPREGS: /* Get the child FPU state. */
+@@ -304,6 +310,11 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
+ }
+ }
+ break;
++ case PTRACE_GETREGS:
++ case PTRACE_SETREGS:
++ case PTRACE_GETFPREGS:
++ case PTRACE_SETFPREGS:
++ return arch_ptrace(child, request, addr, data);
+
+ default:
+ ret = compat_ptrace_request(child, request, addr, data);
+diff --git a/arch/powerpc/perf/imc-pmu.c b/arch/powerpc/perf/imc-pmu.c
+index b0e4241e7609d..d42f2e091201a 100644
+--- a/arch/powerpc/perf/imc-pmu.c
++++ b/arch/powerpc/perf/imc-pmu.c
+@@ -21,7 +21,7 @@
+ * Used to avoid races in counting the nest-pmu units during hotplug
+ * register and unregister
+ */
+-static DEFINE_SPINLOCK(nest_init_lock);
++static DEFINE_MUTEX(nest_init_lock);
+ static DEFINE_PER_CPU(struct imc_pmu_ref *, local_nest_imc_refc);
+ static struct imc_pmu **per_nest_pmu_arr;
+ static cpumask_t nest_imc_cpumask;
+@@ -1605,7 +1605,7 @@ static void imc_common_mem_free(struct imc_pmu *pmu_ptr)
+ static void imc_common_cpuhp_mem_free(struct imc_pmu *pmu_ptr)
+ {
+ if (pmu_ptr->domain == IMC_DOMAIN_NEST) {
+- spin_lock(&nest_init_lock);
++ mutex_lock(&nest_init_lock);
+ if (nest_pmus == 1) {
+ cpuhp_remove_state(CPUHP_AP_PERF_POWERPC_NEST_IMC_ONLINE);
+ kfree(nest_imc_refc);
+@@ -1615,7 +1615,7 @@ static void imc_common_cpuhp_mem_free(struct imc_pmu *pmu_ptr)
+
+ if (nest_pmus > 0)
+ nest_pmus--;
+- spin_unlock(&nest_init_lock);
++ mutex_unlock(&nest_init_lock);
+ }
+
+ /* Free core_imc memory */
+@@ -1772,11 +1772,11 @@ int init_imc_pmu(struct device_node *parent, struct imc_pmu *pmu_ptr, int pmu_id
+ * rest. To handle the cpuhotplug callback unregister, we track
+ * the number of nest pmus in "nest_pmus".
+ */
+- spin_lock(&nest_init_lock);
++ mutex_lock(&nest_init_lock);
+ if (nest_pmus == 0) {
+ ret = init_nest_pmu_ref();
+ if (ret) {
+- spin_unlock(&nest_init_lock);
++ mutex_unlock(&nest_init_lock);
+ kfree(per_nest_pmu_arr);
+ per_nest_pmu_arr = NULL;
+ goto err_free_mem;
+@@ -1784,7 +1784,7 @@ int init_imc_pmu(struct device_node *parent, struct imc_pmu *pmu_ptr, int pmu_id
+ /* Register for cpu hotplug notification. */
+ ret = nest_pmu_cpumask_init();
+ if (ret) {
+- spin_unlock(&nest_init_lock);
++ mutex_unlock(&nest_init_lock);
+ kfree(nest_imc_refc);
+ kfree(per_nest_pmu_arr);
+ per_nest_pmu_arr = NULL;
+@@ -1792,7 +1792,7 @@ int init_imc_pmu(struct device_node *parent, struct imc_pmu *pmu_ptr, int pmu_id
+ }
+ }
+ nest_pmus++;
+- spin_unlock(&nest_init_lock);
++ mutex_unlock(&nest_init_lock);
+ break;
+ case IMC_DOMAIN_CORE:
+ ret = core_imc_pmu_cpumask_init();
+diff --git a/arch/riscv/Makefile b/arch/riscv/Makefile
+index aed3ba97575bf..1641d82014127 100644
+--- a/arch/riscv/Makefile
++++ b/arch/riscv/Makefile
+@@ -75,6 +75,9 @@ ifeq ($(CONFIG_PERF_EVENTS),y)
+ KBUILD_CFLAGS += -fno-omit-frame-pointer
+ endif
+
++# Avoid generating .eh_frame sections.
++KBUILD_CFLAGS += -fno-asynchronous-unwind-tables -fno-unwind-tables
++
+ KBUILD_CFLAGS_MODULE += $(call cc-option,-mno-relax)
+ KBUILD_AFLAGS_MODULE += $(call as-option,-Wa$(comma)-mno-relax)
+
+diff --git a/arch/riscv/mm/cacheflush.c b/arch/riscv/mm/cacheflush.c
+index c54bd3c799552..9d1ec8c8b7183 100644
+--- a/arch/riscv/mm/cacheflush.c
++++ b/arch/riscv/mm/cacheflush.c
+@@ -71,6 +71,8 @@ void flush_icache_pte(pte_t pte)
+ {
+ struct page *page = pte_page(pte);
+
+- if (!test_and_set_bit(PG_dcache_clean, &page->flags))
++ if (!test_bit(PG_dcache_clean, &page->flags)) {
+ flush_icache_all();
++ set_bit(PG_dcache_clean, &page->flags);
++ }
+ }
+diff --git a/arch/s390/boot/compressed/decompressor.c b/arch/s390/boot/compressed/decompressor.c
+index 45046630c56ac..c42ab33bd4524 100644
+--- a/arch/s390/boot/compressed/decompressor.c
++++ b/arch/s390/boot/compressed/decompressor.c
+@@ -80,6 +80,6 @@ void *decompress_kernel(void)
+ void *output = (void *)decompress_offset;
+
+ __decompress(_compressed_start, _compressed_end - _compressed_start,
+- NULL, NULL, output, 0, NULL, error);
++ NULL, NULL, output, vmlinux.image_size, NULL, error);
+ return output;
+ }
+diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
+index 93727f8092ee4..cf37a61729972 100644
+--- a/arch/x86/kvm/x86.c
++++ b/arch/x86/kvm/x86.c
+@@ -3948,12 +3948,11 @@ static void kvm_vcpu_ioctl_x86_get_debugregs(struct kvm_vcpu *vcpu,
+ {
+ unsigned long val;
+
++ memset(dbgregs, 0, sizeof(*dbgregs));
+ memcpy(dbgregs->db, vcpu->arch.db, sizeof(vcpu->arch.db));
+ kvm_get_dr(vcpu, 6, &val);
+ dbgregs->dr6 = val;
+ dbgregs->dr7 = vcpu->arch.dr7;
+- dbgregs->flags = 0;
+- memset(&dbgregs->reserved, 0, sizeof(dbgregs->reserved));
+ }
+
+ static int kvm_vcpu_ioctl_x86_set_debugregs(struct kvm_vcpu *vcpu,
+diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
+index fbb1676aa33f1..c06f618b1aa3c 100644
+--- a/drivers/ata/libata-core.c
++++ b/drivers/ata/libata-core.c
+@@ -3096,7 +3096,7 @@ int sata_down_spd_limit(struct ata_link *link, u32 spd_limit)
+ */
+ if (spd > 1)
+ mask &= (1 << (spd - 1)) - 1;
+- else
++ else if (link->sata_spd)
+ return -EINVAL;
+
+ /* were we already at the bottom? */
+diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c
+index f8c29b888e6b4..98cbb18f17fa3 100644
+--- a/drivers/bus/sunxi-rsb.c
++++ b/drivers/bus/sunxi-rsb.c
+@@ -781,7 +781,13 @@ static int __init sunxi_rsb_init(void)
+ return ret;
+ }
+
+- return platform_driver_register(&sunxi_rsb_driver);
++ ret = platform_driver_register(&sunxi_rsb_driver);
++ if (ret) {
++ bus_unregister(&sunxi_rsb_bus);
++ return ret;
++ }
++
++ return 0;
+ }
+ module_init(sunxi_rsb_init);
+
+diff --git a/drivers/firewire/core-cdev.c b/drivers/firewire/core-cdev.c
+index 3a43e5d6ed3b2..4fe36d549d615 100644
+--- a/drivers/firewire/core-cdev.c
++++ b/drivers/firewire/core-cdev.c
+@@ -818,8 +818,10 @@ static int ioctl_send_response(struct client *client, union ioctl_arg *arg)
+
+ r = container_of(resource, struct inbound_transaction_resource,
+ resource);
+- if (is_fcp_request(r->request))
++ if (is_fcp_request(r->request)) {
++ kfree(r->data);
+ goto out;
++ }
+
+ if (a->length != fw_get_response_length(r->request)) {
+ ret = -EINVAL;
+diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c
+index eb98018ab420e..ed31b08855f94 100644
+--- a/drivers/firmware/efi/efi.c
++++ b/drivers/firmware/efi/efi.c
+@@ -1022,6 +1022,8 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size)
+ /* first try to find a slot in an existing linked list entry */
+ for (prsv = efi_memreserve_root->next; prsv; ) {
+ rsv = memremap(prsv, sizeof(*rsv), MEMREMAP_WB);
++ if (!rsv)
++ return -ENOMEM;
+ index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size);
+ if (index < rsv->size) {
+ rsv->entry[index].base = addr;
+diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c
+index 5d343dc8e5354..5f577357c3e7e 100644
+--- a/drivers/firmware/efi/memattr.c
++++ b/drivers/firmware/efi/memattr.c
+@@ -32,7 +32,7 @@ int __init efi_memattr_init(void)
+ return -ENOMEM;
+ }
+
+- if (tbl->version > 1) {
++ if (tbl->version > 2) {
+ pr_warn("Unexpected EFI Memory Attributes table version %d\n",
+ tbl->version);
+ goto unmap;
+diff --git a/drivers/fpga/stratix10-soc.c b/drivers/fpga/stratix10-soc.c
+index 559839a960b60..04452520fd811 100644
+--- a/drivers/fpga/stratix10-soc.c
++++ b/drivers/fpga/stratix10-soc.c
+@@ -218,9 +218,9 @@ static int s10_ops_write_init(struct fpga_manager *mgr,
+ /* Allocate buffers from the service layer's pool. */
+ for (i = 0; i < NUM_SVC_BUFS; i++) {
+ kbuf = stratix10_svc_allocate_memory(priv->chan, SVC_BUF_SIZE);
+- if (!kbuf) {
++ if (IS_ERR(kbuf)) {
+ s10_free_buffers(mgr);
+- ret = -ENOMEM;
++ ret = PTR_ERR(kbuf);
+ goto init_done;
+ }
+
+diff --git a/drivers/fsi/fsi-sbefifo.c b/drivers/fsi/fsi-sbefifo.c
+index c8ccc99e214fa..84a60d2d8e8ad 100644
+--- a/drivers/fsi/fsi-sbefifo.c
++++ b/drivers/fsi/fsi-sbefifo.c
+@@ -640,7 +640,7 @@ static void sbefifo_collect_async_ffdc(struct sbefifo *sbefifo)
+ }
+ ffdc_iov.iov_base = ffdc;
+ ffdc_iov.iov_len = SBEFIFO_MAX_FFDC_SIZE;
+- iov_iter_kvec(&ffdc_iter, WRITE, &ffdc_iov, 1, SBEFIFO_MAX_FFDC_SIZE);
++ iov_iter_kvec(&ffdc_iter, READ, &ffdc_iov, 1, SBEFIFO_MAX_FFDC_SIZE);
+ cmd[0] = cpu_to_be32(2);
+ cmd[1] = cpu_to_be32(SBEFIFO_CMD_GET_SBE_FFDC);
+ rc = sbefifo_do_command(sbefifo, cmd, 2, &ffdc_iter);
+@@ -737,7 +737,7 @@ int sbefifo_submit(struct device *dev, const __be32 *command, size_t cmd_len,
+ rbytes = (*resp_len) * sizeof(__be32);
+ resp_iov.iov_base = response;
+ resp_iov.iov_len = rbytes;
+- iov_iter_kvec(&resp_iter, WRITE, &resp_iov, 1, rbytes);
++ iov_iter_kvec(&resp_iter, READ, &resp_iov, 1, rbytes);
+
+ /* Perform the command */
+ mutex_lock(&sbefifo->lock);
+@@ -817,7 +817,7 @@ static ssize_t sbefifo_user_read(struct file *file, char __user *buf,
+ /* Prepare iov iterator */
+ resp_iov.iov_base = buf;
+ resp_iov.iov_len = len;
+- iov_iter_init(&resp_iter, WRITE, &resp_iov, 1, len);
++ iov_iter_init(&resp_iter, READ, &resp_iov, 1, len);
+
+ /* Perform the command */
+ mutex_lock(&sbefifo->lock);
+diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
+index 1107a5e7229e4..ac3ae14a4c079 100644
+--- a/drivers/i2c/busses/i2c-rk3x.c
++++ b/drivers/i2c/busses/i2c-rk3x.c
+@@ -79,7 +79,7 @@ enum {
+ #define DEFAULT_SCL_RATE (100 * 1000) /* Hz */
+
+ /**
+- * struct i2c_spec_values:
++ * struct i2c_spec_values - I2C specification values for various modes
+ * @min_hold_start_ns: min hold time (repeated) START condition
+ * @min_low_ns: min LOW period of the SCL clock
+ * @min_high_ns: min HIGH period of the SCL cloc
+@@ -135,7 +135,7 @@ static const struct i2c_spec_values fast_mode_plus_spec = {
+ };
+
+ /**
+- * struct rk3x_i2c_calced_timings:
++ * struct rk3x_i2c_calced_timings - calculated V1 timings
+ * @div_low: Divider output for low
+ * @div_high: Divider output for high
+ * @tuning: Used to adjust setup/hold data time,
+@@ -158,7 +158,7 @@ enum rk3x_i2c_state {
+ };
+
+ /**
+- * struct rk3x_i2c_soc_data:
++ * struct rk3x_i2c_soc_data - SOC-specific data
+ * @grf_offset: offset inside the grf regmap for setting the i2c type
+ * @calc_timings: Callback function for i2c timing information calculated
+ */
+@@ -238,7 +238,8 @@ static inline void rk3x_i2c_clean_ipd(struct rk3x_i2c *i2c)
+ }
+
+ /**
+- * Generate a START condition, which triggers a REG_INT_START interrupt.
++ * rk3x_i2c_start - Generate a START condition, which triggers a REG_INT_START interrupt.
++ * @i2c: target controller data
+ */
+ static void rk3x_i2c_start(struct rk3x_i2c *i2c)
+ {
+@@ -257,8 +258,8 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c)
+ }
+
+ /**
+- * Generate a STOP condition, which triggers a REG_INT_STOP interrupt.
+- *
++ * rk3x_i2c_stop - Generate a STOP condition, which triggers a REG_INT_STOP interrupt.
++ * @i2c: target controller data
+ * @error: Error code to return in rk3x_i2c_xfer
+ */
+ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
+@@ -297,7 +298,8 @@ static void rk3x_i2c_stop(struct rk3x_i2c *i2c, int error)
+ }
+
+ /**
+- * Setup a read according to i2c->msg
++ * rk3x_i2c_prepare_read - Setup a read according to i2c->msg
++ * @i2c: target controller data
+ */
+ static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c)
+ {
+@@ -328,7 +330,8 @@ static void rk3x_i2c_prepare_read(struct rk3x_i2c *i2c)
+ }
+
+ /**
+- * Fill the transmit buffer with data from i2c->msg
++ * rk3x_i2c_fill_transmit_buf - Fill the transmit buffer with data from i2c->msg
++ * @i2c: target controller data
+ */
+ static void rk3x_i2c_fill_transmit_buf(struct rk3x_i2c *i2c)
+ {
+@@ -531,11 +534,10 @@ out:
+ }
+
+ /**
+- * Get timing values of I2C specification
+- *
++ * rk3x_i2c_get_spec - Get timing values of I2C specification
+ * @speed: Desired SCL frequency
+ *
+- * Returns: Matched i2c spec values.
++ * Return: Matched i2c_spec_values.
+ */
+ static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed)
+ {
+@@ -548,13 +550,12 @@ static const struct i2c_spec_values *rk3x_i2c_get_spec(unsigned int speed)
+ }
+
+ /**
+- * Calculate divider values for desired SCL frequency
+- *
++ * rk3x_i2c_v0_calc_timings - Calculate divider values for desired SCL frequency
+ * @clk_rate: I2C input clock rate
+ * @t: Known I2C timing information
+ * @t_calc: Caculated rk3x private timings that would be written into regs
+ *
+- * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
++ * Return: %0 on success, -%EINVAL if the goal SCL rate is too slow. In that case
+ * a best-effort divider value is returned in divs. If the target rate is
+ * too high, we silently use the highest possible rate.
+ */
+@@ -709,13 +710,12 @@ static int rk3x_i2c_v0_calc_timings(unsigned long clk_rate,
+ }
+
+ /**
+- * Calculate timing values for desired SCL frequency
+- *
++ * rk3x_i2c_v1_calc_timings - Calculate timing values for desired SCL frequency
+ * @clk_rate: I2C input clock rate
+ * @t: Known I2C timing information
+ * @t_calc: Caculated rk3x private timings that would be written into regs
+ *
+- * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case
++ * Return: %0 on success, -%EINVAL if the goal SCL rate is too slow. In that case
+ * a best-effort divider value is returned in divs. If the target rate is
+ * too high, we silently use the highest possible rate.
+ * The following formulas are v1's method to calculate timings.
+@@ -959,14 +959,14 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
+ }
+
+ /**
+- * Setup I2C registers for an I2C operation specified by msgs, num.
+- *
+- * Must be called with i2c->lock held.
+- *
++ * rk3x_i2c_setup - Setup I2C registers for an I2C operation specified by msgs, num.
++ * @i2c: target controller data
+ * @msgs: I2C msgs to process
+ * @num: Number of msgs
+ *
+- * returns: Number of I2C msgs processed or negative in case of error
++ * Must be called with i2c->lock held.
++ *
++ * Return: Number of I2C msgs processed or negative in case of error
+ */
+ static int rk3x_i2c_setup(struct rk3x_i2c *i2c, struct i2c_msg *msgs, int num)
+ {
+diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
+index f908476fa095d..a4e0e2b129bcf 100644
+--- a/drivers/iio/accel/hid-sensor-accel-3d.c
++++ b/drivers/iio/accel/hid-sensor-accel-3d.c
+@@ -279,6 +279,7 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev,
+ hid_sensor_convert_timestamp(
+ &accel_state->common_attributes,
+ *(int64_t *)raw_data);
++ ret = 0;
+ break;
+ default:
+ break;
+diff --git a/drivers/iio/adc/berlin2-adc.c b/drivers/iio/adc/berlin2-adc.c
+index 72d8fa94ab31c..86fdfea329799 100644
+--- a/drivers/iio/adc/berlin2-adc.c
++++ b/drivers/iio/adc/berlin2-adc.c
+@@ -289,8 +289,10 @@ static int berlin2_adc_probe(struct platform_device *pdev)
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
+- if (!indio_dev)
++ if (!indio_dev) {
++ of_node_put(parent_np);
+ return -ENOMEM;
++ }
+
+ priv = iio_priv(indio_dev);
+ platform_set_drvdata(pdev, indio_dev);
+diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
+index c2948defa7853..0c92eb30f0d85 100644
+--- a/drivers/iio/adc/stm32-dfsdm-adc.c
++++ b/drivers/iio/adc/stm32-dfsdm-adc.c
+@@ -1544,6 +1544,7 @@ static const struct of_device_id stm32_dfsdm_adc_match[] = {
+ },
+ {}
+ };
++MODULE_DEVICE_TABLE(of, stm32_dfsdm_adc_match);
+
+ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
+ {
+diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
+index fb77c3ff5a3e4..0a76081747580 100644
+--- a/drivers/iio/adc/twl6030-gpadc.c
++++ b/drivers/iio/adc/twl6030-gpadc.c
+@@ -57,6 +57,18 @@
+ #define TWL6030_GPADCS BIT(1)
+ #define TWL6030_GPADCR BIT(0)
+
++#define USB_VBUS_CTRL_SET 0x04
++#define USB_ID_CTRL_SET 0x06
++
++#define TWL6030_MISC1 0xE4
++#define VBUS_MEAS 0x01
++#define ID_MEAS 0x01
++
++#define VAC_MEAS 0x04
++#define VBAT_MEAS 0x02
++#define BB_MEAS 0x01
++
++
+ /**
+ * struct twl6030_chnl_calib - channel calibration
+ * @gain: slope coefficient for ideal curve
+@@ -927,6 +939,26 @@ static int twl6030_gpadc_probe(struct platform_device *pdev)
+ return ret;
+ }
+
++ ret = twl_i2c_write_u8(TWL_MODULE_USB, VBUS_MEAS, USB_VBUS_CTRL_SET);
++ if (ret < 0) {
++ dev_err(dev, "failed to wire up inputs\n");
++ return ret;
++ }
++
++ ret = twl_i2c_write_u8(TWL_MODULE_USB, ID_MEAS, USB_ID_CTRL_SET);
++ if (ret < 0) {
++ dev_err(dev, "failed to wire up inputs\n");
++ return ret;
++ }
++
++ ret = twl_i2c_write_u8(TWL6030_MODULE_ID0,
++ VBAT_MEAS | BB_MEAS | VAC_MEAS,
++ TWL6030_MISC1);
++ if (ret < 0) {
++ dev_err(dev, "failed to wire up inputs\n");
++ return ret;
++ }
++
+ indio_dev->name = DRIVER_NAME;
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &twl6030_gpadc_iio_info;
+diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c
+index efd977f70f9ea..607e2636a6d16 100644
+--- a/drivers/infiniband/hw/hfi1/file_ops.c
++++ b/drivers/infiniband/hw/hfi1/file_ops.c
+@@ -1363,12 +1363,15 @@ static int user_exp_rcv_setup(struct hfi1_filedata *fd, unsigned long arg,
+ addr = arg + offsetof(struct hfi1_tid_info, tidcnt);
+ if (copy_to_user((void __user *)addr, &tinfo.tidcnt,
+ sizeof(tinfo.tidcnt)))
+- return -EFAULT;
++ ret = -EFAULT;
+
+ addr = arg + offsetof(struct hfi1_tid_info, length);
+- if (copy_to_user((void __user *)addr, &tinfo.length,
++ if (!ret && copy_to_user((void __user *)addr, &tinfo.length,
+ sizeof(tinfo.length)))
+ ret = -EFAULT;
++
++ if (ret)
++ hfi1_user_exp_rcv_invalid(fd, &tinfo);
+ }
+
+ return ret;
+diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c
+index 62e6ffa9ad78e..1d060ae47933e 100644
+--- a/drivers/infiniband/hw/usnic/usnic_uiom.c
++++ b/drivers/infiniband/hw/usnic/usnic_uiom.c
+@@ -281,8 +281,8 @@ iter_chunk:
+ size = pa_end - pa_start + PAGE_SIZE;
+ usnic_dbg("va 0x%lx pa %pa size 0x%zx flags 0x%x",
+ va_start, &pa_start, size, flags);
+- err = iommu_map(pd->domain, va_start, pa_start,
+- size, flags);
++ err = iommu_map_atomic(pd->domain, va_start,
++ pa_start, size, flags);
+ if (err) {
+ usnic_err("Failed to map va 0x%lx pa %pa size 0x%zx with err %d\n",
+ va_start, &pa_start, size, err);
+@@ -298,8 +298,8 @@ iter_chunk:
+ size = pa - pa_start + PAGE_SIZE;
+ usnic_dbg("va 0x%lx pa %pa size 0x%zx flags 0x%x\n",
+ va_start, &pa_start, size, flags);
+- err = iommu_map(pd->domain, va_start, pa_start,
+- size, flags);
++ err = iommu_map_atomic(pd->domain, va_start,
++ pa_start, size, flags);
+ if (err) {
+ usnic_err("Failed to map va 0x%lx pa %pa size 0x%zx with err %d\n",
+ va_start, &pa_start, size, err);
+diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c
+index 69ecf37053a81..3c3cc6af0a1ef 100644
+--- a/drivers/infiniband/ulp/ipoib/ipoib_main.c
++++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c
+@@ -2171,6 +2171,14 @@ int ipoib_intf_init(struct ib_device *hca, u8 port, const char *name,
+ rn->attach_mcast = ipoib_mcast_attach;
+ rn->detach_mcast = ipoib_mcast_detach;
+ rn->hca = hca;
++
++ rc = netif_set_real_num_tx_queues(dev, 1);
++ if (rc)
++ goto out;
++
++ rc = netif_set_real_num_rx_queues(dev, 1);
++ if (rc)
++ goto out;
+ }
+
+ priv->rn_ops = dev->netdev_ops;
+diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
+index 0282c4c55e9da..6b2e88da30766 100644
+--- a/drivers/input/serio/i8042-x86ia64io.h
++++ b/drivers/input/serio/i8042-x86ia64io.h
+@@ -67,25 +67,84 @@ static inline void i8042_write_command(int val)
+
+ #include <linux/dmi.h>
+
+-static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
++#define SERIO_QUIRK_NOKBD BIT(0)
++#define SERIO_QUIRK_NOAUX BIT(1)
++#define SERIO_QUIRK_NOMUX BIT(2)
++#define SERIO_QUIRK_FORCEMUX BIT(3)
++#define SERIO_QUIRK_UNLOCK BIT(4)
++#define SERIO_QUIRK_PROBE_DEFER BIT(5)
++#define SERIO_QUIRK_RESET_ALWAYS BIT(6)
++#define SERIO_QUIRK_RESET_NEVER BIT(7)
++#define SERIO_QUIRK_DIECT BIT(8)
++#define SERIO_QUIRK_DUMBKBD BIT(9)
++#define SERIO_QUIRK_NOLOOP BIT(10)
++#define SERIO_QUIRK_NOTIMEOUT BIT(11)
++#define SERIO_QUIRK_KBDRESET BIT(12)
++#define SERIO_QUIRK_DRITEK BIT(13)
++#define SERIO_QUIRK_NOPNP BIT(14)
++
++/* Quirk table for different mainboards. Options similar or identical to i8042
++ * module parameters.
++ * ORDERING IS IMPORTANT! The first match will be apllied and the rest ignored.
++ * This allows entries to overwrite vendor wide quirks on a per device basis.
++ * Where this is irrelevant, entries are sorted case sensitive by DMI_SYS_VENDOR
++ * and/or DMI_BOARD_VENDOR to make it easier to avoid dublicate entries.
++ */
++static const struct dmi_system_id i8042_dmi_quirk_table[] __initconst = {
+ {
+- /*
+- * Arima-Rioworks HDAMB -
+- * AUX LOOP command does not raise AUX IRQ
+- */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"),
+- DMI_MATCH(DMI_BOARD_NAME, "HDAMB"),
+- DMI_MATCH(DMI_BOARD_VERSION, "Rev E"),
++ DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* ASUS G1S */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
+- DMI_MATCH(DMI_BOARD_NAME, "G1S"),
+- DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
++ },
++ {
++ /* Asus X450LCP */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_NEVER)
++ },
++ {
++ /* ASUS ZenBook UX425UA */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER)
++ },
++ {
++ /* ASUS ZenBook UM325UA */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_PROBE_DEFER | SERIO_QUIRK_RESET_NEVER)
++ },
++ /*
++ * On some Asus laptops, just running self tests cause problems.
++ */
++ {
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
++ DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
++ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_NEVER)
++ },
++ {
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
++ DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */
++ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_NEVER)
+ },
+ {
+ /* ASUS P65UP5 - AUX LOOP command does not raise AUX IRQ */
+@@ -94,585 +153,681 @@ static const struct dmi_system_id __initconst i8042_dmi_noloop_table[] = {
+ DMI_MATCH(DMI_BOARD_NAME, "P/I-P65UP5"),
+ DMI_MATCH(DMI_BOARD_VERSION, "REV 2.X"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
++ /* ASUS G1S */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "X750LN"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer Inc."),
++ DMI_MATCH(DMI_BOARD_NAME, "G1S"),
++ DMI_MATCH(DMI_BOARD_VERSION, "1.0"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+- DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "8500"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
++ /* Acer Aspire 5710 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
+- DMI_MATCH(DMI_PRODUCT_NAME , "ProLiant"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Dell Embedded Box PC 3000 */
++ /* Acer Aspire 7738 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* OQO Model 01 */
++ /* Acer Aspire 5536 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "00"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* ULI EV4873 - AUX LOOP does not work properly */
++ /*
++ * Acer Aspire 5738z
++ * Touchpad stops working in mux mode when dis- + re-enabled
++ * with the touchpad enable/disable toggle hotkey
++ */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ULI"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "5a"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Microsoft Virtual Machine */
++ /* Acer Aspire One 150 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Medion MAM 2070 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "5a"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A114-31"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Medion Akoya E7225 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Medion"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A314-31"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Blue FB5601 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "blue"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "M606"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-31"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Gigabyte M912 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "M912"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "01"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-132"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Gigabyte M1022M netbook */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."),
+- DMI_MATCH(DMI_BOARD_NAME, "M1022E"),
+- DMI_MATCH(DMI_BOARD_VERSION, "1.02"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-332"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Gigabyte Spring Peak - defines wrong chassis type */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-432"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Gigabyte T1005 - defines wrong chassis type ("Other") */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "T1005"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate Spin B118-RN"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
++ /*
++ * Some Wistron based laptops need us to explicitly enable the 'Dritek
++ * keyboard extension' to make their extra keys start generating scancodes.
++ * Originally, this was just confined to older laptops, but a few Acer laptops
++ * have turned up in 2007 that also need this again.
++ */
+ {
+- /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */
++ /* Acer Aspire 5100 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
++ /* Acer Aspire 5610 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
++ /* Acer Aspire 5630 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "C15B"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
++ /* Acer Aspire 5650 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+- { }
+-};
+-
+-/*
+- * Some Fujitsu notebooks are having trouble with touchpads if
+- * active multiplexing mode is activated. Luckily they don't have
+- * external PS/2 ports so we can safely disable it.
+- * ... apparently some Toshibas don't like MUX mode either and
+- * die horrible death on reboot.
+- */
+-static const struct dmi_system_id __initconst i8042_dmi_nomux_table[] = {
+ {
+- /* Fujitsu Lifebook P7010/P7010D */
++ /* Acer Aspire 5680 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "P7010"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
+- /* Fujitsu Lifebook P7010 */
++ /* Acer Aspire 5720 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
+- /* Fujitsu Lifebook P5020D */
++ /* Acer Aspire 9110 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
+- /* Fujitsu Lifebook S2000 */
++ /* Acer TravelMate 660 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
+- /* Fujitsu Lifebook S6230 */
++ /* Acer TravelMate 2490 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
+- /* Fujitsu Lifebook T725 laptop */
++ /* Acer TravelMate 4280 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_DRITEK)
+ },
+ {
+- /* Fujitsu Lifebook U745 */
++ /* Amoi M636/A737 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Fujitsu T70H */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"),
++ DMI_MATCH(DMI_SYS_VENDOR, "ByteSpeed LLC"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "ByteSpeed Laptop C15B"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Fujitsu-Siemens Lifebook T3010 */
++ /* Compal HEL80I */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"),
++ DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Fujitsu-Siemens Lifebook E4010 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "8500"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Fujitsu-Siemens Amilo Pro 2010 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Compaq"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "ProLiant"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "DL760"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Fujitsu-Siemens Amilo Pro 2030 */
++ /* Advent 4211 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
++ DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /*
+- * No data is coming from the touchscreen unless KBC
+- * is in legacy mode.
+- */
+- /* Panasonic CF-29 */
++ /* Dell Embedded Box PC 3000 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Embedded Box PC 3000"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /*
+- * HP Pavilion DV4017EA -
+- * errors on MUX ports are reported without raising AUXDATA
+- * causing "spurious NAK" messages.
+- */
++ /* Dell XPS M1530 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /*
+- * HP Pavilion ZT1000 -
+- * like DV4017EA does not raise AUXERR for errors on MUX ports.
+- */
++ /* Dell Vostro 1510 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /*
+- * HP Pavilion DV4270ca -
+- * like DV4017EA does not raise AUXERR for errors on MUX ports.
+- */
++ /* Dell Vostro V13 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT)
+ },
+ {
++ /* Dell Vostro 1320 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
++ /* Dell Vostro 1520 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
++ /* Dell Vostro 1720 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
++ },
++ {
++ /* Entroware Proteus */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Entroware"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS)
+ },
++ /*
++ * Some Fujitsu notebooks are having trouble with touchpads if
++ * active multiplexing mode is activated. Luckily they don't have
++ * external PS/2 ports so we can safely disable it.
++ * ... apparently some Toshibas don't like MUX mode either and
++ * die horrible death on reboot.
++ */
+ {
++ /* Fujitsu Lifebook P7010/P7010D */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Sentia"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "P7010"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Sharp Actius MM20 */
++ /* Fujitsu Lifebook P5020D */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook P Series"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Sony Vaio FS-115b */
++ /* Fujitsu Lifebook S2000 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S Series"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /*
+- * Sony Vaio FZ-240E -
+- * reset and GET ID commands issued via KBD port are
+- * sometimes being delivered to AUX3.
+- */
++ /* Fujitsu Lifebook S6230 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LifeBook S6230"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /*
+- * Most (all?) VAIOs do not have external PS/2 ports nor
+- * they implement active multiplexing properly, and
+- * MUX discovery usually messes up keyboard/touchpad.
+- */
++ /* Fujitsu Lifebook T725 laptop */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+- DMI_MATCH(DMI_BOARD_NAME, "VAIO"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT)
+ },
+ {
+- /* Amoi M636/A737 */
++ /* Fujitsu Lifebook U745 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U745"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Lenovo 3000 n100 */
++ /* Fujitsu T70H */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "076804U"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "FMVLT70H"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Lenovo XiaoXin Air 12 */
++ /* Fujitsu A544 laptop */
++ /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "80UN"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT)
+ },
+ {
++ /* Fujitsu AH544 laptop */
++ /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1360"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT)
++ },
++ {
++ /* Fujitsu U574 laptop */
++ /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT)
++ },
++ {
++ /* Fujitsu UH554 laptop */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_NOTIMEOUT)
++ },
++ {
++ /* Fujitsu Lifebook P7010 */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "0000000000"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
++ },
++ {
++ /* Fujitsu-Siemens Lifebook T3010 */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T3010"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Acer Aspire 5710 */
++ /* Fujitsu-Siemens Lifebook E4010 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5710"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK E4010"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Acer Aspire 7738 */
++ /* Fujitsu-Siemens Amilo Pro 2010 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 7738"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pro V2010"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Gericom Bellagio */
++ /* Fujitsu-Siemens Amilo Pro 2030 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Gericom"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"),
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO PRO V2030"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* IBM 2656 */
++ /* Gigabyte M912 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "2656"),
++ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "M912"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "01"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Dell XPS M1530 */
++ /* Gigabyte Spring Peak - defines wrong chassis type */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "XPS M1530"),
++ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Spring Peak"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Compal HEL80I */
++ /* Gigabyte T1005 - defines wrong chassis type ("Other") */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "COMPAL"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "HEL80I"),
++ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "T1005"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Dell Vostro 1510 */
++ /* Gigabyte T1005M/P - defines wrong chassis type ("Other") */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Vostro1510"),
++ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "T1005M/P"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
++ /*
++ * Some laptops need keyboard reset before probing for the trackpad to get
++ * it detected, initialised & finally work.
++ */
+ {
+- /* Acer Aspire 5536 */
++ /* Gigabyte P35 v2 - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5536"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
++ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_KBDRESET)
+ },
+- {
+- /* Dell Vostro V13 */
++ {
++ /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
++ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "X3"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_KBDRESET)
+ },
+ {
+- /* Newer HP Pavilion dv4 models */
++ /* Gigabyte P34 - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"),
++ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "P34"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_KBDRESET)
+ },
+ {
+- /* Asus X450LCP */
++ /* Gigabyte P57 - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "X450LCP"),
++ DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "P57"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_KBDRESET)
+ },
+ {
+- /* Avatar AVIU-145A6 */
++ /* Gericom Bellagio */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Gericom"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "N34AS6"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* TUXEDO BU1406 */
++ /* Gigabyte M1022M netbook */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "Gigabyte Technology Co.,Ltd."),
++ DMI_MATCH(DMI_BOARD_NAME, "M1022E"),
++ DMI_MATCH(DMI_BOARD_VERSION, "1.02"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Lenovo LaVie Z */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv9700"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "Rev 1"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+ /*
+- * Acer Aspire 5738z
+- * Touchpad stops working in mux mode when dis- + re-enabled
+- * with the touchpad enable/disable toggle hotkey
++ * HP Pavilion DV4017EA -
++ * errors on MUX ports are reported without raising AUXDATA
++ * causing "spurious NAK" messages.
+ */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5738"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EA032EA#ABF)"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Entroware Proteus */
++ /*
++ * HP Pavilion ZT1000 -
++ * like DV4017EA does not raise AUXERR for errors on MUX ports.
++ */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Entroware"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion Notebook PC"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "HP Pavilion Notebook ZT1000"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+- { }
+-};
+-
+-static const struct dmi_system_id i8042_dmi_forcemux_table[] __initconst = {
+ {
+ /*
+- * Sony Vaio VGN-CS series require MUX or the touch sensor
+- * buttons will disturb touchpad operation
++ * HP Pavilion DV4270ca -
++ * like DV4017EA does not raise AUXERR for errors on MUX ports.
+ */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Pavilion dv4000 (EH476UA#ABL)"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+- { }
+-};
+-
+-/*
+- * On some Asus laptops, just running self tests cause problems.
+- */
+-static const struct dmi_system_id i8042_dmi_noselftest_table[] = {
+ {
++ /* Newer HP Pavilion dv4 models */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+- DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
+- },
+- }, {
+- .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+- DMI_MATCH(DMI_CHASSIS_TYPE, "31"), /* Convertible Notebook */
++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_NOTIMEOUT)
+ },
+- { }
+-};
+-static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
+ {
+- /* MSI Wind U-100 */
++ /* IBM 2656 */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_NAME, "U-100"),
+- DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
++ DMI_MATCH(DMI_SYS_VENDOR, "IBM"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "2656"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* LG Electronics X110 */
++ /* Avatar AVIU-145A6 */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_NAME, "X110"),
+- DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."),
++ DMI_MATCH(DMI_SYS_VENDOR, "Intel"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "IC4I"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Acer Aspire One 150 */
++ /* Intel MBO Desktop D845PESV */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "AOA150"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
++ DMI_MATCH(DMI_BOARD_NAME, "D845PESV"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOPNP)
+ },
+ {
++ /*
++ * Intel NUC D54250WYK - does not have i8042 controller but
++ * declares PS/2 devices in DSDT.
++ */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A114-31"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
++ DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOPNP)
+ },
+ {
++ /* Lenovo 3000 n100 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A314-31"),
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "076804U"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
++ /* Lenovo XiaoXin Air 12 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire A315-31"),
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "80UN"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
++ /* Lenovo LaVie Z */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-132"),
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo LaVie Z"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
++ /* Lenovo Ideapad U455 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-332"),
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "20046"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
++ /* Lenovo ThinkPad L460 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire ES1-432"),
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
++ /* Lenovo ThinkPad Twist S230u */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate Spin B118-RN"),
++ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Advent 4211 */
++ /* LG Electronics X110 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "DIXONSXP"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Advent 4211"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "LG Electronics Inc."),
++ DMI_MATCH(DMI_BOARD_NAME, "X110"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+ /* Medion Akoya Mini E1210 */
+@@ -680,6 +835,7 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E1210"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+ /* Medion Akoya E1222 */
+@@ -687,48 +843,62 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDION"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "E122X"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
+ {
+- /* Mivvy M310 */
++ /* MSI Wind U-100 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "N10"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
++ DMI_MATCH(DMI_BOARD_NAME, "U-100"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Dell Vostro 1320 */
++ /*
++ * No data is coming from the touchscreen unless KBC
++ * is in legacy mode.
++ */
++ /* Panasonic CF-29 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1320"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Matsushita"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "CF-29"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Dell Vostro 1520 */
++ /* Medion Akoya E7225 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1520"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Medion"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Akoya E7225"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "1.0"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Dell Vostro 1720 */
++ /* Microsoft Virtual Machine */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Vostro 1720"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Virtual Machine"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "VS2005R2"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Lenovo Ideapad U455 */
++ /* Medion MAM 2070 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "20046"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "MAM 2070"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "5a"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Lenovo ThinkPad L460 */
++ /* TUXEDO BU1406 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad L460"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "N24_25BU"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+ /* Clevo P650RS, 650RP6, Sager NP8152-S, and others */
+@@ -736,282 +906,318 @@ static const struct dmi_system_id __initconst i8042_dmi_reset_table[] = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Notebook"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "P65xRP"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
++ },
++ {
++ /* OQO Model 01 */
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "OQO"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "ZEPTO"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "00"),
++ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Lenovo ThinkPad Twist S230u */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "33474HU"),
++ DMI_MATCH(DMI_SYS_VENDOR, "PEGATRON CORPORATION"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "C15B"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* Entroware Proteus */
++ /* Acer Aspire 5 A515 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Entroware"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Proteus"),
+- DMI_MATCH(DMI_PRODUCT_VERSION, "EL07R4"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "PK"),
++ DMI_MATCH(DMI_BOARD_NAME, "Grumpy_PK"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOPNP)
+ },
+- { }
+-};
+-
+-#ifdef CONFIG_PNP
+-static const struct dmi_system_id __initconst i8042_dmi_nopnp_table[] = {
+ {
+- /* Intel MBO Desktop D845PESV */
++ /* ULI EV4873 - AUX LOOP does not work properly */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_NAME, "D845PESV"),
+- DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
++ DMI_MATCH(DMI_SYS_VENDOR, "ULI"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "EV4873"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "5a"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+ /*
+- * Intel NUC D54250WYK - does not have i8042 controller but
+- * declares PS/2 devices in DSDT.
++ * Arima-Rioworks HDAMB -
++ * AUX LOOP command does not raise AUX IRQ
+ */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_NAME, "D54250WYK"),
+- DMI_MATCH(DMI_BOARD_VENDOR, "Intel Corporation"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "RIOWORKS"),
++ DMI_MATCH(DMI_BOARD_NAME, "HDAMB"),
++ DMI_MATCH(DMI_BOARD_VERSION, "Rev E"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+ {
+- /* MSI Wind U-100 */
++ /* Sharp Actius MM20 */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_NAME, "U-100"),
+- DMI_MATCH(DMI_BOARD_VENDOR, "MICRO-STAR INTERNATIONAL CO., LTD"),
++ DMI_MATCH(DMI_SYS_VENDOR, "SHARP"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "PC-MM20 Series"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Acer Aspire 5 A515 */
++ /*
++ * Sony Vaio FZ-240E -
++ * reset and GET ID commands issued via KBD port are
++ * sometimes being delivered to AUX3.
++ */
+ .matches = {
+- DMI_MATCH(DMI_BOARD_NAME, "Grumpy_PK"),
+- DMI_MATCH(DMI_BOARD_VENDOR, "PK"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FZ240E"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+- { }
+-};
+-
+-static const struct dmi_system_id __initconst i8042_dmi_laptop_table[] = {
+ {
++ /*
++ * Most (all?) VAIOs do not have external PS/2 ports nor
++ * they implement active multiplexing properly, and
++ * MUX discovery usually messes up keyboard/touchpad.
++ */
+ .matches = {
+- DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
++ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
++ DMI_MATCH(DMI_BOARD_NAME, "VAIO"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
++ /* Sony Vaio FS-115b */
+ .matches = {
+- DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
++ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
++ /*
++ * Sony Vaio VGN-CS series require MUX or the touch sensor
++ * buttons will disturb touchpad operation
++ */
+ .matches = {
+- DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
++ DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "VGN-CS"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_FORCEMUX)
+ },
+ {
+ .matches = {
+- DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
++ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Satellite P10"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+- { }
+-};
+-#endif
+-
+-static const struct dmi_system_id __initconst i8042_dmi_notimeout_table[] = {
+ {
+- /* Dell Vostro V13 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Vostro V13"),
++ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
+ {
+- /* Newer HP Pavilion dv4 models */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion dv4 Notebook PC"),
++ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE C850D"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX)
+ },
++ /*
++ * A lot of modern Clevo barebones have touchpad and/or keyboard issues
++ * after suspend fixable with nomux + reset + noloop + nopnp. Luckily,
++ * none of them have an external PS/2 port so this can safely be set for
++ * all of them. These two are based on a Clevo design, but have the
++ * board_name changed.
++ */
+ {
+- /* Fujitsu A544 laptop */
+- /* https://bugzilla.redhat.com/show_bug.cgi?id=1111138 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK A544"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "TUXEDO"),
++ DMI_MATCH(DMI_BOARD_NAME, "AURA1501"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Fujitsu AH544 laptop */
+- /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK AH544"),
++ DMI_MATCH(DMI_BOARD_VENDOR, "TUXEDO"),
++ DMI_MATCH(DMI_BOARD_NAME, "EDUBOOK1502"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Fujitsu Lifebook T725 laptop */
++ /* Mivvy M310 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK T725"),
++ DMI_MATCH(DMI_SYS_VENDOR, "VIOOO"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "N10"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_RESET_ALWAYS)
+ },
++ /*
++ * Some laptops need keyboard reset before probing for the trackpad to get
++ * it detected, initialised & finally work.
++ */
+ {
+- /* Fujitsu U574 laptop */
+- /* https://bugzilla.kernel.org/show_bug.cgi?id=69731 */
++ /* Schenker XMG C504 - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK U574"),
++ DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "C504"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_KBDRESET)
+ },
+ {
+- /* Fujitsu UH554 laptop */
++ /* Blue FB5601 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK UH544"),
++ DMI_MATCH(DMI_SYS_VENDOR, "blue"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "FB5601"),
++ DMI_MATCH(DMI_PRODUCT_VERSION, "M606"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOLOOP)
+ },
+- { }
+-};
+-
+-/*
+- * Some Wistron based laptops need us to explicitly enable the 'Dritek
+- * keyboard extension' to make their extra keys start generating scancodes.
+- * Originally, this was just confined to older laptops, but a few Acer laptops
+- * have turned up in 2007 that also need this again.
+- */
+-static const struct dmi_system_id __initconst i8042_dmi_dritek_table[] = {
++ /*
++ * A lot of modern Clevo barebones have touchpad and/or keyboard issues
++ * after suspend fixable with nomux + reset + noloop + nopnp. Luckily,
++ * none of them have an external PS/2 port so this can safely be set for
++ * all of them.
++ * Clevo barebones come with board_vendor and/or system_vendor set to
++ * either the very generic string "Notebook" and/or a different value
++ * for each individual reseller. The only somewhat universal way to
++ * identify them is by board_name.
++ */
+ {
+- /* Acer Aspire 5100 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5100"),
++ DMI_MATCH(DMI_BOARD_NAME, "LAPQC71A"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Acer Aspire 5610 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5610"),
++ DMI_MATCH(DMI_BOARD_NAME, "LAPQC71B"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Acer Aspire 5630 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5630"),
++ DMI_MATCH(DMI_BOARD_NAME, "N140CU"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Acer Aspire 5650 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5650"),
++ DMI_MATCH(DMI_BOARD_NAME, "N141CU"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Acer Aspire 5680 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5680"),
++ DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Acer Aspire 5720 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 5720"),
++ DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
++ /*
++ * At least one modern Clevo barebone has the touchpad connected both
++ * via PS/2 and i2c interface. This causes a race condition between the
++ * psmouse and i2c-hid driver. Since the full capability of the touchpad
++ * is available via the i2c interface and the device has no external
++ * PS/2 port, it is safe to just ignore all ps2 mouses here to avoid
++ * this issue. The known affected device is the
++ * TUXEDO InfinityBook S17 Gen6 / Clevo NS70MU which comes with one of
++ * the two different dmi strings below. NS50MU is not a typo!
++ */
+ {
+- /* Acer Aspire 9110 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 9110"),
++ DMI_MATCH(DMI_BOARD_NAME, "NS50MU"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOAUX | SERIO_QUIRK_NOMUX |
++ SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP |
++ SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Acer TravelMate 660 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 660"),
++ DMI_MATCH(DMI_BOARD_NAME, "NS50_70MU"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOAUX | SERIO_QUIRK_NOMUX |
++ SERIO_QUIRK_RESET_ALWAYS | SERIO_QUIRK_NOLOOP |
++ SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Acer TravelMate 2490 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2490"),
++ DMI_MATCH(DMI_BOARD_NAME, "NJ50_70CU"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Acer TravelMate 4280 */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 4280"),
++ DMI_MATCH(DMI_BOARD_NAME, "PB50_70DFx,DDx"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+- { }
+-};
+-
+-/*
+- * Some laptops need keyboard reset before probing for the trackpad to get
+- * it detected, initialised & finally work.
+- */
+-static const struct dmi_system_id __initconst i8042_dmi_kbdreset_table[] = {
+ {
+- /* Gigabyte P35 v2 - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "P35V2"),
++ DMI_MATCH(DMI_BOARD_NAME, "PCX0DX"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+- {
+- /* Aorus branded Gigabyte X3 Plus - Elantech touchpad */
++ {
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "X3"),
++ DMI_MATCH(DMI_BOARD_NAME, "X170SM"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
+ {
+- /* Gigabyte P34 - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "P34"),
++ DMI_MATCH(DMI_BOARD_NAME, "X170KM-G"),
+ },
++ .driver_data = (void *)(SERIO_QUIRK_NOMUX | SERIO_QUIRK_RESET_ALWAYS |
++ SERIO_QUIRK_NOLOOP | SERIO_QUIRK_NOPNP)
+ },
++ { }
++};
++
++#ifdef CONFIG_PNP
++static const struct dmi_system_id i8042_dmi_laptop_table[] __initconst = {
+ {
+- /* Gigabyte P57 - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "GIGABYTE"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "P57"),
++ DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
+ },
+ },
+ {
+- /* Schenker XMG C504 - Elantech touchpad */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "XMG"),
+- DMI_MATCH(DMI_PRODUCT_NAME, "C504"),
++ DMI_MATCH(DMI_CHASSIS_TYPE, "9"), /* Laptop */
+ },
+ },
+- { }
+-};
+-
+-static const struct dmi_system_id i8042_dmi_probe_defer_table[] __initconst = {
+ {
+- /* ASUS ZenBook UX425UA */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA"),
++ DMI_MATCH(DMI_CHASSIS_TYPE, "10"), /* Notebook */
+ },
+ },
+ {
+- /* ASUS ZenBook UM325UA */
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+- DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
++ DMI_MATCH(DMI_CHASSIS_TYPE, "14"), /* Sub-Notebook */
+ },
+ },
+ { }
+ };
++#endif
+
+ #endif /* CONFIG_X86 */
+
+@@ -1166,11 +1372,6 @@ static int __init i8042_pnp_init(void)
+ bool pnp_data_busted = false;
+ int err;
+
+-#ifdef CONFIG_X86
+- if (dmi_check_system(i8042_dmi_nopnp_table))
+- i8042_nopnp = true;
+-#endif
+-
+ if (i8042_nopnp) {
+ pr_info("PNP detection disabled\n");
+ return 0;
+@@ -1274,6 +1475,59 @@ static inline int i8042_pnp_init(void) { return 0; }
+ static inline void i8042_pnp_exit(void) { }
+ #endif /* CONFIG_PNP */
+
++
++#ifdef CONFIG_X86
++static void __init i8042_check_quirks(void)
++{
++ const struct dmi_system_id *device_quirk_info;
++ uintptr_t quirks;
++
++ device_quirk_info = dmi_first_match(i8042_dmi_quirk_table);
++ if (!device_quirk_info)
++ return;
++
++ quirks = (uintptr_t)device_quirk_info->driver_data;
++
++ if (quirks & SERIO_QUIRK_NOKBD)
++ i8042_nokbd = true;
++ if (quirks & SERIO_QUIRK_NOAUX)
++ i8042_noaux = true;
++ if (quirks & SERIO_QUIRK_NOMUX)
++ i8042_nomux = true;
++ if (quirks & SERIO_QUIRK_FORCEMUX)
++ i8042_nomux = false;
++ if (quirks & SERIO_QUIRK_UNLOCK)
++ i8042_unlock = true;
++ if (quirks & SERIO_QUIRK_PROBE_DEFER)
++ i8042_probe_defer = true;
++ /* Honor module parameter when value is not default */
++ if (i8042_reset == I8042_RESET_DEFAULT) {
++ if (quirks & SERIO_QUIRK_RESET_ALWAYS)
++ i8042_reset = I8042_RESET_ALWAYS;
++ if (quirks & SERIO_QUIRK_RESET_NEVER)
++ i8042_reset = I8042_RESET_NEVER;
++ }
++ if (quirks & SERIO_QUIRK_DIECT)
++ i8042_direct = true;
++ if (quirks & SERIO_QUIRK_DUMBKBD)
++ i8042_dumbkbd = true;
++ if (quirks & SERIO_QUIRK_NOLOOP)
++ i8042_noloop = true;
++ if (quirks & SERIO_QUIRK_NOTIMEOUT)
++ i8042_notimeout = true;
++ if (quirks & SERIO_QUIRK_KBDRESET)
++ i8042_kbdreset = true;
++ if (quirks & SERIO_QUIRK_DRITEK)
++ i8042_dritek = true;
++#ifdef CONFIG_PNP
++ if (quirks & SERIO_QUIRK_NOPNP)
++ i8042_nopnp = true;
++#endif
++}
++#else
++static inline void i8042_check_quirks(void) {}
++#endif
++
+ static int __init i8042_platform_init(void)
+ {
+ int retval;
+@@ -1296,45 +1550,17 @@ static int __init i8042_platform_init(void)
+ i8042_kbd_irq = I8042_MAP_IRQ(1);
+ i8042_aux_irq = I8042_MAP_IRQ(12);
+
+- retval = i8042_pnp_init();
+- if (retval)
+- return retval;
+-
+ #if defined(__ia64__)
+- i8042_reset = I8042_RESET_ALWAYS;
++ i8042_reset = I8042_RESET_ALWAYS;
+ #endif
+
+-#ifdef CONFIG_X86
+- /* Honor module parameter when value is not default */
+- if (i8042_reset == I8042_RESET_DEFAULT) {
+- if (dmi_check_system(i8042_dmi_reset_table))
+- i8042_reset = I8042_RESET_ALWAYS;
+-
+- if (dmi_check_system(i8042_dmi_noselftest_table))
+- i8042_reset = I8042_RESET_NEVER;
+- }
+-
+- if (dmi_check_system(i8042_dmi_noloop_table))
+- i8042_noloop = true;
+-
+- if (dmi_check_system(i8042_dmi_nomux_table))
+- i8042_nomux = true;
+-
+- if (dmi_check_system(i8042_dmi_forcemux_table))
+- i8042_nomux = false;
+-
+- if (dmi_check_system(i8042_dmi_notimeout_table))
+- i8042_notimeout = true;
+-
+- if (dmi_check_system(i8042_dmi_dritek_table))
+- i8042_dritek = true;
+-
+- if (dmi_check_system(i8042_dmi_kbdreset_table))
+- i8042_kbdreset = true;
++ i8042_check_quirks();
+
+- if (dmi_check_system(i8042_dmi_probe_defer_table))
+- i8042_probe_defer = true;
++ retval = i8042_pnp_init();
++ if (retval)
++ return retval;
+
++#ifdef CONFIG_X86
+ /*
+ * A20 was already enabled during early kernel init. But some buggy
+ * BIOSes (in MSI Laptops) require A20 to be enabled using 8042 to
+diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
+index c392930253a30..b79309928d786 100644
+--- a/drivers/iommu/amd_iommu.c
++++ b/drivers/iommu/amd_iommu.c
+@@ -3098,7 +3098,8 @@ static int amd_iommu_attach_device(struct iommu_domain *dom,
+ }
+
+ static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
+- phys_addr_t paddr, size_t page_size, int iommu_prot)
++ phys_addr_t paddr, size_t page_size, int iommu_prot,
++ gfp_t gfp)
+ {
+ struct protection_domain *domain = to_pdomain(dom);
+ int prot = 0;
+@@ -3113,7 +3114,7 @@ static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
+ prot |= IOMMU_PROT_IW;
+
+ mutex_lock(&domain->api_lock);
+- ret = iommu_map_page(domain, iova, paddr, page_size, prot, GFP_KERNEL);
++ ret = iommu_map_page(domain, iova, paddr, page_size, prot, gfp);
+ mutex_unlock(&domain->api_lock);
+
+ domain_flush_np_cache(domain, iova, page_size);
+diff --git a/drivers/iommu/arm-smmu-v3.c b/drivers/iommu/arm-smmu-v3.c
+index 02c2fb551f381..4f64c3a9ee88d 100644
+--- a/drivers/iommu/arm-smmu-v3.c
++++ b/drivers/iommu/arm-smmu-v3.c
+@@ -2451,7 +2451,7 @@ out_unlock:
+ }
+
+ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
+
+diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
+index 2185ea5191c15..775acae84d7d2 100644
+--- a/drivers/iommu/arm-smmu.c
++++ b/drivers/iommu/arm-smmu.c
+@@ -1160,7 +1160,7 @@ rpm_put:
+ }
+
+ static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ struct io_pgtable_ops *ops = to_smmu_domain(domain)->pgtbl_ops;
+ struct arm_smmu_device *smmu = to_smmu_domain(domain)->smmu;
+diff --git a/drivers/iommu/dma-iommu.c b/drivers/iommu/dma-iommu.c
+index 9c3e630c6c4c8..4fc8fb92d45ef 100644
+--- a/drivers/iommu/dma-iommu.c
++++ b/drivers/iommu/dma-iommu.c
+@@ -477,7 +477,7 @@ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
+ if (!iova)
+ return DMA_MAPPING_ERROR;
+
+- if (iommu_map(domain, iova, phys - iova_off, size, prot)) {
++ if (iommu_map_atomic(domain, iova, phys - iova_off, size, prot)) {
+ iommu_dma_free_iova(cookie, iova, size);
+ return DMA_MAPPING_ERROR;
+ }
+@@ -612,7 +612,7 @@ static void *iommu_dma_alloc_remap(struct device *dev, size_t size,
+ arch_dma_prep_coherent(sg_page(sg), sg->length);
+ }
+
+- if (iommu_map_sg(domain, iova, sgt.sgl, sgt.orig_nents, ioprot)
++ if (iommu_map_sg_atomic(domain, iova, sgt.sgl, sgt.orig_nents, ioprot)
+ < size)
+ goto out_free_sg;
+
+@@ -872,7 +872,7 @@ static int iommu_dma_map_sg(struct device *dev, struct scatterlist *sg,
+ * We'll leave any physical concatenation to the IOMMU driver's
+ * implementation - it knows better than we do.
+ */
+- if (iommu_map_sg(domain, iova, sg, nents, prot) < iova_len)
++ if (iommu_map_sg_atomic(domain, iova, sg, nents, prot) < iova_len)
+ goto out_free_iova;
+
+ return __finalise_sg(dev, sg, nents, iova);
+diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
+index 31a9b9885653f..acf21e4237949 100644
+--- a/drivers/iommu/exynos-iommu.c
++++ b/drivers/iommu/exynos-iommu.c
+@@ -1077,7 +1077,7 @@ static int lv2set_page(sysmmu_pte_t *pent, phys_addr_t paddr, size_t size,
+ */
+ static int exynos_iommu_map(struct iommu_domain *iommu_domain,
+ unsigned long l_iova, phys_addr_t paddr, size_t size,
+- int prot)
++ int prot, gfp_t gfp)
+ {
+ struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
+ sysmmu_pte_t *entry;
+diff --git a/drivers/iommu/intel-iommu.c b/drivers/iommu/intel-iommu.c
+index ff120d7ed3424..2a53fa7d2b47d 100644
+--- a/drivers/iommu/intel-iommu.c
++++ b/drivers/iommu/intel-iommu.c
+@@ -5458,7 +5458,7 @@ static void intel_iommu_aux_detach_device(struct iommu_domain *domain,
+
+ static int intel_iommu_map(struct iommu_domain *domain,
+ unsigned long iova, phys_addr_t hpa,
+- size_t size, int iommu_prot)
++ size_t size, int iommu_prot, gfp_t gfp)
+ {
+ struct dmar_domain *dmar_domain = to_dmar_domain(domain);
+ u64 max_addr;
+diff --git a/drivers/iommu/iommu.c b/drivers/iommu/iommu.c
+index c5758fb696cc8..ef8f0d1ac5ab1 100644
+--- a/drivers/iommu/iommu.c
++++ b/drivers/iommu/iommu.c
+@@ -1858,8 +1858,8 @@ static size_t iommu_pgsize(struct iommu_domain *domain,
+ return pgsize;
+ }
+
+-int iommu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++int __iommu_map(struct iommu_domain *domain, unsigned long iova,
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ const struct iommu_ops *ops = domain->ops;
+ unsigned long orig_iova = iova;
+@@ -1896,8 +1896,8 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
+
+ pr_debug("mapping: iova 0x%lx pa %pa pgsize 0x%zx\n",
+ iova, &paddr, pgsize);
++ ret = ops->map(domain, iova, paddr, pgsize, prot, gfp);
+
+- ret = ops->map(domain, iova, paddr, pgsize, prot);
+ if (ret)
+ break;
+
+@@ -1917,8 +1917,22 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
+
+ return ret;
+ }
++
++int iommu_map(struct iommu_domain *domain, unsigned long iova,
++ phys_addr_t paddr, size_t size, int prot)
++{
++ might_sleep();
++ return __iommu_map(domain, iova, paddr, size, prot, GFP_KERNEL);
++}
+ EXPORT_SYMBOL_GPL(iommu_map);
+
++int iommu_map_atomic(struct iommu_domain *domain, unsigned long iova,
++ phys_addr_t paddr, size_t size, int prot)
++{
++ return __iommu_map(domain, iova, paddr, size, prot, GFP_ATOMIC);
++}
++EXPORT_SYMBOL_GPL(iommu_map_atomic);
++
+ static size_t __iommu_unmap(struct iommu_domain *domain,
+ unsigned long iova, size_t size,
+ struct iommu_iotlb_gather *iotlb_gather)
+@@ -1995,8 +2009,9 @@ size_t iommu_unmap_fast(struct iommu_domain *domain,
+ }
+ EXPORT_SYMBOL_GPL(iommu_unmap_fast);
+
+-size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
+- struct scatterlist *sg, unsigned int nents, int prot)
++size_t __iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
++ struct scatterlist *sg, unsigned int nents, int prot,
++ gfp_t gfp)
+ {
+ size_t len = 0, mapped = 0;
+ phys_addr_t start;
+@@ -2007,7 +2022,9 @@ size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t s_phys = sg_phys(sg);
+
+ if (len && s_phys != start + len) {
+- ret = iommu_map(domain, iova + mapped, start, len, prot);
++ ret = __iommu_map(domain, iova + mapped, start,
++ len, prot, gfp);
++
+ if (ret)
+ goto out_err;
+
+@@ -2035,8 +2052,22 @@ out_err:
+ return 0;
+
+ }
++
++size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
++ struct scatterlist *sg, unsigned int nents, int prot)
++{
++ might_sleep();
++ return __iommu_map_sg(domain, iova, sg, nents, prot, GFP_KERNEL);
++}
+ EXPORT_SYMBOL_GPL(iommu_map_sg);
+
++size_t iommu_map_sg_atomic(struct iommu_domain *domain, unsigned long iova,
++ struct scatterlist *sg, unsigned int nents, int prot)
++{
++ return __iommu_map_sg(domain, iova, sg, nents, prot, GFP_ATOMIC);
++}
++EXPORT_SYMBOL_GPL(iommu_map_sg_atomic);
++
+ int iommu_domain_window_enable(struct iommu_domain *domain, u32 wnd_nr,
+ phys_addr_t paddr, u64 size, int prot)
+ {
+diff --git a/drivers/iommu/ipmmu-vmsa.c b/drivers/iommu/ipmmu-vmsa.c
+index 584eefab1dbcd..33874c7aea42c 100644
+--- a/drivers/iommu/ipmmu-vmsa.c
++++ b/drivers/iommu/ipmmu-vmsa.c
+@@ -724,7 +724,7 @@ static void ipmmu_detach_device(struct iommu_domain *io_domain,
+ }
+
+ static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
+
+diff --git a/drivers/iommu/msm_iommu.c b/drivers/iommu/msm_iommu.c
+index cba0097eba39c..696a6c53a69fd 100644
+--- a/drivers/iommu/msm_iommu.c
++++ b/drivers/iommu/msm_iommu.c
+@@ -504,7 +504,7 @@ fail:
+ }
+
+ static int msm_iommu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t pa, size_t len, int prot)
++ phys_addr_t pa, size_t len, int prot, gfp_t gfp)
+ {
+ struct msm_priv *priv = to_msm_priv(domain);
+ unsigned long flags;
+diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
+index 18d7c818a174c..dad44e10b3314 100644
+--- a/drivers/iommu/mtk_iommu.c
++++ b/drivers/iommu/mtk_iommu.c
+@@ -427,7 +427,7 @@ static void mtk_iommu_detach_device(struct iommu_domain *domain,
+ }
+
+ static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+ struct mtk_iommu_data *data = mtk_iommu_get_m4u_data();
+diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
+index e31bd281e59d6..fa602bfe69512 100644
+--- a/drivers/iommu/mtk_iommu_v1.c
++++ b/drivers/iommu/mtk_iommu_v1.c
+@@ -295,7 +295,7 @@ static void mtk_iommu_detach_device(struct iommu_domain *domain,
+ }
+
+ static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+ unsigned int page_num = size >> MT2701_IOMMU_PAGE_SHIFT;
+diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
+index 09c6e1c680db9..be551cc34be45 100644
+--- a/drivers/iommu/omap-iommu.c
++++ b/drivers/iommu/omap-iommu.c
+@@ -1339,7 +1339,7 @@ static u32 iotlb_init_entry(struct iotlb_entry *e, u32 da, u32 pa, int pgsz)
+ }
+
+ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
+- phys_addr_t pa, size_t bytes, int prot)
++ phys_addr_t pa, size_t bytes, int prot, gfp_t gfp)
+ {
+ struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
+ struct device *dev = omap_domain->dev;
+diff --git a/drivers/iommu/qcom_iommu.c b/drivers/iommu/qcom_iommu.c
+index b6e546b62a7cb..e377f7771e30b 100644
+--- a/drivers/iommu/qcom_iommu.c
++++ b/drivers/iommu/qcom_iommu.c
+@@ -419,7 +419,7 @@ static void qcom_iommu_detach_dev(struct iommu_domain *domain, struct device *de
+ }
+
+ static int qcom_iommu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ int ret;
+ unsigned long flags;
+diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
+index 0df091934361b..96f37d9d4d93c 100644
+--- a/drivers/iommu/rockchip-iommu.c
++++ b/drivers/iommu/rockchip-iommu.c
+@@ -758,7 +758,7 @@ unwind:
+ }
+
+ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
+ unsigned long flags;
+diff --git a/drivers/iommu/s390-iommu.c b/drivers/iommu/s390-iommu.c
+index 3b0b18e23187c..1137f3ddcb851 100644
+--- a/drivers/iommu/s390-iommu.c
++++ b/drivers/iommu/s390-iommu.c
+@@ -265,7 +265,7 @@ undo_cpu_trans:
+ }
+
+ static int s390_iommu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ struct s390_domain *s390_domain = to_s390_domain(domain);
+ int flags = ZPCI_PTE_VALID, rc = 0;
+diff --git a/drivers/iommu/tegra-gart.c b/drivers/iommu/tegra-gart.c
+index 3924f7c055440..3fb7ba72507de 100644
+--- a/drivers/iommu/tegra-gart.c
++++ b/drivers/iommu/tegra-gart.c
+@@ -178,7 +178,7 @@ static inline int __gart_iommu_map(struct gart_device *gart, unsigned long iova,
+ }
+
+ static int gart_iommu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t pa, size_t bytes, int prot)
++ phys_addr_t pa, size_t bytes, int prot, gfp_t gfp)
+ {
+ struct gart_device *gart = gart_handle;
+ int ret;
+diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c
+index dd486233e2828..576be3f245daa 100644
+--- a/drivers/iommu/tegra-smmu.c
++++ b/drivers/iommu/tegra-smmu.c
+@@ -651,7 +651,7 @@ static void tegra_smmu_set_pte(struct tegra_smmu_as *as, unsigned long iova,
+ }
+
+ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ struct tegra_smmu_as *as = to_smmu_as(domain);
+ dma_addr_t pte_dma;
+diff --git a/drivers/iommu/virtio-iommu.c b/drivers/iommu/virtio-iommu.c
+index 60e659a24f90b..37e2267acf295 100644
+--- a/drivers/iommu/virtio-iommu.c
++++ b/drivers/iommu/virtio-iommu.c
+@@ -715,7 +715,7 @@ static int viommu_attach_dev(struct iommu_domain *domain, struct device *dev)
+ }
+
+ static int viommu_map(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot)
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp)
+ {
+ int ret;
+ u32 flags;
+diff --git a/drivers/mmc/core/sdio_bus.c b/drivers/mmc/core/sdio_bus.c
+index aef235203d571..69eeff7a7d165 100644
+--- a/drivers/mmc/core/sdio_bus.c
++++ b/drivers/mmc/core/sdio_bus.c
+@@ -269,6 +269,12 @@ static void sdio_release_func(struct device *dev)
+ if (!(func->card->quirks & MMC_QUIRK_NONSTD_SDIO))
+ sdio_free_func_cis(func);
+
++ /*
++ * We have now removed the link to the tuples in the
++ * card structure, so remove the reference.
++ */
++ put_device(&func->card->dev);
++
+ kfree(func->info);
+ kfree(func->tmpbuf);
+ kfree(func);
+@@ -299,6 +305,12 @@ struct sdio_func *sdio_alloc_func(struct mmc_card *card)
+
+ device_initialize(&func->dev);
+
++ /*
++ * We may link to tuples in the card structure,
++ * we need make sure we have a reference to it.
++ */
++ get_device(&func->card->dev);
++
+ func->dev.parent = &card->dev;
+ func->dev.bus = &sdio_bus_type;
+ func->dev.release = sdio_release_func;
+@@ -352,10 +364,9 @@ int sdio_add_func(struct sdio_func *func)
+ */
+ void sdio_remove_func(struct sdio_func *func)
+ {
+- if (!sdio_func_present(func))
+- return;
++ if (sdio_func_present(func))
++ device_del(&func->dev);
+
+- device_del(&func->dev);
+ of_node_put(func->dev.of_node);
+ put_device(&func->dev);
+ }
+diff --git a/drivers/mmc/core/sdio_cis.c b/drivers/mmc/core/sdio_cis.c
+index 9a5aaac29099b..e45053284fdc9 100644
+--- a/drivers/mmc/core/sdio_cis.c
++++ b/drivers/mmc/core/sdio_cis.c
+@@ -383,12 +383,6 @@ int sdio_read_func_cis(struct sdio_func *func)
+ if (ret)
+ return ret;
+
+- /*
+- * Since we've linked to tuples in the card structure,
+- * we must make sure we have a reference to it.
+- */
+- get_device(&func->card->dev);
+-
+ /*
+ * Vendor/device id is optional for function CIS, so
+ * copy it from the card structure as needed.
+@@ -414,11 +408,5 @@ void sdio_free_func_cis(struct sdio_func *func)
+ }
+
+ func->tuples = NULL;
+-
+- /*
+- * We have now removed the link to the tuples in the
+- * card structure, so remove the reference.
+- */
+- put_device(&func->card->dev);
+ }
+
+diff --git a/drivers/mmc/host/mmc_spi.c b/drivers/mmc/host/mmc_spi.c
+index 7083d8ddd4951..7874b266a4448 100644
+--- a/drivers/mmc/host/mmc_spi.c
++++ b/drivers/mmc/host/mmc_spi.c
+@@ -1420,7 +1420,7 @@ static int mmc_spi_probe(struct spi_device *spi)
+
+ status = mmc_add_host(mmc);
+ if (status != 0)
+- goto fail_add_host;
++ goto fail_glue_init;
+
+ /*
+ * Index 0 is card detect
+@@ -1428,7 +1428,7 @@ static int mmc_spi_probe(struct spi_device *spi)
+ */
+ status = mmc_gpiod_request_cd(mmc, NULL, 0, false, 1, NULL);
+ if (status == -EPROBE_DEFER)
+- goto fail_add_host;
++ goto fail_gpiod_request;
+ if (!status) {
+ /*
+ * The platform has a CD GPIO signal that may support
+@@ -1443,7 +1443,7 @@ static int mmc_spi_probe(struct spi_device *spi)
+ /* Index 1 is write protect/read only */
+ status = mmc_gpiod_request_ro(mmc, NULL, 1, 0, NULL);
+ if (status == -EPROBE_DEFER)
+- goto fail_add_host;
++ goto fail_gpiod_request;
+ if (!status)
+ has_ro = true;
+
+@@ -1457,7 +1457,7 @@ static int mmc_spi_probe(struct spi_device *spi)
+ ? ", cd polling" : "");
+ return 0;
+
+-fail_add_host:
++fail_gpiod_request:
+ mmc_remove_host(mmc);
+ fail_glue_init:
+ if (host->dma_dev)
+diff --git a/drivers/net/bonding/bond_debugfs.c b/drivers/net/bonding/bond_debugfs.c
+index f3f86ef68ae0c..8b6cf2bf9025a 100644
+--- a/drivers/net/bonding/bond_debugfs.c
++++ b/drivers/net/bonding/bond_debugfs.c
+@@ -76,7 +76,7 @@ void bond_debug_reregister(struct bonding *bond)
+
+ d = debugfs_rename(bonding_debug_root, bond->debug_dir,
+ bonding_debug_root, bond->dev->name);
+- if (d) {
++ if (!IS_ERR(d)) {
+ bond->debug_dir = d;
+ } else {
+ netdev_warn(bond->dev, "failed to reregister, so just unregister old one\n");
+diff --git a/drivers/net/ethernet/broadcom/bgmac-bcma.c b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+index 2d52754afc33a..7d844b0ea0da0 100644
+--- a/drivers/net/ethernet/broadcom/bgmac-bcma.c
++++ b/drivers/net/ethernet/broadcom/bgmac-bcma.c
+@@ -228,12 +228,12 @@ static int bgmac_probe(struct bcma_device *core)
+ bgmac->feature_flags |= BGMAC_FEAT_CLKCTLST;
+ bgmac->feature_flags |= BGMAC_FEAT_FLW_CTRL1;
+ bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_PHY;
+- if (ci->pkg == BCMA_PKG_ID_BCM47188 ||
+- ci->pkg == BCMA_PKG_ID_BCM47186) {
++ if ((ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg == BCMA_PKG_ID_BCM47186) ||
++ (ci->id == BCMA_CHIP_ID_BCM53572 && ci->pkg == BCMA_PKG_ID_BCM47188)) {
+ bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_RGMII;
+ bgmac->feature_flags |= BGMAC_FEAT_IOST_ATTACHED;
+ }
+- if (ci->pkg == BCMA_PKG_ID_BCM5358)
++ if (ci->id == BCMA_CHIP_ID_BCM5357 && ci->pkg == BCMA_PKG_ID_BCM5358)
+ bgmac->feature_flags |= BGMAC_FEAT_SW_TYPE_EPHYRMII;
+ break;
+ case BCMA_CHIP_ID_BCM53573:
+diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+index 3636849f63656..ef8225b7445d3 100644
+--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
++++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
+@@ -8205,10 +8205,14 @@ int bnxt_reserve_rings(struct bnxt *bp, bool irq_re_init)
+ netdev_err(bp->dev, "ring reservation/IRQ init failure rc: %d\n", rc);
+ return rc;
+ }
+- if (tcs && (bp->tx_nr_rings_per_tc * tcs != bp->tx_nr_rings)) {
++ if (tcs && (bp->tx_nr_rings_per_tc * tcs !=
++ bp->tx_nr_rings - bp->tx_nr_rings_xdp)) {
+ netdev_err(bp->dev, "tx ring reservation failure\n");
+ netdev_reset_tc(bp->dev);
+- bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
++ if (bp->tx_nr_rings_xdp)
++ bp->tx_nr_rings_per_tc = bp->tx_nr_rings_xdp;
++ else
++ bp->tx_nr_rings_per_tc = bp->tx_nr_rings;
+ return -ENOMEM;
+ }
+ return 0;
+diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
+index d612b23c2e3f9..3f983d69f10eb 100644
+--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
++++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
+@@ -2702,7 +2702,7 @@ static int i40e_change_mtu(struct net_device *netdev, int new_mtu)
+ struct i40e_pf *pf = vsi->back;
+
+ if (i40e_enabled_xdp_vsi(vsi)) {
+- int frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN + VLAN_HLEN;
++ int frame_size = new_mtu + I40E_PACKET_HDR_PAD;
+
+ if (frame_size > i40e_max_xdp_frame_size(vsi))
+ return -EINVAL;
+@@ -12535,6 +12535,8 @@ static int i40e_ndo_bridge_setlink(struct net_device *dev,
+ }
+
+ br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC);
++ if (!br_spec)
++ return -EINVAL;
+
+ nla_for_each_nested(attr, br_spec, rem) {
+ __u16 mode;
+diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
+index 7d28563ab7946..ae1d305672259 100644
+--- a/drivers/net/ethernet/intel/ice/ice_main.c
++++ b/drivers/net/ethernet/intel/ice/ice_main.c
+@@ -3211,7 +3211,7 @@ static int __init ice_module_init(void)
+ pr_info("%s - version %s\n", ice_driver_string, ice_drv_ver);
+ pr_info("%s\n", ice_copyright);
+
+- ice_wq = alloc_workqueue("%s", WQ_MEM_RECLAIM, 0, KBUILD_MODNAME);
++ ice_wq = alloc_workqueue("%s", 0, 0, KBUILD_MODNAME);
+ if (!ice_wq) {
+ pr_err("Failed to create workqueue\n");
+ return -ENOMEM;
+diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe.h b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+index fa49ef2afde5f..0142ca226bf50 100644
+--- a/drivers/net/ethernet/intel/ixgbe/ixgbe.h
++++ b/drivers/net/ethernet/intel/ixgbe/ixgbe.h
+@@ -67,6 +67,8 @@
+ #define IXGBE_RXBUFFER_4K 4096
+ #define IXGBE_MAX_RXBUFFER 16384 /* largest size for a single descriptor */
+
++#define IXGBE_PKT_HDR_PAD (ETH_HLEN + ETH_FCS_LEN + (VLAN_HLEN * 2))
++
+ /* Attempt to maximize the headroom available for incoming frames. We
+ * use a 2K buffer for receives and need 1536/1534 to store the data for
+ * the frame. This leaves us with 512 bytes of room. From that we need
+diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+index f8aa1a0b89c5d..a864b91065e33 100644
+--- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
++++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c
+@@ -6721,6 +6721,18 @@ static void ixgbe_free_all_rx_resources(struct ixgbe_adapter *adapter)
+ ixgbe_free_rx_resources(adapter->rx_ring[i]);
+ }
+
++/**
++ * ixgbe_max_xdp_frame_size - returns the maximum allowed frame size for XDP
++ * @adapter: device handle, pointer to adapter
++ */
++static int ixgbe_max_xdp_frame_size(struct ixgbe_adapter *adapter)
++{
++ if (PAGE_SIZE >= 8192 || adapter->flags2 & IXGBE_FLAG2_RX_LEGACY)
++ return IXGBE_RXBUFFER_2K;
++ else
++ return IXGBE_RXBUFFER_3K;
++}
++
+ /**
+ * ixgbe_change_mtu - Change the Maximum Transfer Unit
+ * @netdev: network interface device structure
+@@ -6732,18 +6744,12 @@ static int ixgbe_change_mtu(struct net_device *netdev, int new_mtu)
+ {
+ struct ixgbe_adapter *adapter = netdev_priv(netdev);
+
+- if (adapter->xdp_prog) {
+- int new_frame_size = new_mtu + ETH_HLEN + ETH_FCS_LEN +
+- VLAN_HLEN;
+- int i;
+-
+- for (i = 0; i < adapter->num_rx_queues; i++) {
+- struct ixgbe_ring *ring = adapter->rx_ring[i];
++ if (ixgbe_enabled_xdp_adapter(adapter)) {
++ int new_frame_size = new_mtu + IXGBE_PKT_HDR_PAD;
+
+- if (new_frame_size > ixgbe_rx_bufsz(ring)) {
+- e_warn(probe, "Requested MTU size is not supported with XDP\n");
+- return -EINVAL;
+- }
++ if (new_frame_size > ixgbe_max_xdp_frame_size(adapter)) {
++ e_warn(probe, "Requested MTU size is not supported with XDP\n");
++ return -EINVAL;
+ }
+ }
+
+diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+index f9c303d76658a..d0841836cf705 100644
+--- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c
++++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c
+@@ -190,6 +190,7 @@ static int ionic_qcq_enable(struct ionic_qcq *qcq)
+ .oper = IONIC_Q_ENABLE,
+ },
+ };
++ int ret;
+
+ idev = &lif->ionic->idev;
+ dev = lif->ionic->dev;
+@@ -197,16 +198,24 @@ static int ionic_qcq_enable(struct ionic_qcq *qcq)
+ dev_dbg(dev, "q_enable.index %d q_enable.qtype %d\n",
+ ctx.cmd.q_control.index, ctx.cmd.q_control.type);
+
++ if (qcq->flags & IONIC_QCQ_F_INTR)
++ ionic_intr_clean(idev->intr_ctrl, qcq->intr.index);
++
++ ret = ionic_adminq_post_wait(lif, &ctx);
++ if (ret)
++ return ret;
++
++ if (qcq->napi.poll)
++ napi_enable(&qcq->napi);
++
+ if (qcq->flags & IONIC_QCQ_F_INTR) {
+ irq_set_affinity_hint(qcq->intr.vector,
+ &qcq->intr.affinity_mask);
+- napi_enable(&qcq->napi);
+- ionic_intr_clean(idev->intr_ctrl, qcq->intr.index);
+ ionic_intr_mask(idev->intr_ctrl, qcq->intr.index,
+ IONIC_INTR_MASK_CLEAR);
+ }
+
+- return ionic_adminq_post_wait(lif, &ctx);
++ return 0;
+ }
+
+ static int ionic_qcq_disable(struct ionic_qcq *qcq)
+diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+index bfc4a92f1d92b..78be62ecc9a9a 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+@@ -505,6 +505,8 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
+ plat_dat->has_gmac4 = 1;
+ plat_dat->pmt = 1;
+ plat_dat->tso_en = of_property_read_bool(np, "snps,tso");
++ if (of_device_is_compatible(np, "qcom,qcs404-ethqos"))
++ plat_dat->rx_clk_runs_in_lpi = 1;
+
+ ret = stmmac_dvr_probe(&pdev->dev, plat_dat, &stmmac_res);
+ if (ret)
+diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
+index e436fa160c7d6..59165c0560d74 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
++++ b/drivers/net/ethernet/stmicro/stmmac/dwmac5.c
+@@ -520,9 +520,9 @@ int dwmac5_flex_pps_config(void __iomem *ioaddr, int index,
+ return 0;
+ }
+
+- val |= PPSCMDx(index, 0x2);
+ val |= TRGTMODSELx(index, 0x2);
+ val |= PPSEN0;
++ writel(val, ioaddr + MAC_PPS_CONTROL);
+
+ writel(cfg->start.tv_sec, ioaddr + MAC_PPSx_TARGET_TIME_SEC(index));
+
+@@ -547,6 +547,7 @@ int dwmac5_flex_pps_config(void __iomem *ioaddr, int index,
+ writel(period - 1, ioaddr + MAC_PPSx_WIDTH(index));
+
+ /* Finally, activate it */
++ val |= PPSCMDx(index, 0x2);
+ writel(val, ioaddr + MAC_PPS_CONTROL);
+ return 0;
+ }
+diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+index 3079e52546663..6a3b0f76d9729 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c
+@@ -932,7 +932,8 @@ static void stmmac_mac_link_up(struct phylink_config *config,
+
+ stmmac_mac_set(priv, priv->ioaddr, true);
+ if (phy && priv->dma_cap.eee) {
+- priv->eee_active = phy_init_eee(phy, 1) >= 0;
++ priv->eee_active =
++ phy_init_eee(phy, !priv->plat->rx_clk_runs_in_lpi) >= 0;
+ priv->eee_enabled = stmmac_eee_init(priv);
+ stmmac_set_eee_pls(priv, priv->hw, true);
+ }
+diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+index a2ff9b4727ecd..6388bae68b659 100644
+--- a/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
++++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_platform.c
+@@ -554,7 +554,7 @@ stmmac_probe_config_dt(struct platform_device *pdev, const char **mac)
+ dma_cfg->mixed_burst = of_property_read_bool(np, "snps,mixed-burst");
+
+ plat->force_thresh_dma_mode = of_property_read_bool(np, "snps,force_thresh_dma_mode");
+- if (plat->force_thresh_dma_mode) {
++ if (plat->force_thresh_dma_mode && plat->force_sf_dma_mode) {
+ plat->force_sf_dma_mode = 0;
+ dev_warn(&pdev->dev,
+ "force_sf_dma_mode is ignored if force_thresh_dma_mode is set.\n");
+diff --git a/drivers/net/phy/meson-gxl.c b/drivers/net/phy/meson-gxl.c
+index e8f2ca625837f..39151ec6f65e2 100644
+--- a/drivers/net/phy/meson-gxl.c
++++ b/drivers/net/phy/meson-gxl.c
+@@ -235,6 +235,8 @@ static struct phy_driver meson_gxl_phy[] = {
+ .config_intr = meson_gxl_config_intr,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
++ .read_mmd = genphy_read_mmd_unsupported,
++ .write_mmd = genphy_write_mmd_unsupported,
+ }, {
+ PHY_ID_MATCH_EXACT(0x01803301),
+ .name = "Meson G12A Internal PHY",
+@@ -245,6 +247,8 @@ static struct phy_driver meson_gxl_phy[] = {
+ .config_intr = meson_gxl_config_intr,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
++ .read_mmd = genphy_read_mmd_unsupported,
++ .write_mmd = genphy_write_mmd_unsupported,
+ },
+ };
+
+diff --git a/drivers/net/usb/kalmia.c b/drivers/net/usb/kalmia.c
+index fc5895f85cee2..a552bb1665b8a 100644
+--- a/drivers/net/usb/kalmia.c
++++ b/drivers/net/usb/kalmia.c
+@@ -65,8 +65,8 @@ kalmia_send_init_packet(struct usbnet *dev, u8 *init_msg, u8 init_msg_len,
+ init_msg, init_msg_len, &act_len, KALMIA_USB_TIMEOUT);
+ if (status != 0) {
+ netdev_err(dev->net,
+- "Error sending init packet. Status %i, length %i\n",
+- status, act_len);
++ "Error sending init packet. Status %i\n",
++ status);
+ return status;
+ }
+ else if (act_len != init_msg_len) {
+@@ -83,8 +83,8 @@ kalmia_send_init_packet(struct usbnet *dev, u8 *init_msg, u8 init_msg_len,
+
+ if (status != 0)
+ netdev_err(dev->net,
+- "Error receiving init result. Status %i, length %i\n",
+- status, act_len);
++ "Error receiving init result. Status %i\n",
++ status);
+ else if (act_len != expected_len)
+ netdev_err(dev->net, "Unexpected init result length: %i\n",
+ act_len);
+diff --git a/drivers/net/usb/plusb.c b/drivers/net/usb/plusb.c
+index 17c9c63b8eebb..ce7862dac2b75 100644
+--- a/drivers/net/usb/plusb.c
++++ b/drivers/net/usb/plusb.c
+@@ -57,9 +57,7 @@
+ static inline int
+ pl_vendor_req(struct usbnet *dev, u8 req, u8 val, u8 index)
+ {
+- return usbnet_read_cmd(dev, req,
+- USB_DIR_IN | USB_TYPE_VENDOR |
+- USB_RECIP_DEVICE,
++ return usbnet_write_cmd(dev, req, USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ val, index, NULL, 0);
+ }
+
+diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
+index 579df7c5411d3..5212d9cb03728 100644
+--- a/drivers/net/virtio_net.c
++++ b/drivers/net/virtio_net.c
+@@ -1910,8 +1910,8 @@ static int virtnet_close(struct net_device *dev)
+ cancel_delayed_work_sync(&vi->refill);
+
+ for (i = 0; i < vi->max_queue_pairs; i++) {
+- xdp_rxq_info_unreg(&vi->rq[i].xdp_rxq);
+ napi_disable(&vi->rq[i].napi);
++ xdp_rxq_info_unreg(&vi->rq[i].xdp_rxq);
+ virtnet_napi_tx_disable(&vi->sq[i].napi);
+ }
+
+diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+index 6439adcd2f995..cd146bbca670b 100644
+--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
++++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+@@ -87,6 +87,9 @@
+ #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
+ (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
+
++#define BRCMF_MAX_CHANSPEC_LIST \
++ (BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1)
++
+ static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
+ {
+ if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
+@@ -6067,6 +6070,13 @@ static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
+ band->channels[i].flags = IEEE80211_CHAN_DISABLED;
+
+ total = le32_to_cpu(list->count);
++ if (total > BRCMF_MAX_CHANSPEC_LIST) {
++ bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
++ total);
++ err = -EINVAL;
++ goto fail_pbuf;
++ }
++
+ for (i = 0; i < total; i++) {
+ ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ cfg->d11inf.decchspec(&ch);
+@@ -6212,6 +6222,13 @@ static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
+ band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
+ list = (struct brcmf_chanspec_list *)pbuf;
+ num_chan = le32_to_cpu(list->count);
++ if (num_chan > BRCMF_MAX_CHANSPEC_LIST) {
++ bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
++ num_chan);
++ kfree(pbuf);
++ return -EINVAL;
++ }
++
+ for (i = 0; i < num_chan; i++) {
+ ch.chspec = (u16)le32_to_cpu(list->element[i]);
+ cfg->d11inf.decchspec(&ch);
+diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c
+index 5d62d1042c0e6..a58711c488509 100644
+--- a/drivers/nvme/host/pci.c
++++ b/drivers/nvme/host/pci.c
+@@ -3199,7 +3199,6 @@ static const struct pci_device_id nvme_id_table[] = {
+ NVME_QUIRK_IGNORE_DEV_SUBNQN, },
+ { PCI_DEVICE(0x1c5c, 0x1504), /* SK Hynix PC400 */
+ .driver_data = NVME_QUIRK_DISABLE_WRITE_ZEROES, },
+- { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },
+ { PCI_DEVICE(0x2646, 0x2263), /* KINGSTON A2000 NVMe SSD */
+ .driver_data = NVME_QUIRK_NO_DEEPEST_PS, },
+ { PCI_DEVICE(PCI_VENDOR_ID_APPLE, 0x2001),
+@@ -3209,6 +3208,8 @@ static const struct pci_device_id nvme_id_table[] = {
+ .driver_data = NVME_QUIRK_SINGLE_VECTOR |
+ NVME_QUIRK_128_BYTES_SQES |
+ NVME_QUIRK_SHARED_TAGS },
++
++ { PCI_DEVICE_CLASS(PCI_CLASS_STORAGE_EXPRESS, 0xffffff) },
+ { 0, }
+ };
+ MODULE_DEVICE_TABLE(pci, nvme_id_table);
+diff --git a/drivers/nvme/target/fc.c b/drivers/nvme/target/fc.c
+index 9b07e8c7689ab..f74fc6481731d 100644
+--- a/drivers/nvme/target/fc.c
++++ b/drivers/nvme/target/fc.c
+@@ -1362,8 +1362,10 @@ nvmet_fc_ls_create_association(struct nvmet_fc_tgtport *tgtport,
+ else {
+ queue = nvmet_fc_alloc_target_queue(iod->assoc, 0,
+ be16_to_cpu(rqst->assoc_cmd.sqsize));
+- if (!queue)
++ if (!queue) {
+ ret = VERR_QUEUE_ALLOC_FAIL;
++ nvmet_fc_tgt_a_put(iod->assoc);
++ }
+ }
+ }
+
+diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c
+index c0f4324d8f7c8..cde1d77845fee 100644
+--- a/drivers/nvmem/core.c
++++ b/drivers/nvmem/core.c
+@@ -439,7 +439,7 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
+ if (config->cells) {
+ rval = nvmem_add_cells(nvmem, config->cells, config->ncells);
+ if (rval)
+- goto err_teardown_compat;
++ goto err_remove_cells;
+ }
+
+ rval = nvmem_add_cells_from_table(nvmem);
+@@ -456,7 +456,6 @@ struct nvmem_device *nvmem_register(const struct nvmem_config *config)
+
+ err_remove_cells:
+ nvmem_device_remove_all_cells(nvmem);
+-err_teardown_compat:
+ if (config->compat)
+ nvmem_sysfs_remove_compat(nvmem, config);
+ err_device_del:
+diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed.c b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+index 22aca6d182c0c..2c1e799b029e7 100644
+--- a/drivers/pinctrl/aspeed/pinctrl-aspeed.c
++++ b/drivers/pinctrl/aspeed/pinctrl-aspeed.c
+@@ -115,7 +115,7 @@ static int aspeed_disable_sig(struct aspeed_pinmux_data *ctx,
+ int ret = 0;
+
+ if (!exprs)
+- return true;
++ return -EINVAL;
+
+ while (*exprs && !ret) {
+ ret = aspeed_sig_expr_disable(ctx, *exprs);
+diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
+index e0cb76f7e5407..32c6326337f73 100644
+--- a/drivers/pinctrl/intel/pinctrl-intel.c
++++ b/drivers/pinctrl/intel/pinctrl-intel.c
+@@ -1510,6 +1510,12 @@ int intel_pinctrl_probe_by_uid(struct platform_device *pdev)
+ EXPORT_SYMBOL_GPL(intel_pinctrl_probe_by_uid);
+
+ #ifdef CONFIG_PM_SLEEP
++static bool __intel_gpio_is_direct_irq(u32 value)
++{
++ return (value & PADCFG0_GPIROUTIOXAPIC) && (value & PADCFG0_GPIOTXDIS) &&
++ (__intel_gpio_get_gpio_mode(value) == PADCFG0_PMODE_GPIO);
++}
++
+ static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned int pin)
+ {
+ const struct pin_desc *pd = pin_desc_get(pctrl->pctldev, pin);
+@@ -1543,8 +1549,7 @@ static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned int
+ * See https://bugzilla.kernel.org/show_bug.cgi?id=214749.
+ */
+ value = readl(intel_get_padcfg(pctrl, pin, PADCFG0));
+- if ((value & PADCFG0_GPIROUTIOXAPIC) && (value & PADCFG0_GPIOTXDIS) &&
+- (__intel_gpio_get_gpio_mode(value) == PADCFG0_PMODE_GPIO))
++ if (__intel_gpio_is_direct_irq(value))
+ return true;
+
+ return false;
+@@ -1656,7 +1661,12 @@ int intel_pinctrl_resume_noirq(struct device *dev)
+ void __iomem *padcfg;
+ u32 val;
+
+- if (!intel_pinctrl_should_save(pctrl, desc->number))
++ if (!(intel_pinctrl_should_save(pctrl, desc->number) ||
++ /*
++ * If the firmware mangled the register contents too much,
++ * check the saved value for the Direct IRQ mode.
++ */
++ __intel_gpio_is_direct_irq(pads[i].padcfg0)))
+ continue;
+
+ padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG0);
+diff --git a/drivers/pinctrl/pinctrl-single.c b/drivers/pinctrl/pinctrl-single.c
+index 20c89023d312e..ce5be6f0b7aac 100644
+--- a/drivers/pinctrl/pinctrl-single.c
++++ b/drivers/pinctrl/pinctrl-single.c
+@@ -345,6 +345,8 @@ static int pcs_set_mux(struct pinctrl_dev *pctldev, unsigned fselector,
+ if (!pcs->fmask)
+ return 0;
+ function = pinmux_generic_get_function(pctldev, fselector);
++ if (!function)
++ return -EINVAL;
+ func = function->data;
+ if (!func)
+ return -EINVAL;
+diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c
+index b5dd1caae5e92..9320a0a92bb2f 100644
+--- a/drivers/scsi/iscsi_tcp.c
++++ b/drivers/scsi/iscsi_tcp.c
+@@ -770,7 +770,7 @@ static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
+ enum iscsi_host_param param, char *buf)
+ {
+ struct iscsi_sw_tcp_host *tcp_sw_host = iscsi_host_priv(shost);
+- struct iscsi_session *session = tcp_sw_host->session;
++ struct iscsi_session *session;
+ struct iscsi_conn *conn;
+ struct iscsi_tcp_conn *tcp_conn;
+ struct iscsi_sw_tcp_conn *tcp_sw_conn;
+@@ -779,6 +779,7 @@ static int iscsi_sw_tcp_host_get_param(struct Scsi_Host *shost,
+
+ switch (param) {
+ case ISCSI_HOST_PARAM_IPADDRESS:
++ session = tcp_sw_host->session;
+ if (!session)
+ return -ENOTCONN;
+
+@@ -867,12 +868,14 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max,
+ if (!cls_session)
+ goto remove_host;
+ session = cls_session->dd_data;
+- tcp_sw_host = iscsi_host_priv(shost);
+- tcp_sw_host->session = session;
+
+ shost->can_queue = session->scsi_cmds_max;
+ if (iscsi_tcp_r2tpool_alloc(session))
+ goto remove_session;
++
++ /* We are now fully setup so expose the session to sysfs. */
++ tcp_sw_host = iscsi_host_priv(shost);
++ tcp_sw_host->session = session;
+ return cls_session;
+
+ remove_session:
+diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
+index 3fd109fd9335d..d236322ced30e 100644
+--- a/drivers/scsi/scsi_scan.c
++++ b/drivers/scsi/scsi_scan.c
+@@ -1130,8 +1130,7 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
+ * that no LUN is present, so don't add sdev in these cases.
+ * Two specific examples are:
+ * 1) NetApp targets: return PQ=1, PDT=0x1f
+- * 2) IBM/2145 targets: return PQ=1, PDT=0
+- * 3) USB UFI: returns PDT=0x1f, with the PQ bits being "reserved"
++ * 2) USB UFI: returns PDT=0x1f, with the PQ bits being "reserved"
+ * in the UFI 1.0 spec (we cannot rely on reserved bits).
+ *
+ * References:
+@@ -1145,8 +1144,8 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget,
+ * PDT=00h Direct-access device (floppy)
+ * PDT=1Fh none (no FDD connected to the requested logical unit)
+ */
+- if (((result[0] >> 5) == 1 ||
+- (starget->pdt_1f_for_no_lun && (result[0] & 0x1f) == 0x1f)) &&
++ if (((result[0] >> 5) == 1 || starget->pdt_1f_for_no_lun) &&
++ (result[0] & 0x1f) == 0x1f &&
+ !scsi_is_wlun(lun)) {
+ SCSI_LOG_SCAN_BUS(3, sdev_printk(KERN_INFO, sdev,
+ "scsi scan: peripheral device type"
+diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c
+index 7143d03f0e027..18fbbe510d018 100644
+--- a/drivers/target/target_core_file.c
++++ b/drivers/target/target_core_file.c
+@@ -340,7 +340,7 @@ static int fd_do_rw(struct se_cmd *cmd, struct file *fd,
+ len += sg->length;
+ }
+
+- iov_iter_bvec(&iter, READ, bvec, sgl_nents, len);
++ iov_iter_bvec(&iter, is_write, bvec, sgl_nents, len);
+ if (is_write)
+ ret = vfs_iter_write(fd, &iter, &pos, 0);
+ else
+@@ -477,7 +477,7 @@ fd_execute_write_same(struct se_cmd *cmd)
+ len += se_dev->dev_attrib.block_size;
+ }
+
+- iov_iter_bvec(&iter, READ, bvec, nolb, len);
++ iov_iter_bvec(&iter, WRITE, bvec, nolb, len);
+ ret = vfs_iter_write(fd_dev->fd_file, &iter, &pos, 0);
+
+ kfree(bvec);
+diff --git a/drivers/target/target_core_tmr.c b/drivers/target/target_core_tmr.c
+index feeba3966617b..6928ebf0be9c7 100644
+--- a/drivers/target/target_core_tmr.c
++++ b/drivers/target/target_core_tmr.c
+@@ -82,8 +82,8 @@ static bool __target_check_io_state(struct se_cmd *se_cmd,
+ {
+ struct se_session *sess = se_cmd->se_sess;
+
+- assert_spin_locked(&sess->sess_cmd_lock);
+- WARN_ON_ONCE(!irqs_disabled());
++ lockdep_assert_held(&sess->sess_cmd_lock);
++
+ /*
+ * If command already reached CMD_T_COMPLETE state within
+ * target_complete_cmd() or CMD_T_FABRIC_STOP due to shutdown,
+diff --git a/drivers/tty/serial/8250/8250_dma.c b/drivers/tty/serial/8250/8250_dma.c
+index 890fa7ddaa7f3..bdc7bb7a1b929 100644
+--- a/drivers/tty/serial/8250/8250_dma.c
++++ b/drivers/tty/serial/8250/8250_dma.c
+@@ -46,19 +46,39 @@ static void __dma_rx_complete(void *param)
+ struct uart_8250_dma *dma = p->dma;
+ struct tty_port *tty_port = &p->port.state->port;
+ struct dma_tx_state state;
++ enum dma_status dma_status;
+ int count;
+
+- dma->rx_running = 0;
+- dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
++ /*
++ * New DMA Rx can be started during the completion handler before it
++ * could acquire port's lock and it might still be ongoing. Don't to
++ * anything in such case.
++ */
++ dma_status = dmaengine_tx_status(dma->rxchan, dma->rx_cookie, &state);
++ if (dma_status == DMA_IN_PROGRESS)
++ return;
+
+ count = dma->rx_size - state.residue;
+
+ tty_insert_flip_string(tty_port, dma->rx_buf, count);
+ p->port.icount.rx += count;
++ dma->rx_running = 0;
+
+ tty_flip_buffer_push(tty_port);
+ }
+
++static void dma_rx_complete(void *param)
++{
++ struct uart_8250_port *p = param;
++ struct uart_8250_dma *dma = p->dma;
++ unsigned long flags;
++
++ spin_lock_irqsave(&p->port.lock, flags);
++ if (dma->rx_running)
++ __dma_rx_complete(p);
++ spin_unlock_irqrestore(&p->port.lock, flags);
++}
++
+ int serial8250_tx_dma(struct uart_8250_port *p)
+ {
+ struct uart_8250_dma *dma = p->dma;
+@@ -121,7 +141,7 @@ int serial8250_rx_dma(struct uart_8250_port *p)
+ return -EBUSY;
+
+ dma->rx_running = 1;
+- desc->callback = __dma_rx_complete;
++ desc->callback = dma_rx_complete;
+ desc->callback_param = p;
+
+ dma->rx_cookie = dmaengine_submit(desc);
+diff --git a/drivers/tty/vt/vc_screen.c b/drivers/tty/vt/vc_screen.c
+index 778f83ea22493..e61fd04a0d8d0 100644
+--- a/drivers/tty/vt/vc_screen.c
++++ b/drivers/tty/vt/vc_screen.c
+@@ -265,10 +265,6 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+
+ uni_mode = use_unicode(inode);
+ attr = use_attributes(inode);
+- ret = -ENXIO;
+- vc = vcs_vc(inode, &viewed);
+- if (!vc)
+- goto unlock_out;
+
+ ret = -EINVAL;
+ if (pos < 0)
+@@ -288,6 +284,11 @@ vcs_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
+ ssize_t orig_count;
+ long p = pos;
+
++ ret = -ENXIO;
++ vc = vcs_vc(inode, &viewed);
++ if (!vc)
++ goto unlock_out;
++
+ /* Check whether we are above size each round,
+ * as copy_to_user at the end of this loop
+ * could sleep.
+diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
+index 35a11f7bcb658..1346c600ebedf 100644
+--- a/drivers/usb/core/quirks.c
++++ b/drivers/usb/core/quirks.c
+@@ -527,6 +527,9 @@ static const struct usb_device_id usb_quirk_list[] = {
+ /* DJI CineSSD */
+ { USB_DEVICE(0x2ca3, 0x0031), .driver_info = USB_QUIRK_NO_LPM },
+
++ /* Alcor Link AK9563 SC Reader used in 2022 Lenovo ThinkPads */
++ { USB_DEVICE(0x2ce3, 0x9563), .driver_info = USB_QUIRK_NO_LPM },
++
+ /* DELL USB GEN2 */
+ { USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },
+
+diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c
+index aed35276e0e0c..2dcdeb52fc293 100644
+--- a/drivers/usb/dwc3/dwc3-qcom.c
++++ b/drivers/usb/dwc3/dwc3-qcom.c
+@@ -102,7 +102,7 @@ static inline void dwc3_qcom_clrbits(void __iomem *base, u32 offset, u32 val)
+ readl(base + offset);
+ }
+
+-static void dwc3_qcom_vbus_overrride_enable(struct dwc3_qcom *qcom, bool enable)
++static void dwc3_qcom_vbus_override_enable(struct dwc3_qcom *qcom, bool enable)
+ {
+ if (enable) {
+ dwc3_qcom_setbits(qcom->qscratch_base, QSCRATCH_SS_PHY_CTRL,
+@@ -123,7 +123,7 @@ static int dwc3_qcom_vbus_notifier(struct notifier_block *nb,
+ struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, vbus_nb);
+
+ /* enable vbus override for device mode */
+- dwc3_qcom_vbus_overrride_enable(qcom, event);
++ dwc3_qcom_vbus_override_enable(qcom, event);
+ qcom->mode = event ? USB_DR_MODE_PERIPHERAL : USB_DR_MODE_HOST;
+
+ return NOTIFY_DONE;
+@@ -135,7 +135,7 @@ static int dwc3_qcom_host_notifier(struct notifier_block *nb,
+ struct dwc3_qcom *qcom = container_of(nb, struct dwc3_qcom, host_nb);
+
+ /* disable vbus override in host mode */
+- dwc3_qcom_vbus_overrride_enable(qcom, !event);
++ dwc3_qcom_vbus_override_enable(qcom, !event);
+ qcom->mode = event ? USB_DR_MODE_HOST : USB_DR_MODE_PERIPHERAL;
+
+ return NOTIFY_DONE;
+@@ -669,8 +669,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev)
+ qcom->mode = usb_get_dr_mode(&qcom->dwc3->dev);
+
+ /* enable vbus override for device mode */
+- if (qcom->mode == USB_DR_MODE_PERIPHERAL)
+- dwc3_qcom_vbus_overrride_enable(qcom, true);
++ if (qcom->mode != USB_DR_MODE_HOST)
++ dwc3_qcom_vbus_override_enable(qcom, true);
+
+ /* register extcon to override sw_vbus on Vbus change later */
+ ret = dwc3_qcom_register_extcon(qcom);
+diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
+index 431ab6d07497f..5fe7490367734 100644
+--- a/drivers/usb/gadget/function/f_fs.c
++++ b/drivers/usb/gadget/function/f_fs.c
+@@ -278,8 +278,10 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
+ struct usb_request *req = ffs->ep0req;
+ int ret;
+
+- if (!req)
++ if (!req) {
++ spin_unlock_irq(&ffs->ev.waitq.lock);
+ return -EINVAL;
++ }
+
+ req->zero = len < le16_to_cpu(ffs->ev.setup.wLength);
+
+diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c
+index 1aa1bd991ef43..c840f03996fb6 100644
+--- a/drivers/usb/typec/altmodes/displayport.c
++++ b/drivers/usb/typec/altmodes/displayport.c
+@@ -522,10 +522,10 @@ int dp_altmode_probe(struct typec_altmode *alt)
+ /* FIXME: Port can only be DFP_U. */
+
+ /* Make sure we have compatiple pin configurations */
+- if (!(DP_CAP_DFP_D_PIN_ASSIGN(port->vdo) &
+- DP_CAP_UFP_D_PIN_ASSIGN(alt->vdo)) &&
+- !(DP_CAP_UFP_D_PIN_ASSIGN(port->vdo) &
+- DP_CAP_DFP_D_PIN_ASSIGN(alt->vdo)))
++ if (!(DP_CAP_PIN_ASSIGN_DFP_D(port->vdo) &
++ DP_CAP_PIN_ASSIGN_UFP_D(alt->vdo)) &&
++ !(DP_CAP_PIN_ASSIGN_UFP_D(port->vdo) &
++ DP_CAP_PIN_ASSIGN_DFP_D(alt->vdo)))
+ return -ENODEV;
+
+ ret = sysfs_create_group(&alt->dev.kobj, &dp_altmode_group);
+diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c
+index 673af5937489f..e487928289392 100644
+--- a/drivers/video/fbdev/core/fbcon.c
++++ b/drivers/video/fbdev/core/fbcon.c
+@@ -2497,9 +2497,12 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font,
+ h > FBCON_SWAP(info->var.rotate, info->var.yres, info->var.xres))
+ return -EINVAL;
+
++ if (font->width > 32 || font->height > 32)
++ return -EINVAL;
++
+ /* Make sure drawing engine can handle the font */
+- if (!(info->pixmap.blit_x & (1 << (font->width - 1))) ||
+- !(info->pixmap.blit_y & (1 << (font->height - 1))))
++ if (!(info->pixmap.blit_x & BIT(font->width - 1)) ||
++ !(info->pixmap.blit_y & BIT(font->height - 1)))
+ return -EINVAL;
+
+ /* Make sure driver can handle the font length */
+diff --git a/drivers/video/fbdev/smscufx.c b/drivers/video/fbdev/smscufx.c
+index a5abdec58fe26..53fb34f074c74 100644
+--- a/drivers/video/fbdev/smscufx.c
++++ b/drivers/video/fbdev/smscufx.c
+@@ -1622,7 +1622,7 @@ static int ufx_usb_probe(struct usb_interface *interface,
+ struct usb_device *usbdev;
+ struct ufx_data *dev;
+ struct fb_info *info;
+- int retval;
++ int retval = -ENOMEM;
+ u32 id_rev, fpga_rev;
+
+ /* usb initialization */
+@@ -1654,15 +1654,17 @@ static int ufx_usb_probe(struct usb_interface *interface,
+
+ if (!ufx_alloc_urb_list(dev, WRITES_IN_FLIGHT, MAX_TRANSFER)) {
+ dev_err(dev->gdev, "ufx_alloc_urb_list failed\n");
+- goto e_nomem;
++ goto put_ref;
+ }
+
+ /* We don't register a new USB class. Our client interface is fbdev */
+
+ /* allocates framebuffer driver structure, not framebuffer memory */
+ info = framebuffer_alloc(0, &usbdev->dev);
+- if (!info)
+- goto e_nomem;
++ if (!info) {
++ dev_err(dev->gdev, "framebuffer_alloc failed\n");
++ goto free_urb_list;
++ }
+
+ dev->info = info;
+ info->par = dev;
+@@ -1705,22 +1707,34 @@ static int ufx_usb_probe(struct usb_interface *interface,
+ check_warn_goto_error(retval, "unable to find common mode for display and adapter");
+
+ retval = ufx_reg_set_bits(dev, 0x4000, 0x00000001);
+- check_warn_goto_error(retval, "error %d enabling graphics engine", retval);
++ if (retval < 0) {
++ dev_err(dev->gdev, "error %d enabling graphics engine", retval);
++ goto setup_modes;
++ }
+
+ /* ready to begin using device */
+ atomic_set(&dev->usb_active, 1);
+
+ dev_dbg(dev->gdev, "checking var");
+ retval = ufx_ops_check_var(&info->var, info);
+- check_warn_goto_error(retval, "error %d ufx_ops_check_var", retval);
++ if (retval < 0) {
++ dev_err(dev->gdev, "error %d ufx_ops_check_var", retval);
++ goto reset_active;
++ }
+
+ dev_dbg(dev->gdev, "setting par");
+ retval = ufx_ops_set_par(info);
+- check_warn_goto_error(retval, "error %d ufx_ops_set_par", retval);
++ if (retval < 0) {
++ dev_err(dev->gdev, "error %d ufx_ops_set_par", retval);
++ goto reset_active;
++ }
+
+ dev_dbg(dev->gdev, "registering framebuffer");
+ retval = register_framebuffer(info);
+- check_warn_goto_error(retval, "error %d register_framebuffer", retval);
++ if (retval < 0) {
++ dev_err(dev->gdev, "error %d register_framebuffer", retval);
++ goto reset_active;
++ }
+
+ dev_info(dev->gdev, "SMSC UDX USB device /dev/fb%d attached. %dx%d resolution."
+ " Using %dK framebuffer memory\n", info->node,
+@@ -1728,21 +1742,23 @@ static int ufx_usb_probe(struct usb_interface *interface,
+
+ return 0;
+
+-error:
+- fb_dealloc_cmap(&info->cmap);
+-destroy_modedb:
++reset_active:
++ atomic_set(&dev->usb_active, 0);
++setup_modes:
+ fb_destroy_modedb(info->monspecs.modedb);
+ vfree(info->screen_base);
+ fb_destroy_modelist(&info->modelist);
++error:
++ fb_dealloc_cmap(&info->cmap);
++destroy_modedb:
+ framebuffer_release(info);
++free_urb_list:
++ if (dev->urbs.count > 0)
++ ufx_free_urb_list(dev);
+ put_ref:
+ kref_put(&dev->kref, ufx_free); /* ref for framebuffer */
+ kref_put(&dev->kref, ufx_free); /* last ref from kref_init */
+ return retval;
+-
+-e_nomem:
+- retval = -ENOMEM;
+- goto put_ref;
+ }
+
+ static void ufx_usb_disconnect(struct usb_interface *interface)
+diff --git a/drivers/watchdog/diag288_wdt.c b/drivers/watchdog/diag288_wdt.c
+index aafc8d98bf9fd..370f648cb4b1a 100644
+--- a/drivers/watchdog/diag288_wdt.c
++++ b/drivers/watchdog/diag288_wdt.c
+@@ -86,7 +86,7 @@ static int __diag288(unsigned int func, unsigned int timeout,
+ "1:\n"
+ EX_TABLE(0b, 1b)
+ : "+d" (err) : "d"(__func), "d"(__timeout),
+- "d"(__action), "d"(__len) : "1", "cc");
++ "d"(__action), "d"(__len) : "1", "cc", "memory");
+ return err;
+ }
+
+@@ -272,12 +272,21 @@ static int __init diag288_init(void)
+ char ebc_begin[] = {
+ 194, 197, 199, 201, 213
+ };
++ char *ebc_cmd;
+
+ watchdog_set_nowayout(&wdt_dev, nowayout_info);
+
+ if (MACHINE_IS_VM) {
+- if (__diag288_vm(WDT_FUNC_INIT, 15,
+- ebc_begin, sizeof(ebc_begin)) != 0) {
++ ebc_cmd = kmalloc(sizeof(ebc_begin), GFP_KERNEL);
++ if (!ebc_cmd) {
++ pr_err("The watchdog cannot be initialized\n");
++ return -ENOMEM;
++ }
++ memcpy(ebc_cmd, ebc_begin, sizeof(ebc_begin));
++ ret = __diag288_vm(WDT_FUNC_INIT, 15,
++ ebc_cmd, sizeof(ebc_begin));
++ kfree(ebc_cmd);
++ if (ret != 0) {
+ pr_err("The watchdog cannot be initialized\n");
+ return -EINVAL;
+ }
+diff --git a/drivers/xen/pvcalls-back.c b/drivers/xen/pvcalls-back.c
+index 9439de2ca0e45..9c267e27d9d95 100644
+--- a/drivers/xen/pvcalls-back.c
++++ b/drivers/xen/pvcalls-back.c
+@@ -129,13 +129,13 @@ static bool pvcalls_conn_back_read(void *opaque)
+ if (masked_prod < masked_cons) {
+ vec[0].iov_base = data->in + masked_prod;
+ vec[0].iov_len = wanted;
+- iov_iter_kvec(&msg.msg_iter, WRITE, vec, 1, wanted);
++ iov_iter_kvec(&msg.msg_iter, READ, vec, 1, wanted);
+ } else {
+ vec[0].iov_base = data->in + masked_prod;
+ vec[0].iov_len = array_size - masked_prod;
+ vec[1].iov_base = data->in;
+ vec[1].iov_len = wanted - vec[0].iov_len;
+- iov_iter_kvec(&msg.msg_iter, WRITE, vec, 2, wanted);
++ iov_iter_kvec(&msg.msg_iter, READ, vec, 2, wanted);
+ }
+
+ atomic_set(&map->read, 0);
+@@ -188,13 +188,13 @@ static bool pvcalls_conn_back_write(struct sock_mapping *map)
+ if (pvcalls_mask(prod, array_size) > pvcalls_mask(cons, array_size)) {
+ vec[0].iov_base = data->out + pvcalls_mask(cons, array_size);
+ vec[0].iov_len = size;
+- iov_iter_kvec(&msg.msg_iter, READ, vec, 1, size);
++ iov_iter_kvec(&msg.msg_iter, WRITE, vec, 1, size);
+ } else {
+ vec[0].iov_base = data->out + pvcalls_mask(cons, array_size);
+ vec[0].iov_len = array_size - pvcalls_mask(cons, array_size);
+ vec[1].iov_base = data->out;
+ vec[1].iov_len = size - vec[0].iov_len;
+- iov_iter_kvec(&msg.msg_iter, READ, vec, 2, size);
++ iov_iter_kvec(&msg.msg_iter, WRITE, vec, 2, size);
+ }
+
+ atomic_set(&map->write, 0);
+diff --git a/fs/aio.c b/fs/aio.c
+index fb92c32a6f1e9..1ec5a773d09c5 100644
+--- a/fs/aio.c
++++ b/fs/aio.c
+@@ -336,6 +336,9 @@ static int aio_ring_mremap(struct vm_area_struct *vma)
+ spin_lock(&mm->ioctx_lock);
+ rcu_read_lock();
+ table = rcu_dereference(mm->ioctx_table);
++ if (!table)
++ goto out_unlock;
++
+ for (i = 0; i < table->nr; i++) {
+ struct kioctx *ctx;
+
+@@ -349,6 +352,7 @@ static int aio_ring_mremap(struct vm_area_struct *vma)
+ }
+ }
+
++out_unlock:
+ rcu_read_unlock();
+ spin_unlock(&mm->ioctx_lock);
+ return res;
+diff --git a/fs/btrfs/volumes.c b/fs/btrfs/volumes.c
+index 548de841cee53..3fa972a43b5e1 100644
+--- a/fs/btrfs/volumes.c
++++ b/fs/btrfs/volumes.c
+@@ -354,6 +354,7 @@ void btrfs_free_device(struct btrfs_device *device)
+ static void free_fs_devices(struct btrfs_fs_devices *fs_devices)
+ {
+ struct btrfs_device *device;
++
+ WARN_ON(fs_devices->opened);
+ while (!list_empty(&fs_devices->devices)) {
+ device = list_entry(fs_devices->devices.next,
+@@ -1401,6 +1402,17 @@ int btrfs_close_devices(struct btrfs_fs_devices *fs_devices)
+ if (!fs_devices->opened) {
+ seed_devices = fs_devices->seed;
+ fs_devices->seed = NULL;
++
++ /*
++ * If the struct btrfs_fs_devices is not assembled with any
++ * other device, it can be re-initialized during the next mount
++ * without the needing device-scan step. Therefore, it can be
++ * fully freed.
++ */
++ if (fs_devices->num_devices == 1) {
++ list_del(&fs_devices->fs_list);
++ free_fs_devices(fs_devices);
++ }
+ }
+ mutex_unlock(&uuid_mutex);
+
+@@ -1701,7 +1713,7 @@ again:
+ goto out;
+ }
+
+- while (1) {
++ while (search_start < search_end) {
+ l = path->nodes[0];
+ slot = path->slots[0];
+ if (slot >= btrfs_header_nritems(l)) {
+@@ -1724,6 +1736,9 @@ again:
+ if (key.type != BTRFS_DEV_EXTENT_KEY)
+ goto next;
+
++ if (key.offset > search_end)
++ break;
++
+ if (key.offset > search_start) {
+ hole_size = key.offset - search_start;
+
+@@ -1794,6 +1809,7 @@ next:
+ else
+ ret = 0;
+
++ ASSERT(max_hole_start + max_hole_size <= search_end);
+ out:
+ btrfs_free_path(path);
+ *start = max_hole_start;
+diff --git a/fs/btrfs/zlib.c b/fs/btrfs/zlib.c
+index df1aace5df501..9385d0bb276dd 100644
+--- a/fs/btrfs/zlib.c
++++ b/fs/btrfs/zlib.c
+@@ -74,7 +74,7 @@ static struct list_head *zlib_alloc_workspace(unsigned int level)
+
+ workspacesize = max(zlib_deflate_workspacesize(MAX_WBITS, MAX_MEM_LEVEL),
+ zlib_inflate_workspacesize());
+- workspace->strm.workspace = kvmalloc(workspacesize, GFP_KERNEL);
++ workspace->strm.workspace = kvzalloc(workspacesize, GFP_KERNEL);
+ workspace->level = level;
+ workspace->buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!workspace->strm.workspace || !workspace->buf)
+diff --git a/fs/ceph/mds_client.c b/fs/ceph/mds_client.c
+index 37fb71797b341..3bf81fe5f10a3 100644
+--- a/fs/ceph/mds_client.c
++++ b/fs/ceph/mds_client.c
+@@ -3151,6 +3151,12 @@ static void handle_session(struct ceph_mds_session *session,
+ break;
+
+ case CEPH_SESSION_FLUSHMSG:
++ /* flush cap releases */
++ spin_lock(&session->s_cap_lock);
++ if (session->s_num_cap_releases)
++ ceph_flush_cap_releases(mdsc, session);
++ spin_unlock(&session->s_cap_lock);
++
+ send_flushmsg_ack(mdsc, session, seq);
+ break;
+
+diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c
+index ef71d9cda5851..939be9df0207e 100644
+--- a/fs/f2fs/gc.c
++++ b/fs/f2fs/gc.c
+@@ -612,7 +612,7 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
+ {
+ struct page *node_page;
+ nid_t nid;
+- unsigned int ofs_in_node, max_addrs;
++ unsigned int ofs_in_node, max_addrs, base;
+ block_t source_blkaddr;
+
+ nid = le32_to_cpu(sum->nid);
+@@ -638,11 +638,17 @@ static bool is_alive(struct f2fs_sb_info *sbi, struct f2fs_summary *sum,
+ return false;
+ }
+
+- max_addrs = IS_INODE(node_page) ? DEF_ADDRS_PER_INODE :
+- DEF_ADDRS_PER_BLOCK;
+- if (ofs_in_node >= max_addrs) {
+- f2fs_err(sbi, "Inconsistent ofs_in_node:%u in summary, ino:%u, nid:%u, max:%u",
+- ofs_in_node, dni->ino, dni->nid, max_addrs);
++ if (IS_INODE(node_page)) {
++ base = offset_in_addr(F2FS_INODE(node_page));
++ max_addrs = DEF_ADDRS_PER_INODE;
++ } else {
++ base = 0;
++ max_addrs = DEF_ADDRS_PER_BLOCK;
++ }
++
++ if (base + ofs_in_node >= max_addrs) {
++ f2fs_err(sbi, "Inconsistent blkaddr offset: base:%u, ofs_in_node:%u, max:%u, ino:%u, nid:%u",
++ base, ofs_in_node, max_addrs, dni->ino, dni->nid);
+ f2fs_put_page(node_page, 1);
+ return false;
+ }
+diff --git a/fs/nilfs2/ioctl.c b/fs/nilfs2/ioctl.c
+index 91b9dac6b2cc0..262783cd79cc3 100644
+--- a/fs/nilfs2/ioctl.c
++++ b/fs/nilfs2/ioctl.c
+@@ -1130,7 +1130,14 @@ static int nilfs_ioctl_set_alloc_range(struct inode *inode, void __user *argp)
+
+ minseg = range[0] + segbytes - 1;
+ do_div(minseg, segbytes);
++
++ if (range[1] < 4096)
++ goto out;
++
+ maxseg = NILFS_SB2_OFFSET_BYTES(range[1]);
++ if (maxseg < segbytes)
++ goto out;
++
+ do_div(maxseg, segbytes);
+ maxseg--;
+
+diff --git a/fs/nilfs2/super.c b/fs/nilfs2/super.c
+index 049768a22388e..b1015e97f37b7 100644
+--- a/fs/nilfs2/super.c
++++ b/fs/nilfs2/super.c
+@@ -403,6 +403,15 @@ int nilfs_resize_fs(struct super_block *sb, __u64 newsize)
+ if (newsize > devsize)
+ goto out;
+
++ /*
++ * Prevent underflow in second superblock position calculation.
++ * The exact minimum size check is done in nilfs_sufile_resize().
++ */
++ if (newsize < 4096) {
++ ret = -ENOSPC;
++ goto out;
++ }
++
+ /*
+ * Write lock is required to protect some functions depending
+ * on the number of segments, the number of reserved segments,
+diff --git a/fs/nilfs2/the_nilfs.c b/fs/nilfs2/the_nilfs.c
+index 74ef3d313686f..6541e29a8b200 100644
+--- a/fs/nilfs2/the_nilfs.c
++++ b/fs/nilfs2/the_nilfs.c
+@@ -517,9 +517,15 @@ static int nilfs_load_super_block(struct the_nilfs *nilfs,
+ {
+ struct nilfs_super_block **sbp = nilfs->ns_sbp;
+ struct buffer_head **sbh = nilfs->ns_sbh;
+- u64 sb2off = NILFS_SB2_OFFSET_BYTES(nilfs->ns_bdev->bd_inode->i_size);
++ u64 sb2off, devsize = nilfs->ns_bdev->bd_inode->i_size;
+ int valid[2], swp = 0;
+
++ if (devsize < NILFS_SEG_MIN_BLOCKS * NILFS_MIN_BLOCK_SIZE + 4096) {
++ nilfs_msg(sb, KERN_ERR, "device size too small");
++ return -EINVAL;
++ }
++ sb2off = NILFS_SB2_OFFSET_BYTES(devsize);
++
+ sbp[0] = nilfs_read_super_block(sb, NILFS_SB_OFFSET_BYTES, blocksize,
+ &sbh[0]);
+ sbp[1] = nilfs_read_super_block(sb, sb2off, blocksize, &sbh[1]);
+diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c
+index d9633e5a5ddd2..0bdfef023d345 100644
+--- a/fs/proc/task_mmu.c
++++ b/fs/proc/task_mmu.c
+@@ -723,9 +723,7 @@ static int smaps_hugetlb_range(pte_t *pte, unsigned long hmask,
+ page = device_private_entry_to_page(swpent);
+ }
+ if (page) {
+- int mapcount = page_mapcount(page);
+-
+- if (mapcount >= 2)
++ if (page_mapcount(page) >= 2 || hugetlb_pmd_shared(pte))
+ mss->shared_hugetlb += huge_page_size(hstate_vma(vma));
+ else
+ mss->private_hugetlb += huge_page_size(hstate_vma(vma));
+diff --git a/fs/squashfs/squashfs_fs.h b/fs/squashfs/squashfs_fs.h
+index 236664d691419..ccfaf8c81958d 100644
+--- a/fs/squashfs/squashfs_fs.h
++++ b/fs/squashfs/squashfs_fs.h
+@@ -183,7 +183,7 @@ static inline int squashfs_block_size(__le32 raw)
+ #define SQUASHFS_ID_BLOCK_BYTES(A) (SQUASHFS_ID_BLOCKS(A) *\
+ sizeof(u64))
+ /* xattr id lookup table defines */
+-#define SQUASHFS_XATTR_BYTES(A) ((A) * sizeof(struct squashfs_xattr_id))
++#define SQUASHFS_XATTR_BYTES(A) (((u64) (A)) * sizeof(struct squashfs_xattr_id))
+
+ #define SQUASHFS_XATTR_BLOCK(A) (SQUASHFS_XATTR_BYTES(A) / \
+ SQUASHFS_METADATA_SIZE)
+diff --git a/fs/squashfs/squashfs_fs_sb.h b/fs/squashfs/squashfs_fs_sb.h
+index 166e98806265b..8f9445e290e72 100644
+--- a/fs/squashfs/squashfs_fs_sb.h
++++ b/fs/squashfs/squashfs_fs_sb.h
+@@ -63,7 +63,7 @@ struct squashfs_sb_info {
+ long long bytes_used;
+ unsigned int inodes;
+ unsigned int fragments;
+- int xattr_ids;
++ unsigned int xattr_ids;
+ unsigned int ids;
+ };
+ #endif
+diff --git a/fs/squashfs/xattr.h b/fs/squashfs/xattr.h
+index d8a270d3ac4cb..f1a463d8bfa02 100644
+--- a/fs/squashfs/xattr.h
++++ b/fs/squashfs/xattr.h
+@@ -10,12 +10,12 @@
+
+ #ifdef CONFIG_SQUASHFS_XATTR
+ extern __le64 *squashfs_read_xattr_id_table(struct super_block *, u64,
+- u64 *, int *);
++ u64 *, unsigned int *);
+ extern int squashfs_xattr_lookup(struct super_block *, unsigned int, int *,
+ unsigned int *, unsigned long long *);
+ #else
+ static inline __le64 *squashfs_read_xattr_id_table(struct super_block *sb,
+- u64 start, u64 *xattr_table_start, int *xattr_ids)
++ u64 start, u64 *xattr_table_start, unsigned int *xattr_ids)
+ {
+ struct squashfs_xattr_id_table *id_table;
+
+diff --git a/fs/squashfs/xattr_id.c b/fs/squashfs/xattr_id.c
+index 087cab8c78f4e..c8469c656e0dc 100644
+--- a/fs/squashfs/xattr_id.c
++++ b/fs/squashfs/xattr_id.c
+@@ -56,7 +56,7 @@ int squashfs_xattr_lookup(struct super_block *sb, unsigned int index,
+ * Read uncompressed xattr id lookup table indexes from disk into memory
+ */
+ __le64 *squashfs_read_xattr_id_table(struct super_block *sb, u64 table_start,
+- u64 *xattr_table_start, int *xattr_ids)
++ u64 *xattr_table_start, unsigned int *xattr_ids)
+ {
+ struct squashfs_sb_info *msblk = sb->s_fs_info;
+ unsigned int len, indexes;
+diff --git a/fs/xfs/libxfs/xfs_defer.c b/fs/xfs/libxfs/xfs_defer.c
+index 8cc3faa624049..3a78a189ea018 100644
+--- a/fs/xfs/libxfs/xfs_defer.c
++++ b/fs/xfs/libxfs/xfs_defer.c
+@@ -16,6 +16,8 @@
+ #include "xfs_inode.h"
+ #include "xfs_inode_item.h"
+ #include "xfs_trace.h"
++#include "xfs_icache.h"
++#include "xfs_log.h"
+
+ /*
+ * Deferred Operations in XFS
+@@ -178,6 +180,19 @@ static const struct xfs_defer_op_type *defer_op_types[] = {
+ [XFS_DEFER_OPS_TYPE_AGFL_FREE] = &xfs_agfl_free_defer_type,
+ };
+
++static void
++xfs_defer_create_intent(
++ struct xfs_trans *tp,
++ struct xfs_defer_pending *dfp,
++ bool sort)
++{
++ const struct xfs_defer_op_type *ops = defer_op_types[dfp->dfp_type];
++
++ if (!dfp->dfp_intent)
++ dfp->dfp_intent = ops->create_intent(tp, &dfp->dfp_work,
++ dfp->dfp_count, sort);
++}
++
+ /*
+ * For each pending item in the intake list, log its intent item and the
+ * associated extents, then add the entire intake list to the end of
+@@ -187,17 +202,11 @@ STATIC void
+ xfs_defer_create_intents(
+ struct xfs_trans *tp)
+ {
+- struct list_head *li;
+ struct xfs_defer_pending *dfp;
+- const struct xfs_defer_op_type *ops;
+
+ list_for_each_entry(dfp, &tp->t_dfops, dfp_list) {
+- ops = defer_op_types[dfp->dfp_type];
+- dfp->dfp_intent = ops->create_intent(tp, dfp->dfp_count);
+ trace_xfs_defer_create_intent(tp->t_mountp, dfp);
+- list_sort(tp->t_mountp, &dfp->dfp_work, ops->diff_items);
+- list_for_each(li, &dfp->dfp_work)
+- ops->log_item(tp, dfp->dfp_intent, li);
++ xfs_defer_create_intent(tp, dfp, true);
+ }
+ }
+
+@@ -353,6 +362,106 @@ xfs_defer_cancel_list(
+ }
+ }
+
++/*
++ * Prevent a log intent item from pinning the tail of the log by logging a
++ * done item to release the intent item; and then log a new intent item.
++ * The caller should provide a fresh transaction and roll it after we're done.
++ */
++static int
++xfs_defer_relog(
++ struct xfs_trans **tpp,
++ struct list_head *dfops)
++{
++ struct xlog *log = (*tpp)->t_mountp->m_log;
++ struct xfs_defer_pending *dfp;
++ xfs_lsn_t threshold_lsn = NULLCOMMITLSN;
++
++
++ ASSERT((*tpp)->t_flags & XFS_TRANS_PERM_LOG_RES);
++
++ list_for_each_entry(dfp, dfops, dfp_list) {
++ /*
++ * If the log intent item for this deferred op is not a part of
++ * the current log checkpoint, relog the intent item to keep
++ * the log tail moving forward. We're ok with this being racy
++ * because an incorrect decision means we'll be a little slower
++ * at pushing the tail.
++ */
++ if (dfp->dfp_intent == NULL ||
++ xfs_log_item_in_current_chkpt(dfp->dfp_intent))
++ continue;
++
++ /*
++ * Figure out where we need the tail to be in order to maintain
++ * the minimum required free space in the log. Only sample
++ * the log threshold once per call.
++ */
++ if (threshold_lsn == NULLCOMMITLSN) {
++ threshold_lsn = xlog_grant_push_threshold(log, 0);
++ if (threshold_lsn == NULLCOMMITLSN)
++ break;
++ }
++ if (XFS_LSN_CMP(dfp->dfp_intent->li_lsn, threshold_lsn) >= 0)
++ continue;
++
++ trace_xfs_defer_relog_intent((*tpp)->t_mountp, dfp);
++ XFS_STATS_INC((*tpp)->t_mountp, defer_relog);
++ dfp->dfp_intent = xfs_trans_item_relog(dfp->dfp_intent, *tpp);
++ }
++
++ if ((*tpp)->t_flags & XFS_TRANS_DIRTY)
++ return xfs_defer_trans_roll(tpp);
++ return 0;
++}
++
++/*
++ * Log an intent-done item for the first pending intent, and finish the work
++ * items.
++ */
++static int
++xfs_defer_finish_one(
++ struct xfs_trans *tp,
++ struct xfs_defer_pending *dfp)
++{
++ const struct xfs_defer_op_type *ops = defer_op_types[dfp->dfp_type];
++ void *state = NULL;
++ struct list_head *li, *n;
++ int error;
++
++ trace_xfs_defer_pending_finish(tp->t_mountp, dfp);
++
++ dfp->dfp_done = ops->create_done(tp, dfp->dfp_intent, dfp->dfp_count);
++ list_for_each_safe(li, n, &dfp->dfp_work) {
++ list_del(li);
++ dfp->dfp_count--;
++ error = ops->finish_item(tp, li, dfp->dfp_done, &state);
++ if (error == -EAGAIN) {
++ /*
++ * Caller wants a fresh transaction; put the work item
++ * back on the list and log a new log intent item to
++ * replace the old one. See "Requesting a Fresh
++ * Transaction while Finishing Deferred Work" above.
++ */
++ list_add(li, &dfp->dfp_work);
++ dfp->dfp_count++;
++ dfp->dfp_done = NULL;
++ dfp->dfp_intent = NULL;
++ xfs_defer_create_intent(tp, dfp, false);
++ }
++
++ if (error)
++ goto out;
++ }
++
++ /* Done with the dfp, free it. */
++ list_del(&dfp->dfp_list);
++ kmem_free(dfp);
++out:
++ if (ops->finish_cleanup)
++ ops->finish_cleanup(tp, state, error);
++ return error;
++}
++
+ /*
+ * Finish all the pending work. This involves logging intent items for
+ * any work items that wandered in since the last transaction roll (if
+@@ -366,11 +475,7 @@ xfs_defer_finish_noroll(
+ struct xfs_trans **tp)
+ {
+ struct xfs_defer_pending *dfp;
+- struct list_head *li;
+- struct list_head *n;
+- void *state;
+ int error = 0;
+- const struct xfs_defer_op_type *ops;
+ LIST_HEAD(dop_pending);
+
+ ASSERT((*tp)->t_flags & XFS_TRANS_PERM_LOG_RES);
+@@ -379,87 +484,44 @@ xfs_defer_finish_noroll(
+
+ /* Until we run out of pending work to finish... */
+ while (!list_empty(&dop_pending) || !list_empty(&(*tp)->t_dfops)) {
+- /* log intents and pull in intake items */
+- xfs_defer_create_intents(*tp);
+- list_splice_tail_init(&(*tp)->t_dfops, &dop_pending);
+-
+ /*
+- * Roll the transaction.
++ * Deferred items that are created in the process of finishing
++ * other deferred work items should be queued at the head of
++ * the pending list, which puts them ahead of the deferred work
++ * that was created by the caller. This keeps the number of
++ * pending work items to a minimum, which decreases the amount
++ * of time that any one intent item can stick around in memory,
++ * pinning the log tail.
+ */
++ xfs_defer_create_intents(*tp);
++ list_splice_init(&(*tp)->t_dfops, &dop_pending);
++
+ error = xfs_defer_trans_roll(tp);
+ if (error)
+- goto out;
++ goto out_shutdown;
++
++ /* Possibly relog intent items to keep the log moving. */
++ error = xfs_defer_relog(tp, &dop_pending);
++ if (error)
++ goto out_shutdown;
+
+- /* Log an intent-done item for the first pending item. */
+ dfp = list_first_entry(&dop_pending, struct xfs_defer_pending,
+ dfp_list);
+- ops = defer_op_types[dfp->dfp_type];
+- trace_xfs_defer_pending_finish((*tp)->t_mountp, dfp);
+- dfp->dfp_done = ops->create_done(*tp, dfp->dfp_intent,
+- dfp->dfp_count);
+-
+- /* Finish the work items. */
+- state = NULL;
+- list_for_each_safe(li, n, &dfp->dfp_work) {
+- list_del(li);
+- dfp->dfp_count--;
+- error = ops->finish_item(*tp, li, dfp->dfp_done,
+- &state);
+- if (error == -EAGAIN) {
+- /*
+- * Caller wants a fresh transaction;
+- * put the work item back on the list
+- * and jump out.
+- */
+- list_add(li, &dfp->dfp_work);
+- dfp->dfp_count++;
+- break;
+- } else if (error) {
+- /*
+- * Clean up after ourselves and jump out.
+- * xfs_defer_cancel will take care of freeing
+- * all these lists and stuff.
+- */
+- if (ops->finish_cleanup)
+- ops->finish_cleanup(*tp, state, error);
+- goto out;
+- }
+- }
+- if (error == -EAGAIN) {
+- /*
+- * Caller wants a fresh transaction, so log a
+- * new log intent item to replace the old one
+- * and roll the transaction. See "Requesting
+- * a Fresh Transaction while Finishing
+- * Deferred Work" above.
+- */
+- dfp->dfp_intent = ops->create_intent(*tp,
+- dfp->dfp_count);
+- dfp->dfp_done = NULL;
+- list_for_each(li, &dfp->dfp_work)
+- ops->log_item(*tp, dfp->dfp_intent, li);
+- } else {
+- /* Done with the dfp, free it. */
+- list_del(&dfp->dfp_list);
+- kmem_free(dfp);
+- }
+-
+- if (ops->finish_cleanup)
+- ops->finish_cleanup(*tp, state, error);
+- }
+-
+-out:
+- if (error) {
+- xfs_defer_trans_abort(*tp, &dop_pending);
+- xfs_force_shutdown((*tp)->t_mountp, SHUTDOWN_CORRUPT_INCORE);
+- trace_xfs_defer_finish_error(*tp, error);
+- xfs_defer_cancel_list((*tp)->t_mountp, &dop_pending);
+- xfs_defer_cancel(*tp);
+- return error;
++ error = xfs_defer_finish_one(*tp, dfp);
++ if (error && error != -EAGAIN)
++ goto out_shutdown;
+ }
+
+ trace_xfs_defer_finish_done(*tp, _RET_IP_);
+ return 0;
++
++out_shutdown:
++ xfs_defer_trans_abort(*tp, &dop_pending);
++ xfs_force_shutdown((*tp)->t_mountp, SHUTDOWN_CORRUPT_INCORE);
++ trace_xfs_defer_finish_error(*tp, error);
++ xfs_defer_cancel_list((*tp)->t_mountp, &dop_pending);
++ xfs_defer_cancel(*tp);
++ return error;
+ }
+
+ int
+@@ -560,3 +622,137 @@ xfs_defer_move(
+
+ xfs_defer_reset(stp);
+ }
++
++/*
++ * Prepare a chain of fresh deferred ops work items to be completed later. Log
++ * recovery requires the ability to put off until later the actual finishing
++ * work so that it can process unfinished items recovered from the log in
++ * correct order.
++ *
++ * Create and log intent items for all the work that we're capturing so that we
++ * can be assured that the items will get replayed if the system goes down
++ * before log recovery gets a chance to finish the work it put off. The entire
++ * deferred ops state is transferred to the capture structure and the
++ * transaction is then ready for the caller to commit it. If there are no
++ * intent items to capture, this function returns NULL.
++ *
++ * If capture_ip is not NULL, the capture structure will obtain an extra
++ * reference to the inode.
++ */
++static struct xfs_defer_capture *
++xfs_defer_ops_capture(
++ struct xfs_trans *tp,
++ struct xfs_inode *capture_ip)
++{
++ struct xfs_defer_capture *dfc;
++
++ if (list_empty(&tp->t_dfops))
++ return NULL;
++
++ /* Create an object to capture the defer ops. */
++ dfc = kmem_zalloc(sizeof(*dfc), KM_NOFS);
++ INIT_LIST_HEAD(&dfc->dfc_list);
++ INIT_LIST_HEAD(&dfc->dfc_dfops);
++
++ xfs_defer_create_intents(tp);
++
++ /* Move the dfops chain and transaction state to the capture struct. */
++ list_splice_init(&tp->t_dfops, &dfc->dfc_dfops);
++ dfc->dfc_tpflags = tp->t_flags & XFS_TRANS_LOWMODE;
++ tp->t_flags &= ~XFS_TRANS_LOWMODE;
++
++ /* Capture the remaining block reservations along with the dfops. */
++ dfc->dfc_blkres = tp->t_blk_res - tp->t_blk_res_used;
++ dfc->dfc_rtxres = tp->t_rtx_res - tp->t_rtx_res_used;
++
++ /* Preserve the log reservation size. */
++ dfc->dfc_logres = tp->t_log_res;
++
++ /*
++ * Grab an extra reference to this inode and attach it to the capture
++ * structure.
++ */
++ if (capture_ip) {
++ ihold(VFS_I(capture_ip));
++ dfc->dfc_capture_ip = capture_ip;
++ }
++
++ return dfc;
++}
++
++/* Release all resources that we used to capture deferred ops. */
++void
++xfs_defer_ops_release(
++ struct xfs_mount *mp,
++ struct xfs_defer_capture *dfc)
++{
++ xfs_defer_cancel_list(mp, &dfc->dfc_dfops);
++ if (dfc->dfc_capture_ip)
++ xfs_irele(dfc->dfc_capture_ip);
++ kmem_free(dfc);
++}
++
++/*
++ * Capture any deferred ops and commit the transaction. This is the last step
++ * needed to finish a log intent item that we recovered from the log. If any
++ * of the deferred ops operate on an inode, the caller must pass in that inode
++ * so that the reference can be transferred to the capture structure. The
++ * caller must hold ILOCK_EXCL on the inode, and must unlock it before calling
++ * xfs_defer_ops_continue.
++ */
++int
++xfs_defer_ops_capture_and_commit(
++ struct xfs_trans *tp,
++ struct xfs_inode *capture_ip,
++ struct list_head *capture_list)
++{
++ struct xfs_mount *mp = tp->t_mountp;
++ struct xfs_defer_capture *dfc;
++ int error;
++
++ ASSERT(!capture_ip || xfs_isilocked(capture_ip, XFS_ILOCK_EXCL));
++
++ /* If we don't capture anything, commit transaction and exit. */
++ dfc = xfs_defer_ops_capture(tp, capture_ip);
++ if (!dfc)
++ return xfs_trans_commit(tp);
++
++ /* Commit the transaction and add the capture structure to the list. */
++ error = xfs_trans_commit(tp);
++ if (error) {
++ xfs_defer_ops_release(mp, dfc);
++ return error;
++ }
++
++ list_add_tail(&dfc->dfc_list, capture_list);
++ return 0;
++}
++
++/*
++ * Attach a chain of captured deferred ops to a new transaction and free the
++ * capture structure. If an inode was captured, it will be passed back to the
++ * caller with ILOCK_EXCL held and joined to the transaction with lockflags==0.
++ * The caller now owns the inode reference.
++ */
++void
++xfs_defer_ops_continue(
++ struct xfs_defer_capture *dfc,
++ struct xfs_trans *tp,
++ struct xfs_inode **captured_ipp)
++{
++ ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES);
++ ASSERT(!(tp->t_flags & XFS_TRANS_DIRTY));
++
++ /* Lock and join the captured inode to the new transaction. */
++ if (dfc->dfc_capture_ip) {
++ xfs_ilock(dfc->dfc_capture_ip, XFS_ILOCK_EXCL);
++ xfs_trans_ijoin(tp, dfc->dfc_capture_ip, 0);
++ }
++ *captured_ipp = dfc->dfc_capture_ip;
++
++ /* Move captured dfops chain and state to the transaction. */
++ list_splice_init(&dfc->dfc_dfops, &tp->t_dfops);
++ tp->t_flags |= dfc->dfc_tpflags;
++
++ kmem_free(dfc);
++}
+diff --git a/fs/xfs/libxfs/xfs_defer.h b/fs/xfs/libxfs/xfs_defer.h
+index 7c28d7608ac62..4c3248d47a350 100644
+--- a/fs/xfs/libxfs/xfs_defer.h
++++ b/fs/xfs/libxfs/xfs_defer.h
+@@ -7,6 +7,7 @@
+ #define __XFS_DEFER_H__
+
+ struct xfs_defer_op_type;
++struct xfs_defer_capture;
+
+ /*
+ * Header for deferred operation list.
+@@ -28,7 +29,7 @@ enum xfs_defer_ops_type {
+ struct xfs_defer_pending {
+ struct list_head dfp_list; /* pending items */
+ struct list_head dfp_work; /* work items */
+- void *dfp_intent; /* log intent item */
++ struct xfs_log_item *dfp_intent; /* log intent item */
+ void *dfp_done; /* log done item */
+ unsigned int dfp_count; /* # extent items */
+ enum xfs_defer_ops_type dfp_type;
+@@ -43,15 +44,15 @@ void xfs_defer_move(struct xfs_trans *dtp, struct xfs_trans *stp);
+
+ /* Description of a deferred type. */
+ struct xfs_defer_op_type {
+- void (*abort_intent)(void *);
+- void *(*create_done)(struct xfs_trans *, void *, unsigned int);
++ struct xfs_log_item *(*create_intent)(struct xfs_trans *tp,
++ struct list_head *items, unsigned int count, bool sort);
++ void (*abort_intent)(struct xfs_log_item *intent);
++ void *(*create_done)(struct xfs_trans *tp, struct xfs_log_item *intent,
++ unsigned int count);
+ int (*finish_item)(struct xfs_trans *, struct list_head *, void *,
+ void **);
+ void (*finish_cleanup)(struct xfs_trans *, void *, int);
+ void (*cancel_item)(struct list_head *);
+- int (*diff_items)(void *, struct list_head *, struct list_head *);
+- void *(*create_intent)(struct xfs_trans *, uint);
+- void (*log_item)(struct xfs_trans *, void *, struct list_head *);
+ unsigned int max_items;
+ };
+
+@@ -61,4 +62,40 @@ extern const struct xfs_defer_op_type xfs_rmap_update_defer_type;
+ extern const struct xfs_defer_op_type xfs_extent_free_defer_type;
+ extern const struct xfs_defer_op_type xfs_agfl_free_defer_type;
+
++/*
++ * This structure enables a dfops user to detach the chain of deferred
++ * operations from a transaction so that they can be continued later.
++ */
++struct xfs_defer_capture {
++ /* List of other capture structures. */
++ struct list_head dfc_list;
++
++ /* Deferred ops state saved from the transaction. */
++ struct list_head dfc_dfops;
++ unsigned int dfc_tpflags;
++
++ /* Block reservations for the data and rt devices. */
++ unsigned int dfc_blkres;
++ unsigned int dfc_rtxres;
++
++ /* Log reservation saved from the transaction. */
++ unsigned int dfc_logres;
++
++ /*
++ * An inode reference that must be maintained to complete the deferred
++ * work.
++ */
++ struct xfs_inode *dfc_capture_ip;
++};
++
++/*
++ * Functions to capture a chain of deferred operations and continue them later.
++ * This doesn't normally happen except log recovery.
++ */
++int xfs_defer_ops_capture_and_commit(struct xfs_trans *tp,
++ struct xfs_inode *capture_ip, struct list_head *capture_list);
++void xfs_defer_ops_continue(struct xfs_defer_capture *d, struct xfs_trans *tp,
++ struct xfs_inode **captured_ipp);
++void xfs_defer_ops_release(struct xfs_mount *mp, struct xfs_defer_capture *d);
++
+ #endif /* __XFS_DEFER_H__ */
+diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c
+index 15d6f947620ff..93357072b19da 100644
+--- a/fs/xfs/libxfs/xfs_inode_fork.c
++++ b/fs/xfs/libxfs/xfs_inode_fork.c
+@@ -592,7 +592,7 @@ void
+ xfs_iflush_fork(
+ xfs_inode_t *ip,
+ xfs_dinode_t *dip,
+- xfs_inode_log_item_t *iip,
++ struct xfs_inode_log_item *iip,
+ int whichfork)
+ {
+ char *cp;
+diff --git a/fs/xfs/libxfs/xfs_trans_inode.c b/fs/xfs/libxfs/xfs_trans_inode.c
+index 0ba7368b9a5f0..1d0e78e0099d9 100644
+--- a/fs/xfs/libxfs/xfs_trans_inode.c
++++ b/fs/xfs/libxfs/xfs_trans_inode.c
+@@ -27,7 +27,7 @@ xfs_trans_ijoin(
+ struct xfs_inode *ip,
+ uint lock_flags)
+ {
+- xfs_inode_log_item_t *iip;
++ struct xfs_inode_log_item *iip;
+
+ ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL));
+ if (ip->i_itemp == NULL)
+diff --git a/fs/xfs/xfs_aops.c b/fs/xfs/xfs_aops.c
+index f16d5f196c6b1..5d9f8e4c4cdee 100644
+--- a/fs/xfs/xfs_aops.c
++++ b/fs/xfs/xfs_aops.c
+@@ -495,7 +495,7 @@ xfs_map_blocks(
+ ssize_t count = i_blocksize(inode);
+ xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset);
+ xfs_fileoff_t end_fsb = XFS_B_TO_FSB(mp, offset + count);
+- xfs_fileoff_t cow_fsb = NULLFILEOFF;
++ xfs_fileoff_t cow_fsb;
+ struct xfs_bmbt_irec imap;
+ struct xfs_iext_cursor icur;
+ int retries = 0;
+@@ -529,6 +529,8 @@ xfs_map_blocks(
+ * landed in a hole and we skip the block.
+ */
+ retry:
++ cow_fsb = NULLFILEOFF;
++ wpc->fork = XFS_DATA_FORK;
+ xfs_ilock(ip, XFS_ILOCK_SHARED);
+ ASSERT(ip->i_d.di_format != XFS_DINODE_FMT_BTREE ||
+ (ip->i_df.if_flags & XFS_IFEXTENTS));
+diff --git a/fs/xfs/xfs_bmap_item.c b/fs/xfs/xfs_bmap_item.c
+index 243e5e0f82a30..7b0c4d9679d96 100644
+--- a/fs/xfs/xfs_bmap_item.c
++++ b/fs/xfs/xfs_bmap_item.c
+@@ -22,6 +22,7 @@
+ #include "xfs_bmap_btree.h"
+ #include "xfs_trans_space.h"
+ #include "xfs_error.h"
++#include "xfs_quota.h"
+
+ kmem_zone_t *xfs_bui_zone;
+ kmem_zone_t *xfs_bud_zone;
+@@ -124,34 +125,6 @@ xfs_bui_item_release(
+ xfs_bui_release(BUI_ITEM(lip));
+ }
+
+-static const struct xfs_item_ops xfs_bui_item_ops = {
+- .iop_size = xfs_bui_item_size,
+- .iop_format = xfs_bui_item_format,
+- .iop_unpin = xfs_bui_item_unpin,
+- .iop_release = xfs_bui_item_release,
+-};
+-
+-/*
+- * Allocate and initialize an bui item with the given number of extents.
+- */
+-struct xfs_bui_log_item *
+-xfs_bui_init(
+- struct xfs_mount *mp)
+-
+-{
+- struct xfs_bui_log_item *buip;
+-
+- buip = kmem_zone_zalloc(xfs_bui_zone, 0);
+-
+- xfs_log_item_init(mp, &buip->bui_item, XFS_LI_BUI, &xfs_bui_item_ops);
+- buip->bui_format.bui_nextents = XFS_BUI_MAX_FAST_EXTENTS;
+- buip->bui_format.bui_id = (uintptr_t)(void *)buip;
+- atomic_set(&buip->bui_next_extent, 0);
+- atomic_set(&buip->bui_refcount, 2);
+-
+- return buip;
+-}
+-
+ static inline struct xfs_bud_log_item *BUD_ITEM(struct xfs_log_item *lip)
+ {
+ return container_of(lip, struct xfs_bud_log_item, bud_item);
+@@ -278,27 +251,6 @@ xfs_bmap_update_diff_items(
+ return ba->bi_owner->i_ino - bb->bi_owner->i_ino;
+ }
+
+-/* Get an BUI. */
+-STATIC void *
+-xfs_bmap_update_create_intent(
+- struct xfs_trans *tp,
+- unsigned int count)
+-{
+- struct xfs_bui_log_item *buip;
+-
+- ASSERT(count == XFS_BUI_MAX_FAST_EXTENTS);
+- ASSERT(tp != NULL);
+-
+- buip = xfs_bui_init(tp->t_mountp);
+- ASSERT(buip != NULL);
+-
+- /*
+- * Get a log_item_desc to point at the new item.
+- */
+- xfs_trans_add_item(tp, &buip->bui_item);
+- return buip;
+-}
+-
+ /* Set the map extent flags for this mapping. */
+ static void
+ xfs_trans_set_bmap_flags(
+@@ -326,16 +278,12 @@ xfs_trans_set_bmap_flags(
+ STATIC void
+ xfs_bmap_update_log_item(
+ struct xfs_trans *tp,
+- void *intent,
+- struct list_head *item)
++ struct xfs_bui_log_item *buip,
++ struct xfs_bmap_intent *bmap)
+ {
+- struct xfs_bui_log_item *buip = intent;
+- struct xfs_bmap_intent *bmap;
+ uint next_extent;
+ struct xfs_map_extent *map;
+
+- bmap = container_of(item, struct xfs_bmap_intent, bi_list);
+-
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ set_bit(XFS_LI_DIRTY, &buip->bui_item.li_flags);
+
+@@ -355,14 +303,35 @@ xfs_bmap_update_log_item(
+ bmap->bi_bmap.br_state);
+ }
+
++static struct xfs_log_item *
++xfs_bmap_update_create_intent(
++ struct xfs_trans *tp,
++ struct list_head *items,
++ unsigned int count,
++ bool sort)
++{
++ struct xfs_mount *mp = tp->t_mountp;
++ struct xfs_bui_log_item *buip = xfs_bui_init(mp);
++ struct xfs_bmap_intent *bmap;
++
++ ASSERT(count == XFS_BUI_MAX_FAST_EXTENTS);
++
++ xfs_trans_add_item(tp, &buip->bui_item);
++ if (sort)
++ list_sort(mp, items, xfs_bmap_update_diff_items);
++ list_for_each_entry(bmap, items, bi_list)
++ xfs_bmap_update_log_item(tp, buip, bmap);
++ return &buip->bui_item;
++}
++
+ /* Get an BUD so we can process all the deferred rmap updates. */
+ STATIC void *
+ xfs_bmap_update_create_done(
+ struct xfs_trans *tp,
+- void *intent,
++ struct xfs_log_item *intent,
+ unsigned int count)
+ {
+- return xfs_trans_get_bud(tp, intent);
++ return xfs_trans_get_bud(tp, BUI_ITEM(intent));
+ }
+
+ /* Process a deferred rmap update. */
+@@ -398,9 +367,9 @@ xfs_bmap_update_finish_item(
+ /* Abort all pending BUIs. */
+ STATIC void
+ xfs_bmap_update_abort_intent(
+- void *intent)
++ struct xfs_log_item *intent)
+ {
+- xfs_bui_release(intent);
++ xfs_bui_release(BUI_ITEM(intent));
+ }
+
+ /* Cancel a deferred rmap update. */
+@@ -416,10 +385,8 @@ xfs_bmap_update_cancel_item(
+
+ const struct xfs_defer_op_type xfs_bmap_update_defer_type = {
+ .max_items = XFS_BUI_MAX_FAST_EXTENTS,
+- .diff_items = xfs_bmap_update_diff_items,
+ .create_intent = xfs_bmap_update_create_intent,
+ .abort_intent = xfs_bmap_update_abort_intent,
+- .log_item = xfs_bmap_update_log_item,
+ .create_done = xfs_bmap_update_create_done,
+ .finish_item = xfs_bmap_update_finish_item,
+ .cancel_item = xfs_bmap_update_cancel_item,
+@@ -431,8 +398,8 @@ const struct xfs_defer_op_type xfs_bmap_update_defer_type = {
+ */
+ int
+ xfs_bui_recover(
+- struct xfs_trans *parent_tp,
+- struct xfs_bui_log_item *buip)
++ struct xfs_bui_log_item *buip,
++ struct list_head *capture_list)
+ {
+ int error = 0;
+ unsigned int bui_type;
+@@ -440,15 +407,13 @@ xfs_bui_recover(
+ xfs_fsblock_t startblock_fsb;
+ xfs_fsblock_t inode_fsb;
+ xfs_filblks_t count;
+- bool op_ok;
+ struct xfs_bud_log_item *budp;
+- enum xfs_bmap_intent_type type;
+ int whichfork;
+ xfs_exntst_t state;
+ struct xfs_trans *tp;
+ struct xfs_inode *ip = NULL;
+ struct xfs_bmbt_irec irec;
+- struct xfs_mount *mp = parent_tp->t_mountp;
++ struct xfs_mount *mp = buip->bui_item.li_mountp;
+
+ ASSERT(!test_bit(XFS_BUI_RECOVERED, &buip->bui_flags));
+
+@@ -468,16 +433,19 @@ xfs_bui_recover(
+ XFS_FSB_TO_DADDR(mp, bmap->me_startblock));
+ inode_fsb = XFS_BB_TO_FSB(mp, XFS_FSB_TO_DADDR(mp,
+ XFS_INO_TO_FSB(mp, bmap->me_owner)));
+- switch (bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK) {
++ state = (bmap->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
++ XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
++ whichfork = (bmap->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ?
++ XFS_ATTR_FORK : XFS_DATA_FORK;
++ bui_type = bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK;
++ switch (bui_type) {
+ case XFS_BMAP_MAP:
+ case XFS_BMAP_UNMAP:
+- op_ok = true;
+ break;
+ default:
+- op_ok = false;
+- break;
++ return -EFSCORRUPTED;
+ }
+- if (!op_ok || startblock_fsb == 0 ||
++ if (startblock_fsb == 0 ||
+ bmap->me_len == 0 ||
+ inode_fsb == 0 ||
+ startblock_fsb >= mp->m_sb.sb_dblocks ||
+@@ -493,52 +461,37 @@ xfs_bui_recover(
+ return -EFSCORRUPTED;
+ }
+
+- error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate,
+- XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK), 0, 0, &tp);
++ /* Grab the inode. */
++ error = xfs_iget(mp, NULL, bmap->me_owner, 0, 0, &ip);
+ if (error)
+ return error;
+- /*
+- * Recovery stashes all deferred ops during intent processing and
+- * finishes them on completion. Transfer current dfops state to this
+- * transaction and transfer the result back before we return.
+- */
+- xfs_defer_move(tp, parent_tp);
+- budp = xfs_trans_get_bud(tp, buip);
+
+- /* Grab the inode. */
+- error = xfs_iget(mp, tp, bmap->me_owner, 0, XFS_ILOCK_EXCL, &ip);
++ error = xfs_qm_dqattach(ip);
+ if (error)
+- goto err_inode;
++ goto err_rele;
+
+ if (VFS_I(ip)->i_nlink == 0)
+ xfs_iflags_set(ip, XFS_IRECOVERY);
+
+- /* Process deferred bmap item. */
+- state = (bmap->me_flags & XFS_BMAP_EXTENT_UNWRITTEN) ?
+- XFS_EXT_UNWRITTEN : XFS_EXT_NORM;
+- whichfork = (bmap->me_flags & XFS_BMAP_EXTENT_ATTR_FORK) ?
+- XFS_ATTR_FORK : XFS_DATA_FORK;
+- bui_type = bmap->me_flags & XFS_BMAP_EXTENT_TYPE_MASK;
+- switch (bui_type) {
+- case XFS_BMAP_MAP:
+- case XFS_BMAP_UNMAP:
+- type = bui_type;
+- break;
+- default:
+- XFS_ERROR_REPORT(__func__, XFS_ERRLEVEL_LOW, mp);
+- error = -EFSCORRUPTED;
+- goto err_inode;
+- }
++ /* Allocate transaction and do the work. */
++ error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate,
++ XFS_EXTENTADD_SPACE_RES(mp, XFS_DATA_FORK), 0, 0, &tp);
++ if (error)
++ goto err_rele;
++
++ budp = xfs_trans_get_bud(tp, buip);
++ xfs_ilock(ip, XFS_ILOCK_EXCL);
+ xfs_trans_ijoin(tp, ip, 0);
+
+ count = bmap->me_len;
+- error = xfs_trans_log_finish_bmap_update(tp, budp, type, ip, whichfork,
+- bmap->me_startoff, bmap->me_startblock, &count, state);
++ error = xfs_trans_log_finish_bmap_update(tp, budp, bui_type, ip,
++ whichfork, bmap->me_startoff, bmap->me_startblock,
++ &count, state);
+ if (error)
+- goto err_inode;
++ goto err_cancel;
+
+ if (count > 0) {
+- ASSERT(type == XFS_BMAP_UNMAP);
++ ASSERT(bui_type == XFS_BMAP_UNMAP);
+ irec.br_startblock = bmap->me_startblock;
+ irec.br_blockcount = count;
+ irec.br_startoff = bmap->me_startoff;
+@@ -547,19 +500,78 @@ xfs_bui_recover(
+ }
+
+ set_bit(XFS_BUI_RECOVERED, &buip->bui_flags);
+- xfs_defer_move(parent_tp, tp);
+- error = xfs_trans_commit(tp);
++ /*
++ * Commit transaction, which frees the transaction and saves the inode
++ * for later replay activities.
++ */
++ error = xfs_defer_ops_capture_and_commit(tp, ip, capture_list);
++ if (error)
++ goto err_unlock;
++
+ xfs_iunlock(ip, XFS_ILOCK_EXCL);
+ xfs_irele(ip);
++ return 0;
+
+- return error;
+-
+-err_inode:
+- xfs_defer_move(parent_tp, tp);
++err_cancel:
+ xfs_trans_cancel(tp);
+- if (ip) {
+- xfs_iunlock(ip, XFS_ILOCK_EXCL);
+- xfs_irele(ip);
+- }
++err_unlock:
++ xfs_iunlock(ip, XFS_ILOCK_EXCL);
++err_rele:
++ xfs_irele(ip);
+ return error;
+ }
++
++/* Relog an intent item to push the log tail forward. */
++static struct xfs_log_item *
++xfs_bui_item_relog(
++ struct xfs_log_item *intent,
++ struct xfs_trans *tp)
++{
++ struct xfs_bud_log_item *budp;
++ struct xfs_bui_log_item *buip;
++ struct xfs_map_extent *extp;
++ unsigned int count;
++
++ count = BUI_ITEM(intent)->bui_format.bui_nextents;
++ extp = BUI_ITEM(intent)->bui_format.bui_extents;
++
++ tp->t_flags |= XFS_TRANS_DIRTY;
++ budp = xfs_trans_get_bud(tp, BUI_ITEM(intent));
++ set_bit(XFS_LI_DIRTY, &budp->bud_item.li_flags);
++
++ buip = xfs_bui_init(tp->t_mountp);
++ memcpy(buip->bui_format.bui_extents, extp, count * sizeof(*extp));
++ atomic_set(&buip->bui_next_extent, count);
++ xfs_trans_add_item(tp, &buip->bui_item);
++ set_bit(XFS_LI_DIRTY, &buip->bui_item.li_flags);
++ return &buip->bui_item;
++}
++
++static const struct xfs_item_ops xfs_bui_item_ops = {
++ .iop_size = xfs_bui_item_size,
++ .iop_format = xfs_bui_item_format,
++ .iop_unpin = xfs_bui_item_unpin,
++ .iop_release = xfs_bui_item_release,
++ .iop_relog = xfs_bui_item_relog,
++};
++
++/*
++ * Allocate and initialize an bui item with the given number of extents.
++ */
++struct xfs_bui_log_item *
++xfs_bui_init(
++ struct xfs_mount *mp)
++
++{
++ struct xfs_bui_log_item *buip;
++
++ buip = kmem_zone_zalloc(xfs_bui_zone, 0);
++
++ xfs_log_item_init(mp, &buip->bui_item, XFS_LI_BUI, &xfs_bui_item_ops);
++ buip->bui_format.bui_nextents = XFS_BUI_MAX_FAST_EXTENTS;
++ buip->bui_format.bui_id = (uintptr_t)(void *)buip;
++ atomic_set(&buip->bui_next_extent, 0);
++ atomic_set(&buip->bui_refcount, 2);
++
++ return buip;
++}
+diff --git a/fs/xfs/xfs_bmap_item.h b/fs/xfs/xfs_bmap_item.h
+index ad479cc73de84..a95e99c269790 100644
+--- a/fs/xfs/xfs_bmap_item.h
++++ b/fs/xfs/xfs_bmap_item.h
+@@ -77,6 +77,7 @@ extern struct kmem_zone *xfs_bud_zone;
+ struct xfs_bui_log_item *xfs_bui_init(struct xfs_mount *);
+ void xfs_bui_item_free(struct xfs_bui_log_item *);
+ void xfs_bui_release(struct xfs_bui_log_item *);
+-int xfs_bui_recover(struct xfs_trans *parent_tp, struct xfs_bui_log_item *buip);
++int xfs_bui_recover(struct xfs_bui_log_item *buip,
++ struct list_head *capture_list);
+
+ #endif /* __XFS_BMAP_ITEM_H__ */
+diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c
+index a05a1074e8f81..de3cdce892fda 100644
+--- a/fs/xfs/xfs_extfree_item.c
++++ b/fs/xfs/xfs_extfree_item.c
+@@ -139,44 +139,6 @@ xfs_efi_item_release(
+ xfs_efi_release(EFI_ITEM(lip));
+ }
+
+-static const struct xfs_item_ops xfs_efi_item_ops = {
+- .iop_size = xfs_efi_item_size,
+- .iop_format = xfs_efi_item_format,
+- .iop_unpin = xfs_efi_item_unpin,
+- .iop_release = xfs_efi_item_release,
+-};
+-
+-
+-/*
+- * Allocate and initialize an efi item with the given number of extents.
+- */
+-struct xfs_efi_log_item *
+-xfs_efi_init(
+- struct xfs_mount *mp,
+- uint nextents)
+-
+-{
+- struct xfs_efi_log_item *efip;
+- uint size;
+-
+- ASSERT(nextents > 0);
+- if (nextents > XFS_EFI_MAX_FAST_EXTENTS) {
+- size = (uint)(sizeof(xfs_efi_log_item_t) +
+- ((nextents - 1) * sizeof(xfs_extent_t)));
+- efip = kmem_zalloc(size, 0);
+- } else {
+- efip = kmem_zone_zalloc(xfs_efi_zone, 0);
+- }
+-
+- xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops);
+- efip->efi_format.efi_nextents = nextents;
+- efip->efi_format.efi_id = (uintptr_t)(void *)efip;
+- atomic_set(&efip->efi_next_extent, 0);
+- atomic_set(&efip->efi_refcount, 2);
+-
+- return efip;
+-}
+-
+ /*
+ * Copy an EFI format buffer from the given buf, and into the destination
+ * EFI format structure.
+@@ -412,41 +374,16 @@ xfs_extent_free_diff_items(
+ XFS_FSB_TO_AGNO(mp, rb->xefi_startblock);
+ }
+
+-/* Get an EFI. */
+-STATIC void *
+-xfs_extent_free_create_intent(
+- struct xfs_trans *tp,
+- unsigned int count)
+-{
+- struct xfs_efi_log_item *efip;
+-
+- ASSERT(tp != NULL);
+- ASSERT(count > 0);
+-
+- efip = xfs_efi_init(tp->t_mountp, count);
+- ASSERT(efip != NULL);
+-
+- /*
+- * Get a log_item_desc to point at the new item.
+- */
+- xfs_trans_add_item(tp, &efip->efi_item);
+- return efip;
+-}
+-
+ /* Log a free extent to the intent item. */
+ STATIC void
+ xfs_extent_free_log_item(
+ struct xfs_trans *tp,
+- void *intent,
+- struct list_head *item)
++ struct xfs_efi_log_item *efip,
++ struct xfs_extent_free_item *free)
+ {
+- struct xfs_efi_log_item *efip = intent;
+- struct xfs_extent_free_item *free;
+ uint next_extent;
+ struct xfs_extent *extp;
+
+- free = container_of(item, struct xfs_extent_free_item, xefi_list);
+-
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ set_bit(XFS_LI_DIRTY, &efip->efi_item.li_flags);
+
+@@ -462,14 +399,35 @@ xfs_extent_free_log_item(
+ extp->ext_len = free->xefi_blockcount;
+ }
+
++static struct xfs_log_item *
++xfs_extent_free_create_intent(
++ struct xfs_trans *tp,
++ struct list_head *items,
++ unsigned int count,
++ bool sort)
++{
++ struct xfs_mount *mp = tp->t_mountp;
++ struct xfs_efi_log_item *efip = xfs_efi_init(mp, count);
++ struct xfs_extent_free_item *free;
++
++ ASSERT(count > 0);
++
++ xfs_trans_add_item(tp, &efip->efi_item);
++ if (sort)
++ list_sort(mp, items, xfs_extent_free_diff_items);
++ list_for_each_entry(free, items, xefi_list)
++ xfs_extent_free_log_item(tp, efip, free);
++ return &efip->efi_item;
++}
++
+ /* Get an EFD so we can process all the free extents. */
+ STATIC void *
+ xfs_extent_free_create_done(
+ struct xfs_trans *tp,
+- void *intent,
++ struct xfs_log_item *intent,
+ unsigned int count)
+ {
+- return xfs_trans_get_efd(tp, intent, count);
++ return xfs_trans_get_efd(tp, EFI_ITEM(intent), count);
+ }
+
+ /* Process a free extent. */
+@@ -495,9 +453,9 @@ xfs_extent_free_finish_item(
+ /* Abort all pending EFIs. */
+ STATIC void
+ xfs_extent_free_abort_intent(
+- void *intent)
++ struct xfs_log_item *intent)
+ {
+- xfs_efi_release(intent);
++ xfs_efi_release(EFI_ITEM(intent));
+ }
+
+ /* Cancel a free extent. */
+@@ -513,10 +471,8 @@ xfs_extent_free_cancel_item(
+
+ const struct xfs_defer_op_type xfs_extent_free_defer_type = {
+ .max_items = XFS_EFI_MAX_FAST_EXTENTS,
+- .diff_items = xfs_extent_free_diff_items,
+ .create_intent = xfs_extent_free_create_intent,
+ .abort_intent = xfs_extent_free_abort_intent,
+- .log_item = xfs_extent_free_log_item,
+ .create_done = xfs_extent_free_create_done,
+ .finish_item = xfs_extent_free_finish_item,
+ .cancel_item = xfs_extent_free_cancel_item,
+@@ -579,10 +535,8 @@ xfs_agfl_free_finish_item(
+ /* sub-type with special handling for AGFL deferred frees */
+ const struct xfs_defer_op_type xfs_agfl_free_defer_type = {
+ .max_items = XFS_EFI_MAX_FAST_EXTENTS,
+- .diff_items = xfs_extent_free_diff_items,
+ .create_intent = xfs_extent_free_create_intent,
+ .abort_intent = xfs_extent_free_abort_intent,
+- .log_item = xfs_extent_free_log_item,
+ .create_done = xfs_extent_free_create_done,
+ .finish_item = xfs_agfl_free_finish_item,
+ .cancel_item = xfs_extent_free_cancel_item,
+@@ -594,9 +548,10 @@ const struct xfs_defer_op_type xfs_agfl_free_defer_type = {
+ */
+ int
+ xfs_efi_recover(
+- struct xfs_mount *mp,
+- struct xfs_efi_log_item *efip)
++ struct xfs_efi_log_item *efip,
++ struct list_head *capture_list)
+ {
++ struct xfs_mount *mp = efip->efi_item.li_mountp;
+ struct xfs_efd_log_item *efdp;
+ struct xfs_trans *tp;
+ int i;
+@@ -645,10 +600,76 @@ xfs_efi_recover(
+ }
+
+ set_bit(XFS_EFI_RECOVERED, &efip->efi_flags);
+- error = xfs_trans_commit(tp);
+- return error;
++
++ return xfs_defer_ops_capture_and_commit(tp, NULL, capture_list);
+
+ abort_error:
+ xfs_trans_cancel(tp);
+ return error;
+ }
++
++/* Relog an intent item to push the log tail forward. */
++static struct xfs_log_item *
++xfs_efi_item_relog(
++ struct xfs_log_item *intent,
++ struct xfs_trans *tp)
++{
++ struct xfs_efd_log_item *efdp;
++ struct xfs_efi_log_item *efip;
++ struct xfs_extent *extp;
++ unsigned int count;
++
++ count = EFI_ITEM(intent)->efi_format.efi_nextents;
++ extp = EFI_ITEM(intent)->efi_format.efi_extents;
++
++ tp->t_flags |= XFS_TRANS_DIRTY;
++ efdp = xfs_trans_get_efd(tp, EFI_ITEM(intent), count);
++ efdp->efd_next_extent = count;
++ memcpy(efdp->efd_format.efd_extents, extp, count * sizeof(*extp));
++ set_bit(XFS_LI_DIRTY, &efdp->efd_item.li_flags);
++
++ efip = xfs_efi_init(tp->t_mountp, count);
++ memcpy(efip->efi_format.efi_extents, extp, count * sizeof(*extp));
++ atomic_set(&efip->efi_next_extent, count);
++ xfs_trans_add_item(tp, &efip->efi_item);
++ set_bit(XFS_LI_DIRTY, &efip->efi_item.li_flags);
++ return &efip->efi_item;
++}
++
++static const struct xfs_item_ops xfs_efi_item_ops = {
++ .iop_size = xfs_efi_item_size,
++ .iop_format = xfs_efi_item_format,
++ .iop_unpin = xfs_efi_item_unpin,
++ .iop_release = xfs_efi_item_release,
++ .iop_relog = xfs_efi_item_relog,
++};
++
++/*
++ * Allocate and initialize an efi item with the given number of extents.
++ */
++struct xfs_efi_log_item *
++xfs_efi_init(
++ struct xfs_mount *mp,
++ uint nextents)
++
++{
++ struct xfs_efi_log_item *efip;
++ uint size;
++
++ ASSERT(nextents > 0);
++ if (nextents > XFS_EFI_MAX_FAST_EXTENTS) {
++ size = (uint)(sizeof(struct xfs_efi_log_item) +
++ ((nextents - 1) * sizeof(xfs_extent_t)));
++ efip = kmem_zalloc(size, 0);
++ } else {
++ efip = kmem_zone_zalloc(xfs_efi_zone, 0);
++ }
++
++ xfs_log_item_init(mp, &efip->efi_item, XFS_LI_EFI, &xfs_efi_item_ops);
++ efip->efi_format.efi_nextents = nextents;
++ efip->efi_format.efi_id = (uintptr_t)(void *)efip;
++ atomic_set(&efip->efi_next_extent, 0);
++ atomic_set(&efip->efi_refcount, 2);
++
++ return efip;
++}
+diff --git a/fs/xfs/xfs_extfree_item.h b/fs/xfs/xfs_extfree_item.h
+index 16aaab06d4ecc..883f0f1d8989d 100644
+--- a/fs/xfs/xfs_extfree_item.h
++++ b/fs/xfs/xfs_extfree_item.h
+@@ -50,25 +50,25 @@ struct kmem_zone;
+ * of commit failure or log I/O errors. Note that the EFD is not inserted in the
+ * AIL, so at this point both the EFI and EFD are freed.
+ */
+-typedef struct xfs_efi_log_item {
++struct xfs_efi_log_item {
+ struct xfs_log_item efi_item;
+ atomic_t efi_refcount;
+ atomic_t efi_next_extent;
+ unsigned long efi_flags; /* misc flags */
+ xfs_efi_log_format_t efi_format;
+-} xfs_efi_log_item_t;
++};
+
+ /*
+ * This is the "extent free done" log item. It is used to log
+ * the fact that some extents earlier mentioned in an efi item
+ * have been freed.
+ */
+-typedef struct xfs_efd_log_item {
++struct xfs_efd_log_item {
+ struct xfs_log_item efd_item;
+- xfs_efi_log_item_t *efd_efip;
++ struct xfs_efi_log_item *efd_efip;
+ uint efd_next_extent;
+ xfs_efd_log_format_t efd_format;
+-} xfs_efd_log_item_t;
++};
+
+ /*
+ * Max number of extents in fast allocation path.
+@@ -78,13 +78,13 @@ typedef struct xfs_efd_log_item {
+ extern struct kmem_zone *xfs_efi_zone;
+ extern struct kmem_zone *xfs_efd_zone;
+
+-xfs_efi_log_item_t *xfs_efi_init(struct xfs_mount *, uint);
++struct xfs_efi_log_item *xfs_efi_init(struct xfs_mount *, uint);
+ int xfs_efi_copy_format(xfs_log_iovec_t *buf,
+ xfs_efi_log_format_t *dst_efi_fmt);
+-void xfs_efi_item_free(xfs_efi_log_item_t *);
++void xfs_efi_item_free(struct xfs_efi_log_item *);
+ void xfs_efi_release(struct xfs_efi_log_item *);
+
+-int xfs_efi_recover(struct xfs_mount *mp,
+- struct xfs_efi_log_item *efip);
++int xfs_efi_recover(struct xfs_efi_log_item *efip,
++ struct list_head *capture_list);
+
+ #endif /* __XFS_EXTFREE_ITEM_H__ */
+diff --git a/fs/xfs/xfs_icreate_item.c b/fs/xfs/xfs_icreate_item.c
+index 3ebd1b7f49d82..7d940b289db54 100644
+--- a/fs/xfs/xfs_icreate_item.c
++++ b/fs/xfs/xfs_icreate_item.c
+@@ -10,6 +10,7 @@
+ #include "xfs_trans.h"
+ #include "xfs_trans_priv.h"
+ #include "xfs_icreate_item.h"
++#include "xfs_log_priv.h"
+ #include "xfs_log.h"
+
+ kmem_zone_t *xfs_icreate_zone; /* inode create item zone */
+diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c
+index e5a90a0b8f8a2..02f77a359972e 100644
+--- a/fs/xfs/xfs_inode.c
++++ b/fs/xfs/xfs_inode.c
+@@ -2555,7 +2555,7 @@ xfs_ifree_cluster(
+ xfs_daddr_t blkno;
+ xfs_buf_t *bp;
+ xfs_inode_t *ip;
+- xfs_inode_log_item_t *iip;
++ struct xfs_inode_log_item *iip;
+ struct xfs_log_item *lip;
+ struct xfs_perag *pag;
+ struct xfs_ino_geometry *igeo = M_IGEO(mp);
+@@ -2617,7 +2617,7 @@ xfs_ifree_cluster(
+ */
+ list_for_each_entry(lip, &bp->b_li_list, li_bio_list) {
+ if (lip->li_type == XFS_LI_INODE) {
+- iip = (xfs_inode_log_item_t *)lip;
++ iip = (struct xfs_inode_log_item *)lip;
+ ASSERT(iip->ili_logged == 1);
+ lip->li_cb = xfs_istale_done;
+ xfs_trans_ail_copy_lsn(mp->m_ail,
+diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c
+index 76a60526af94f..83b8f5655636e 100644
+--- a/fs/xfs/xfs_inode_item.c
++++ b/fs/xfs/xfs_inode_item.c
+@@ -781,7 +781,7 @@ xfs_iflush_abort(
+ xfs_inode_t *ip,
+ bool stale)
+ {
+- xfs_inode_log_item_t *iip = ip->i_itemp;
++ struct xfs_inode_log_item *iip = ip->i_itemp;
+
+ if (iip) {
+ if (test_bit(XFS_LI_IN_AIL, &iip->ili_item.li_flags)) {
+diff --git a/fs/xfs/xfs_inode_item.h b/fs/xfs/xfs_inode_item.h
+index 07a60e74c39c8..ad667fd4ae622 100644
+--- a/fs/xfs/xfs_inode_item.h
++++ b/fs/xfs/xfs_inode_item.h
+@@ -13,7 +13,7 @@ struct xfs_bmbt_rec;
+ struct xfs_inode;
+ struct xfs_mount;
+
+-typedef struct xfs_inode_log_item {
++struct xfs_inode_log_item {
+ struct xfs_log_item ili_item; /* common portion */
+ struct xfs_inode *ili_inode; /* inode ptr */
+ xfs_lsn_t ili_flush_lsn; /* lsn at last flush */
+@@ -23,7 +23,7 @@ typedef struct xfs_inode_log_item {
+ unsigned int ili_last_fields; /* fields when flushed */
+ unsigned int ili_fields; /* fields to be logged */
+ unsigned int ili_fsync_fields; /* logged since last fsync */
+-} xfs_inode_log_item_t;
++};
+
+ static inline int xfs_inode_clean(xfs_inode_t *ip)
+ {
+diff --git a/fs/xfs/xfs_iwalk.c b/fs/xfs/xfs_iwalk.c
+index aa375cf53021a..cc5c0c835884e 100644
+--- a/fs/xfs/xfs_iwalk.c
++++ b/fs/xfs/xfs_iwalk.c
+@@ -55,6 +55,9 @@ struct xfs_iwalk_ag {
+ /* Where do we start the traversal? */
+ xfs_ino_t startino;
+
++ /* What was the last inode number we saw when iterating the inobt? */
++ xfs_ino_t lastino;
++
+ /* Array of inobt records we cache. */
+ struct xfs_inobt_rec_incore *recs;
+
+@@ -300,6 +303,9 @@ xfs_iwalk_ag_start(
+ return error;
+ XFS_WANT_CORRUPTED_RETURN(mp, *has_more == 1);
+
++ iwag->lastino = XFS_AGINO_TO_INO(mp, agno,
++ irec->ir_startino + XFS_INODES_PER_CHUNK - 1);
++
+ /*
+ * If the LE lookup yielded an inobt record before the cursor position,
+ * skip it and see if there's another one after it.
+@@ -346,15 +352,17 @@ xfs_iwalk_run_callbacks(
+ struct xfs_mount *mp = iwag->mp;
+ struct xfs_trans *tp = iwag->tp;
+ struct xfs_inobt_rec_incore *irec;
+- xfs_agino_t restart;
++ xfs_agino_t next_agino;
+ int error;
+
++ next_agino = XFS_INO_TO_AGINO(mp, iwag->lastino) + 1;
++
+ ASSERT(iwag->nr_recs > 0);
+
+ /* Delete cursor but remember the last record we cached... */
+ xfs_iwalk_del_inobt(tp, curpp, agi_bpp, 0);
+ irec = &iwag->recs[iwag->nr_recs - 1];
+- restart = irec->ir_startino + XFS_INODES_PER_CHUNK - 1;
++ ASSERT(next_agino >= irec->ir_startino + XFS_INODES_PER_CHUNK);
+
+ error = xfs_iwalk_ag_recs(iwag);
+ if (error)
+@@ -371,7 +379,7 @@ xfs_iwalk_run_callbacks(
+ if (error)
+ return error;
+
+- return xfs_inobt_lookup(*curpp, restart, XFS_LOOKUP_GE, has_more);
++ return xfs_inobt_lookup(*curpp, next_agino, XFS_LOOKUP_GE, has_more);
+ }
+
+ /* Walk all inodes in a single AG, from @iwag->startino to the end of the AG. */
+@@ -395,6 +403,7 @@ xfs_iwalk_ag(
+
+ while (!error && has_more) {
+ struct xfs_inobt_rec_incore *irec;
++ xfs_ino_t rec_fsino;
+
+ cond_resched();
+ if (xfs_pwork_want_abort(&iwag->pwork))
+@@ -406,6 +415,15 @@ xfs_iwalk_ag(
+ if (error || !has_more)
+ break;
+
++ /* Make sure that we always move forward. */
++ rec_fsino = XFS_AGINO_TO_INO(mp, agno, irec->ir_startino);
++ if (iwag->lastino != NULLFSINO && iwag->lastino >= rec_fsino) {
++ ASSERT(iwag->lastino < rec_fsino);
++ error = -EFSCORRUPTED;
++ goto out;
++ }
++ iwag->lastino = rec_fsino + XFS_INODES_PER_CHUNK - 1;
++
+ /* No allocated inodes in this chunk; skip it. */
+ if (iwag->skip_empty && irec->ir_freecount == irec->ir_count) {
+ error = xfs_btree_increment(cur, 0, &has_more);
+@@ -534,6 +552,7 @@ xfs_iwalk(
+ .trim_start = 1,
+ .skip_empty = 1,
+ .pwork = XFS_PWORK_SINGLE_THREADED,
++ .lastino = NULLFSINO,
+ };
+ xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino);
+ int error;
+@@ -622,6 +641,7 @@ xfs_iwalk_threaded(
+ iwag->data = data;
+ iwag->startino = startino;
+ iwag->sz_recs = xfs_iwalk_prefetch(inode_records);
++ iwag->lastino = NULLFSINO;
+ xfs_pwork_queue(&pctl, &iwag->pwork);
+ startino = XFS_AGINO_TO_INO(mp, agno + 1, 0);
+ if (flags & XFS_INOBT_WALK_SAME_AG)
+@@ -695,6 +715,7 @@ xfs_inobt_walk(
+ .startino = startino,
+ .sz_recs = xfs_inobt_walk_prefetch(inobt_records),
+ .pwork = XFS_PWORK_SINGLE_THREADED,
++ .lastino = NULLFSINO,
+ };
+ xfs_agnumber_t agno = XFS_INO_TO_AGNO(mp, startino);
+ int error;
+diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c
+index 63c0f1e9d1018..03a52b3919b82 100644
+--- a/fs/xfs/xfs_log.c
++++ b/fs/xfs/xfs_log.c
+@@ -369,6 +369,25 @@ xlog_tic_add_region(xlog_ticket_t *tic, uint len, uint type)
+ tic->t_res_num++;
+ }
+
++bool
++xfs_log_writable(
++ struct xfs_mount *mp)
++{
++ /*
++ * Never write to the log on norecovery mounts, if the block device is
++ * read-only, or if the filesystem is shutdown. Read-only mounts still
++ * allow internal writes for log recovery and unmount purposes, so don't
++ * restrict that case here.
++ */
++ if (mp->m_flags & XFS_MOUNT_NORECOVERY)
++ return false;
++ if (xfs_readonly_buftarg(mp->m_log->l_targ))
++ return false;
++ if (XFS_FORCED_SHUTDOWN(mp))
++ return false;
++ return true;
++}
++
+ /*
+ * Replenish the byte reservation required by moving the grant write head.
+ */
+@@ -895,15 +914,8 @@ xfs_log_unmount_write(xfs_mount_t *mp)
+ #endif
+ int error;
+
+- /*
+- * Don't write out unmount record on norecovery mounts or ro devices.
+- * Or, if we are doing a forced umount (typically because of IO errors).
+- */
+- if (mp->m_flags & XFS_MOUNT_NORECOVERY ||
+- xfs_readonly_buftarg(log->l_targ)) {
+- ASSERT(mp->m_flags & XFS_MOUNT_RDONLY);
++ if (!xfs_log_writable(mp))
+ return 0;
+- }
+
+ error = xfs_log_force(mp, XFS_LOG_SYNC);
+ ASSERT(error || !(XLOG_FORCED_SHUTDOWN(log)));
+@@ -1537,14 +1549,14 @@ xlog_commit_record(
+ }
+
+ /*
+- * Push on the buffer cache code if we ever use more than 75% of the on-disk
+- * log space. This code pushes on the lsn which would supposedly free up
+- * the 25% which we want to leave free. We may need to adopt a policy which
+- * pushes on an lsn which is further along in the log once we reach the high
+- * water mark. In this manner, we would be creating a low water mark.
++ * Compute the LSN that we'd need to push the log tail towards in order to have
++ * (a) enough on-disk log space to log the number of bytes specified, (b) at
++ * least 25% of the log space free, and (c) at least 256 blocks free. If the
++ * log free space already meets all three thresholds, this function returns
++ * NULLCOMMITLSN.
+ */
+-STATIC void
+-xlog_grant_push_ail(
++xfs_lsn_t
++xlog_grant_push_threshold(
+ struct xlog *log,
+ int need_bytes)
+ {
+@@ -1570,7 +1582,7 @@ xlog_grant_push_ail(
+ free_threshold = max(free_threshold, (log->l_logBBsize >> 2));
+ free_threshold = max(free_threshold, 256);
+ if (free_blocks >= free_threshold)
+- return;
++ return NULLCOMMITLSN;
+
+ xlog_crack_atomic_lsn(&log->l_tail_lsn, &threshold_cycle,
+ &threshold_block);
+@@ -1590,13 +1602,33 @@ xlog_grant_push_ail(
+ if (XFS_LSN_CMP(threshold_lsn, last_sync_lsn) > 0)
+ threshold_lsn = last_sync_lsn;
+
++ return threshold_lsn;
++}
++
++/*
++ * Push the tail of the log if we need to do so to maintain the free log space
++ * thresholds set out by xlog_grant_push_threshold. We may need to adopt a
++ * policy which pushes on an lsn which is further along in the log once we
++ * reach the high water mark. In this manner, we would be creating a low water
++ * mark.
++ */
++STATIC void
++xlog_grant_push_ail(
++ struct xlog *log,
++ int need_bytes)
++{
++ xfs_lsn_t threshold_lsn;
++
++ threshold_lsn = xlog_grant_push_threshold(log, need_bytes);
++ if (threshold_lsn == NULLCOMMITLSN || XLOG_FORCED_SHUTDOWN(log))
++ return;
++
+ /*
+ * Get the transaction layer to kick the dirty buffers out to
+ * disk asynchronously. No point in trying to do this if
+ * the filesystem is shutting down.
+ */
+- if (!XLOG_FORCED_SHUTDOWN(log))
+- xfs_ail_push(log->l_ailp, threshold_lsn);
++ xfs_ail_push(log->l_ailp, threshold_lsn);
+ }
+
+ /*
+diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h
+index 84e06805160f8..dc9229e7ddaaa 100644
+--- a/fs/xfs/xfs_log.h
++++ b/fs/xfs/xfs_log.h
+@@ -132,6 +132,7 @@ int xfs_log_reserve(struct xfs_mount *mp,
+ int xfs_log_regrant(struct xfs_mount *mp, struct xlog_ticket *tic);
+ void xfs_log_unmount(struct xfs_mount *mp);
+ int xfs_log_force_umount(struct xfs_mount *mp, int logerror);
++bool xfs_log_writable(struct xfs_mount *mp);
+
+ struct xlog_ticket *xfs_log_ticket_get(struct xlog_ticket *ticket);
+ void xfs_log_ticket_put(struct xlog_ticket *ticket);
+@@ -146,4 +147,6 @@ void xfs_log_quiesce(struct xfs_mount *mp);
+ bool xfs_log_check_lsn(struct xfs_mount *, xfs_lsn_t);
+ bool xfs_log_in_recovery(struct xfs_mount *);
+
++xfs_lsn_t xlog_grant_push_threshold(struct xlog *log, int need_bytes);
++
+ #endif /* __XFS_LOG_H__ */
+diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c
+index 550fd5de2404a..ae9b8efcfa545 100644
+--- a/fs/xfs/xfs_log_cil.c
++++ b/fs/xfs/xfs_log_cil.c
+@@ -1178,21 +1178,19 @@ out_shutdown:
+ */
+ bool
+ xfs_log_item_in_current_chkpt(
+- struct xfs_log_item *lip)
++ struct xfs_log_item *lip)
+ {
+- struct xfs_cil_ctx *ctx;
++ struct xfs_cil *cil = lip->li_mountp->m_log->l_cilp;
+
+ if (list_empty(&lip->li_cil))
+ return false;
+
+- ctx = lip->li_mountp->m_log->l_cilp->xc_ctx;
+-
+ /*
+ * li_seq is written on the first commit of a log item to record the
+ * first checkpoint it is written to. Hence if it is different to the
+ * current sequence, we're in a new checkpoint.
+ */
+- if (XFS_LSN_CMP(lip->li_seq, ctx->sequence) != 0)
++ if (XFS_LSN_CMP(lip->li_seq, READ_ONCE(cil->xc_current_sequence)) != 0)
+ return false;
+ return true;
+ }
+diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c
+index 46b1e255f55fc..6c60cdd10d330 100644
+--- a/fs/xfs/xfs_log_recover.c
++++ b/fs/xfs/xfs_log_recover.c
+@@ -2206,6 +2206,8 @@ xlog_recover_get_buf_lsn(
+ case XFS_ABTC_MAGIC:
+ case XFS_RMAP_CRC_MAGIC:
+ case XFS_REFC_CRC_MAGIC:
++ case XFS_FIBT_CRC_MAGIC:
++ case XFS_FIBT_MAGIC:
+ case XFS_IBT_CRC_MAGIC:
+ case XFS_IBT_MAGIC: {
+ struct xfs_btree_block *btb = blk;
+@@ -3384,7 +3386,7 @@ xlog_recover_efd_pass2(
+ struct xlog_recover_item *item)
+ {
+ xfs_efd_log_format_t *efd_formatp;
+- xfs_efi_log_item_t *efip = NULL;
++ struct xfs_efi_log_item *efip = NULL;
+ struct xfs_log_item *lip;
+ uint64_t efi_id;
+ struct xfs_ail_cursor cur;
+@@ -3405,7 +3407,7 @@ xlog_recover_efd_pass2(
+ lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+ while (lip != NULL) {
+ if (lip->li_type == XFS_LI_EFI) {
+- efip = (xfs_efi_log_item_t *)lip;
++ efip = (struct xfs_efi_log_item *)lip;
+ if (efip->efi_format.efi_id == efi_id) {
+ /*
+ * Drop the EFD reference to the EFI. This
+@@ -4585,9 +4587,9 @@ xlog_recover_process_data(
+ /* Recover the EFI if necessary. */
+ STATIC int
+ xlog_recover_process_efi(
+- struct xfs_mount *mp,
+ struct xfs_ail *ailp,
+- struct xfs_log_item *lip)
++ struct xfs_log_item *lip,
++ struct list_head *capture_list)
+ {
+ struct xfs_efi_log_item *efip;
+ int error;
+@@ -4600,7 +4602,7 @@ xlog_recover_process_efi(
+ return 0;
+
+ spin_unlock(&ailp->ail_lock);
+- error = xfs_efi_recover(mp, efip);
++ error = xfs_efi_recover(efip, capture_list);
+ spin_lock(&ailp->ail_lock);
+
+ return error;
+@@ -4625,9 +4627,9 @@ xlog_recover_cancel_efi(
+ /* Recover the RUI if necessary. */
+ STATIC int
+ xlog_recover_process_rui(
+- struct xfs_mount *mp,
+ struct xfs_ail *ailp,
+- struct xfs_log_item *lip)
++ struct xfs_log_item *lip,
++ struct list_head *capture_list)
+ {
+ struct xfs_rui_log_item *ruip;
+ int error;
+@@ -4640,7 +4642,7 @@ xlog_recover_process_rui(
+ return 0;
+
+ spin_unlock(&ailp->ail_lock);
+- error = xfs_rui_recover(mp, ruip);
++ error = xfs_rui_recover(ruip, capture_list);
+ spin_lock(&ailp->ail_lock);
+
+ return error;
+@@ -4665,9 +4667,9 @@ xlog_recover_cancel_rui(
+ /* Recover the CUI if necessary. */
+ STATIC int
+ xlog_recover_process_cui(
+- struct xfs_trans *parent_tp,
+ struct xfs_ail *ailp,
+- struct xfs_log_item *lip)
++ struct xfs_log_item *lip,
++ struct list_head *capture_list)
+ {
+ struct xfs_cui_log_item *cuip;
+ int error;
+@@ -4680,7 +4682,7 @@ xlog_recover_process_cui(
+ return 0;
+
+ spin_unlock(&ailp->ail_lock);
+- error = xfs_cui_recover(parent_tp, cuip);
++ error = xfs_cui_recover(cuip, capture_list);
+ spin_lock(&ailp->ail_lock);
+
+ return error;
+@@ -4705,9 +4707,9 @@ xlog_recover_cancel_cui(
+ /* Recover the BUI if necessary. */
+ STATIC int
+ xlog_recover_process_bui(
+- struct xfs_trans *parent_tp,
+ struct xfs_ail *ailp,
+- struct xfs_log_item *lip)
++ struct xfs_log_item *lip,
++ struct list_head *capture_list)
+ {
+ struct xfs_bui_log_item *buip;
+ int error;
+@@ -4720,7 +4722,7 @@ xlog_recover_process_bui(
+ return 0;
+
+ spin_unlock(&ailp->ail_lock);
+- error = xfs_bui_recover(parent_tp, buip);
++ error = xfs_bui_recover(buip, capture_list);
+ spin_lock(&ailp->ail_lock);
+
+ return error;
+@@ -4759,37 +4761,66 @@ static inline bool xlog_item_is_intent(struct xfs_log_item *lip)
+ /* Take all the collected deferred ops and finish them in order. */
+ static int
+ xlog_finish_defer_ops(
+- struct xfs_trans *parent_tp)
++ struct xfs_mount *mp,
++ struct list_head *capture_list)
+ {
+- struct xfs_mount *mp = parent_tp->t_mountp;
++ struct xfs_defer_capture *dfc, *next;
+ struct xfs_trans *tp;
+- int64_t freeblks;
+- uint resblks;
+- int error;
++ struct xfs_inode *ip;
++ int error = 0;
+
+- /*
+- * We're finishing the defer_ops that accumulated as a result of
+- * recovering unfinished intent items during log recovery. We
+- * reserve an itruncate transaction because it is the largest
+- * permanent transaction type. Since we're the only user of the fs
+- * right now, take 93% (15/16) of the available free blocks. Use
+- * weird math to avoid a 64-bit division.
+- */
+- freeblks = percpu_counter_sum(&mp->m_fdblocks);
+- if (freeblks <= 0)
+- return -ENOSPC;
+- resblks = min_t(int64_t, UINT_MAX, freeblks);
+- resblks = (resblks * 15) >> 4;
+- error = xfs_trans_alloc(mp, &M_RES(mp)->tr_itruncate, resblks,
+- 0, XFS_TRANS_RESERVE, &tp);
+- if (error)
+- return error;
+- /* transfer all collected dfops to this transaction */
+- xfs_defer_move(tp, parent_tp);
++ list_for_each_entry_safe(dfc, next, capture_list, dfc_list) {
++ struct xfs_trans_res resv;
++
++ /*
++ * Create a new transaction reservation from the captured
++ * information. Set logcount to 1 to force the new transaction
++ * to regrant every roll so that we can make forward progress
++ * in recovery no matter how full the log might be.
++ */
++ resv.tr_logres = dfc->dfc_logres;
++ resv.tr_logcount = 1;
++ resv.tr_logflags = XFS_TRANS_PERM_LOG_RES;
++
++ error = xfs_trans_alloc(mp, &resv, dfc->dfc_blkres,
++ dfc->dfc_rtxres, XFS_TRANS_RESERVE, &tp);
++ if (error)
++ return error;
++
++ /*
++ * Transfer to this new transaction all the dfops we captured
++ * from recovering a single intent item.
++ */
++ list_del_init(&dfc->dfc_list);
++ xfs_defer_ops_continue(dfc, tp, &ip);
++
++ error = xfs_trans_commit(tp);
++ if (ip) {
++ xfs_iunlock(ip, XFS_ILOCK_EXCL);
++ xfs_irele(ip);
++ }
++ if (error)
++ return error;
++ }
+
+- return xfs_trans_commit(tp);
++ ASSERT(list_empty(capture_list));
++ return 0;
+ }
+
++/* Release all the captured defer ops and capture structures in this list. */
++static void
++xlog_abort_defer_ops(
++ struct xfs_mount *mp,
++ struct list_head *capture_list)
++{
++ struct xfs_defer_capture *dfc;
++ struct xfs_defer_capture *next;
++
++ list_for_each_entry_safe(dfc, next, capture_list, dfc_list) {
++ list_del_init(&dfc->dfc_list);
++ xfs_defer_ops_release(mp, dfc);
++ }
++}
+ /*
+ * When this is called, all of the log intent items which did not have
+ * corresponding log done items should be in the AIL. What we do now
+@@ -4810,35 +4841,23 @@ STATIC int
+ xlog_recover_process_intents(
+ struct xlog *log)
+ {
+- struct xfs_trans *parent_tp;
++ LIST_HEAD(capture_list);
+ struct xfs_ail_cursor cur;
+ struct xfs_log_item *lip;
+ struct xfs_ail *ailp;
+- int error;
++ int error = 0;
+ #if defined(DEBUG) || defined(XFS_WARN)
+ xfs_lsn_t last_lsn;
+ #endif
+
+- /*
+- * The intent recovery handlers commit transactions to complete recovery
+- * for individual intents, but any new deferred operations that are
+- * queued during that process are held off until the very end. The
+- * purpose of this transaction is to serve as a container for deferred
+- * operations. Each intent recovery handler must transfer dfops here
+- * before its local transaction commits, and we'll finish the entire
+- * list below.
+- */
+- error = xfs_trans_alloc_empty(log->l_mp, &parent_tp);
+- if (error)
+- return error;
+-
+ ailp = log->l_ailp;
+ spin_lock(&ailp->ail_lock);
+- lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
+ #if defined(DEBUG) || defined(XFS_WARN)
+ last_lsn = xlog_assign_lsn(log->l_curr_cycle, log->l_curr_block);
+ #endif
+- while (lip != NULL) {
++ for (lip = xfs_trans_ail_cursor_first(ailp, &cur, 0);
++ lip != NULL;
++ lip = xfs_trans_ail_cursor_next(ailp, &cur)) {
+ /*
+ * We're done when we see something other than an intent.
+ * There should be no intents left in the AIL now.
+@@ -4860,35 +4879,40 @@ xlog_recover_process_intents(
+
+ /*
+ * NOTE: If your intent processing routine can create more
+- * deferred ops, you /must/ attach them to the dfops in this
+- * routine or else those subsequent intents will get
++ * deferred ops, you /must/ attach them to the capture list in
++ * the recover routine or else those subsequent intents will be
+ * replayed in the wrong order!
+ */
+ switch (lip->li_type) {
+ case XFS_LI_EFI:
+- error = xlog_recover_process_efi(log->l_mp, ailp, lip);
++ error = xlog_recover_process_efi(ailp, lip, &capture_list);
+ break;
+ case XFS_LI_RUI:
+- error = xlog_recover_process_rui(log->l_mp, ailp, lip);
++ error = xlog_recover_process_rui(ailp, lip, &capture_list);
+ break;
+ case XFS_LI_CUI:
+- error = xlog_recover_process_cui(parent_tp, ailp, lip);
++ error = xlog_recover_process_cui(ailp, lip, &capture_list);
+ break;
+ case XFS_LI_BUI:
+- error = xlog_recover_process_bui(parent_tp, ailp, lip);
++ error = xlog_recover_process_bui(ailp, lip, &capture_list);
+ break;
+ }
+ if (error)
+- goto out;
+- lip = xfs_trans_ail_cursor_next(ailp, &cur);
++ break;
+ }
+-out:
++
+ xfs_trans_ail_cursor_done(&cur);
+ spin_unlock(&ailp->ail_lock);
+- if (!error)
+- error = xlog_finish_defer_ops(parent_tp);
+- xfs_trans_cancel(parent_tp);
++ if (error)
++ goto err;
+
++ error = xlog_finish_defer_ops(log->l_mp, &capture_list);
++ if (error)
++ goto err;
++
++ return 0;
++err:
++ xlog_abort_defer_ops(log->l_mp, &capture_list);
+ return error;
+ }
+
+diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c
+index bbcf48a625b2a..2860966af6c20 100644
+--- a/fs/xfs/xfs_mount.c
++++ b/fs/xfs/xfs_mount.c
+@@ -1218,8 +1218,7 @@ xfs_fs_writable(
+ int
+ xfs_log_sbcount(xfs_mount_t *mp)
+ {
+- /* allow this to proceed during the freeze sequence... */
+- if (!xfs_fs_writable(mp, SB_FREEZE_COMPLETE))
++ if (!xfs_log_writable(mp))
+ return 0;
+
+ /*
+diff --git a/fs/xfs/xfs_refcount_item.c b/fs/xfs/xfs_refcount_item.c
+index d5708d40ad877..fa1018a6e6777 100644
+--- a/fs/xfs/xfs_refcount_item.c
++++ b/fs/xfs/xfs_refcount_item.c
+@@ -123,40 +123,6 @@ xfs_cui_item_release(
+ xfs_cui_release(CUI_ITEM(lip));
+ }
+
+-static const struct xfs_item_ops xfs_cui_item_ops = {
+- .iop_size = xfs_cui_item_size,
+- .iop_format = xfs_cui_item_format,
+- .iop_unpin = xfs_cui_item_unpin,
+- .iop_release = xfs_cui_item_release,
+-};
+-
+-/*
+- * Allocate and initialize an cui item with the given number of extents.
+- */
+-struct xfs_cui_log_item *
+-xfs_cui_init(
+- struct xfs_mount *mp,
+- uint nextents)
+-
+-{
+- struct xfs_cui_log_item *cuip;
+-
+- ASSERT(nextents > 0);
+- if (nextents > XFS_CUI_MAX_FAST_EXTENTS)
+- cuip = kmem_zalloc(xfs_cui_log_item_sizeof(nextents),
+- 0);
+- else
+- cuip = kmem_zone_zalloc(xfs_cui_zone, 0);
+-
+- xfs_log_item_init(mp, &cuip->cui_item, XFS_LI_CUI, &xfs_cui_item_ops);
+- cuip->cui_format.cui_nextents = nextents;
+- cuip->cui_format.cui_id = (uintptr_t)(void *)cuip;
+- atomic_set(&cuip->cui_next_extent, 0);
+- atomic_set(&cuip->cui_refcount, 2);
+-
+- return cuip;
+-}
+-
+ static inline struct xfs_cud_log_item *CUD_ITEM(struct xfs_log_item *lip)
+ {
+ return container_of(lip, struct xfs_cud_log_item, cud_item);
+@@ -284,27 +250,6 @@ xfs_refcount_update_diff_items(
+ XFS_FSB_TO_AGNO(mp, rb->ri_startblock);
+ }
+
+-/* Get an CUI. */
+-STATIC void *
+-xfs_refcount_update_create_intent(
+- struct xfs_trans *tp,
+- unsigned int count)
+-{
+- struct xfs_cui_log_item *cuip;
+-
+- ASSERT(tp != NULL);
+- ASSERT(count > 0);
+-
+- cuip = xfs_cui_init(tp->t_mountp, count);
+- ASSERT(cuip != NULL);
+-
+- /*
+- * Get a log_item_desc to point at the new item.
+- */
+- xfs_trans_add_item(tp, &cuip->cui_item);
+- return cuip;
+-}
+-
+ /* Set the phys extent flags for this reverse mapping. */
+ static void
+ xfs_trans_set_refcount_flags(
+@@ -328,16 +273,12 @@ xfs_trans_set_refcount_flags(
+ STATIC void
+ xfs_refcount_update_log_item(
+ struct xfs_trans *tp,
+- void *intent,
+- struct list_head *item)
++ struct xfs_cui_log_item *cuip,
++ struct xfs_refcount_intent *refc)
+ {
+- struct xfs_cui_log_item *cuip = intent;
+- struct xfs_refcount_intent *refc;
+ uint next_extent;
+ struct xfs_phys_extent *ext;
+
+- refc = container_of(item, struct xfs_refcount_intent, ri_list);
+-
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ set_bit(XFS_LI_DIRTY, &cuip->cui_item.li_flags);
+
+@@ -354,14 +295,35 @@ xfs_refcount_update_log_item(
+ xfs_trans_set_refcount_flags(ext, refc->ri_type);
+ }
+
++static struct xfs_log_item *
++xfs_refcount_update_create_intent(
++ struct xfs_trans *tp,
++ struct list_head *items,
++ unsigned int count,
++ bool sort)
++{
++ struct xfs_mount *mp = tp->t_mountp;
++ struct xfs_cui_log_item *cuip = xfs_cui_init(mp, count);
++ struct xfs_refcount_intent *refc;
++
++ ASSERT(count > 0);
++
++ xfs_trans_add_item(tp, &cuip->cui_item);
++ if (sort)
++ list_sort(mp, items, xfs_refcount_update_diff_items);
++ list_for_each_entry(refc, items, ri_list)
++ xfs_refcount_update_log_item(tp, cuip, refc);
++ return &cuip->cui_item;
++}
++
+ /* Get an CUD so we can process all the deferred refcount updates. */
+ STATIC void *
+ xfs_refcount_update_create_done(
+ struct xfs_trans *tp,
+- void *intent,
++ struct xfs_log_item *intent,
+ unsigned int count)
+ {
+- return xfs_trans_get_cud(tp, intent);
++ return xfs_trans_get_cud(tp, CUI_ITEM(intent));
+ }
+
+ /* Process a deferred refcount update. */
+@@ -411,9 +373,9 @@ xfs_refcount_update_finish_cleanup(
+ /* Abort all pending CUIs. */
+ STATIC void
+ xfs_refcount_update_abort_intent(
+- void *intent)
++ struct xfs_log_item *intent)
+ {
+- xfs_cui_release(intent);
++ xfs_cui_release(CUI_ITEM(intent));
+ }
+
+ /* Cancel a deferred refcount update. */
+@@ -429,10 +391,8 @@ xfs_refcount_update_cancel_item(
+
+ const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
+ .max_items = XFS_CUI_MAX_FAST_EXTENTS,
+- .diff_items = xfs_refcount_update_diff_items,
+ .create_intent = xfs_refcount_update_create_intent,
+ .abort_intent = xfs_refcount_update_abort_intent,
+- .log_item = xfs_refcount_update_log_item,
+ .create_done = xfs_refcount_update_create_done,
+ .finish_item = xfs_refcount_update_finish_item,
+ .finish_cleanup = xfs_refcount_update_finish_cleanup,
+@@ -445,8 +405,8 @@ const struct xfs_defer_op_type xfs_refcount_update_defer_type = {
+ */
+ int
+ xfs_cui_recover(
+- struct xfs_trans *parent_tp,
+- struct xfs_cui_log_item *cuip)
++ struct xfs_cui_log_item *cuip,
++ struct list_head *capture_list)
+ {
+ int i;
+ int error = 0;
+@@ -462,7 +422,7 @@ xfs_cui_recover(
+ xfs_extlen_t new_len;
+ struct xfs_bmbt_irec irec;
+ bool requeue_only = false;
+- struct xfs_mount *mp = parent_tp->t_mountp;
++ struct xfs_mount *mp = cuip->cui_item.li_mountp;
+
+ ASSERT(!test_bit(XFS_CUI_RECOVERED, &cuip->cui_flags));
+
+@@ -517,12 +477,7 @@ xfs_cui_recover(
+ mp->m_refc_maxlevels * 2, 0, XFS_TRANS_RESERVE, &tp);
+ if (error)
+ return error;
+- /*
+- * Recovery stashes all deferred ops during intent processing and
+- * finishes them on completion. Transfer current dfops state to this
+- * transaction and transfer the result back before we return.
+- */
+- xfs_defer_move(tp, parent_tp);
++
+ cudp = xfs_trans_get_cud(tp, cuip);
+
+ for (i = 0; i < cuip->cui_format.cui_nextents; i++) {
+@@ -580,13 +535,71 @@ xfs_cui_recover(
+
+ xfs_refcount_finish_one_cleanup(tp, rcur, error);
+ set_bit(XFS_CUI_RECOVERED, &cuip->cui_flags);
+- xfs_defer_move(parent_tp, tp);
+- error = xfs_trans_commit(tp);
+- return error;
++ return xfs_defer_ops_capture_and_commit(tp, NULL, capture_list);
+
+ abort_error:
+ xfs_refcount_finish_one_cleanup(tp, rcur, error);
+- xfs_defer_move(parent_tp, tp);
+ xfs_trans_cancel(tp);
+ return error;
+ }
++
++/* Relog an intent item to push the log tail forward. */
++static struct xfs_log_item *
++xfs_cui_item_relog(
++ struct xfs_log_item *intent,
++ struct xfs_trans *tp)
++{
++ struct xfs_cud_log_item *cudp;
++ struct xfs_cui_log_item *cuip;
++ struct xfs_phys_extent *extp;
++ unsigned int count;
++
++ count = CUI_ITEM(intent)->cui_format.cui_nextents;
++ extp = CUI_ITEM(intent)->cui_format.cui_extents;
++
++ tp->t_flags |= XFS_TRANS_DIRTY;
++ cudp = xfs_trans_get_cud(tp, CUI_ITEM(intent));
++ set_bit(XFS_LI_DIRTY, &cudp->cud_item.li_flags);
++
++ cuip = xfs_cui_init(tp->t_mountp, count);
++ memcpy(cuip->cui_format.cui_extents, extp, count * sizeof(*extp));
++ atomic_set(&cuip->cui_next_extent, count);
++ xfs_trans_add_item(tp, &cuip->cui_item);
++ set_bit(XFS_LI_DIRTY, &cuip->cui_item.li_flags);
++ return &cuip->cui_item;
++}
++
++static const struct xfs_item_ops xfs_cui_item_ops = {
++ .iop_size = xfs_cui_item_size,
++ .iop_format = xfs_cui_item_format,
++ .iop_unpin = xfs_cui_item_unpin,
++ .iop_release = xfs_cui_item_release,
++ .iop_relog = xfs_cui_item_relog,
++};
++
++/*
++ * Allocate and initialize an cui item with the given number of extents.
++ */
++struct xfs_cui_log_item *
++xfs_cui_init(
++ struct xfs_mount *mp,
++ uint nextents)
++
++{
++ struct xfs_cui_log_item *cuip;
++
++ ASSERT(nextents > 0);
++ if (nextents > XFS_CUI_MAX_FAST_EXTENTS)
++ cuip = kmem_zalloc(xfs_cui_log_item_sizeof(nextents),
++ 0);
++ else
++ cuip = kmem_zone_zalloc(xfs_cui_zone, 0);
++
++ xfs_log_item_init(mp, &cuip->cui_item, XFS_LI_CUI, &xfs_cui_item_ops);
++ cuip->cui_format.cui_nextents = nextents;
++ cuip->cui_format.cui_id = (uintptr_t)(void *)cuip;
++ atomic_set(&cuip->cui_next_extent, 0);
++ atomic_set(&cuip->cui_refcount, 2);
++
++ return cuip;
++}
+diff --git a/fs/xfs/xfs_refcount_item.h b/fs/xfs/xfs_refcount_item.h
+index e47530f30489d..de5f48ff4f74e 100644
+--- a/fs/xfs/xfs_refcount_item.h
++++ b/fs/xfs/xfs_refcount_item.h
+@@ -80,6 +80,7 @@ extern struct kmem_zone *xfs_cud_zone;
+ struct xfs_cui_log_item *xfs_cui_init(struct xfs_mount *, uint);
+ void xfs_cui_item_free(struct xfs_cui_log_item *);
+ void xfs_cui_release(struct xfs_cui_log_item *);
+-int xfs_cui_recover(struct xfs_trans *parent_tp, struct xfs_cui_log_item *cuip);
++int xfs_cui_recover(struct xfs_cui_log_item *cuip,
++ struct list_head *capture_list);
+
+ #endif /* __XFS_REFCOUNT_ITEM_H__ */
+diff --git a/fs/xfs/xfs_rmap_item.c b/fs/xfs/xfs_rmap_item.c
+index 02f84d9a511c3..ba1dbb6c40632 100644
+--- a/fs/xfs/xfs_rmap_item.c
++++ b/fs/xfs/xfs_rmap_item.c
+@@ -122,39 +122,6 @@ xfs_rui_item_release(
+ xfs_rui_release(RUI_ITEM(lip));
+ }
+
+-static const struct xfs_item_ops xfs_rui_item_ops = {
+- .iop_size = xfs_rui_item_size,
+- .iop_format = xfs_rui_item_format,
+- .iop_unpin = xfs_rui_item_unpin,
+- .iop_release = xfs_rui_item_release,
+-};
+-
+-/*
+- * Allocate and initialize an rui item with the given number of extents.
+- */
+-struct xfs_rui_log_item *
+-xfs_rui_init(
+- struct xfs_mount *mp,
+- uint nextents)
+-
+-{
+- struct xfs_rui_log_item *ruip;
+-
+- ASSERT(nextents > 0);
+- if (nextents > XFS_RUI_MAX_FAST_EXTENTS)
+- ruip = kmem_zalloc(xfs_rui_log_item_sizeof(nextents), 0);
+- else
+- ruip = kmem_zone_zalloc(xfs_rui_zone, 0);
+-
+- xfs_log_item_init(mp, &ruip->rui_item, XFS_LI_RUI, &xfs_rui_item_ops);
+- ruip->rui_format.rui_nextents = nextents;
+- ruip->rui_format.rui_id = (uintptr_t)(void *)ruip;
+- atomic_set(&ruip->rui_next_extent, 0);
+- atomic_set(&ruip->rui_refcount, 2);
+-
+- return ruip;
+-}
+-
+ /*
+ * Copy an RUI format buffer from the given buf, and into the destination
+ * RUI format structure. The RUI/RUD items were designed not to need any
+@@ -352,41 +319,16 @@ xfs_rmap_update_diff_items(
+ XFS_FSB_TO_AGNO(mp, rb->ri_bmap.br_startblock);
+ }
+
+-/* Get an RUI. */
+-STATIC void *
+-xfs_rmap_update_create_intent(
+- struct xfs_trans *tp,
+- unsigned int count)
+-{
+- struct xfs_rui_log_item *ruip;
+-
+- ASSERT(tp != NULL);
+- ASSERT(count > 0);
+-
+- ruip = xfs_rui_init(tp->t_mountp, count);
+- ASSERT(ruip != NULL);
+-
+- /*
+- * Get a log_item_desc to point at the new item.
+- */
+- xfs_trans_add_item(tp, &ruip->rui_item);
+- return ruip;
+-}
+-
+ /* Log rmap updates in the intent item. */
+ STATIC void
+ xfs_rmap_update_log_item(
+ struct xfs_trans *tp,
+- void *intent,
+- struct list_head *item)
++ struct xfs_rui_log_item *ruip,
++ struct xfs_rmap_intent *rmap)
+ {
+- struct xfs_rui_log_item *ruip = intent;
+- struct xfs_rmap_intent *rmap;
+ uint next_extent;
+ struct xfs_map_extent *map;
+
+- rmap = container_of(item, struct xfs_rmap_intent, ri_list);
+-
+ tp->t_flags |= XFS_TRANS_DIRTY;
+ set_bit(XFS_LI_DIRTY, &ruip->rui_item.li_flags);
+
+@@ -406,14 +348,35 @@ xfs_rmap_update_log_item(
+ rmap->ri_bmap.br_state);
+ }
+
++static struct xfs_log_item *
++xfs_rmap_update_create_intent(
++ struct xfs_trans *tp,
++ struct list_head *items,
++ unsigned int count,
++ bool sort)
++{
++ struct xfs_mount *mp = tp->t_mountp;
++ struct xfs_rui_log_item *ruip = xfs_rui_init(mp, count);
++ struct xfs_rmap_intent *rmap;
++
++ ASSERT(count > 0);
++
++ xfs_trans_add_item(tp, &ruip->rui_item);
++ if (sort)
++ list_sort(mp, items, xfs_rmap_update_diff_items);
++ list_for_each_entry(rmap, items, ri_list)
++ xfs_rmap_update_log_item(tp, ruip, rmap);
++ return &ruip->rui_item;
++}
++
+ /* Get an RUD so we can process all the deferred rmap updates. */
+ STATIC void *
+ xfs_rmap_update_create_done(
+ struct xfs_trans *tp,
+- void *intent,
++ struct xfs_log_item *intent,
+ unsigned int count)
+ {
+- return xfs_trans_get_rud(tp, intent);
++ return xfs_trans_get_rud(tp, RUI_ITEM(intent));
+ }
+
+ /* Process a deferred rmap update. */
+@@ -455,9 +418,9 @@ xfs_rmap_update_finish_cleanup(
+ /* Abort all pending RUIs. */
+ STATIC void
+ xfs_rmap_update_abort_intent(
+- void *intent)
++ struct xfs_log_item *intent)
+ {
+- xfs_rui_release(intent);
++ xfs_rui_release(RUI_ITEM(intent));
+ }
+
+ /* Cancel a deferred rmap update. */
+@@ -473,10 +436,8 @@ xfs_rmap_update_cancel_item(
+
+ const struct xfs_defer_op_type xfs_rmap_update_defer_type = {
+ .max_items = XFS_RUI_MAX_FAST_EXTENTS,
+- .diff_items = xfs_rmap_update_diff_items,
+ .create_intent = xfs_rmap_update_create_intent,
+ .abort_intent = xfs_rmap_update_abort_intent,
+- .log_item = xfs_rmap_update_log_item,
+ .create_done = xfs_rmap_update_create_done,
+ .finish_item = xfs_rmap_update_finish_item,
+ .finish_cleanup = xfs_rmap_update_finish_cleanup,
+@@ -489,9 +450,10 @@ const struct xfs_defer_op_type xfs_rmap_update_defer_type = {
+ */
+ int
+ xfs_rui_recover(
+- struct xfs_mount *mp,
+- struct xfs_rui_log_item *ruip)
++ struct xfs_rui_log_item *ruip,
++ struct list_head *capture_list)
+ {
++ struct xfs_mount *mp = ruip->rui_item.li_mountp;
+ int i;
+ int error = 0;
+ struct xfs_map_extent *rmap;
+@@ -598,11 +560,70 @@ xfs_rui_recover(
+
+ xfs_rmap_finish_one_cleanup(tp, rcur, error);
+ set_bit(XFS_RUI_RECOVERED, &ruip->rui_flags);
+- error = xfs_trans_commit(tp);
+- return error;
++ return xfs_defer_ops_capture_and_commit(tp, NULL, capture_list);
+
+ abort_error:
+ xfs_rmap_finish_one_cleanup(tp, rcur, error);
+ xfs_trans_cancel(tp);
+ return error;
+ }
++
++/* Relog an intent item to push the log tail forward. */
++static struct xfs_log_item *
++xfs_rui_item_relog(
++ struct xfs_log_item *intent,
++ struct xfs_trans *tp)
++{
++ struct xfs_rud_log_item *rudp;
++ struct xfs_rui_log_item *ruip;
++ struct xfs_map_extent *extp;
++ unsigned int count;
++
++ count = RUI_ITEM(intent)->rui_format.rui_nextents;
++ extp = RUI_ITEM(intent)->rui_format.rui_extents;
++
++ tp->t_flags |= XFS_TRANS_DIRTY;
++ rudp = xfs_trans_get_rud(tp, RUI_ITEM(intent));
++ set_bit(XFS_LI_DIRTY, &rudp->rud_item.li_flags);
++
++ ruip = xfs_rui_init(tp->t_mountp, count);
++ memcpy(ruip->rui_format.rui_extents, extp, count * sizeof(*extp));
++ atomic_set(&ruip->rui_next_extent, count);
++ xfs_trans_add_item(tp, &ruip->rui_item);
++ set_bit(XFS_LI_DIRTY, &ruip->rui_item.li_flags);
++ return &ruip->rui_item;
++}
++
++static const struct xfs_item_ops xfs_rui_item_ops = {
++ .iop_size = xfs_rui_item_size,
++ .iop_format = xfs_rui_item_format,
++ .iop_unpin = xfs_rui_item_unpin,
++ .iop_release = xfs_rui_item_release,
++ .iop_relog = xfs_rui_item_relog,
++};
++
++/*
++ * Allocate and initialize an rui item with the given number of extents.
++ */
++struct xfs_rui_log_item *
++xfs_rui_init(
++ struct xfs_mount *mp,
++ uint nextents)
++
++{
++ struct xfs_rui_log_item *ruip;
++
++ ASSERT(nextents > 0);
++ if (nextents > XFS_RUI_MAX_FAST_EXTENTS)
++ ruip = kmem_zalloc(xfs_rui_log_item_sizeof(nextents), 0);
++ else
++ ruip = kmem_zone_zalloc(xfs_rui_zone, 0);
++
++ xfs_log_item_init(mp, &ruip->rui_item, XFS_LI_RUI, &xfs_rui_item_ops);
++ ruip->rui_format.rui_nextents = nextents;
++ ruip->rui_format.rui_id = (uintptr_t)(void *)ruip;
++ atomic_set(&ruip->rui_next_extent, 0);
++ atomic_set(&ruip->rui_refcount, 2);
++
++ return ruip;
++}
+diff --git a/fs/xfs/xfs_rmap_item.h b/fs/xfs/xfs_rmap_item.h
+index 8708e4a5aa5c3..5cf4acb0e915e 100644
+--- a/fs/xfs/xfs_rmap_item.h
++++ b/fs/xfs/xfs_rmap_item.h
+@@ -82,6 +82,7 @@ int xfs_rui_copy_format(struct xfs_log_iovec *buf,
+ struct xfs_rui_log_format *dst_rui_fmt);
+ void xfs_rui_item_free(struct xfs_rui_log_item *);
+ void xfs_rui_release(struct xfs_rui_log_item *);
+-int xfs_rui_recover(struct xfs_mount *mp, struct xfs_rui_log_item *ruip);
++int xfs_rui_recover(struct xfs_rui_log_item *ruip,
++ struct list_head *capture_list);
+
+ #endif /* __XFS_RMAP_ITEM_H__ */
+diff --git a/fs/xfs/xfs_stats.c b/fs/xfs/xfs_stats.c
+index f70f1255220b3..20e0534a772c9 100644
+--- a/fs/xfs/xfs_stats.c
++++ b/fs/xfs/xfs_stats.c
+@@ -23,6 +23,7 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
+ uint64_t xs_xstrat_bytes = 0;
+ uint64_t xs_write_bytes = 0;
+ uint64_t xs_read_bytes = 0;
++ uint64_t defer_relog = 0;
+
+ static const struct xstats_entry {
+ char *desc;
+@@ -70,10 +71,13 @@ int xfs_stats_format(struct xfsstats __percpu *stats, char *buf)
+ xs_xstrat_bytes += per_cpu_ptr(stats, i)->s.xs_xstrat_bytes;
+ xs_write_bytes += per_cpu_ptr(stats, i)->s.xs_write_bytes;
+ xs_read_bytes += per_cpu_ptr(stats, i)->s.xs_read_bytes;
++ defer_relog += per_cpu_ptr(stats, i)->s.defer_relog;
+ }
+
+ len += scnprintf(buf + len, PATH_MAX-len, "xpc %Lu %Lu %Lu\n",
+ xs_xstrat_bytes, xs_write_bytes, xs_read_bytes);
++ len += scnprintf(buf + len, PATH_MAX-len, "defer_relog %llu\n",
++ defer_relog);
+ len += scnprintf(buf + len, PATH_MAX-len, "debug %u\n",
+ #if defined(DEBUG)
+ 1);
+diff --git a/fs/xfs/xfs_stats.h b/fs/xfs/xfs_stats.h
+index 34d704f703d25..43ffba74f045e 100644
+--- a/fs/xfs/xfs_stats.h
++++ b/fs/xfs/xfs_stats.h
+@@ -137,6 +137,7 @@ struct __xfsstats {
+ uint64_t xs_xstrat_bytes;
+ uint64_t xs_write_bytes;
+ uint64_t xs_read_bytes;
++ uint64_t defer_relog;
+ };
+
+ #define xfsstats_offset(f) (offsetof(struct __xfsstats, f)/sizeof(uint32_t))
+diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
+index f1407900aeef9..9e73d2b29911d 100644
+--- a/fs/xfs/xfs_super.c
++++ b/fs/xfs/xfs_super.c
+@@ -1914,13 +1914,13 @@ xfs_init_zones(void)
+ if (!xfs_buf_item_zone)
+ goto out_destroy_trans_zone;
+
+- xfs_efd_zone = kmem_zone_init((sizeof(xfs_efd_log_item_t) +
++ xfs_efd_zone = kmem_zone_init((sizeof(struct xfs_efd_log_item) +
+ ((XFS_EFD_MAX_FAST_EXTENTS - 1) *
+ sizeof(xfs_extent_t))), "xfs_efd_item");
+ if (!xfs_efd_zone)
+ goto out_destroy_buf_item_zone;
+
+- xfs_efi_zone = kmem_zone_init((sizeof(xfs_efi_log_item_t) +
++ xfs_efi_zone = kmem_zone_init((sizeof(struct xfs_efi_log_item) +
+ ((XFS_EFI_MAX_FAST_EXTENTS - 1) *
+ sizeof(xfs_extent_t))), "xfs_efi_item");
+ if (!xfs_efi_zone)
+@@ -1934,8 +1934,8 @@ xfs_init_zones(void)
+ goto out_destroy_efi_zone;
+
+ xfs_ili_zone =
+- kmem_zone_init_flags(sizeof(xfs_inode_log_item_t), "xfs_ili",
+- KM_ZONE_SPREAD, NULL);
++ kmem_zone_init_flags(sizeof(struct xfs_inode_log_item),
++ "xfs_ili", KM_ZONE_SPREAD, NULL);
+ if (!xfs_ili_zone)
+ goto out_destroy_inode_zone;
+ xfs_icreate_zone = kmem_zone_init(sizeof(struct xfs_icreate_item),
+diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h
+index f94908125e8f9..4b58183954064 100644
+--- a/fs/xfs/xfs_trace.h
++++ b/fs/xfs/xfs_trace.h
+@@ -2418,6 +2418,7 @@ DEFINE_DEFER_PENDING_EVENT(xfs_defer_create_intent);
+ DEFINE_DEFER_PENDING_EVENT(xfs_defer_cancel_list);
+ DEFINE_DEFER_PENDING_EVENT(xfs_defer_pending_finish);
+ DEFINE_DEFER_PENDING_EVENT(xfs_defer_pending_abort);
++DEFINE_DEFER_PENDING_EVENT(xfs_defer_relog_intent);
+
+ #define DEFINE_BMAP_FREE_DEFERRED_EVENT DEFINE_PHYS_EXTENT_DEFERRED_EVENT
+ DEFINE_BMAP_FREE_DEFERRED_EVENT(xfs_bmap_free_defer);
+diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h
+index 64d7f171ebd32..941647027f00f 100644
+--- a/fs/xfs/xfs_trans.h
++++ b/fs/xfs/xfs_trans.h
+@@ -77,6 +77,8 @@ struct xfs_item_ops {
+ void (*iop_release)(struct xfs_log_item *);
+ xfs_lsn_t (*iop_committed)(struct xfs_log_item *, xfs_lsn_t);
+ void (*iop_error)(struct xfs_log_item *, xfs_buf_t *);
++ struct xfs_log_item *(*iop_relog)(struct xfs_log_item *intent,
++ struct xfs_trans *tp);
+ };
+
+ /*
+@@ -244,4 +246,12 @@ void xfs_trans_buf_copy_type(struct xfs_buf *dst_bp,
+
+ extern kmem_zone_t *xfs_trans_zone;
+
++static inline struct xfs_log_item *
++xfs_trans_item_relog(
++ struct xfs_log_item *lip,
++ struct xfs_trans *tp)
++{
++ return lip->li_ops->iop_relog(lip, tp);
++}
++
+ #endif /* __XFS_TRANS_H__ */
+diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h
+index c7507135c2a05..311dd8e921826 100644
+--- a/include/linux/hugetlb.h
++++ b/include/linux/hugetlb.h
+@@ -7,6 +7,7 @@
+ #include <linux/fs.h>
+ #include <linux/hugetlb_inline.h>
+ #include <linux/cgroup.h>
++#include <linux/page_ref.h>
+ #include <linux/list.h>
+ #include <linux/kref.h>
+ #include <asm/pgtable.h>
+@@ -396,7 +397,10 @@ static inline struct hstate *hstate_sizelog(int page_size_log)
+ if (!page_size_log)
+ return &default_hstate;
+
+- return size_to_hstate(1UL << page_size_log);
++ if (page_size_log < BITS_PER_LONG)
++ return size_to_hstate(1UL << page_size_log);
++
++ return NULL;
+ }
+
+ static inline struct hstate *hstate_vma(struct vm_area_struct *vma)
+@@ -744,4 +748,16 @@ static inline spinlock_t *huge_pte_lock(struct hstate *h,
+ return ptl;
+ }
+
++#ifdef CONFIG_ARCH_WANT_HUGE_PMD_SHARE
++static inline bool hugetlb_pmd_shared(pte_t *pte)
++{
++ return page_count(virt_to_page(pte)) > 1;
++}
++#else
++static inline bool hugetlb_pmd_shared(pte_t *pte)
++{
++ return false;
++}
++#endif
++
+ #endif /* _LINUX_HUGETLB_H */
+diff --git a/include/linux/iommu.h b/include/linux/iommu.h
+index 29bac5345563a..6ca3fb2873d7d 100644
+--- a/include/linux/iommu.h
++++ b/include/linux/iommu.h
+@@ -256,7 +256,7 @@ struct iommu_ops {
+ int (*attach_dev)(struct iommu_domain *domain, struct device *dev);
+ void (*detach_dev)(struct iommu_domain *domain, struct device *dev);
+ int (*map)(struct iommu_domain *domain, unsigned long iova,
+- phys_addr_t paddr, size_t size, int prot);
++ phys_addr_t paddr, size_t size, int prot, gfp_t gfp);
+ size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
+ size_t size, struct iommu_iotlb_gather *iotlb_gather);
+ void (*flush_iotlb_all)(struct iommu_domain *domain);
+@@ -421,6 +421,8 @@ extern struct iommu_domain *iommu_get_domain_for_dev(struct device *dev);
+ extern struct iommu_domain *iommu_get_dma_domain(struct device *dev);
+ extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot);
++extern int iommu_map_atomic(struct iommu_domain *domain, unsigned long iova,
++ phys_addr_t paddr, size_t size, int prot);
+ extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
+ size_t size);
+ extern size_t iommu_unmap_fast(struct iommu_domain *domain,
+@@ -428,6 +430,9 @@ extern size_t iommu_unmap_fast(struct iommu_domain *domain,
+ struct iommu_iotlb_gather *iotlb_gather);
+ extern size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
+ struct scatterlist *sg,unsigned int nents, int prot);
++extern size_t iommu_map_sg_atomic(struct iommu_domain *domain,
++ unsigned long iova, struct scatterlist *sg,
++ unsigned int nents, int prot);
+ extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
+ extern void iommu_set_fault_handler(struct iommu_domain *domain,
+ iommu_fault_handler_t handler, void *token);
+@@ -662,6 +667,13 @@ static inline int iommu_map(struct iommu_domain *domain, unsigned long iova,
+ return -ENODEV;
+ }
+
++static inline int iommu_map_atomic(struct iommu_domain *domain,
++ unsigned long iova, phys_addr_t paddr,
++ size_t size, int prot)
++{
++ return -ENODEV;
++}
++
+ static inline size_t iommu_unmap(struct iommu_domain *domain,
+ unsigned long iova, size_t size)
+ {
+@@ -682,6 +694,13 @@ static inline size_t iommu_map_sg(struct iommu_domain *domain,
+ return 0;
+ }
+
++static inline size_t iommu_map_sg_atomic(struct iommu_domain *domain,
++ unsigned long iova, struct scatterlist *sg,
++ unsigned int nents, int prot)
++{
++ return 0;
++}
++
+ static inline void iommu_flush_tlb_all(struct iommu_domain *domain)
+ {
+ }
+diff --git a/include/linux/stmmac.h b/include/linux/stmmac.h
+index 0b35747c9837a..88b107e20fc7c 100644
+--- a/include/linux/stmmac.h
++++ b/include/linux/stmmac.h
+@@ -178,6 +178,7 @@ struct plat_stmmacenet_data {
+ int rss_en;
+ int mac_port_sel_speed;
+ bool en_tx_lpi_clockgating;
++ bool rx_clk_runs_in_lpi;
+ int has_xgmac;
+ bool sph_disable;
+ };
+diff --git a/include/net/sock.h b/include/net/sock.h
+index 5fa255b1e0a65..976433438c6f2 100644
+--- a/include/net/sock.h
++++ b/include/net/sock.h
+@@ -2167,6 +2167,19 @@ static inline __must_check bool skb_set_owner_sk_safe(struct sk_buff *skb, struc
+ return false;
+ }
+
++static inline struct sk_buff *skb_clone_and_charge_r(struct sk_buff *skb, struct sock *sk)
++{
++ skb = skb_clone(skb, sk_gfp_mask(sk, GFP_ATOMIC));
++ if (skb) {
++ if (sk_rmem_schedule(sk, skb, skb->truesize)) {
++ skb_set_owner_r(skb, sk);
++ return skb;
++ }
++ __kfree_skb(skb);
++ }
++ return NULL;
++}
++
+ void sk_reset_timer(struct sock *sk, struct timer_list *timer,
+ unsigned long expires);
+
+diff --git a/kernel/sched/psi.c b/kernel/sched/psi.c
+index 9dd83eb74a9da..8c19a5438d0ee 100644
+--- a/kernel/sched/psi.c
++++ b/kernel/sched/psi.c
+@@ -1092,10 +1092,11 @@ void psi_trigger_destroy(struct psi_trigger *t)
+
+ group = t->group;
+ /*
+- * Wakeup waiters to stop polling. Can happen if cgroup is deleted
+- * from under a polling process.
++ * Wakeup waiters to stop polling and clear the queue to prevent it from
++ * being accessed later. Can happen if cgroup is deleted from under a
++ * polling process.
+ */
+- wake_up_interruptible(&t->event_wait);
++ wake_up_pollfree(&t->event_wait);
+
+ mutex_lock(&group->trigger_lock);
+
+diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
+index 8b87f1f74e325..306fbe14747bf 100644
+--- a/kernel/trace/trace.c
++++ b/kernel/trace/trace.c
+@@ -8298,9 +8298,6 @@ buffer_percent_write(struct file *filp, const char __user *ubuf,
+ if (val > 100)
+ return -EINVAL;
+
+- if (!val)
+- val = 1;
+-
+ tr->buffer_percent = val;
+
+ (*ppos)++;
+diff --git a/mm/memblock.c b/mm/memblock.c
+index 84a14b63e9129..a75cc65f03307 100644
+--- a/mm/memblock.c
++++ b/mm/memblock.c
+@@ -1546,13 +1546,7 @@ void __init __memblock_free_late(phys_addr_t base, phys_addr_t size)
+ end = PFN_DOWN(base + size);
+
+ for (; cursor < end; cursor++) {
+- /*
+- * Reserved pages are always initialized by the end of
+- * memblock_free_all() (by memmap_init() and, if deferred
+- * initialization is enabled, memmap_init_reserved_pages()), so
+- * these pages can be released directly to the buddy allocator.
+- */
+- __free_pages_core(pfn_to_page(cursor), 0);
++ memblock_free_pages(pfn_to_page(cursor), cursor, 0);
+ totalram_pages_inc();
+ }
+ }
+diff --git a/mm/mempolicy.c b/mm/mempolicy.c
+index f7b231f67156b..36c2a9619ba56 100644
+--- a/mm/mempolicy.c
++++ b/mm/mempolicy.c
+@@ -571,7 +571,8 @@ static int queue_pages_hugetlb(pte_t *pte, unsigned long hmask,
+ goto unlock;
+ /* With MPOL_MF_MOVE, we migrate only unshared hugepage. */
+ if (flags & (MPOL_MF_MOVE_ALL) ||
+- (flags & MPOL_MF_MOVE && page_mapcount(page) == 1))
++ (flags & MPOL_MF_MOVE && page_mapcount(page) == 1 &&
++ !hugetlb_pmd_shared(pte)))
+ isolate_huge_page(page, qp->pagelist);
+ unlock:
+ spin_unlock(ptl);
+diff --git a/mm/swapfile.c b/mm/swapfile.c
+index f6964212c6c8f..d6bdacaedc95c 100644
+--- a/mm/swapfile.c
++++ b/mm/swapfile.c
+@@ -1061,6 +1061,7 @@ start_over:
+ goto check_out;
+ pr_debug("scan_swap_map of si %d failed to find offset\n",
+ si->type);
++ cond_resched();
+
+ spin_lock(&swap_avail_lock);
+ nextsi:
+@@ -1950,10 +1951,14 @@ static int unuse_pte_range(struct vm_area_struct *vma, pmd_t *pmd,
+
+ pte_unmap(pte);
+ swap_map = &si->swap_map[offset];
+- vmf.vma = vma;
+- vmf.address = addr;
+- vmf.pmd = pmd;
+- page = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE, &vmf);
++ page = lookup_swap_cache(entry, vma, addr);
++ if (!page) {
++ vmf.vma = vma;
++ vmf.address = addr;
++ vmf.pmd = pmd;
++ page = swapin_readahead(entry, GFP_HIGHUSER_MOVABLE,
++ &vmf);
++ }
+ if (!page) {
+ if (*swap_map == 0 || *swap_map == SWAP_MAP_BAD)
+ goto try_next;
+diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c
+index 01e33724d10c3..43cb7aab4eed6 100644
+--- a/net/bridge/br_netfilter_hooks.c
++++ b/net/bridge/br_netfilter_hooks.c
+@@ -871,6 +871,7 @@ static unsigned int ip_sabotage_in(void *priv,
+ if (nf_bridge && !nf_bridge->in_prerouting &&
+ !netif_is_l3_master(skb->dev) &&
+ !netif_is_l3_slave(skb->dev)) {
++ nf_bridge_info_free(skb);
+ state->okfn(state->net, state->sk, skb);
+ return NF_STOLEN;
+ }
+diff --git a/net/can/j1939/address-claim.c b/net/can/j1939/address-claim.c
+index f33c473279278..ca4ad6cdd5cbf 100644
+--- a/net/can/j1939/address-claim.c
++++ b/net/can/j1939/address-claim.c
+@@ -165,6 +165,46 @@ static void j1939_ac_process(struct j1939_priv *priv, struct sk_buff *skb)
+ * leaving this function.
+ */
+ ecu = j1939_ecu_get_by_name_locked(priv, name);
++
++ if (ecu && ecu->addr == skcb->addr.sa) {
++ /* The ISO 11783-5 standard, in "4.5.2 - Address claim
++ * requirements", states:
++ * d) No CF shall begin, or resume, transmission on the
++ * network until 250 ms after it has successfully claimed
++ * an address except when responding to a request for
++ * address-claimed.
++ *
++ * But "Figure 6" and "Figure 7" in "4.5.4.2 - Address-claim
++ * prioritization" show that the CF begins the transmission
++ * after 250 ms from the first AC (address-claimed) message
++ * even if it sends another AC message during that time window
++ * to resolve the address contention with another CF.
++ *
++ * As stated in "4.4.2.3 - Address-claimed message":
++ * In order to successfully claim an address, the CF sending
++ * an address claimed message shall not receive a contending
++ * claim from another CF for at least 250 ms.
++ *
++ * As stated in "4.4.3.2 - NAME management (NM) message":
++ * 1) A commanding CF can
++ * d) request that a CF with a specified NAME transmit
++ * the address-claimed message with its current NAME.
++ * 2) A target CF shall
++ * d) send an address-claimed message in response to a
++ * request for a matching NAME
++ *
++ * Taking the above arguments into account, the 250 ms wait is
++ * requested only during network initialization.
++ *
++ * Do not restart the timer on AC message if both the NAME and
++ * the address match and so if the address has already been
++ * claimed (timer has expired) or the AC message has been sent
++ * to resolve the contention with another CF (timer is still
++ * running).
++ */
++ goto out_ecu_put;
++ }
++
+ if (!ecu && j1939_address_is_unicast(skcb->addr.sa))
+ ecu = j1939_ecu_create_locked(priv, name);
+
+diff --git a/net/can/j1939/transport.c b/net/can/j1939/transport.c
+index 9ca19dfe3e839..9c8c7c5dc9c36 100644
+--- a/net/can/j1939/transport.c
++++ b/net/can/j1939/transport.c
+@@ -1087,10 +1087,6 @@ static bool j1939_session_deactivate(struct j1939_session *session)
+ bool active;
+
+ j1939_session_list_lock(priv);
+- /* This function should be called with a session ref-count of at
+- * least 2.
+- */
+- WARN_ON_ONCE(kref_read(&session->kref) < 2);
+ active = j1939_session_deactivate_locked(session);
+ j1939_session_list_unlock(priv);
+
+diff --git a/net/core/dev.c b/net/core/dev.c
+index 296bed9431f3b..615cdaac3e729 100644
+--- a/net/core/dev.c
++++ b/net/core/dev.c
+@@ -9467,7 +9467,7 @@ void netdev_stats_to_stats64(struct rtnl_link_stats64 *stats64,
+
+ BUILD_BUG_ON(n > sizeof(*stats64) / sizeof(u64));
+ for (i = 0; i < n; i++)
+- dst[i] = atomic_long_read(&src[i]);
++ dst[i] = (unsigned long)atomic_long_read(&src[i]);
+ /* zero out counters that only exist in rtnl_link_stats64 */
+ memset((char *)stats64 + n * sizeof(u64), 0,
+ sizeof(*stats64) - n * sizeof(u64));
+diff --git a/net/core/filter.c b/net/core/filter.c
+index 71fcb4e7edae4..051b9710d7b5e 100644
+--- a/net/core/filter.c
++++ b/net/core/filter.c
+@@ -4617,7 +4617,6 @@ static int bpf_fib_set_fwd_params(struct bpf_fib_lookup *params,
+ memcpy(params->smac, dev->dev_addr, ETH_ALEN);
+ params->h_vlan_TCI = 0;
+ params->h_vlan_proto = 0;
+- params->ifindex = dev->ifindex;
+
+ return 0;
+ }
+@@ -4714,6 +4713,7 @@ static int bpf_ipv4_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
+ dev = nhc->nhc_dev;
+
+ params->rt_metric = res.fi->fib_priority;
++ params->ifindex = dev->ifindex;
+
+ /* xdp and cls_bpf programs are run in RCU-bh so
+ * rcu_read_lock_bh is not needed here
+@@ -4839,6 +4839,7 @@ static int bpf_ipv6_fib_lookup(struct net *net, struct bpf_fib_lookup *params,
+
+ dev = res.nh->fib_nh_dev;
+ params->rt_metric = res.f6i->fib6_metric;
++ params->ifindex = dev->ifindex;
+
+ /* xdp and cls_bpf programs are run in RCU-bh so rcu_read_lock_bh is
+ * not needed here.
+diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c
+index 1bf267d36a9c8..c2844fb442c4f 100644
+--- a/net/dccp/ipv6.c
++++ b/net/dccp/ipv6.c
+@@ -541,11 +541,9 @@ static struct sock *dccp_v6_request_recv_sock(const struct sock *sk,
+ *own_req = inet_ehash_nolisten(newsk, req_to_sk(req_unhash), NULL);
+ /* Clone pktoptions received with SYN, if we own the req */
+ if (*own_req && ireq->pktopts) {
+- newnp->pktoptions = skb_clone(ireq->pktopts, GFP_ATOMIC);
++ newnp->pktoptions = skb_clone_and_charge_r(ireq->pktopts, newsk);
+ consume_skb(ireq->pktopts);
+ ireq->pktopts = NULL;
+- if (newnp->pktoptions)
+- skb_set_owner_r(newnp->pktoptions, newsk);
+ }
+
+ return newsk;
+@@ -605,7 +603,7 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ --ANK (980728)
+ */
+ if (np->rxopt.all)
+- opt_skb = skb_clone(skb, GFP_ATOMIC);
++ opt_skb = skb_clone_and_charge_r(skb, sk);
+
+ if (sk->sk_state == DCCP_OPEN) { /* Fast path */
+ if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len))
+@@ -669,7 +667,6 @@ ipv6_pktoptions:
+ np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb));
+ if (ipv6_opt_accepted(sk, opt_skb,
+ &DCCP_SKB_CB(opt_skb)->header.h6)) {
+- skb_set_owner_r(opt_skb, sk);
+ memmove(IP6CB(opt_skb),
+ &DCCP_SKB_CB(opt_skb)->header.h6,
+ sizeof(struct inet6_skb_parm));
+diff --git a/net/ipv6/datagram.c b/net/ipv6/datagram.c
+index 390bedde21a56..06261603e083b 100644
+--- a/net/ipv6/datagram.c
++++ b/net/ipv6/datagram.c
+@@ -50,7 +50,7 @@ static void ip6_datagram_flow_key_init(struct flowi6 *fl6, struct sock *sk)
+ fl6->flowi6_mark = sk->sk_mark;
+ fl6->fl6_dport = inet->inet_dport;
+ fl6->fl6_sport = inet->inet_sport;
+- fl6->flowlabel = np->flow_label;
++ fl6->flowlabel = ip6_make_flowinfo(np->tclass, np->flow_label);
+ fl6->flowi6_uid = sk->sk_uid;
+
+ if (!fl6->flowi6_oif)
+diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
+index e84b79357b2ff..da8611a75d2aa 100644
+--- a/net/ipv6/tcp_ipv6.c
++++ b/net/ipv6/tcp_ipv6.c
+@@ -264,6 +264,7 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,
+ fl6.flowi6_proto = IPPROTO_TCP;
+ fl6.daddr = sk->sk_v6_daddr;
+ fl6.saddr = saddr ? *saddr : np->saddr;
++ fl6.flowlabel = ip6_make_flowinfo(np->tclass, np->flow_label);
+ fl6.flowi6_oif = sk->sk_bound_dev_if;
+ fl6.flowi6_mark = sk->sk_mark;
+ fl6.fl6_dport = usin->sin6_port;
+@@ -1318,14 +1319,11 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *
+
+ /* Clone pktoptions received with SYN, if we own the req */
+ if (ireq->pktopts) {
+- newnp->pktoptions = skb_clone(ireq->pktopts,
+- sk_gfp_mask(sk, GFP_ATOMIC));
++ newnp->pktoptions = skb_clone_and_charge_r(ireq->pktopts, newsk);
+ consume_skb(ireq->pktopts);
+ ireq->pktopts = NULL;
+- if (newnp->pktoptions) {
++ if (newnp->pktoptions)
+ tcp_v6_restore_cb(newnp->pktoptions);
+- skb_set_owner_r(newnp->pktoptions, newsk);
+- }
+ }
+ } else {
+ if (!req_unhash && found_dup_sk) {
+@@ -1393,7 +1391,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb)
+ --ANK (980728)
+ */
+ if (np->rxopt.all)
+- opt_skb = skb_clone(skb, sk_gfp_mask(sk, GFP_ATOMIC));
++ opt_skb = skb_clone_and_charge_r(skb, sk);
+
+ if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */
+ struct dst_entry *dst;
+@@ -1475,7 +1473,6 @@ ipv6_pktoptions:
+ if (np->repflow)
+ np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb));
+ if (ipv6_opt_accepted(sk, opt_skb, &TCP_SKB_CB(opt_skb)->header.h6)) {
+- skb_set_owner_r(opt_skb, sk);
+ tcp_v6_restore_cb(opt_skb);
+ opt_skb = xchg(&np->pktoptions, opt_skb);
+ } else {
+diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c
+index d5e3656fc67ca..3a55a392e0218 100644
+--- a/net/mpls/af_mpls.c
++++ b/net/mpls/af_mpls.c
+@@ -1428,6 +1428,7 @@ static int mpls_dev_sysctl_register(struct net_device *dev,
+ free:
+ kfree(table);
+ out:
++ mdev->sysctl = NULL;
+ return -ENOBUFS;
+ }
+
+@@ -1437,6 +1438,9 @@ static void mpls_dev_sysctl_unregister(struct net_device *dev,
+ struct net *net = dev_net(dev);
+ struct ctl_table *table;
+
++ if (!mdev->sysctl)
++ return;
++
+ table = mdev->sysctl->ctl_table_arg;
+ unregister_net_sysctl_table(mdev->sysctl);
+ kfree(table);
+diff --git a/net/netfilter/nft_tproxy.c b/net/netfilter/nft_tproxy.c
+index b97ab1198b03f..a0e30bf4a845c 100644
+--- a/net/netfilter/nft_tproxy.c
++++ b/net/netfilter/nft_tproxy.c
+@@ -289,6 +289,13 @@ static int nft_tproxy_dump(struct sk_buff *skb,
+ return 0;
+ }
+
++static int nft_tproxy_validate(const struct nft_ctx *ctx,
++ const struct nft_expr *expr,
++ const struct nft_data **data)
++{
++ return nft_chain_validate_hooks(ctx->chain, 1 << NF_INET_PRE_ROUTING);
++}
++
+ static struct nft_expr_type nft_tproxy_type;
+ static const struct nft_expr_ops nft_tproxy_ops = {
+ .type = &nft_tproxy_type,
+@@ -296,6 +303,7 @@ static const struct nft_expr_ops nft_tproxy_ops = {
+ .eval = nft_tproxy_eval,
+ .init = nft_tproxy_init,
+ .dump = nft_tproxy_dump,
++ .validate = nft_tproxy_validate,
+ };
+
+ static struct nft_expr_type nft_tproxy_type __read_mostly = {
+diff --git a/net/netrom/af_netrom.c b/net/netrom/af_netrom.c
+index 58d5373c513c7..7da77ddba5f4d 100644
+--- a/net/netrom/af_netrom.c
++++ b/net/netrom/af_netrom.c
+@@ -378,6 +378,11 @@ static int nr_listen(struct socket *sock, int backlog)
+ struct sock *sk = sock->sk;
+
+ lock_sock(sk);
++ if (sock->state != SS_UNCONNECTED) {
++ release_sock(sk);
++ return -EINVAL;
++ }
++
+ if (sk->sk_state != TCP_LISTEN) {
+ memset(&nr_sk(sk)->user_addr, 0, AX25_ADDR_LEN);
+ sk->sk_max_ack_backlog = backlog;
+diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c
+index a8a8396dd9838..4c537e74b18c7 100644
+--- a/net/openvswitch/datapath.c
++++ b/net/openvswitch/datapath.c
+@@ -941,14 +941,14 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
+ key = kzalloc(sizeof(*key), GFP_KERNEL);
+ if (!key) {
+ error = -ENOMEM;
+- goto err_kfree_key;
++ goto err_kfree_flow;
+ }
+
+ ovs_match_init(&match, key, false, &mask);
+ error = ovs_nla_get_match(net, &match, a[OVS_FLOW_ATTR_KEY],
+ a[OVS_FLOW_ATTR_MASK], log);
+ if (error)
+- goto err_kfree_flow;
++ goto err_kfree_key;
+
+ ovs_flow_mask_key(&new_flow->key, key, true, &mask);
+
+@@ -956,14 +956,14 @@ static int ovs_flow_cmd_new(struct sk_buff *skb, struct genl_info *info)
+ error = ovs_nla_get_identifier(&new_flow->id, a[OVS_FLOW_ATTR_UFID],
+ key, log);
+ if (error)
+- goto err_kfree_flow;
++ goto err_kfree_key;
+
+ /* Validate actions. */
+ error = ovs_nla_copy_actions(net, a[OVS_FLOW_ATTR_ACTIONS],
+ &new_flow->key, &acts, log);
+ if (error) {
+ OVS_NLERR(log, "Flow actions may not be safe on all matching packets.");
+- goto err_kfree_flow;
++ goto err_kfree_key;
+ }
+
+ reply = ovs_flow_cmd_alloc_info(acts, &new_flow->id, info, false,
+@@ -1063,10 +1063,10 @@ err_unlock_ovs:
+ kfree_skb(reply);
+ err_kfree_acts:
+ ovs_nla_free_flow_actions(acts);
+-err_kfree_flow:
+- ovs_flow_free(new_flow, false);
+ err_kfree_key:
+ kfree(key);
++err_kfree_flow:
++ ovs_flow_free(new_flow, false);
+ error:
+ return error;
+ }
+diff --git a/net/rds/message.c b/net/rds/message.c
+index 92b6b22884d4c..be6a0a073b12a 100644
+--- a/net/rds/message.c
++++ b/net/rds/message.c
+@@ -104,9 +104,9 @@ static void rds_rm_zerocopy_callback(struct rds_sock *rs,
+ spin_lock_irqsave(&q->lock, flags);
+ head = &q->zcookie_head;
+ if (!list_empty(head)) {
+- info = list_entry(head, struct rds_msg_zcopy_info,
+- rs_zcookie_next);
+- if (info && rds_zcookie_add(info, cookie)) {
++ info = list_first_entry(head, struct rds_msg_zcopy_info,
++ rs_zcookie_next);
++ if (rds_zcookie_add(info, cookie)) {
+ spin_unlock_irqrestore(&q->lock, flags);
+ kfree(rds_info_from_znotifier(znotif));
+ /* caller invokes rds_wake_sk_sleep() */
+diff --git a/net/rose/af_rose.c b/net/rose/af_rose.c
+index 95dda29058a0e..6fb158172ddc2 100644
+--- a/net/rose/af_rose.c
++++ b/net/rose/af_rose.c
+@@ -465,6 +465,12 @@ static int rose_listen(struct socket *sock, int backlog)
+ {
+ struct sock *sk = sock->sk;
+
++ lock_sock(sk);
++ if (sock->state != SS_UNCONNECTED) {
++ release_sock(sk);
++ return -EINVAL;
++ }
++
+ if (sk->sk_state != TCP_LISTEN) {
+ struct rose_sock *rose = rose_sk(sk);
+
+@@ -474,8 +480,10 @@ static int rose_listen(struct socket *sock, int backlog)
+ memset(rose->dest_digis, 0, AX25_ADDR_LEN * ROSE_MAX_DIGIS);
+ sk->sk_max_ack_backlog = backlog;
+ sk->sk_state = TCP_LISTEN;
++ release_sock(sk);
+ return 0;
+ }
++ release_sock(sk);
+
+ return -EOPNOTSUPP;
+ }
+diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c
+index 8184c87da8bec..27cb82b6237da 100644
+--- a/net/sched/sch_htb.c
++++ b/net/sched/sch_htb.c
+@@ -405,7 +405,10 @@ static void htb_activate_prios(struct htb_sched *q, struct htb_class *cl)
+ while (cl->cmode == HTB_MAY_BORROW && p && mask) {
+ m = mask;
+ while (m) {
+- int prio = ffz(~m);
++ unsigned int prio = ffz(~m);
++
++ if (WARN_ON_ONCE(prio >= ARRAY_SIZE(p->inner.clprio)))
++ break;
+ m &= ~(1 << prio);
+
+ if (p->inner.clprio[prio].feed.rb_node)
+diff --git a/net/sctp/diag.c b/net/sctp/diag.c
+index 5a918e74bb82b..2d0318a7352c2 100644
+--- a/net/sctp/diag.c
++++ b/net/sctp/diag.c
+@@ -349,11 +349,9 @@ static int sctp_sock_filter(struct sctp_endpoint *ep, struct sctp_transport *tsp
+ struct sctp_comm_param *commp = p;
+ struct sock *sk = ep->base.sk;
+ const struct inet_diag_req_v2 *r = commp->r;
+- struct sctp_association *assoc =
+- list_entry(ep->asocs.next, struct sctp_association, asocs);
+
+ /* find the ep only once through the transports by this condition */
+- if (tsp->asoc != assoc)
++ if (!list_is_first(&tsp->asoc->asocs, &ep->asocs))
+ return 0;
+
+ if (r->sdiag_family != AF_UNSPEC && sk->sk_family != r->sdiag_family)
+diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c
+index 0f4d39fdb48f2..cfae1a871578f 100644
+--- a/net/sunrpc/xprtrdma/verbs.c
++++ b/net/sunrpc/xprtrdma/verbs.c
+@@ -1034,9 +1034,9 @@ struct rpcrdma_req *rpcrdma_req_create(struct rpcrdma_xprt *r_xprt, size_t size,
+ return req;
+
+ out4:
+- kfree(req->rl_sendbuf);
++ rpcrdma_regbuf_free(req->rl_sendbuf);
+ out3:
+- kfree(req->rl_rdmabuf);
++ rpcrdma_regbuf_free(req->rl_rdmabuf);
+ out2:
+ kfree(req);
+ out1:
+diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c
+index c94aa587e0c92..43dd489ad6db9 100644
+--- a/net/x25/af_x25.c
++++ b/net/x25/af_x25.c
+@@ -492,6 +492,12 @@ static int x25_listen(struct socket *sock, int backlog)
+ int rc = -EOPNOTSUPP;
+
+ lock_sock(sk);
++ if (sock->state != SS_UNCONNECTED) {
++ rc = -EINVAL;
++ release_sock(sk);
++ return rc;
++ }
++
+ if (sk->sk_state != TCP_LISTEN) {
+ memset(&x25_sk(sk)->dest_addr, 0, X25_ADDR_LEN);
+ sk->sk_max_ack_backlog = backlog;
+diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c
+index e120df0a6da13..4d8d7cf3d1994 100644
+--- a/net/xfrm/xfrm_input.c
++++ b/net/xfrm/xfrm_input.c
+@@ -274,8 +274,7 @@ static int xfrm6_remove_tunnel_encap(struct xfrm_state *x, struct sk_buff *skb)
+ goto out;
+
+ if (x->props.flags & XFRM_STATE_DECAP_DSCP)
+- ipv6_copy_dscp(ipv6_get_dsfield(ipv6_hdr(skb)),
+- ipipv6_hdr(skb));
++ ipv6_copy_dscp(XFRM_MODE_SKB_CB(skb)->tos, ipipv6_hdr(skb));
+ if (!(x->props.flags & XFRM_STATE_NOECN))
+ ipip6_ecn_decapsulate(skb);
+
+diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
+index d181dc31cd8d6..51f65d634a15e 100644
+--- a/sound/pci/hda/patch_conexant.c
++++ b/sound/pci/hda/patch_conexant.c
+@@ -1093,6 +1093,7 @@ static const struct hda_device_id snd_hda_id_conexant[] = {
+ HDA_CODEC_ENTRY(0x14f11f86, "CX8070", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f12008, "CX8200", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f120d0, "CX11970", patch_conexant_auto),
++ HDA_CODEC_ENTRY(0x14f120d1, "SN6180", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
+ HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
+diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
+index 413d4886f3d28..75c1645dd5c14 100644
+--- a/sound/pci/hda/patch_realtek.c
++++ b/sound/pci/hda/patch_realtek.c
+@@ -770,7 +770,7 @@ do_sku:
+ alc_setup_gpio(codec, 0x02);
+ break;
+ case 7:
+- alc_setup_gpio(codec, 0x03);
++ alc_setup_gpio(codec, 0x04);
+ break;
+ case 5:
+ default:
+diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c
+index 3edb4e25797de..4a74ccf7cf3e6 100644
+--- a/sound/pci/hda/patch_via.c
++++ b/sound/pci/hda/patch_via.c
+@@ -821,6 +821,9 @@ static int add_secret_dac_path(struct hda_codec *codec)
+ return 0;
+ nums = snd_hda_get_connections(codec, spec->gen.mixer_nid, conn,
+ ARRAY_SIZE(conn) - 1);
++ if (nums < 0)
++ return nums;
++
+ for (i = 0; i < nums; i++) {
+ if (get_wcaps_type(get_wcaps(codec, conn[i])) == AC_WID_AUD_OUT)
+ return 0;
+diff --git a/sound/pci/lx6464es/lx_core.c b/sound/pci/lx6464es/lx_core.c
+index dd3a873777eb5..00975e86473c5 100644
+--- a/sound/pci/lx6464es/lx_core.c
++++ b/sound/pci/lx6464es/lx_core.c
+@@ -493,12 +493,11 @@ int lx_buffer_ask(struct lx6464es *chip, u32 pipe, int is_capture,
+ dev_dbg(chip->card->dev,
+ "CMD_08_ASK_BUFFERS: needed %d, freed %d\n",
+ *r_needed, *r_freed);
+- for (i = 0; i < MAX_STREAM_BUFFER; ++i) {
+- for (i = 0; i != chip->rmh.stat_len; ++i)
+- dev_dbg(chip->card->dev,
+- " stat[%d]: %x, %x\n", i,
+- chip->rmh.stat[i],
+- chip->rmh.stat[i] & MASK_DATA_SIZE);
++ for (i = 0; i < MAX_STREAM_BUFFER && i < chip->rmh.stat_len;
++ ++i) {
++ dev_dbg(chip->card->dev, " stat[%d]: %x, %x\n", i,
++ chip->rmh.stat[i],
++ chip->rmh.stat[i] & MASK_DATA_SIZE);
+ }
+ }
+
+diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
+index 8be7d83f0ce9a..732405587c5a4 100644
+--- a/sound/soc/codecs/cs42l56.c
++++ b/sound/soc/codecs/cs42l56.c
+@@ -1192,18 +1192,12 @@ static int cs42l56_i2c_probe(struct i2c_client *i2c_client,
+ if (pdata) {
+ cs42l56->pdata = *pdata;
+ } else {
+- pdata = devm_kzalloc(&i2c_client->dev, sizeof(*pdata),
+- GFP_KERNEL);
+- if (!pdata)
+- return -ENOMEM;
+-
+ if (i2c_client->dev.of_node) {
+ ret = cs42l56_handle_of_data(i2c_client,
+ &cs42l56->pdata);
+ if (ret != 0)
+ return ret;
+ }
+- cs42l56->pdata = *pdata;
+ }
+
+ if (cs42l56->pdata.gpio_nreset) {
+diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
+index 921c09cdb4800..0c1c8628b9917 100644
+--- a/sound/soc/intel/boards/bytcr_rt5651.c
++++ b/sound/soc/intel/boards/bytcr_rt5651.c
+@@ -919,7 +919,6 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
+ if (adev) {
+ snprintf(byt_rt5651_codec_name, sizeof(byt_rt5651_codec_name),
+ "i2c-%s", acpi_dev_name(adev));
+- put_device(&adev->dev);
+ byt_rt5651_dais[dai_index].codecs->name = byt_rt5651_codec_name;
+ } else {
+ dev_err(&pdev->dev, "Error cannot find '%s' dev\n", mach->id);
+@@ -928,6 +927,7 @@ static int snd_byt_rt5651_mc_probe(struct platform_device *pdev)
+
+ codec_dev = bus_find_device_by_name(&i2c_bus_type, NULL,
+ byt_rt5651_codec_name);
++ acpi_dev_put(adev);
+ if (!codec_dev)
+ return -EPROBE_DEFER;
+
+diff --git a/sound/soc/sof/intel/hda-dai.c b/sound/soc/sof/intel/hda-dai.c
+index b3cdd10c83ae1..c30e450fa9702 100644
+--- a/sound/soc/sof/intel/hda-dai.c
++++ b/sound/soc/sof/intel/hda-dai.c
+@@ -211,6 +211,10 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
+ int stream_tag;
+ int ret;
+
++ link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
++ if (!link)
++ return -EINVAL;
++
+ /* get stored dma data if resuming from system suspend */
+ link_dev = snd_soc_dai_get_dma_data(dai, substream);
+ if (!link_dev) {
+@@ -231,10 +235,6 @@ static int hda_link_hw_params(struct snd_pcm_substream *substream,
+ if (ret < 0)
+ return ret;
+
+- link = snd_hdac_ext_bus_get_link(bus, codec_dai->component->name);
+- if (!link)
+- return -EINVAL;
+-
+ /* set the stream tag in the codec dai dma params */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0);
+diff --git a/sound/synth/emux/emux_nrpn.c b/sound/synth/emux/emux_nrpn.c
+index 1ac22676d4648..641dc02ff709e 100644
+--- a/sound/synth/emux/emux_nrpn.c
++++ b/sound/synth/emux/emux_nrpn.c
+@@ -349,6 +349,9 @@ int
+ snd_emux_xg_control(struct snd_emux_port *port, struct snd_midi_channel *chan,
+ int param)
+ {
++ if (param >= ARRAY_SIZE(chan->control))
++ return -EINVAL;
++
+ return send_converted_effect(xg_effects, ARRAY_SIZE(xg_effects),
+ port, chan, param,
+ chan->control[param],
+diff --git a/tools/testing/selftests/bpf/verifier/search_pruning.c b/tools/testing/selftests/bpf/verifier/search_pruning.c
+index 7e50cb80873a5..7e36078f8f482 100644
+--- a/tools/testing/selftests/bpf/verifier/search_pruning.c
++++ b/tools/testing/selftests/bpf/verifier/search_pruning.c
+@@ -154,3 +154,39 @@
+ .result_unpriv = ACCEPT,
+ .insn_processed = 15,
+ },
++/* The test performs a conditional 64-bit write to a stack location
++ * fp[-8], this is followed by an unconditional 8-bit write to fp[-8],
++ * then data is read from fp[-8]. This sequence is unsafe.
++ *
++ * The test would be mistakenly marked as safe w/o dst register parent
++ * preservation in verifier.c:copy_register_state() function.
++ *
++ * Note the usage of BPF_F_TEST_STATE_FREQ to force creation of the
++ * checkpoint state after conditional 64-bit assignment.
++ */
++{
++ "write tracking and register parent chain bug",
++ .insns = {
++ /* r6 = ktime_get_ns() */
++ BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
++ BPF_MOV64_REG(BPF_REG_6, BPF_REG_0),
++ /* r0 = ktime_get_ns() */
++ BPF_EMIT_CALL(BPF_FUNC_ktime_get_ns),
++ /* if r0 > r6 goto +1 */
++ BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_6, 1),
++ /* *(u64 *)(r10 - 8) = 0xdeadbeef */
++ BPF_ST_MEM(BPF_DW, BPF_REG_FP, -8, 0xdeadbeef),
++ /* r1 = 42 */
++ BPF_MOV64_IMM(BPF_REG_1, 42),
++ /* *(u8 *)(r10 - 8) = r1 */
++ BPF_STX_MEM(BPF_B, BPF_REG_FP, BPF_REG_1, -8),
++ /* r2 = *(u64 *)(r10 - 8) */
++ BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_FP, -8),
++ /* exit(0) */
++ BPF_MOV64_IMM(BPF_REG_0, 0),
++ BPF_EXIT_INSN(),
++ },
++ .flags = BPF_F_TEST_STATE_FREQ,
++ .errstr = "invalid read from stack off -8+1 size 8",
++ .result = REJECT,
++},
+diff --git a/tools/testing/selftests/net/fib_tests.sh b/tools/testing/selftests/net/fib_tests.sh
+new file mode 100644
+index 0000000000000..6986086035d6c
+--- /dev/null
++++ b/tools/testing/selftests/net/fib_tests.sh
+@@ -0,0 +1,1727 @@
++#!/bin/bash
++# SPDX-License-Identifier: GPL-2.0
++
++# This test is for checking IPv4 and IPv6 FIB behavior in response to
++# different events.
++
++ret=0
++# Kselftest framework requirement - SKIP code is 4.
++ksft_skip=4
++
++# all tests in this script. Can be overridden with -t option
++TESTS="unregister down carrier nexthop suppress ipv6_rt ipv4_rt ipv6_addr_metric ipv4_addr_metric ipv6_route_metrics ipv4_route_metrics ipv4_route_v6_gw rp_filter"
++
++VERBOSE=0
++PAUSE_ON_FAIL=no
++PAUSE=no
++IP="ip -netns ns1"
++NS_EXEC="ip netns exec ns1"
++
++which ping6 > /dev/null 2>&1 && ping6=$(which ping6) || ping6=$(which ping)
++
++log_test()
++{
++ local rc=$1
++ local expected=$2
++ local msg="$3"
++
++ if [ ${rc} -eq ${expected} ]; then
++ printf " TEST: %-60s [ OK ]\n" "${msg}"
++ nsuccess=$((nsuccess+1))
++ else
++ ret=1
++ nfail=$((nfail+1))
++ printf " TEST: %-60s [FAIL]\n" "${msg}"
++ if [ "${PAUSE_ON_FAIL}" = "yes" ]; then
++ echo
++ echo "hit enter to continue, 'q' to quit"
++ read a
++ [ "$a" = "q" ] && exit 1
++ fi
++ fi
++
++ if [ "${PAUSE}" = "yes" ]; then
++ echo
++ echo "hit enter to continue, 'q' to quit"
++ read a
++ [ "$a" = "q" ] && exit 1
++ fi
++}
++
++setup()
++{
++ set -e
++ ip netns add ns1
++ ip netns set ns1 auto
++ $IP link set dev lo up
++ ip netns exec ns1 sysctl -qw net.ipv4.ip_forward=1
++ ip netns exec ns1 sysctl -qw net.ipv6.conf.all.forwarding=1
++
++ $IP link add dummy0 type dummy
++ $IP link set dev dummy0 up
++ $IP address add 198.51.100.1/24 dev dummy0
++ $IP -6 address add 2001:db8:1::1/64 dev dummy0
++ set +e
++
++}
++
++cleanup()
++{
++ $IP link del dev dummy0 &> /dev/null
++ ip netns del ns1
++ ip netns del ns2 &> /dev/null
++}
++
++get_linklocal()
++{
++ local dev=$1
++ local addr
++
++ addr=$($IP -6 -br addr show dev ${dev} | \
++ awk '{
++ for (i = 3; i <= NF; ++i) {
++ if ($i ~ /^fe80/)
++ print $i
++ }
++ }'
++ )
++ addr=${addr/\/*}
++
++ [ -z "$addr" ] && return 1
++
++ echo $addr
++
++ return 0
++}
++
++fib_unreg_unicast_test()
++{
++ echo
++ echo "Single path route test"
++
++ setup
++
++ echo " Start point"
++ $IP route get fibmatch 198.51.100.2 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ set -e
++ $IP link del dev dummy0
++ set +e
++
++ echo " Nexthop device deleted"
++ $IP route get fibmatch 198.51.100.2 &> /dev/null
++ log_test $? 2 "IPv4 fibmatch - no route"
++ $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
++ log_test $? 2 "IPv6 fibmatch - no route"
++
++ cleanup
++}
++
++fib_unreg_multipath_test()
++{
++
++ echo
++ echo "Multipath route test"
++
++ setup
++
++ set -e
++ $IP link add dummy1 type dummy
++ $IP link set dev dummy1 up
++ $IP address add 192.0.2.1/24 dev dummy1
++ $IP -6 address add 2001:db8:2::1/64 dev dummy1
++
++ $IP route add 203.0.113.0/24 \
++ nexthop via 198.51.100.2 dev dummy0 \
++ nexthop via 192.0.2.2 dev dummy1
++ $IP -6 route add 2001:db8:3::/64 \
++ nexthop via 2001:db8:1::2 dev dummy0 \
++ nexthop via 2001:db8:2::2 dev dummy1
++ set +e
++
++ echo " Start point"
++ $IP route get fibmatch 203.0.113.1 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ set -e
++ $IP link del dev dummy0
++ set +e
++
++ echo " One nexthop device deleted"
++ $IP route get fibmatch 203.0.113.1 &> /dev/null
++ log_test $? 2 "IPv4 - multipath route removed on delete"
++
++ $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
++ # In IPv6 we do not flush the entire multipath route.
++ log_test $? 0 "IPv6 - multipath down to single path"
++
++ set -e
++ $IP link del dev dummy1
++ set +e
++
++ echo " Second nexthop device deleted"
++ $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
++ log_test $? 2 "IPv6 - no route"
++
++ cleanup
++}
++
++fib_unreg_test()
++{
++ fib_unreg_unicast_test
++ fib_unreg_multipath_test
++}
++
++fib_down_unicast_test()
++{
++ echo
++ echo "Single path, admin down"
++
++ setup
++
++ echo " Start point"
++ $IP route get fibmatch 198.51.100.2 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ set -e
++ $IP link set dev dummy0 down
++ set +e
++
++ echo " Route deleted on down"
++ $IP route get fibmatch 198.51.100.2 &> /dev/null
++ log_test $? 2 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
++ log_test $? 2 "IPv6 fibmatch"
++
++ cleanup
++}
++
++fib_down_multipath_test_do()
++{
++ local down_dev=$1
++ local up_dev=$2
++
++ $IP route get fibmatch 203.0.113.1 \
++ oif $down_dev &> /dev/null
++ log_test $? 2 "IPv4 fibmatch on down device"
++ $IP -6 route get fibmatch 2001:db8:3::1 \
++ oif $down_dev &> /dev/null
++ log_test $? 2 "IPv6 fibmatch on down device"
++
++ $IP route get fibmatch 203.0.113.1 \
++ oif $up_dev &> /dev/null
++ log_test $? 0 "IPv4 fibmatch on up device"
++ $IP -6 route get fibmatch 2001:db8:3::1 \
++ oif $up_dev &> /dev/null
++ log_test $? 0 "IPv6 fibmatch on up device"
++
++ $IP route get fibmatch 203.0.113.1 | \
++ grep $down_dev | grep -q "dead linkdown"
++ log_test $? 0 "IPv4 flags on down device"
++ $IP -6 route get fibmatch 2001:db8:3::1 | \
++ grep $down_dev | grep -q "dead linkdown"
++ log_test $? 0 "IPv6 flags on down device"
++
++ $IP route get fibmatch 203.0.113.1 | \
++ grep $up_dev | grep -q "dead linkdown"
++ log_test $? 1 "IPv4 flags on up device"
++ $IP -6 route get fibmatch 2001:db8:3::1 | \
++ grep $up_dev | grep -q "dead linkdown"
++ log_test $? 1 "IPv6 flags on up device"
++}
++
++fib_down_multipath_test()
++{
++ echo
++ echo "Admin down multipath"
++
++ setup
++
++ set -e
++ $IP link add dummy1 type dummy
++ $IP link set dev dummy1 up
++
++ $IP address add 192.0.2.1/24 dev dummy1
++ $IP -6 address add 2001:db8:2::1/64 dev dummy1
++
++ $IP route add 203.0.113.0/24 \
++ nexthop via 198.51.100.2 dev dummy0 \
++ nexthop via 192.0.2.2 dev dummy1
++ $IP -6 route add 2001:db8:3::/64 \
++ nexthop via 2001:db8:1::2 dev dummy0 \
++ nexthop via 2001:db8:2::2 dev dummy1
++ set +e
++
++ echo " Verify start point"
++ $IP route get fibmatch 203.0.113.1 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++
++ $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ set -e
++ $IP link set dev dummy0 down
++ set +e
++
++ echo " One device down, one up"
++ fib_down_multipath_test_do "dummy0" "dummy1"
++
++ set -e
++ $IP link set dev dummy0 up
++ $IP link set dev dummy1 down
++ set +e
++
++ echo " Other device down and up"
++ fib_down_multipath_test_do "dummy1" "dummy0"
++
++ set -e
++ $IP link set dev dummy0 down
++ set +e
++
++ echo " Both devices down"
++ $IP route get fibmatch 203.0.113.1 &> /dev/null
++ log_test $? 2 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:3::1 &> /dev/null
++ log_test $? 2 "IPv6 fibmatch"
++
++ $IP link del dev dummy1
++ cleanup
++}
++
++fib_down_test()
++{
++ fib_down_unicast_test
++ fib_down_multipath_test
++}
++
++# Local routes should not be affected when carrier changes.
++fib_carrier_local_test()
++{
++ echo
++ echo "Local carrier tests - single path"
++
++ setup
++
++ set -e
++ $IP link set dev dummy0 carrier on
++ set +e
++
++ echo " Start point"
++ $IP route get fibmatch 198.51.100.1 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ $IP route get fibmatch 198.51.100.1 | \
++ grep -q "linkdown"
++ log_test $? 1 "IPv4 - no linkdown flag"
++ $IP -6 route get fibmatch 2001:db8:1::1 | \
++ grep -q "linkdown"
++ log_test $? 1 "IPv6 - no linkdown flag"
++
++ set -e
++ $IP link set dev dummy0 carrier off
++ sleep 1
++ set +e
++
++ echo " Carrier off on nexthop"
++ $IP route get fibmatch 198.51.100.1 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:1::1 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ $IP route get fibmatch 198.51.100.1 | \
++ grep -q "linkdown"
++ log_test $? 1 "IPv4 - linkdown flag set"
++ $IP -6 route get fibmatch 2001:db8:1::1 | \
++ grep -q "linkdown"
++ log_test $? 1 "IPv6 - linkdown flag set"
++
++ set -e
++ $IP address add 192.0.2.1/24 dev dummy0
++ $IP -6 address add 2001:db8:2::1/64 dev dummy0
++ set +e
++
++ echo " Route to local address with carrier down"
++ $IP route get fibmatch 192.0.2.1 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:2::1 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ $IP route get fibmatch 192.0.2.1 | \
++ grep -q "linkdown"
++ log_test $? 1 "IPv4 linkdown flag set"
++ $IP -6 route get fibmatch 2001:db8:2::1 | \
++ grep -q "linkdown"
++ log_test $? 1 "IPv6 linkdown flag set"
++
++ cleanup
++}
++
++fib_carrier_unicast_test()
++{
++ ret=0
++
++ echo
++ echo "Single path route carrier test"
++
++ setup
++
++ set -e
++ $IP link set dev dummy0 carrier on
++ set +e
++
++ echo " Start point"
++ $IP route get fibmatch 198.51.100.2 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ $IP route get fibmatch 198.51.100.2 | \
++ grep -q "linkdown"
++ log_test $? 1 "IPv4 no linkdown flag"
++ $IP -6 route get fibmatch 2001:db8:1::2 | \
++ grep -q "linkdown"
++ log_test $? 1 "IPv6 no linkdown flag"
++
++ set -e
++ $IP link set dev dummy0 carrier off
++ sleep 1
++ set +e
++
++ echo " Carrier down"
++ $IP route get fibmatch 198.51.100.2 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:1::2 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ $IP route get fibmatch 198.51.100.2 | \
++ grep -q "linkdown"
++ log_test $? 0 "IPv4 linkdown flag set"
++ $IP -6 route get fibmatch 2001:db8:1::2 | \
++ grep -q "linkdown"
++ log_test $? 0 "IPv6 linkdown flag set"
++
++ set -e
++ $IP address add 192.0.2.1/24 dev dummy0
++ $IP -6 address add 2001:db8:2::1/64 dev dummy0
++ set +e
++
++ echo " Second address added with carrier down"
++ $IP route get fibmatch 192.0.2.2 &> /dev/null
++ log_test $? 0 "IPv4 fibmatch"
++ $IP -6 route get fibmatch 2001:db8:2::2 &> /dev/null
++ log_test $? 0 "IPv6 fibmatch"
++
++ $IP route get fibmatch 192.0.2.2 | \
++ grep -q "linkdown"
++ log_test $? 0 "IPv4 linkdown flag set"
++ $IP -6 route get fibmatch 2001:db8:2::2 | \
++ grep -q "linkdown"
++ log_test $? 0 "IPv6 linkdown flag set"
++
++ cleanup
++}
++
++fib_carrier_test()
++{
++ fib_carrier_local_test
++ fib_carrier_unicast_test
++}
++
++fib_rp_filter_test()
++{
++ echo
++ echo "IPv4 rp_filter tests"
++
++ setup
++
++ set -e
++ ip netns add ns2
++ ip netns set ns2 auto
++
++ ip -netns ns2 link set dev lo up
++
++ $IP link add name veth1 type veth peer name veth2
++ $IP link set dev veth2 netns ns2
++ $IP address add 192.0.2.1/24 dev veth1
++ ip -netns ns2 address add 192.0.2.1/24 dev veth2
++ $IP link set dev veth1 up
++ ip -netns ns2 link set dev veth2 up
++
++ $IP link set dev lo address 52:54:00:6a:c7:5e
++ $IP link set dev veth1 address 52:54:00:6a:c7:5e
++ ip -netns ns2 link set dev lo address 52:54:00:6a:c7:5e
++ ip -netns ns2 link set dev veth2 address 52:54:00:6a:c7:5e
++
++ # 1. (ns2) redirect lo's egress to veth2's egress
++ ip netns exec ns2 tc qdisc add dev lo parent root handle 1: fq_codel
++ ip netns exec ns2 tc filter add dev lo parent 1: protocol arp basic \
++ action mirred egress redirect dev veth2
++ ip netns exec ns2 tc filter add dev lo parent 1: protocol ip basic \
++ action mirred egress redirect dev veth2
++
++ # 2. (ns1) redirect veth1's ingress to lo's ingress
++ $NS_EXEC tc qdisc add dev veth1 ingress
++ $NS_EXEC tc filter add dev veth1 ingress protocol arp basic \
++ action mirred ingress redirect dev lo
++ $NS_EXEC tc filter add dev veth1 ingress protocol ip basic \
++ action mirred ingress redirect dev lo
++
++ # 3. (ns1) redirect lo's egress to veth1's egress
++ $NS_EXEC tc qdisc add dev lo parent root handle 1: fq_codel
++ $NS_EXEC tc filter add dev lo parent 1: protocol arp basic \
++ action mirred egress redirect dev veth1
++ $NS_EXEC tc filter add dev lo parent 1: protocol ip basic \
++ action mirred egress redirect dev veth1
++
++ # 4. (ns2) redirect veth2's ingress to lo's ingress
++ ip netns exec ns2 tc qdisc add dev veth2 ingress
++ ip netns exec ns2 tc filter add dev veth2 ingress protocol arp basic \
++ action mirred ingress redirect dev lo
++ ip netns exec ns2 tc filter add dev veth2 ingress protocol ip basic \
++ action mirred ingress redirect dev lo
++
++ $NS_EXEC sysctl -qw net.ipv4.conf.all.rp_filter=1
++ $NS_EXEC sysctl -qw net.ipv4.conf.all.accept_local=1
++ $NS_EXEC sysctl -qw net.ipv4.conf.all.route_localnet=1
++ ip netns exec ns2 sysctl -qw net.ipv4.conf.all.rp_filter=1
++ ip netns exec ns2 sysctl -qw net.ipv4.conf.all.accept_local=1
++ ip netns exec ns2 sysctl -qw net.ipv4.conf.all.route_localnet=1
++ set +e
++
++ run_cmd "ip netns exec ns2 ping -w1 -c1 192.0.2.1"
++ log_test $? 0 "rp_filter passes local packets"
++
++ run_cmd "ip netns exec ns2 ping -w1 -c1 127.0.0.1"
++ log_test $? 0 "rp_filter passes loopback packets"
++
++ cleanup
++}
++
++################################################################################
++# Tests on nexthop spec
++
++# run 'ip route add' with given spec
++add_rt()
++{
++ local desc="$1"
++ local erc=$2
++ local vrf=$3
++ local pfx=$4
++ local gw=$5
++ local dev=$6
++ local cmd out rc
++
++ [ "$vrf" = "-" ] && vrf="default"
++ [ -n "$gw" ] && gw="via $gw"
++ [ -n "$dev" ] && dev="dev $dev"
++
++ cmd="$IP route add vrf $vrf $pfx $gw $dev"
++ if [ "$VERBOSE" = "1" ]; then
++ printf "\n COMMAND: $cmd\n"
++ fi
++
++ out=$(eval $cmd 2>&1)
++ rc=$?
++ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
++ echo " $out"
++ fi
++ log_test $rc $erc "$desc"
++}
++
++fib4_nexthop()
++{
++ echo
++ echo "IPv4 nexthop tests"
++
++ echo "<<< write me >>>"
++}
++
++fib6_nexthop()
++{
++ local lldummy=$(get_linklocal dummy0)
++ local llv1=$(get_linklocal dummy0)
++
++ if [ -z "$lldummy" ]; then
++ echo "Failed to get linklocal address for dummy0"
++ return 1
++ fi
++ if [ -z "$llv1" ]; then
++ echo "Failed to get linklocal address for veth1"
++ return 1
++ fi
++
++ echo
++ echo "IPv6 nexthop tests"
++
++ add_rt "Directly connected nexthop, unicast address" 0 \
++ - 2001:db8:101::/64 2001:db8:1::2
++ add_rt "Directly connected nexthop, unicast address with device" 0 \
++ - 2001:db8:102::/64 2001:db8:1::2 "dummy0"
++ add_rt "Gateway is linklocal address" 0 \
++ - 2001:db8:103::1/64 $llv1 "veth0"
++
++ # fails because LL address requires a device
++ add_rt "Gateway is linklocal address, no device" 2 \
++ - 2001:db8:104::1/64 $llv1
++
++ # local address can not be a gateway
++ add_rt "Gateway can not be local unicast address" 2 \
++ - 2001:db8:105::/64 2001:db8:1::1
++ add_rt "Gateway can not be local unicast address, with device" 2 \
++ - 2001:db8:106::/64 2001:db8:1::1 "dummy0"
++ add_rt "Gateway can not be a local linklocal address" 2 \
++ - 2001:db8:107::1/64 $lldummy "dummy0"
++
++ # VRF tests
++ add_rt "Gateway can be local address in a VRF" 0 \
++ - 2001:db8:108::/64 2001:db8:51::2
++ add_rt "Gateway can be local address in a VRF, with device" 0 \
++ - 2001:db8:109::/64 2001:db8:51::2 "veth0"
++ add_rt "Gateway can be local linklocal address in a VRF" 0 \
++ - 2001:db8:110::1/64 $llv1 "veth0"
++
++ add_rt "Redirect to VRF lookup" 0 \
++ - 2001:db8:111::/64 "" "red"
++
++ add_rt "VRF route, gateway can be local address in default VRF" 0 \
++ red 2001:db8:112::/64 2001:db8:51::1
++
++ # local address in same VRF fails
++ add_rt "VRF route, gateway can not be a local address" 2 \
++ red 2001:db8:113::1/64 2001:db8:2::1
++ add_rt "VRF route, gateway can not be a local addr with device" 2 \
++ red 2001:db8:114::1/64 2001:db8:2::1 "dummy1"
++}
++
++# Default VRF:
++# dummy0 - 198.51.100.1/24 2001:db8:1::1/64
++# veth0 - 192.0.2.1/24 2001:db8:51::1/64
++#
++# VRF red:
++# dummy1 - 192.168.2.1/24 2001:db8:2::1/64
++# veth1 - 192.0.2.2/24 2001:db8:51::2/64
++#
++# [ dummy0 veth0 ]--[ veth1 dummy1 ]
++
++fib_nexthop_test()
++{
++ setup
++
++ set -e
++
++ $IP -4 rule add pref 32765 table local
++ $IP -4 rule del pref 0
++ $IP -6 rule add pref 32765 table local
++ $IP -6 rule del pref 0
++
++ $IP link add red type vrf table 1
++ $IP link set red up
++ $IP -4 route add vrf red unreachable default metric 4278198272
++ $IP -6 route add vrf red unreachable default metric 4278198272
++
++ $IP link add veth0 type veth peer name veth1
++ $IP link set dev veth0 up
++ $IP address add 192.0.2.1/24 dev veth0
++ $IP -6 address add 2001:db8:51::1/64 dev veth0
++
++ $IP link set dev veth1 vrf red up
++ $IP address add 192.0.2.2/24 dev veth1
++ $IP -6 address add 2001:db8:51::2/64 dev veth1
++
++ $IP link add dummy1 type dummy
++ $IP link set dev dummy1 vrf red up
++ $IP address add 192.168.2.1/24 dev dummy1
++ $IP -6 address add 2001:db8:2::1/64 dev dummy1
++ set +e
++
++ sleep 1
++ fib4_nexthop
++ fib6_nexthop
++
++ (
++ $IP link del dev dummy1
++ $IP link del veth0
++ $IP link del red
++ ) 2>/dev/null
++ cleanup
++}
++
++fib_suppress_test()
++{
++ echo
++ echo "FIB rule with suppress_prefixlength"
++ setup
++
++ $IP link add dummy1 type dummy
++ $IP link set dummy1 up
++ $IP -6 route add default dev dummy1
++ $IP -6 rule add table main suppress_prefixlength 0
++ ping -f -c 1000 -W 1 1234::1 >/dev/null 2>&1
++ $IP -6 rule del table main suppress_prefixlength 0
++ $IP link del dummy1
++
++ # If we got here without crashing, we're good.
++ log_test 0 0 "FIB rule suppress test"
++
++ cleanup
++}
++
++################################################################################
++# Tests on route add and replace
++
++run_cmd()
++{
++ local cmd="$1"
++ local out
++ local stderr="2>/dev/null"
++
++ if [ "$VERBOSE" = "1" ]; then
++ printf " COMMAND: $cmd\n"
++ stderr=
++ fi
++
++ out=$(eval $cmd $stderr)
++ rc=$?
++ if [ "$VERBOSE" = "1" -a -n "$out" ]; then
++ echo " $out"
++ fi
++
++ [ "$VERBOSE" = "1" ] && echo
++
++ return $rc
++}
++
++check_expected()
++{
++ local out="$1"
++ local expected="$2"
++ local rc=0
++
++ [ "${out}" = "${expected}" ] && return 0
++
++ if [ -z "${out}" ]; then
++ if [ "$VERBOSE" = "1" ]; then
++ printf "\nNo route entry found\n"
++ printf "Expected:\n"
++ printf " ${expected}\n"
++ fi
++ return 1
++ fi
++
++ # tricky way to convert output to 1-line without ip's
++ # messy '\'; this drops all extra white space
++ out=$(echo ${out})
++ if [ "${out}" != "${expected}" ]; then
++ rc=1
++ if [ "${VERBOSE}" = "1" ]; then
++ printf " Unexpected route entry. Have:\n"
++ printf " ${out}\n"
++ printf " Expected:\n"
++ printf " ${expected}\n\n"
++ fi
++ fi
++
++ return $rc
++}
++
++# add route for a prefix, flushing any existing routes first
++# expected to be the first step of a test
++add_route6()
++{
++ local pfx="$1"
++ local nh="$2"
++ local out
++
++ if [ "$VERBOSE" = "1" ]; then
++ echo
++ echo " ##################################################"
++ echo
++ fi
++
++ run_cmd "$IP -6 ro flush ${pfx}"
++ [ $? -ne 0 ] && exit 1
++
++ out=$($IP -6 ro ls match ${pfx})
++ if [ -n "$out" ]; then
++ echo "Failed to flush routes for prefix used for tests."
++ exit 1
++ fi
++
++ run_cmd "$IP -6 ro add ${pfx} ${nh}"
++ if [ $? -ne 0 ]; then
++ echo "Failed to add initial route for test."
++ exit 1
++ fi
++}
++
++# add initial route - used in replace route tests
++add_initial_route6()
++{
++ add_route6 "2001:db8:104::/64" "$1"
++}
++
++check_route6()
++{
++ local pfx
++ local expected="$1"
++ local out
++ local rc=0
++
++ set -- $expected
++ pfx=$1
++
++ out=$($IP -6 ro ls match ${pfx} | sed -e 's/ pref medium//')
++ check_expected "${out}" "${expected}"
++}
++
++route_cleanup()
++{
++ $IP li del red 2>/dev/null
++ $IP li del dummy1 2>/dev/null
++ $IP li del veth1 2>/dev/null
++ $IP li del veth3 2>/dev/null
++
++ cleanup &> /dev/null
++}
++
++route_setup()
++{
++ route_cleanup
++ setup
++
++ [ "${VERBOSE}" = "1" ] && set -x
++ set -e
++
++ ip netns add ns2
++ ip netns set ns2 auto
++ ip -netns ns2 link set dev lo up
++ ip netns exec ns2 sysctl -qw net.ipv4.ip_forward=1
++ ip netns exec ns2 sysctl -qw net.ipv6.conf.all.forwarding=1
++
++ $IP li add veth1 type veth peer name veth2
++ $IP li add veth3 type veth peer name veth4
++
++ $IP li set veth1 up
++ $IP li set veth3 up
++ $IP li set veth2 netns ns2 up
++ $IP li set veth4 netns ns2 up
++ ip -netns ns2 li add dummy1 type dummy
++ ip -netns ns2 li set dummy1 up
++
++ $IP -6 addr add 2001:db8:101::1/64 dev veth1 nodad
++ $IP -6 addr add 2001:db8:103::1/64 dev veth3 nodad
++ $IP addr add 172.16.101.1/24 dev veth1
++ $IP addr add 172.16.103.1/24 dev veth3
++
++ ip -netns ns2 -6 addr add 2001:db8:101::2/64 dev veth2 nodad
++ ip -netns ns2 -6 addr add 2001:db8:103::2/64 dev veth4 nodad
++ ip -netns ns2 -6 addr add 2001:db8:104::1/64 dev dummy1 nodad
++
++ ip -netns ns2 addr add 172.16.101.2/24 dev veth2
++ ip -netns ns2 addr add 172.16.103.2/24 dev veth4
++ ip -netns ns2 addr add 172.16.104.1/24 dev dummy1
++
++ set +e
++}
++
++# assumption is that basic add of a single path route works
++# otherwise just adding an address on an interface is broken
++ipv6_rt_add()
++{
++ local rc
++
++ echo
++ echo "IPv6 route add / append tests"
++
++ # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
++ add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::2"
++ log_test $? 2 "Attempt to add duplicate route - gw"
++
++ # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
++ add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro add 2001:db8:104::/64 dev veth3"
++ log_test $? 2 "Attempt to add duplicate route - dev only"
++
++ # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
++ add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro add unreachable 2001:db8:104::/64"
++ log_test $? 2 "Attempt to add duplicate route - reject route"
++
++ # route append with same prefix adds a new route
++ # - iproute2 sets NLM_F_CREATE | NLM_F_APPEND
++ add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro append 2001:db8:104::/64 via 2001:db8:103::2"
++ check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
++ log_test $? 0 "Append nexthop to existing route - gw"
++
++ # insert mpath directly
++ add_route6 "2001:db8:104::/64" "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
++ log_test $? 0 "Add multipath route"
++
++ add_route6 "2001:db8:104::/64" "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ run_cmd "$IP -6 ro add 2001:db8:104::/64 nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ log_test $? 2 "Attempt to add duplicate multipath route"
++
++ # insert of a second route without append but different metric
++ add_route6 "2001:db8:104::/64" "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::2 metric 512"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ run_cmd "$IP -6 ro add 2001:db8:104::/64 via 2001:db8:103::3 metric 256"
++ rc=$?
++ fi
++ log_test $rc 0 "Route add with different metrics"
++
++ run_cmd "$IP -6 ro del 2001:db8:104::/64 metric 512"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route6 "2001:db8:104::/64 via 2001:db8:103::3 dev veth3 metric 256 2001:db8:104::/64 via 2001:db8:101::2 dev veth1 metric 1024"
++ rc=$?
++ fi
++ log_test $rc 0 "Route delete with metric"
++}
++
++ipv6_rt_replace_single()
++{
++ # single path with single path
++ #
++ add_initial_route6 "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:103::2"
++ check_route6 "2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
++ log_test $? 0 "Single path with single path"
++
++ # single path with multipath
++ #
++ add_initial_route6 "nexthop via 2001:db8:101::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::2"
++ check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::3 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
++ log_test $? 0 "Single path with multipath"
++
++ # single path with single path using MULTIPATH attribute
++ #
++ add_initial_route6 "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:103::2"
++ check_route6 "2001:db8:104::/64 via 2001:db8:103::2 dev veth3 metric 1024"
++ log_test $? 0 "Single path with single path via multipath attribute"
++
++ # route replace fails - invalid nexthop
++ add_initial_route6 "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:104::2"
++ if [ $? -eq 0 ]; then
++ # previous command is expected to fail so if it returns 0
++ # that means the test failed.
++ log_test 0 1 "Invalid nexthop"
++ else
++ check_route6 "2001:db8:104::/64 via 2001:db8:101::2 dev veth1 metric 1024"
++ log_test $? 0 "Invalid nexthop"
++ fi
++
++ # replace non-existent route
++ # - note use of change versus replace since ip adds NLM_F_CREATE
++ # for replace
++ add_initial_route6 "via 2001:db8:101::2"
++ run_cmd "$IP -6 ro change 2001:db8:105::/64 via 2001:db8:101::2"
++ log_test $? 2 "Single path - replace of non-existent route"
++}
++
++ipv6_rt_replace_mpath()
++{
++ # multipath with multipath
++ add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::3"
++ check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::3 dev veth1 weight 1 nexthop via 2001:db8:103::3 dev veth3 weight 1"
++ log_test $? 0 "Multipath with multipath"
++
++ # multipath with single
++ add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 via 2001:db8:101::3"
++ check_route6 "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024"
++ log_test $? 0 "Multipath with single path"
++
++ # multipath with single
++ add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3"
++ check_route6 "2001:db8:104::/64 via 2001:db8:101::3 dev veth1 metric 1024"
++ log_test $? 0 "Multipath with single path via multipath attribute"
++
++ # multipath with dev-only
++ add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 dev veth1"
++ check_route6 "2001:db8:104::/64 dev veth1 metric 1024"
++ log_test $? 0 "Multipath with dev-only"
++
++ # route replace fails - invalid nexthop 1
++ add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:111::3 nexthop via 2001:db8:103::3"
++ check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
++ log_test $? 0 "Multipath - invalid first nexthop"
++
++ # route replace fails - invalid nexthop 2
++ add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ run_cmd "$IP -6 ro replace 2001:db8:104::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:113::3"
++ check_route6 "2001:db8:104::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
++ log_test $? 0 "Multipath - invalid second nexthop"
++
++ # multipath non-existent route
++ add_initial_route6 "nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ run_cmd "$IP -6 ro change 2001:db8:105::/64 nexthop via 2001:db8:101::3 nexthop via 2001:db8:103::3"
++ log_test $? 2 "Multipath - replace of non-existent route"
++}
++
++ipv6_rt_replace()
++{
++ echo
++ echo "IPv6 route replace tests"
++
++ ipv6_rt_replace_single
++ ipv6_rt_replace_mpath
++}
++
++ipv6_route_test()
++{
++ route_setup
++
++ ipv6_rt_add
++ ipv6_rt_replace
++
++ route_cleanup
++}
++
++ip_addr_metric_check()
++{
++ ip addr help 2>&1 | grep -q metric
++ if [ $? -ne 0 ]; then
++ echo "iproute2 command does not support metric for addresses. Skipping test"
++ return 1
++ fi
++
++ return 0
++}
++
++ipv6_addr_metric_test()
++{
++ local rc
++
++ echo
++ echo "IPv6 prefix route tests"
++
++ ip_addr_metric_check || return 1
++
++ setup
++
++ set -e
++ $IP li add dummy1 type dummy
++ $IP li add dummy2 type dummy
++ $IP li set dummy1 up
++ $IP li set dummy2 up
++
++ # default entry is metric 256
++ run_cmd "$IP -6 addr add dev dummy1 2001:db8:104::1/64"
++ run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::2/64"
++ set +e
++
++ check_route6 "2001:db8:104::/64 dev dummy1 proto kernel metric 256 2001:db8:104::/64 dev dummy2 proto kernel metric 256"
++ log_test $? 0 "Default metric"
++
++ set -e
++ run_cmd "$IP -6 addr flush dev dummy1"
++ run_cmd "$IP -6 addr add dev dummy1 2001:db8:104::1/64 metric 257"
++ set +e
++
++ check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 256 2001:db8:104::/64 dev dummy1 proto kernel metric 257"
++ log_test $? 0 "User specified metric on first device"
++
++ set -e
++ run_cmd "$IP -6 addr flush dev dummy2"
++ run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::2/64 metric 258"
++ set +e
++
++ check_route6 "2001:db8:104::/64 dev dummy1 proto kernel metric 257 2001:db8:104::/64 dev dummy2 proto kernel metric 258"
++ log_test $? 0 "User specified metric on second device"
++
++ run_cmd "$IP -6 addr del dev dummy1 2001:db8:104::1/64 metric 257"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 258"
++ rc=$?
++ fi
++ log_test $rc 0 "Delete of address on first device"
++
++ run_cmd "$IP -6 addr change dev dummy2 2001:db8:104::2/64 metric 259"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 259"
++ rc=$?
++ fi
++ log_test $rc 0 "Modify metric of address"
++
++ # verify prefix route removed on down
++ run_cmd "ip netns exec ns1 sysctl -qw net.ipv6.conf.all.keep_addr_on_down=1"
++ run_cmd "$IP li set dev dummy2 down"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ out=$($IP -6 ro ls match 2001:db8:104::/64)
++ check_expected "${out}" ""
++ rc=$?
++ fi
++ log_test $rc 0 "Prefix route removed on link down"
++
++ # verify prefix route re-inserted with assigned metric
++ run_cmd "$IP li set dev dummy2 up"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route6 "2001:db8:104::/64 dev dummy2 proto kernel metric 259"
++ rc=$?
++ fi
++ log_test $rc 0 "Prefix route with metric on link up"
++
++ # verify peer metric added correctly
++ set -e
++ run_cmd "$IP -6 addr flush dev dummy2"
++ run_cmd "$IP -6 addr add dev dummy2 2001:db8:104::1 peer 2001:db8:104::2 metric 260"
++ set +e
++
++ check_route6 "2001:db8:104::1 dev dummy2 proto kernel metric 260"
++ log_test $? 0 "Set metric with peer route on local side"
++ check_route6 "2001:db8:104::2 dev dummy2 proto kernel metric 260"
++ log_test $? 0 "Set metric with peer route on peer side"
++
++ set -e
++ run_cmd "$IP -6 addr change dev dummy2 2001:db8:104::1 peer 2001:db8:104::3 metric 261"
++ set +e
++
++ check_route6 "2001:db8:104::1 dev dummy2 proto kernel metric 261"
++ log_test $? 0 "Modify metric and peer address on local side"
++ check_route6 "2001:db8:104::3 dev dummy2 proto kernel metric 261"
++ log_test $? 0 "Modify metric and peer address on peer side"
++
++ $IP li del dummy1
++ $IP li del dummy2
++ cleanup
++}
++
++ipv6_route_metrics_test()
++{
++ local rc
++
++ echo
++ echo "IPv6 routes with metrics"
++
++ route_setup
++
++ #
++ # single path with metrics
++ #
++ run_cmd "$IP -6 ro add 2001:db8:111::/64 via 2001:db8:101::2 mtu 1400"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route6 "2001:db8:111::/64 via 2001:db8:101::2 dev veth1 metric 1024 mtu 1400"
++ rc=$?
++ fi
++ log_test $rc 0 "Single path route with mtu metric"
++
++
++ #
++ # multipath via separate routes with metrics
++ #
++ run_cmd "$IP -6 ro add 2001:db8:112::/64 via 2001:db8:101::2 mtu 1400"
++ run_cmd "$IP -6 ro append 2001:db8:112::/64 via 2001:db8:103::2"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route6 "2001:db8:112::/64 metric 1024 mtu 1400 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
++ rc=$?
++ fi
++ log_test $rc 0 "Multipath route via 2 single routes with mtu metric on first"
++
++ # second route is coalesced to first to make a multipath route.
++ # MTU of the second path is hidden from display!
++ run_cmd "$IP -6 ro add 2001:db8:113::/64 via 2001:db8:101::2"
++ run_cmd "$IP -6 ro append 2001:db8:113::/64 via 2001:db8:103::2 mtu 1400"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route6 "2001:db8:113::/64 metric 1024 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
++ rc=$?
++ fi
++ log_test $rc 0 "Multipath route via 2 single routes with mtu metric on 2nd"
++
++ run_cmd "$IP -6 ro del 2001:db8:113::/64 via 2001:db8:101::2"
++ if [ $? -eq 0 ]; then
++ check_route6 "2001:db8:113::/64 via 2001:db8:103::2 dev veth3 metric 1024 mtu 1400"
++ log_test $? 0 " MTU of second leg"
++ fi
++
++ #
++ # multipath with metrics
++ #
++ run_cmd "$IP -6 ro add 2001:db8:115::/64 mtu 1400 nexthop via 2001:db8:101::2 nexthop via 2001:db8:103::2"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route6 "2001:db8:115::/64 metric 1024 mtu 1400 nexthop via 2001:db8:101::2 dev veth1 weight 1 nexthop via 2001:db8:103::2 dev veth3 weight 1"
++ rc=$?
++ fi
++ log_test $rc 0 "Multipath route with mtu metric"
++
++ $IP -6 ro add 2001:db8:104::/64 via 2001:db8:101::2 mtu 1300
++ run_cmd "ip netns exec ns1 ${ping6} -w1 -c1 -s 1500 2001:db8:104::1"
++ log_test $? 0 "Using route with mtu metric"
++
++ run_cmd "$IP -6 ro add 2001:db8:114::/64 via 2001:db8:101::2 congctl lock foo"
++ log_test $? 2 "Invalid metric (fails metric_convert)"
++
++ route_cleanup
++}
++
++# add route for a prefix, flushing any existing routes first
++# expected to be the first step of a test
++add_route()
++{
++ local pfx="$1"
++ local nh="$2"
++ local out
++
++ if [ "$VERBOSE" = "1" ]; then
++ echo
++ echo " ##################################################"
++ echo
++ fi
++
++ run_cmd "$IP ro flush ${pfx}"
++ [ $? -ne 0 ] && exit 1
++
++ out=$($IP ro ls match ${pfx})
++ if [ -n "$out" ]; then
++ echo "Failed to flush routes for prefix used for tests."
++ exit 1
++ fi
++
++ run_cmd "$IP ro add ${pfx} ${nh}"
++ if [ $? -ne 0 ]; then
++ echo "Failed to add initial route for test."
++ exit 1
++ fi
++}
++
++# add initial route - used in replace route tests
++add_initial_route()
++{
++ add_route "172.16.104.0/24" "$1"
++}
++
++check_route()
++{
++ local pfx
++ local expected="$1"
++ local out
++
++ set -- $expected
++ pfx=$1
++ [ "${pfx}" = "unreachable" ] && pfx=$2
++
++ out=$($IP ro ls match ${pfx})
++ check_expected "${out}" "${expected}"
++}
++
++# assumption is that basic add of a single path route works
++# otherwise just adding an address on an interface is broken
++ipv4_rt_add()
++{
++ local rc
++
++ echo
++ echo "IPv4 route add / append tests"
++
++ # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
++ add_route "172.16.104.0/24" "via 172.16.101.2"
++ run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.2"
++ log_test $? 2 "Attempt to add duplicate route - gw"
++
++ # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
++ add_route "172.16.104.0/24" "via 172.16.101.2"
++ run_cmd "$IP ro add 172.16.104.0/24 dev veth3"
++ log_test $? 2 "Attempt to add duplicate route - dev only"
++
++ # route add same prefix - fails with EEXISTS b/c ip adds NLM_F_EXCL
++ add_route "172.16.104.0/24" "via 172.16.101.2"
++ run_cmd "$IP ro add unreachable 172.16.104.0/24"
++ log_test $? 2 "Attempt to add duplicate route - reject route"
++
++ # iproute2 prepend only sets NLM_F_CREATE
++ # - adds a new route; does NOT convert existing route to ECMP
++ add_route "172.16.104.0/24" "via 172.16.101.2"
++ run_cmd "$IP ro prepend 172.16.104.0/24 via 172.16.103.2"
++ check_route "172.16.104.0/24 via 172.16.103.2 dev veth3 172.16.104.0/24 via 172.16.101.2 dev veth1"
++ log_test $? 0 "Add new nexthop for existing prefix"
++
++ # route append with same prefix adds a new route
++ # - iproute2 sets NLM_F_CREATE | NLM_F_APPEND
++ add_route "172.16.104.0/24" "via 172.16.101.2"
++ run_cmd "$IP ro append 172.16.104.0/24 via 172.16.103.2"
++ check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 via 172.16.103.2 dev veth3"
++ log_test $? 0 "Append nexthop to existing route - gw"
++
++ add_route "172.16.104.0/24" "via 172.16.101.2"
++ run_cmd "$IP ro append 172.16.104.0/24 dev veth3"
++ check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 dev veth3 scope link"
++ log_test $? 0 "Append nexthop to existing route - dev only"
++
++ add_route "172.16.104.0/24" "via 172.16.101.2"
++ run_cmd "$IP ro append unreachable 172.16.104.0/24"
++ check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 unreachable 172.16.104.0/24"
++ log_test $? 0 "Append nexthop to existing route - reject route"
++
++ run_cmd "$IP ro flush 172.16.104.0/24"
++ run_cmd "$IP ro add unreachable 172.16.104.0/24"
++ run_cmd "$IP ro append 172.16.104.0/24 via 172.16.103.2"
++ check_route "unreachable 172.16.104.0/24 172.16.104.0/24 via 172.16.103.2 dev veth3"
++ log_test $? 0 "Append nexthop to existing reject route - gw"
++
++ run_cmd "$IP ro flush 172.16.104.0/24"
++ run_cmd "$IP ro add unreachable 172.16.104.0/24"
++ run_cmd "$IP ro append 172.16.104.0/24 dev veth3"
++ check_route "unreachable 172.16.104.0/24 172.16.104.0/24 dev veth3 scope link"
++ log_test $? 0 "Append nexthop to existing reject route - dev only"
++
++ # insert mpath directly
++ add_route "172.16.104.0/24" "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ check_route "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
++ log_test $? 0 "add multipath route"
++
++ add_route "172.16.104.0/24" "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ run_cmd "$IP ro add 172.16.104.0/24 nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ log_test $? 2 "Attempt to add duplicate multipath route"
++
++ # insert of a second route without append but different metric
++ add_route "172.16.104.0/24" "via 172.16.101.2"
++ run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.2 metric 512"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ run_cmd "$IP ro add 172.16.104.0/24 via 172.16.103.3 metric 256"
++ rc=$?
++ fi
++ log_test $rc 0 "Route add with different metrics"
++
++ run_cmd "$IP ro del 172.16.104.0/24 metric 512"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.0/24 via 172.16.101.2 dev veth1 172.16.104.0/24 via 172.16.103.3 dev veth3 metric 256"
++ rc=$?
++ fi
++ log_test $rc 0 "Route delete with metric"
++}
++
++ipv4_rt_replace_single()
++{
++ # single path with single path
++ #
++ add_initial_route "via 172.16.101.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 via 172.16.103.2"
++ check_route "172.16.104.0/24 via 172.16.103.2 dev veth3"
++ log_test $? 0 "Single path with single path"
++
++ # single path with multipath
++ #
++ add_initial_route "nexthop via 172.16.101.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.2"
++ check_route "172.16.104.0/24 nexthop via 172.16.101.3 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
++ log_test $? 0 "Single path with multipath"
++
++ # single path with reject
++ #
++ add_initial_route "nexthop via 172.16.101.2"
++ run_cmd "$IP ro replace unreachable 172.16.104.0/24"
++ check_route "unreachable 172.16.104.0/24"
++ log_test $? 0 "Single path with reject route"
++
++ # single path with single path using MULTIPATH attribute
++ #
++ add_initial_route "via 172.16.101.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.103.2"
++ check_route "172.16.104.0/24 via 172.16.103.2 dev veth3"
++ log_test $? 0 "Single path with single path via multipath attribute"
++
++ # route replace fails - invalid nexthop
++ add_initial_route "via 172.16.101.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 via 2001:db8:104::2"
++ if [ $? -eq 0 ]; then
++ # previous command is expected to fail so if it returns 0
++ # that means the test failed.
++ log_test 0 1 "Invalid nexthop"
++ else
++ check_route "172.16.104.0/24 via 172.16.101.2 dev veth1"
++ log_test $? 0 "Invalid nexthop"
++ fi
++
++ # replace non-existent route
++ # - note use of change versus replace since ip adds NLM_F_CREATE
++ # for replace
++ add_initial_route "via 172.16.101.2"
++ run_cmd "$IP ro change 172.16.105.0/24 via 172.16.101.2"
++ log_test $? 2 "Single path - replace of non-existent route"
++}
++
++ipv4_rt_replace_mpath()
++{
++ # multipath with multipath
++ add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.3"
++ check_route "172.16.104.0/24 nexthop via 172.16.101.3 dev veth1 weight 1 nexthop via 172.16.103.3 dev veth3 weight 1"
++ log_test $? 0 "Multipath with multipath"
++
++ # multipath with single
++ add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 via 172.16.101.3"
++ check_route "172.16.104.0/24 via 172.16.101.3 dev veth1"
++ log_test $? 0 "Multipath with single path"
++
++ # multipath with single
++ add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3"
++ check_route "172.16.104.0/24 via 172.16.101.3 dev veth1"
++ log_test $? 0 "Multipath with single path via multipath attribute"
++
++ # multipath with reject
++ add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ run_cmd "$IP ro replace unreachable 172.16.104.0/24"
++ check_route "unreachable 172.16.104.0/24"
++ log_test $? 0 "Multipath with reject route"
++
++ # route replace fails - invalid nexthop 1
++ add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.111.3 nexthop via 172.16.103.3"
++ check_route "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
++ log_test $? 0 "Multipath - invalid first nexthop"
++
++ # route replace fails - invalid nexthop 2
++ add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ run_cmd "$IP ro replace 172.16.104.0/24 nexthop via 172.16.101.3 nexthop via 172.16.113.3"
++ check_route "172.16.104.0/24 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
++ log_test $? 0 "Multipath - invalid second nexthop"
++
++ # multipath non-existent route
++ add_initial_route "nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ run_cmd "$IP ro change 172.16.105.0/24 nexthop via 172.16.101.3 nexthop via 172.16.103.3"
++ log_test $? 2 "Multipath - replace of non-existent route"
++}
++
++ipv4_rt_replace()
++{
++ echo
++ echo "IPv4 route replace tests"
++
++ ipv4_rt_replace_single
++ ipv4_rt_replace_mpath
++}
++
++ipv4_route_test()
++{
++ route_setup
++
++ ipv4_rt_add
++ ipv4_rt_replace
++
++ route_cleanup
++}
++
++ipv4_addr_metric_test()
++{
++ local rc
++
++ echo
++ echo "IPv4 prefix route tests"
++
++ ip_addr_metric_check || return 1
++
++ setup
++
++ set -e
++ $IP li add dummy1 type dummy
++ $IP li add dummy2 type dummy
++ $IP li set dummy1 up
++ $IP li set dummy2 up
++
++ # default entry is metric 256
++ run_cmd "$IP addr add dev dummy1 172.16.104.1/24"
++ run_cmd "$IP addr add dev dummy2 172.16.104.2/24"
++ set +e
++
++ check_route "172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2"
++ log_test $? 0 "Default metric"
++
++ set -e
++ run_cmd "$IP addr flush dev dummy1"
++ run_cmd "$IP addr add dev dummy1 172.16.104.1/24 metric 257"
++ set +e
++
++ check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 metric 257"
++ log_test $? 0 "User specified metric on first device"
++
++ set -e
++ run_cmd "$IP addr flush dev dummy2"
++ run_cmd "$IP addr add dev dummy2 172.16.104.2/24 metric 258"
++ set +e
++
++ check_route "172.16.104.0/24 dev dummy1 proto kernel scope link src 172.16.104.1 metric 257 172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 258"
++ log_test $? 0 "User specified metric on second device"
++
++ run_cmd "$IP addr del dev dummy1 172.16.104.1/24 metric 257"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 258"
++ rc=$?
++ fi
++ log_test $rc 0 "Delete of address on first device"
++
++ run_cmd "$IP addr change dev dummy2 172.16.104.2/24 metric 259"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 259"
++ rc=$?
++ fi
++ log_test $rc 0 "Modify metric of address"
++
++ # verify prefix route removed on down
++ run_cmd "$IP li set dev dummy2 down"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ out=$($IP ro ls match 172.16.104.0/24)
++ check_expected "${out}" ""
++ rc=$?
++ fi
++ log_test $rc 0 "Prefix route removed on link down"
++
++ # verify prefix route re-inserted with assigned metric
++ run_cmd "$IP li set dev dummy2 up"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.2 metric 259"
++ rc=$?
++ fi
++ log_test $rc 0 "Prefix route with metric on link up"
++
++ # explicitly check for metric changes on edge scenarios
++ run_cmd "$IP addr flush dev dummy2"
++ run_cmd "$IP addr add dev dummy2 172.16.104.0/24 metric 259"
++ run_cmd "$IP addr change dev dummy2 172.16.104.0/24 metric 260"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.0/24 dev dummy2 proto kernel scope link src 172.16.104.0 metric 260"
++ rc=$?
++ fi
++ log_test $rc 0 "Modify metric of .0/24 address"
++
++ run_cmd "$IP addr flush dev dummy2"
++ run_cmd "$IP addr add dev dummy2 172.16.104.1/32 peer 172.16.104.2 metric 260"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.2 dev dummy2 proto kernel scope link src 172.16.104.1 metric 260"
++ rc=$?
++ fi
++ log_test $rc 0 "Set metric of address with peer route"
++
++ run_cmd "$IP addr change dev dummy2 172.16.104.1/32 peer 172.16.104.3 metric 261"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.3 dev dummy2 proto kernel scope link src 172.16.104.1 metric 261"
++ rc=$?
++ fi
++ log_test $rc 0 "Modify metric and peer address for peer route"
++
++ $IP li del dummy1
++ $IP li del dummy2
++ cleanup
++}
++
++ipv4_route_metrics_test()
++{
++ local rc
++
++ echo
++ echo "IPv4 route add / append tests"
++
++ route_setup
++
++ run_cmd "$IP ro add 172.16.111.0/24 via 172.16.101.2 mtu 1400"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.111.0/24 via 172.16.101.2 dev veth1 mtu 1400"
++ rc=$?
++ fi
++ log_test $rc 0 "Single path route with mtu metric"
++
++
++ run_cmd "$IP ro add 172.16.112.0/24 mtu 1400 nexthop via 172.16.101.2 nexthop via 172.16.103.2"
++ rc=$?
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.112.0/24 mtu 1400 nexthop via 172.16.101.2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
++ rc=$?
++ fi
++ log_test $rc 0 "Multipath route with mtu metric"
++
++ $IP ro add 172.16.104.0/24 via 172.16.101.2 mtu 1300
++ run_cmd "ip netns exec ns1 ping -w1 -c1 -s 1500 172.16.104.1"
++ log_test $? 0 "Using route with mtu metric"
++
++ run_cmd "$IP ro add 172.16.111.0/24 via 172.16.101.2 congctl lock foo"
++ log_test $? 2 "Invalid metric (fails metric_convert)"
++
++ route_cleanup
++}
++
++ipv4_route_v6_gw_test()
++{
++ local rc
++
++ echo
++ echo "IPv4 route with IPv6 gateway tests"
++
++ route_setup
++ sleep 2
++
++ #
++ # single path route
++ #
++ run_cmd "$IP ro add 172.16.104.0/24 via inet6 2001:db8:101::2"
++ rc=$?
++ log_test $rc 0 "Single path route with IPv6 gateway"
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.0/24 via inet6 2001:db8:101::2 dev veth1"
++ fi
++
++ run_cmd "ip netns exec ns1 ping -w1 -c1 172.16.104.1"
++ log_test $rc 0 "Single path route with IPv6 gateway - ping"
++
++ run_cmd "$IP ro del 172.16.104.0/24 via inet6 2001:db8:101::2"
++ rc=$?
++ log_test $rc 0 "Single path route delete"
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.112.0/24"
++ fi
++
++ #
++ # multipath - v6 then v4
++ #
++ run_cmd "$IP ro add 172.16.104.0/24 nexthop via inet6 2001:db8:101::2 dev veth1 nexthop via 172.16.103.2 dev veth3"
++ rc=$?
++ log_test $rc 0 "Multipath route add - v6 nexthop then v4"
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.0/24 nexthop via inet6 2001:db8:101::2 dev veth1 weight 1 nexthop via 172.16.103.2 dev veth3 weight 1"
++ fi
++
++ run_cmd "$IP ro del 172.16.104.0/24 nexthop via 172.16.103.2 dev veth3 nexthop via inet6 2001:db8:101::2 dev veth1"
++ log_test $? 2 " Multipath route delete - nexthops in wrong order"
++
++ run_cmd "$IP ro del 172.16.104.0/24 nexthop via inet6 2001:db8:101::2 dev veth1 nexthop via 172.16.103.2 dev veth3"
++ log_test $? 0 " Multipath route delete exact match"
++
++ #
++ # multipath - v4 then v6
++ #
++ run_cmd "$IP ro add 172.16.104.0/24 nexthop via 172.16.103.2 dev veth3 nexthop via inet6 2001:db8:101::2 dev veth1"
++ rc=$?
++ log_test $rc 0 "Multipath route add - v4 nexthop then v6"
++ if [ $rc -eq 0 ]; then
++ check_route "172.16.104.0/24 nexthop via 172.16.103.2 dev veth3 weight 1 nexthop via inet6 2001:db8:101::2 dev veth1 weight 1"
++ fi
++
++ run_cmd "$IP ro del 172.16.104.0/24 nexthop via inet6 2001:db8:101::2 dev veth1 nexthop via 172.16.103.2 dev veth3"
++ log_test $? 2 " Multipath route delete - nexthops in wrong order"
++
++ run_cmd "$IP ro del 172.16.104.0/24 nexthop via 172.16.103.2 dev veth3 nexthop via inet6 2001:db8:101::2 dev veth1"
++ log_test $? 0 " Multipath route delete exact match"
++
++ route_cleanup
++}
++
++################################################################################
++# usage
++
++usage()
++{
++ cat <<EOF
++usage: ${0##*/} OPTS
++
++ -t <test> Test(s) to run (default: all)
++ (options: $TESTS)
++ -p Pause on fail
++ -P Pause after each test before cleanup
++ -v verbose mode (show commands and output)
++EOF
++}
++
++################################################################################
++# main
++
++while getopts :t:pPhv o
++do
++ case $o in
++ t) TESTS=$OPTARG;;
++ p) PAUSE_ON_FAIL=yes;;
++ P) PAUSE=yes;;
++ v) VERBOSE=$(($VERBOSE + 1));;
++ h) usage; exit 0;;
++ *) usage; exit 1;;
++ esac
++done
++
++PEER_CMD="ip netns exec ${PEER_NS}"
++
++# make sure we don't pause twice
++[ "${PAUSE}" = "yes" ] && PAUSE_ON_FAIL=no
++
++if [ "$(id -u)" -ne 0 ];then
++ echo "SKIP: Need root privileges"
++ exit $ksft_skip;
++fi
++
++if [ ! -x "$(command -v ip)" ]; then
++ echo "SKIP: Could not run test without ip tool"
++ exit $ksft_skip
++fi
++
++ip route help 2>&1 | grep -q fibmatch
++if [ $? -ne 0 ]; then
++ echo "SKIP: iproute2 too old, missing fibmatch"
++ exit $ksft_skip
++fi
++
++# start clean
++cleanup &> /dev/null
++
++for t in $TESTS
++do
++ case $t in
++ fib_unreg_test|unregister) fib_unreg_test;;
++ fib_down_test|down) fib_down_test;;
++ fib_carrier_test|carrier) fib_carrier_test;;
++ fib_rp_filter_test|rp_filter) fib_rp_filter_test;;
++ fib_nexthop_test|nexthop) fib_nexthop_test;;
++ fib_suppress_test|suppress) fib_suppress_test;;
++ ipv6_route_test|ipv6_rt) ipv6_route_test;;
++ ipv4_route_test|ipv4_rt) ipv4_route_test;;
++ ipv6_addr_metric) ipv6_addr_metric_test;;
++ ipv4_addr_metric) ipv4_addr_metric_test;;
++ ipv6_route_metrics) ipv6_route_metrics_test;;
++ ipv4_route_metrics) ipv4_route_metrics_test;;
++ ipv4_route_v6_gw) ipv4_route_v6_gw_test;;
++
++ help) echo "Test names: $TESTS"; exit 0;;
++ esac
++done
++
++if [ "$TESTS" != "none" ]; then
++ printf "\nTests passed: %3d\n" ${nsuccess}
++ printf "Tests failed: %3d\n" ${nfail}
++fi
++
++exit $ret
+diff --git a/tools/testing/selftests/net/forwarding/lib.sh b/tools/testing/selftests/net/forwarding/lib.sh
+index f190ad58e00d4..4d8845a68cf28 100644
+--- a/tools/testing/selftests/net/forwarding/lib.sh
++++ b/tools/testing/selftests/net/forwarding/lib.sh
+@@ -562,14 +562,14 @@ sysctl_set()
+ local value=$1; shift
+
+ SYSCTL_ORIG[$key]=$(sysctl -n $key)
+- sysctl -qw $key=$value
++ sysctl -qw $key="$value"
+ }
+
+ sysctl_restore()
+ {
+ local key=$1; shift
+
+- sysctl -qw $key=${SYSCTL_ORIG["$key"]}
++ sysctl -qw $key="${SYSCTL_ORIG[$key]}"
+ }
+
+ forwarding_enable()
+diff --git a/tools/testing/selftests/net/udpgso_bench.sh b/tools/testing/selftests/net/udpgso_bench.sh
+index dc932fd653634..640bc43452faa 100755
+--- a/tools/testing/selftests/net/udpgso_bench.sh
++++ b/tools/testing/selftests/net/udpgso_bench.sh
+@@ -7,6 +7,7 @@ readonly GREEN='\033[0;92m'
+ readonly YELLOW='\033[0;33m'
+ readonly RED='\033[0;31m'
+ readonly NC='\033[0m' # No Color
++readonly TESTPORT=8000
+
+ readonly KSFT_PASS=0
+ readonly KSFT_FAIL=1
+@@ -56,11 +57,26 @@ trap wake_children EXIT
+
+ run_one() {
+ local -r args=$@
++ local nr_socks=0
++ local i=0
++ local -r timeout=10
++
++ ./udpgso_bench_rx -p "$TESTPORT" &
++ ./udpgso_bench_rx -p "$TESTPORT" -t &
++
++ # Wait for the above test program to get ready to receive connections.
++ while [ "$i" -lt "$timeout" ]; do
++ nr_socks="$(ss -lnHi | grep -c "\*:${TESTPORT}")"
++ [ "$nr_socks" -eq 2 ] && break
++ i=$((i + 1))
++ sleep 1
++ done
++ if [ "$nr_socks" -ne 2 ]; then
++ echo "timed out while waiting for udpgso_bench_rx"
++ exit 1
++ fi
+
+- ./udpgso_bench_rx &
+- ./udpgso_bench_rx -t &
+-
+- ./udpgso_bench_tx ${args}
++ ./udpgso_bench_tx -p "$TESTPORT" ${args}
+ }
+
+ run_in_netns() {
+diff --git a/tools/testing/selftests/net/udpgso_bench_rx.c b/tools/testing/selftests/net/udpgso_bench_rx.c
+index 6a193425c367f..4058c7451e70d 100644
+--- a/tools/testing/selftests/net/udpgso_bench_rx.c
++++ b/tools/testing/selftests/net/udpgso_bench_rx.c
+@@ -250,7 +250,7 @@ static int recv_msg(int fd, char *buf, int len, int *gso_size)
+ static void do_flush_udp(int fd)
+ {
+ static char rbuf[ETH_MAX_MTU];
+- int ret, len, gso_size, budget = 256;
++ int ret, len, gso_size = 0, budget = 256;
+
+ len = cfg_read_all ? sizeof(rbuf) : 0;
+ while (budget--) {
+@@ -336,6 +336,8 @@ static void parse_opts(int argc, char **argv)
+ cfg_verify = true;
+ cfg_read_all = true;
+ break;
++ default:
++ exit(1);
+ }
+ }
+
+diff --git a/tools/testing/selftests/net/udpgso_bench_tx.c b/tools/testing/selftests/net/udpgso_bench_tx.c
+index f1fdaa2702913..477392715a9ad 100644
+--- a/tools/testing/selftests/net/udpgso_bench_tx.c
++++ b/tools/testing/selftests/net/udpgso_bench_tx.c
+@@ -62,6 +62,7 @@ static int cfg_payload_len = (1472 * 42);
+ static int cfg_port = 8000;
+ static int cfg_runtime_ms = -1;
+ static bool cfg_poll;
++static int cfg_poll_loop_timeout_ms = 2000;
+ static bool cfg_segment;
+ static bool cfg_sendmmsg;
+ static bool cfg_tcp;
+@@ -235,16 +236,17 @@ static void flush_errqueue_recv(int fd)
+ }
+ }
+
+-static void flush_errqueue(int fd, const bool do_poll)
++static void flush_errqueue(int fd, const bool do_poll,
++ unsigned long poll_timeout, const bool poll_err)
+ {
+ if (do_poll) {
+ struct pollfd fds = {0};
+ int ret;
+
+ fds.fd = fd;
+- ret = poll(&fds, 1, 500);
++ ret = poll(&fds, 1, poll_timeout);
+ if (ret == 0) {
+- if (cfg_verbose)
++ if ((cfg_verbose) && (poll_err))
+ fprintf(stderr, "poll timeout\n");
+ } else if (ret < 0) {
+ error(1, errno, "poll");
+@@ -254,6 +256,20 @@ static void flush_errqueue(int fd, const bool do_poll)
+ flush_errqueue_recv(fd);
+ }
+
++static void flush_errqueue_retry(int fd, unsigned long num_sends)
++{
++ unsigned long tnow, tstop;
++ bool first_try = true;
++
++ tnow = gettimeofday_ms();
++ tstop = tnow + cfg_poll_loop_timeout_ms;
++ do {
++ flush_errqueue(fd, true, tstop - tnow, first_try);
++ first_try = false;
++ tnow = gettimeofday_ms();
++ } while ((stat_zcopies != num_sends) && (tnow < tstop));
++}
++
+ static int send_tcp(int fd, char *data)
+ {
+ int ret, done = 0, count = 0;
+@@ -413,7 +429,8 @@ static int send_udp_segment(int fd, char *data)
+
+ static void usage(const char *filepath)
+ {
+- error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
++ error(1, 0, "Usage: %s [-46acmHPtTuvz] [-C cpu] [-D dst ip] [-l secs] "
++ "[-L secs] [-M messagenr] [-p port] [-s sendsize] [-S gsosize]",
+ filepath);
+ }
+
+@@ -423,7 +440,7 @@ static void parse_opts(int argc, char **argv)
+ int max_len, hdrlen;
+ int c;
+
+- while ((c = getopt(argc, argv, "46acC:D:Hl:mM:p:s:PS:tTuvz")) != -1) {
++ while ((c = getopt(argc, argv, "46acC:D:Hl:L:mM:p:s:PS:tTuvz")) != -1) {
+ switch (c) {
+ case '4':
+ if (cfg_family != PF_UNSPEC)
+@@ -452,6 +469,9 @@ static void parse_opts(int argc, char **argv)
+ case 'l':
+ cfg_runtime_ms = strtoul(optarg, NULL, 10) * 1000;
+ break;
++ case 'L':
++ cfg_poll_loop_timeout_ms = strtoul(optarg, NULL, 10) * 1000;
++ break;
+ case 'm':
+ cfg_sendmmsg = true;
+ break;
+@@ -490,6 +510,8 @@ static void parse_opts(int argc, char **argv)
+ case 'z':
+ cfg_zerocopy = true;
+ break;
++ default:
++ exit(1);
+ }
+ }
+
+@@ -677,7 +699,7 @@ int main(int argc, char **argv)
+ num_sends += send_udp(fd, buf[i]);
+ num_msgs++;
+ if ((cfg_zerocopy && ((num_msgs & 0xF) == 0)) || cfg_tx_tstamp)
+- flush_errqueue(fd, cfg_poll);
++ flush_errqueue(fd, cfg_poll, 500, true);
+
+ if (cfg_msg_nr && num_msgs >= cfg_msg_nr)
+ break;
+@@ -696,7 +718,7 @@ int main(int argc, char **argv)
+ } while (!interrupted && (cfg_runtime_ms == -1 || tnow < tstop));
+
+ if (cfg_zerocopy || cfg_tx_tstamp)
+- flush_errqueue(fd, true);
++ flush_errqueue_retry(fd, num_sends);
+
+ if (close(fd))
+ error(1, errno, "close");
+diff --git a/tools/virtio/linux/bug.h b/tools/virtio/linux/bug.h
+index b14c2c3b6b857..74aef964f5099 100644
+--- a/tools/virtio/linux/bug.h
++++ b/tools/virtio/linux/bug.h
+@@ -1,11 +1,9 @@
+ /* SPDX-License-Identifier: GPL-2.0 */
+-#ifndef BUG_H
+-#define BUG_H
++#ifndef _LINUX_BUG_H
++#define _LINUX_BUG_H
+
+ #define BUG_ON(__BUG_ON_cond) assert(!(__BUG_ON_cond))
+
+-#define BUILD_BUG_ON(x)
+-
+ #define BUG() abort()
+
+-#endif /* BUG_H */
++#endif /* _LINUX_BUG_H */
+diff --git a/tools/virtio/linux/build_bug.h b/tools/virtio/linux/build_bug.h
+new file mode 100644
+index 0000000000000..cdbb75e28a604
+--- /dev/null
++++ b/tools/virtio/linux/build_bug.h
+@@ -0,0 +1,7 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef _LINUX_BUILD_BUG_H
++#define _LINUX_BUILD_BUG_H
++
++#define BUILD_BUG_ON(x)
++
++#endif /* _LINUX_BUILD_BUG_H */
+diff --git a/tools/virtio/linux/cpumask.h b/tools/virtio/linux/cpumask.h
+new file mode 100644
+index 0000000000000..307da69d6b26c
+--- /dev/null
++++ b/tools/virtio/linux/cpumask.h
+@@ -0,0 +1,7 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef _LINUX_CPUMASK_H
++#define _LINUX_CPUMASK_H
++
++#include <linux/kernel.h>
++
++#endif /* _LINUX_CPUMASK_H */
+diff --git a/tools/virtio/linux/gfp.h b/tools/virtio/linux/gfp.h
+new file mode 100644
+index 0000000000000..43d146f236f14
+--- /dev/null
++++ b/tools/virtio/linux/gfp.h
+@@ -0,0 +1,7 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef __LINUX_GFP_H
++#define __LINUX_GFP_H
++
++#include <linux/topology.h>
++
++#endif
+diff --git a/tools/virtio/linux/kernel.h b/tools/virtio/linux/kernel.h
+index 6683b4a70b059..3325cdf229410 100644
+--- a/tools/virtio/linux/kernel.h
++++ b/tools/virtio/linux/kernel.h
+@@ -10,6 +10,7 @@
+ #include <stdarg.h>
+
+ #include <linux/compiler.h>
++#include <linux/log2.h>
+ #include <linux/types.h>
+ #include <linux/printk.h>
+ #include <linux/bug.h>
+diff --git a/tools/virtio/linux/kmsan.h b/tools/virtio/linux/kmsan.h
+new file mode 100644
+index 0000000000000..272b5aa285d5a
+--- /dev/null
++++ b/tools/virtio/linux/kmsan.h
+@@ -0,0 +1,12 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef _LINUX_KMSAN_H
++#define _LINUX_KMSAN_H
++
++#include <linux/gfp.h>
++
++inline void kmsan_handle_dma(struct page *page, size_t offset, size_t size,
++ enum dma_data_direction dir)
++{
++}
++
++#endif /* _LINUX_KMSAN_H */
+diff --git a/tools/virtio/linux/scatterlist.h b/tools/virtio/linux/scatterlist.h
+index 369ee308b6686..74d9e1825748e 100644
+--- a/tools/virtio/linux/scatterlist.h
++++ b/tools/virtio/linux/scatterlist.h
+@@ -2,6 +2,7 @@
+ #ifndef SCATTERLIST_H
+ #define SCATTERLIST_H
+ #include <linux/kernel.h>
++#include <linux/bug.h>
+
+ struct scatterlist {
+ unsigned long page_link;
+diff --git a/tools/virtio/linux/topology.h b/tools/virtio/linux/topology.h
+new file mode 100644
+index 0000000000000..910794afb993a
+--- /dev/null
++++ b/tools/virtio/linux/topology.h
+@@ -0,0 +1,7 @@
++/* SPDX-License-Identifier: GPL-2.0 */
++#ifndef _LINUX_TOPOLOGY_H
++#define _LINUX_TOPOLOGY_H
++
++#include <linux/cpumask.h>
++
++#endif /* _LINUX_TOPOLOGY_H */