* Update odroidxu4-current to 6.1.73 * Re-add kernel patchdir --------- Co-authored-by: Igor Pecovnik <igor.pecovnik@gmail.com>
9333 lines
306 KiB
Diff
9333 lines
306 KiB
Diff
diff --git a/MAINTAINERS b/MAINTAINERS
|
|
index 07a9c274c0e29..13d1078808bb5 100644
|
|
--- a/MAINTAINERS
|
|
+++ b/MAINTAINERS
|
|
@@ -10803,6 +10803,8 @@ L: linux-kernel@vger.kernel.org
|
|
S: Maintained
|
|
T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git irq/core
|
|
F: kernel/irq/
|
|
+F: include/linux/group_cpus.h
|
|
+F: lib/group_cpus.c
|
|
|
|
IRQCHIP DRIVERS
|
|
M: Thomas Gleixner <tglx@linutronix.de>
|
|
diff --git a/Makefile b/Makefile
|
|
index 2840e36fd5596..bad3387b3251c 100644
|
|
--- a/Makefile
|
|
+++ b/Makefile
|
|
@@ -1,7 +1,7 @@
|
|
# SPDX-License-Identifier: GPL-2.0
|
|
VERSION = 6
|
|
PATCHLEVEL = 1
|
|
-SUBLEVEL = 71
|
|
+SUBLEVEL = 72
|
|
EXTRAVERSION =
|
|
NAME = Curry Ramen
|
|
|
|
diff --git a/arch/Kconfig b/arch/Kconfig
|
|
index b60d271bf76a9..14273a6203dfc 100644
|
|
--- a/arch/Kconfig
|
|
+++ b/arch/Kconfig
|
|
@@ -34,6 +34,9 @@ config ARCH_HAS_SUBPAGE_FAULTS
|
|
config HOTPLUG_SMT
|
|
bool
|
|
|
|
+config SMT_NUM_THREADS_DYNAMIC
|
|
+ bool
|
|
+
|
|
config GENERIC_ENTRY
|
|
bool
|
|
|
|
diff --git a/arch/arm/mach-sunxi/mc_smp.c b/arch/arm/mach-sunxi/mc_smp.c
|
|
index 26cbce1353387..b2f5f4f28705f 100644
|
|
--- a/arch/arm/mach-sunxi/mc_smp.c
|
|
+++ b/arch/arm/mach-sunxi/mc_smp.c
|
|
@@ -808,12 +808,12 @@ static int __init sunxi_mc_smp_init(void)
|
|
break;
|
|
}
|
|
|
|
- is_a83t = sunxi_mc_smp_data[i].is_a83t;
|
|
-
|
|
of_node_put(node);
|
|
if (ret)
|
|
return -ENODEV;
|
|
|
|
+ is_a83t = sunxi_mc_smp_data[i].is_a83t;
|
|
+
|
|
if (!sunxi_mc_smp_cpu_table_init())
|
|
return -EINVAL;
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi b/arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi
|
|
index a5c0c788969fb..43ee28db61aa8 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-cheza.dtsi
|
|
@@ -150,15 +150,15 @@
|
|
};
|
|
|
|
&psci {
|
|
- /delete-node/ cpu0;
|
|
- /delete-node/ cpu1;
|
|
- /delete-node/ cpu2;
|
|
- /delete-node/ cpu3;
|
|
- /delete-node/ cpu4;
|
|
- /delete-node/ cpu5;
|
|
- /delete-node/ cpu6;
|
|
- /delete-node/ cpu7;
|
|
- /delete-node/ cpu-cluster0;
|
|
+ /delete-node/ power-domain-cpu0;
|
|
+ /delete-node/ power-domain-cpu1;
|
|
+ /delete-node/ power-domain-cpu2;
|
|
+ /delete-node/ power-domain-cpu3;
|
|
+ /delete-node/ power-domain-cpu4;
|
|
+ /delete-node/ power-domain-cpu5;
|
|
+ /delete-node/ power-domain-cpu6;
|
|
+ /delete-node/ power-domain-cpu7;
|
|
+ /delete-node/ power-domain-cluster;
|
|
};
|
|
|
|
&cpus {
|
|
@@ -351,7 +351,9 @@
|
|
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ /delete-property/ power-domains;
|
|
+
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
@@ -633,7 +635,7 @@
|
|
};
|
|
};
|
|
|
|
- pm8005-rpmh-regulators {
|
|
+ regulators-1 {
|
|
compatible = "qcom,pm8005-rpmh-regulators";
|
|
qcom,pmic-id = "c";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
|
|
index c9efcb894a52f..8c9ccf5b4ea41 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-db845c.dts
|
|
@@ -271,7 +271,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
vdd-s1-supply = <&vph_pwr>;
|
|
@@ -396,7 +396,7 @@
|
|
};
|
|
};
|
|
|
|
- pmi8998-rpmh-regulators {
|
|
+ regulators-1 {
|
|
compatible = "qcom,pmi8998-rpmh-regulators";
|
|
qcom,pmic-id = "b";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi
|
|
index 20f275f8694dc..e2921640880a1 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-lg-common.dtsi
|
|
@@ -166,7 +166,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
@@ -419,7 +419,7 @@
|
|
};
|
|
};
|
|
|
|
- pmi8998-rpmh-regulators {
|
|
+ regulators-1 {
|
|
compatible = "qcom,pmi8998-rpmh-regulators";
|
|
qcom,pmic-id = "b";
|
|
|
|
@@ -433,7 +433,7 @@
|
|
};
|
|
};
|
|
|
|
- pm8005-rpmh-regulators {
|
|
+ regulators-2 {
|
|
compatible = "qcom,pm8005-rpmh-regulators";
|
|
qcom,pmic-id = "c";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-mtp.dts b/arch/arm64/boot/dts/qcom/sdm845-mtp.dts
|
|
index 64958dee17d8b..b47e333aa3510 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-mtp.dts
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-mtp.dts
|
|
@@ -117,7 +117,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
@@ -382,7 +382,7 @@
|
|
};
|
|
};
|
|
|
|
- pmi8998-rpmh-regulators {
|
|
+ regulators-1 {
|
|
compatible = "qcom,pmi8998-rpmh-regulators";
|
|
qcom,pmic-id = "b";
|
|
|
|
@@ -396,7 +396,7 @@
|
|
};
|
|
};
|
|
|
|
- pm8005-rpmh-regulators {
|
|
+ regulators-2 {
|
|
compatible = "qcom,pm8005-rpmh-regulators";
|
|
qcom,pmic-id = "c";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
|
|
index 392461c29e76e..0713b774a97be 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-oneplus-common.dtsi
|
|
@@ -144,7 +144,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
@@ -280,7 +280,7 @@
|
|
};
|
|
};
|
|
|
|
- pmi8998-rpmh-regulators {
|
|
+ regulators-1 {
|
|
compatible = "qcom,pmi8998-rpmh-regulators";
|
|
qcom,pmic-id = "b";
|
|
|
|
@@ -294,7 +294,7 @@
|
|
};
|
|
};
|
|
|
|
- pm8005-rpmh-regulators {
|
|
+ regulators-2 {
|
|
compatible = "qcom,pm8005-rpmh-regulators";
|
|
qcom,pmic-id = "c";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts b/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts
|
|
index 83261c9bb4f23..b65c35865dab9 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-shift-axolotl.dts
|
|
@@ -110,7 +110,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
@@ -375,7 +375,7 @@
|
|
};
|
|
};
|
|
|
|
- pmi8998-rpmh-regulators {
|
|
+ regulators-1 {
|
|
compatible = "qcom,pmi8998-rpmh-regulators";
|
|
qcom,pmic-id = "b";
|
|
|
|
@@ -389,7 +389,7 @@
|
|
};
|
|
};
|
|
|
|
- pm8005-rpmh-regulators {
|
|
+ regulators-2 {
|
|
compatible = "qcom,pm8005-rpmh-regulators";
|
|
qcom,pmic-id = "c";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi
|
|
index d6918e6d19799..249a715d5aae1 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-sony-xperia-tama.dtsi
|
|
@@ -78,7 +78,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
@@ -308,7 +308,7 @@
|
|
};
|
|
};
|
|
|
|
- pmi8998-rpmh-regulators {
|
|
+ regulators-1 {
|
|
compatible = "qcom,pmi8998-rpmh-regulators";
|
|
qcom,pmic-id = "b";
|
|
|
|
@@ -319,7 +319,7 @@
|
|
};
|
|
};
|
|
|
|
- pm8005-rpmh-regulators {
|
|
+ regulators-2 {
|
|
compatible = "qcom,pm8005-rpmh-regulators";
|
|
qcom,pmic-id = "c";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium.dts b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium.dts
|
|
index 0f470cf1ed1c1..6d6b3dd699475 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium.dts
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-beryllium.dts
|
|
@@ -125,7 +125,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts
|
|
index 093b04359ec39..ffbe45a99b74a 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm845-xiaomi-polaris.dts
|
|
@@ -143,7 +143,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
@@ -343,7 +343,7 @@
|
|
};
|
|
};
|
|
|
|
- pmi8998-rpmh-regulators {
|
|
+ regulators-1 {
|
|
compatible = "qcom,pmi8998-rpmh-regulators";
|
|
qcom,pmic-id = "b";
|
|
|
|
@@ -355,7 +355,7 @@
|
|
};
|
|
};
|
|
|
|
- pm8005-rpmh-regulators {
|
|
+ regulators-2 {
|
|
compatible = "qcom,pm8005-rpmh-regulators";
|
|
qcom,pmic-id = "c";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
|
|
index 74f43da51fa50..48a41ace8fc58 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm850-lenovo-yoga-c630.dts
|
|
@@ -99,7 +99,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
diff --git a/arch/arm64/boot/dts/qcom/sdm850-samsung-w737.dts b/arch/arm64/boot/dts/qcom/sdm850-samsung-w737.dts
|
|
index d028a7eb364a6..c169d2870bdf4 100644
|
|
--- a/arch/arm64/boot/dts/qcom/sdm850-samsung-w737.dts
|
|
+++ b/arch/arm64/boot/dts/qcom/sdm850-samsung-w737.dts
|
|
@@ -129,7 +129,7 @@
|
|
};
|
|
|
|
&apps_rsc {
|
|
- pm8998-rpmh-regulators {
|
|
+ regulators-0 {
|
|
compatible = "qcom,pm8998-rpmh-regulators";
|
|
qcom,pmic-id = "a";
|
|
|
|
diff --git a/arch/s390/kernel/perf_cpum_cf.c b/arch/s390/kernel/perf_cpum_cf.c
|
|
index f043a7ff220b7..28fa80fd69fa0 100644
|
|
--- a/arch/s390/kernel/perf_cpum_cf.c
|
|
+++ b/arch/s390/kernel/perf_cpum_cf.c
|
|
@@ -2,7 +2,7 @@
|
|
/*
|
|
* Performance event support for s390x - CPU-measurement Counter Facility
|
|
*
|
|
- * Copyright IBM Corp. 2012, 2021
|
|
+ * Copyright IBM Corp. 2012, 2022
|
|
* Author(s): Hendrik Brueckner <brueckner@linux.ibm.com>
|
|
* Thomas Richter <tmricht@linux.ibm.com>
|
|
*/
|
|
@@ -434,6 +434,12 @@ static void cpumf_hw_inuse(void)
|
|
mutex_unlock(&pmc_reserve_mutex);
|
|
}
|
|
|
|
+static int is_userspace_event(u64 ev)
|
|
+{
|
|
+ return cpumf_generic_events_user[PERF_COUNT_HW_CPU_CYCLES] == ev ||
|
|
+ cpumf_generic_events_user[PERF_COUNT_HW_INSTRUCTIONS] == ev;
|
|
+}
|
|
+
|
|
static int __hw_perf_event_init(struct perf_event *event, unsigned int type)
|
|
{
|
|
struct perf_event_attr *attr = &event->attr;
|
|
@@ -456,19 +462,26 @@ static int __hw_perf_event_init(struct perf_event *event, unsigned int type)
|
|
if (is_sampling_event(event)) /* No sampling support */
|
|
return -ENOENT;
|
|
ev = attr->config;
|
|
- /* Count user space (problem-state) only */
|
|
if (!attr->exclude_user && attr->exclude_kernel) {
|
|
- if (ev >= ARRAY_SIZE(cpumf_generic_events_user))
|
|
- return -EOPNOTSUPP;
|
|
- ev = cpumf_generic_events_user[ev];
|
|
-
|
|
- /* No support for kernel space counters only */
|
|
+ /*
|
|
+ * Count user space (problem-state) only
|
|
+ * Handle events 32 and 33 as 0:u and 1:u
|
|
+ */
|
|
+ if (!is_userspace_event(ev)) {
|
|
+ if (ev >= ARRAY_SIZE(cpumf_generic_events_user))
|
|
+ return -EOPNOTSUPP;
|
|
+ ev = cpumf_generic_events_user[ev];
|
|
+ }
|
|
} else if (!attr->exclude_kernel && attr->exclude_user) {
|
|
+ /* No support for kernel space counters only */
|
|
return -EOPNOTSUPP;
|
|
- } else { /* Count user and kernel space */
|
|
- if (ev >= ARRAY_SIZE(cpumf_generic_events_basic))
|
|
- return -EOPNOTSUPP;
|
|
- ev = cpumf_generic_events_basic[ev];
|
|
+ } else {
|
|
+ /* Count user and kernel space, incl. events 32 + 33 */
|
|
+ if (!is_userspace_event(ev)) {
|
|
+ if (ev >= ARRAY_SIZE(cpumf_generic_events_basic))
|
|
+ return -EOPNOTSUPP;
|
|
+ ev = cpumf_generic_events_basic[ev];
|
|
+ }
|
|
}
|
|
break;
|
|
|
|
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
|
|
index 9a0ce5315f36d..3cbb461820666 100644
|
|
--- a/arch/s390/mm/vmem.c
|
|
+++ b/arch/s390/mm/vmem.c
|
|
@@ -11,6 +11,7 @@
|
|
#include <linux/list.h>
|
|
#include <linux/hugetlb.h>
|
|
#include <linux/slab.h>
|
|
+#include <asm/page-states.h>
|
|
#include <asm/cacheflush.h>
|
|
#include <asm/nospec-branch.h>
|
|
#include <asm/pgalloc.h>
|
|
@@ -44,8 +45,11 @@ void *vmem_crst_alloc(unsigned long val)
|
|
unsigned long *table;
|
|
|
|
table = vmem_alloc_pages(CRST_ALLOC_ORDER);
|
|
- if (table)
|
|
- crst_table_init(table, val);
|
|
+ if (!table)
|
|
+ return NULL;
|
|
+ crst_table_init(table, val);
|
|
+ if (slab_is_available())
|
|
+ arch_set_page_dat(virt_to_page(table), CRST_ALLOC_ORDER);
|
|
return table;
|
|
}
|
|
|
|
diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c
|
|
index 2fb5e1541efc1..949129443b1c0 100644
|
|
--- a/arch/x86/events/intel/core.c
|
|
+++ b/arch/x86/events/intel/core.c
|
|
@@ -4033,12 +4033,17 @@ static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data)
|
|
u64 pebs_mask = cpuc->pebs_enabled & x86_pmu.pebs_capable;
|
|
int global_ctrl, pebs_enable;
|
|
|
|
+ /*
|
|
+ * In addition to obeying exclude_guest/exclude_host, remove bits being
|
|
+ * used for PEBS when running a guest, because PEBS writes to virtual
|
|
+ * addresses (not physical addresses).
|
|
+ */
|
|
*nr = 0;
|
|
global_ctrl = (*nr)++;
|
|
arr[global_ctrl] = (struct perf_guest_switch_msr){
|
|
.msr = MSR_CORE_PERF_GLOBAL_CTRL,
|
|
.host = intel_ctrl & ~cpuc->intel_ctrl_guest_mask,
|
|
- .guest = intel_ctrl & (~cpuc->intel_ctrl_host_mask | ~pebs_mask),
|
|
+ .guest = intel_ctrl & ~cpuc->intel_ctrl_host_mask & ~pebs_mask,
|
|
};
|
|
|
|
if (!x86_pmu.pebs)
|
|
diff --git a/arch/x86/kernel/kprobes/core.c b/arch/x86/kernel/kprobes/core.c
|
|
index ea155f0cf545c..6120f25b0d5cc 100644
|
|
--- a/arch/x86/kernel/kprobes/core.c
|
|
+++ b/arch/x86/kernel/kprobes/core.c
|
|
@@ -549,7 +549,8 @@ static void kprobe_emulate_call_indirect(struct kprobe *p, struct pt_regs *regs)
|
|
{
|
|
unsigned long offs = addrmode_regoffs[p->ainsn.indirect.reg];
|
|
|
|
- int3_emulate_call(regs, regs_get_register(regs, offs));
|
|
+ int3_emulate_push(regs, regs->ip - INT3_INSN_SIZE + p->ainsn.size);
|
|
+ int3_emulate_jmp(regs, regs_get_register(regs, offs));
|
|
}
|
|
NOKPROBE_SYMBOL(kprobe_emulate_call_indirect);
|
|
|
|
diff --git a/arch/x86/net/bpf_jit_comp.c b/arch/x86/net/bpf_jit_comp.c
|
|
index 4686c1d9d0cfd..b69aee6245e4a 100644
|
|
--- a/arch/x86/net/bpf_jit_comp.c
|
|
+++ b/arch/x86/net/bpf_jit_comp.c
|
|
@@ -893,6 +893,10 @@ static void emit_nops(u8 **pprog, int len)
|
|
|
|
#define INSN_SZ_DIFF (((addrs[i] - addrs[i - 1]) - (prog - temp)))
|
|
|
|
+/* mov rax, qword ptr [rbp - rounded_stack_depth - 8] */
|
|
+#define RESTORE_TAIL_CALL_CNT(stack) \
|
|
+ EMIT3_off32(0x48, 0x8B, 0x85, -round_up(stack, 8) - 8)
|
|
+
|
|
static int do_jit(struct bpf_prog *bpf_prog, int *addrs, u8 *image, u8 *rw_image,
|
|
int oldproglen, struct jit_context *ctx, bool jmp_padding)
|
|
{
|
|
@@ -1436,9 +1440,7 @@ st: if (is_imm8(insn->off))
|
|
case BPF_JMP | BPF_CALL:
|
|
func = (u8 *) __bpf_call_base + imm32;
|
|
if (tail_call_reachable) {
|
|
- /* mov rax, qword ptr [rbp - rounded_stack_depth - 8] */
|
|
- EMIT3_off32(0x48, 0x8B, 0x85,
|
|
- -round_up(bpf_prog->aux->stack_depth, 8) - 8);
|
|
+ RESTORE_TAIL_CALL_CNT(bpf_prog->aux->stack_depth);
|
|
if (!imm32 || emit_call(&prog, func, image + addrs[i - 1] + 7))
|
|
return -EINVAL;
|
|
} else {
|
|
@@ -1623,16 +1625,24 @@ emit_cond_jmp: /* Convert BPF opcode to x86 */
|
|
break;
|
|
|
|
case BPF_JMP | BPF_JA:
|
|
- if (insn->off == -1)
|
|
- /* -1 jmp instructions will always jump
|
|
- * backwards two bytes. Explicitly handling
|
|
- * this case avoids wasting too many passes
|
|
- * when there are long sequences of replaced
|
|
- * dead code.
|
|
- */
|
|
- jmp_offset = -2;
|
|
- else
|
|
- jmp_offset = addrs[i + insn->off] - addrs[i];
|
|
+ case BPF_JMP32 | BPF_JA:
|
|
+ if (BPF_CLASS(insn->code) == BPF_JMP) {
|
|
+ if (insn->off == -1)
|
|
+ /* -1 jmp instructions will always jump
|
|
+ * backwards two bytes. Explicitly handling
|
|
+ * this case avoids wasting too many passes
|
|
+ * when there are long sequences of replaced
|
|
+ * dead code.
|
|
+ */
|
|
+ jmp_offset = -2;
|
|
+ else
|
|
+ jmp_offset = addrs[i + insn->off] - addrs[i];
|
|
+ } else {
|
|
+ if (insn->imm == -1)
|
|
+ jmp_offset = -2;
|
|
+ else
|
|
+ jmp_offset = addrs[i + insn->imm] - addrs[i];
|
|
+ }
|
|
|
|
if (!jmp_offset) {
|
|
/*
|
|
@@ -1750,63 +1760,37 @@ emit_jmp:
|
|
return proglen;
|
|
}
|
|
|
|
-static void save_regs(const struct btf_func_model *m, u8 **prog, int nr_args,
|
|
+static void save_regs(const struct btf_func_model *m, u8 **prog, int nr_regs,
|
|
int stack_size)
|
|
{
|
|
- int i, j, arg_size, nr_regs;
|
|
+ int i;
|
|
+
|
|
/* Store function arguments to stack.
|
|
* For a function that accepts two pointers the sequence will be:
|
|
* mov QWORD PTR [rbp-0x10],rdi
|
|
* mov QWORD PTR [rbp-0x8],rsi
|
|
*/
|
|
- for (i = 0, j = 0; i < min(nr_args, 6); i++) {
|
|
- if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) {
|
|
- nr_regs = (m->arg_size[i] + 7) / 8;
|
|
- arg_size = 8;
|
|
- } else {
|
|
- nr_regs = 1;
|
|
- arg_size = m->arg_size[i];
|
|
- }
|
|
-
|
|
- while (nr_regs) {
|
|
- emit_stx(prog, bytes_to_bpf_size(arg_size),
|
|
- BPF_REG_FP,
|
|
- j == 5 ? X86_REG_R9 : BPF_REG_1 + j,
|
|
- -(stack_size - j * 8));
|
|
- nr_regs--;
|
|
- j++;
|
|
- }
|
|
- }
|
|
+ for (i = 0; i < min(nr_regs, 6); i++)
|
|
+ emit_stx(prog, BPF_DW, BPF_REG_FP,
|
|
+ i == 5 ? X86_REG_R9 : BPF_REG_1 + i,
|
|
+ -(stack_size - i * 8));
|
|
}
|
|
|
|
-static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_args,
|
|
+static void restore_regs(const struct btf_func_model *m, u8 **prog, int nr_regs,
|
|
int stack_size)
|
|
{
|
|
- int i, j, arg_size, nr_regs;
|
|
+ int i;
|
|
|
|
/* Restore function arguments from stack.
|
|
* For a function that accepts two pointers the sequence will be:
|
|
* EMIT4(0x48, 0x8B, 0x7D, 0xF0); mov rdi,QWORD PTR [rbp-0x10]
|
|
* EMIT4(0x48, 0x8B, 0x75, 0xF8); mov rsi,QWORD PTR [rbp-0x8]
|
|
*/
|
|
- for (i = 0, j = 0; i < min(nr_args, 6); i++) {
|
|
- if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG) {
|
|
- nr_regs = (m->arg_size[i] + 7) / 8;
|
|
- arg_size = 8;
|
|
- } else {
|
|
- nr_regs = 1;
|
|
- arg_size = m->arg_size[i];
|
|
- }
|
|
-
|
|
- while (nr_regs) {
|
|
- emit_ldx(prog, bytes_to_bpf_size(arg_size),
|
|
- j == 5 ? X86_REG_R9 : BPF_REG_1 + j,
|
|
- BPF_REG_FP,
|
|
- -(stack_size - j * 8));
|
|
- nr_regs--;
|
|
- j++;
|
|
- }
|
|
- }
|
|
+ for (i = 0; i < min(nr_regs, 6); i++)
|
|
+ emit_ldx(prog, BPF_DW,
|
|
+ i == 5 ? X86_REG_R9 : BPF_REG_1 + i,
|
|
+ BPF_REG_FP,
|
|
+ -(stack_size - i * 8));
|
|
}
|
|
|
|
static int invoke_bpf_prog(const struct btf_func_model *m, u8 **pprog,
|
|
@@ -2031,8 +2015,8 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
struct bpf_tramp_links *tlinks,
|
|
void *func_addr)
|
|
{
|
|
- int ret, i, nr_args = m->nr_args, extra_nregs = 0;
|
|
- int regs_off, ip_off, args_off, stack_size = nr_args * 8, run_ctx_off;
|
|
+ int i, ret, nr_regs = m->nr_args, stack_size = 0;
|
|
+ int regs_off, nregs_off, ip_off, run_ctx_off;
|
|
struct bpf_tramp_links *fentry = &tlinks[BPF_TRAMP_FENTRY];
|
|
struct bpf_tramp_links *fexit = &tlinks[BPF_TRAMP_FEXIT];
|
|
struct bpf_tramp_links *fmod_ret = &tlinks[BPF_TRAMP_MODIFY_RETURN];
|
|
@@ -2041,17 +2025,14 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
u8 *prog;
|
|
bool save_ret;
|
|
|
|
- /* x86-64 supports up to 6 arguments. 7+ can be added in the future */
|
|
- if (nr_args > 6)
|
|
- return -ENOTSUPP;
|
|
-
|
|
- for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) {
|
|
+ /* extra registers for struct arguments */
|
|
+ for (i = 0; i < m->nr_args; i++)
|
|
if (m->arg_flags[i] & BTF_FMODEL_STRUCT_ARG)
|
|
- extra_nregs += (m->arg_size[i] + 7) / 8 - 1;
|
|
- }
|
|
- if (nr_args + extra_nregs > 6)
|
|
+ nr_regs += (m->arg_size[i] + 7) / 8 - 1;
|
|
+
|
|
+ /* x86-64 supports up to 6 arguments. 7+ can be added in the future */
|
|
+ if (nr_regs > 6)
|
|
return -ENOTSUPP;
|
|
- stack_size += extra_nregs * 8;
|
|
|
|
/* Generated trampoline stack layout:
|
|
*
|
|
@@ -2065,11 +2046,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
* [ ... ]
|
|
* RBP - regs_off [ reg_arg1 ] program's ctx pointer
|
|
*
|
|
- * RBP - args_off [ arg regs count ] always
|
|
+ * RBP - nregs_off [ regs count ] always
|
|
*
|
|
* RBP - ip_off [ traced function ] BPF_TRAMP_F_IP_ARG flag
|
|
*
|
|
* RBP - run_ctx_off [ bpf_tramp_run_ctx ]
|
|
+ * RSP [ tail_call_cnt ] BPF_TRAMP_F_TAIL_CALL_CTX
|
|
*/
|
|
|
|
/* room for return value of orig_call or fentry prog */
|
|
@@ -2077,11 +2059,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
if (save_ret)
|
|
stack_size += 8;
|
|
|
|
+ stack_size += nr_regs * 8;
|
|
regs_off = stack_size;
|
|
|
|
- /* args count */
|
|
+ /* regs count */
|
|
stack_size += 8;
|
|
- args_off = stack_size;
|
|
+ nregs_off = stack_size;
|
|
|
|
if (flags & BPF_TRAMP_F_IP_ARG)
|
|
stack_size += 8; /* room for IP address argument */
|
|
@@ -2106,14 +2089,16 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
EMIT1(0x55); /* push rbp */
|
|
EMIT3(0x48, 0x89, 0xE5); /* mov rbp, rsp */
|
|
EMIT4(0x48, 0x83, 0xEC, stack_size); /* sub rsp, stack_size */
|
|
+ if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
|
|
+ EMIT1(0x50); /* push rax */
|
|
EMIT1(0x53); /* push rbx */
|
|
|
|
/* Store number of argument registers of the traced function:
|
|
- * mov rax, nr_args + extra_nregs
|
|
- * mov QWORD PTR [rbp - args_off], rax
|
|
+ * mov rax, nr_regs
|
|
+ * mov QWORD PTR [rbp - nregs_off], rax
|
|
*/
|
|
- emit_mov_imm64(&prog, BPF_REG_0, 0, (u32) nr_args + extra_nregs);
|
|
- emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -args_off);
|
|
+ emit_mov_imm64(&prog, BPF_REG_0, 0, (u32) nr_regs);
|
|
+ emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -nregs_off);
|
|
|
|
if (flags & BPF_TRAMP_F_IP_ARG) {
|
|
/* Store IP address of the traced function:
|
|
@@ -2124,7 +2109,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
emit_stx(&prog, BPF_DW, BPF_REG_FP, BPF_REG_0, -ip_off);
|
|
}
|
|
|
|
- save_regs(m, &prog, nr_args, regs_off);
|
|
+ save_regs(m, &prog, nr_regs, regs_off);
|
|
|
|
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
|
/* arg1: mov rdi, im */
|
|
@@ -2154,11 +2139,17 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
}
|
|
|
|
if (flags & BPF_TRAMP_F_CALL_ORIG) {
|
|
- restore_regs(m, &prog, nr_args, regs_off);
|
|
+ restore_regs(m, &prog, nr_regs, regs_off);
|
|
+
|
|
+ if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
|
|
+ /* Before calling the original function, restore the
|
|
+ * tail_call_cnt from stack to rax.
|
|
+ */
|
|
+ RESTORE_TAIL_CALL_CNT(stack_size);
|
|
|
|
if (flags & BPF_TRAMP_F_ORIG_STACK) {
|
|
- emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, 8);
|
|
- EMIT2(0xff, 0xd0); /* call *rax */
|
|
+ emit_ldx(&prog, BPF_DW, BPF_REG_6, BPF_REG_FP, 8);
|
|
+ EMIT2(0xff, 0xd3); /* call *rbx */
|
|
} else {
|
|
/* call original function */
|
|
if (emit_call(&prog, orig_call, prog)) {
|
|
@@ -2195,7 +2186,7 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
}
|
|
|
|
if (flags & BPF_TRAMP_F_RESTORE_REGS)
|
|
- restore_regs(m, &prog, nr_args, regs_off);
|
|
+ restore_regs(m, &prog, nr_regs, regs_off);
|
|
|
|
/* This needs to be done regardless. If there were fmod_ret programs,
|
|
* the return value is only updated on the stack and still needs to be
|
|
@@ -2209,7 +2200,12 @@ int arch_prepare_bpf_trampoline(struct bpf_tramp_image *im, void *image, void *i
|
|
ret = -EINVAL;
|
|
goto cleanup;
|
|
}
|
|
- }
|
|
+ } else if (flags & BPF_TRAMP_F_TAIL_CALL_CTX)
|
|
+ /* Before running the original function, restore the
|
|
+ * tail_call_cnt from stack to rax.
|
|
+ */
|
|
+ RESTORE_TAIL_CALL_CNT(stack_size);
|
|
+
|
|
/* restore return value of orig_call or fentry prog back into RAX */
|
|
if (save_ret)
|
|
emit_ldx(&prog, BPF_DW, BPF_REG_0, BPF_REG_FP, -8);
|
|
diff --git a/block/bdev.c b/block/bdev.c
|
|
index d699ecdb32604..b61502ec8da06 100644
|
|
--- a/block/bdev.c
|
|
+++ b/block/bdev.c
|
|
@@ -507,6 +507,8 @@ struct block_device *bdev_alloc(struct gendisk *disk, u8 partno)
|
|
|
|
void bdev_add(struct block_device *bdev, dev_t dev)
|
|
{
|
|
+ if (bdev_stable_writes(bdev))
|
|
+ mapping_set_stable_writes(bdev->bd_inode->i_mapping);
|
|
bdev->bd_dev = dev;
|
|
bdev->bd_inode->i_rdev = dev;
|
|
bdev->bd_inode->i_ino = dev;
|
|
diff --git a/block/blk-mq.c b/block/blk-mq.c
|
|
index 100fb0c3114f8..383d94615e502 100644
|
|
--- a/block/blk-mq.c
|
|
+++ b/block/blk-mq.c
|
|
@@ -2855,11 +2855,8 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q,
|
|
};
|
|
struct request *rq;
|
|
|
|
- if (unlikely(bio_queue_enter(bio)))
|
|
- return NULL;
|
|
-
|
|
if (blk_mq_attempt_bio_merge(q, bio, nsegs))
|
|
- goto queue_exit;
|
|
+ return NULL;
|
|
|
|
rq_qos_throttle(q, bio);
|
|
|
|
@@ -2875,35 +2872,23 @@ static struct request *blk_mq_get_new_requests(struct request_queue *q,
|
|
rq_qos_cleanup(q, bio);
|
|
if (bio->bi_opf & REQ_NOWAIT)
|
|
bio_wouldblock_error(bio);
|
|
-queue_exit:
|
|
- blk_queue_exit(q);
|
|
return NULL;
|
|
}
|
|
|
|
-static inline struct request *blk_mq_get_cached_request(struct request_queue *q,
|
|
- struct blk_plug *plug, struct bio **bio, unsigned int nsegs)
|
|
+/* return true if this @rq can be used for @bio */
|
|
+static bool blk_mq_can_use_cached_rq(struct request *rq, struct blk_plug *plug,
|
|
+ struct bio *bio)
|
|
{
|
|
- struct request *rq;
|
|
- enum hctx_type type, hctx_type;
|
|
+ enum hctx_type type = blk_mq_get_hctx_type(bio->bi_opf);
|
|
+ enum hctx_type hctx_type = rq->mq_hctx->type;
|
|
|
|
- if (!plug)
|
|
- return NULL;
|
|
- rq = rq_list_peek(&plug->cached_rq);
|
|
- if (!rq || rq->q != q)
|
|
- return NULL;
|
|
-
|
|
- if (blk_mq_attempt_bio_merge(q, *bio, nsegs)) {
|
|
- *bio = NULL;
|
|
- return NULL;
|
|
- }
|
|
+ WARN_ON_ONCE(rq_list_peek(&plug->cached_rq) != rq);
|
|
|
|
- type = blk_mq_get_hctx_type((*bio)->bi_opf);
|
|
- hctx_type = rq->mq_hctx->type;
|
|
if (type != hctx_type &&
|
|
!(type == HCTX_TYPE_READ && hctx_type == HCTX_TYPE_DEFAULT))
|
|
- return NULL;
|
|
- if (op_is_flush(rq->cmd_flags) != op_is_flush((*bio)->bi_opf))
|
|
- return NULL;
|
|
+ return false;
|
|
+ if (op_is_flush(rq->cmd_flags) != op_is_flush(bio->bi_opf))
|
|
+ return false;
|
|
|
|
/*
|
|
* If any qos ->throttle() end up blocking, we will have flushed the
|
|
@@ -2911,11 +2896,11 @@ static inline struct request *blk_mq_get_cached_request(struct request_queue *q,
|
|
* before we throttle.
|
|
*/
|
|
plug->cached_rq = rq_list_next(rq);
|
|
- rq_qos_throttle(q, *bio);
|
|
+ rq_qos_throttle(rq->q, bio);
|
|
|
|
- rq->cmd_flags = (*bio)->bi_opf;
|
|
+ rq->cmd_flags = bio->bi_opf;
|
|
INIT_LIST_HEAD(&rq->queuelist);
|
|
- return rq;
|
|
+ return true;
|
|
}
|
|
|
|
static void bio_set_ioprio(struct bio *bio)
|
|
@@ -2944,7 +2929,7 @@ void blk_mq_submit_bio(struct bio *bio)
|
|
struct request_queue *q = bdev_get_queue(bio->bi_bdev);
|
|
struct blk_plug *plug = blk_mq_plug(bio);
|
|
const int is_sync = op_is_sync(bio->bi_opf);
|
|
- struct request *rq;
|
|
+ struct request *rq = NULL;
|
|
unsigned int nr_segs = 1;
|
|
blk_status_t ret;
|
|
|
|
@@ -2955,20 +2940,36 @@ void blk_mq_submit_bio(struct bio *bio)
|
|
return;
|
|
}
|
|
|
|
- if (!bio_integrity_prep(bio))
|
|
- return;
|
|
-
|
|
bio_set_ioprio(bio);
|
|
|
|
- rq = blk_mq_get_cached_request(q, plug, &bio, nr_segs);
|
|
- if (!rq) {
|
|
- if (!bio)
|
|
+ if (plug) {
|
|
+ rq = rq_list_peek(&plug->cached_rq);
|
|
+ if (rq && rq->q != q)
|
|
+ rq = NULL;
|
|
+ }
|
|
+ if (rq) {
|
|
+ if (!bio_integrity_prep(bio))
|
|
return;
|
|
- rq = blk_mq_get_new_requests(q, plug, bio, nr_segs);
|
|
- if (unlikely(!rq))
|
|
+ if (blk_mq_attempt_bio_merge(q, bio, nr_segs))
|
|
return;
|
|
+ if (blk_mq_can_use_cached_rq(rq, plug, bio))
|
|
+ goto done;
|
|
+ percpu_ref_get(&q->q_usage_counter);
|
|
+ } else {
|
|
+ if (unlikely(bio_queue_enter(bio)))
|
|
+ return;
|
|
+ if (!bio_integrity_prep(bio))
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ rq = blk_mq_get_new_requests(q, plug, bio, nr_segs);
|
|
+ if (unlikely(!rq)) {
|
|
+fail:
|
|
+ blk_queue_exit(q);
|
|
+ return;
|
|
}
|
|
|
|
+done:
|
|
trace_block_getrq(bio);
|
|
|
|
rq_qos_track(q, rq, bio);
|
|
diff --git a/block/fops.c b/block/fops.c
|
|
index 6197d1c41652d..01cb6260fa24d 100644
|
|
--- a/block/fops.c
|
|
+++ b/block/fops.c
|
|
@@ -655,24 +655,35 @@ static long blkdev_fallocate(struct file *file, int mode, loff_t start,
|
|
|
|
filemap_invalidate_lock(inode->i_mapping);
|
|
|
|
- /* Invalidate the page cache, including dirty pages. */
|
|
- error = truncate_bdev_range(bdev, file->f_mode, start, end);
|
|
- if (error)
|
|
- goto fail;
|
|
-
|
|
+ /*
|
|
+ * Invalidate the page cache, including dirty pages, for valid
|
|
+ * de-allocate mode calls to fallocate().
|
|
+ */
|
|
switch (mode) {
|
|
case FALLOC_FL_ZERO_RANGE:
|
|
case FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE:
|
|
+ error = truncate_bdev_range(bdev, file->f_mode, start, end);
|
|
+ if (error)
|
|
+ goto fail;
|
|
+
|
|
error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT,
|
|
len >> SECTOR_SHIFT, GFP_KERNEL,
|
|
BLKDEV_ZERO_NOUNMAP);
|
|
break;
|
|
case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE:
|
|
+ error = truncate_bdev_range(bdev, file->f_mode, start, end);
|
|
+ if (error)
|
|
+ goto fail;
|
|
+
|
|
error = blkdev_issue_zeroout(bdev, start >> SECTOR_SHIFT,
|
|
len >> SECTOR_SHIFT, GFP_KERNEL,
|
|
BLKDEV_ZERO_NOFALLBACK);
|
|
break;
|
|
case FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE | FALLOC_FL_NO_HIDE_STALE:
|
|
+ error = truncate_bdev_range(bdev, file->f_mode, start, end);
|
|
+ if (error)
|
|
+ goto fail;
|
|
+
|
|
error = blkdev_issue_discard(bdev, start >> SECTOR_SHIFT,
|
|
len >> SECTOR_SHIFT, GFP_KERNEL);
|
|
break;
|
|
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
|
|
index 9aa0da991cfb9..5d39f3e374dae 100644
|
|
--- a/drivers/base/memory.c
|
|
+++ b/drivers/base/memory.c
|
|
@@ -175,6 +175,9 @@ int memory_notify(unsigned long val, void *v)
|
|
return blocking_notifier_call_chain(&memory_chain, val, v);
|
|
}
|
|
|
|
+/*
|
|
+ * Must acquire mem_hotplug_lock in write mode.
|
|
+ */
|
|
static int memory_block_online(struct memory_block *mem)
|
|
{
|
|
unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr);
|
|
@@ -193,10 +196,11 @@ static int memory_block_online(struct memory_block *mem)
|
|
* stage helps to keep accounting easier to follow - e.g vmemmaps
|
|
* belong to the same zone as the memory they backed.
|
|
*/
|
|
+ mem_hotplug_begin();
|
|
if (nr_vmemmap_pages) {
|
|
ret = mhp_init_memmap_on_memory(start_pfn, nr_vmemmap_pages, zone);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto out;
|
|
}
|
|
|
|
ret = online_pages(start_pfn + nr_vmemmap_pages,
|
|
@@ -204,7 +208,7 @@ static int memory_block_online(struct memory_block *mem)
|
|
if (ret) {
|
|
if (nr_vmemmap_pages)
|
|
mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages);
|
|
- return ret;
|
|
+ goto out;
|
|
}
|
|
|
|
/*
|
|
@@ -216,9 +220,14 @@ static int memory_block_online(struct memory_block *mem)
|
|
nr_vmemmap_pages);
|
|
|
|
mem->zone = zone;
|
|
+out:
|
|
+ mem_hotplug_done();
|
|
return ret;
|
|
}
|
|
|
|
+/*
|
|
+ * Must acquire mem_hotplug_lock in write mode.
|
|
+ */
|
|
static int memory_block_offline(struct memory_block *mem)
|
|
{
|
|
unsigned long start_pfn = section_nr_to_pfn(mem->start_section_nr);
|
|
@@ -233,6 +242,7 @@ static int memory_block_offline(struct memory_block *mem)
|
|
* Unaccount before offlining, such that unpopulated zone and kthreads
|
|
* can properly be torn down in offline_pages().
|
|
*/
|
|
+ mem_hotplug_begin();
|
|
if (nr_vmemmap_pages)
|
|
adjust_present_page_count(pfn_to_page(start_pfn), mem->group,
|
|
-nr_vmemmap_pages);
|
|
@@ -244,13 +254,15 @@ static int memory_block_offline(struct memory_block *mem)
|
|
if (nr_vmemmap_pages)
|
|
adjust_present_page_count(pfn_to_page(start_pfn),
|
|
mem->group, nr_vmemmap_pages);
|
|
- return ret;
|
|
+ goto out;
|
|
}
|
|
|
|
if (nr_vmemmap_pages)
|
|
mhp_deinit_memmap_on_memory(start_pfn, nr_vmemmap_pages);
|
|
|
|
mem->zone = NULL;
|
|
+out:
|
|
+ mem_hotplug_done();
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/drivers/firewire/ohci.c b/drivers/firewire/ohci.c
|
|
index 17c9d825188bb..667ff40f39353 100644
|
|
--- a/drivers/firewire/ohci.c
|
|
+++ b/drivers/firewire/ohci.c
|
|
@@ -279,6 +279,51 @@ static char ohci_driver_name[] = KBUILD_MODNAME;
|
|
#define QUIRK_TI_SLLZ059 0x20
|
|
#define QUIRK_IR_WAKE 0x40
|
|
|
|
+// On PCI Express Root Complex in any type of AMD Ryzen machine, VIA VT6306/6307/6308 with Asmedia
|
|
+// ASM1083/1085 brings an inconvenience that the read accesses to 'Isochronous Cycle Timer' register
|
|
+// (at offset 0xf0 in PCI I/O space) often causes unexpected system reboot. The mechanism is not
|
|
+// clear, since the read access to the other registers is enough safe; e.g. 'Node ID' register,
|
|
+// while it is probable due to detection of any type of PCIe error.
|
|
+#define QUIRK_REBOOT_BY_CYCLE_TIMER_READ 0x80000000
|
|
+
|
|
+#if IS_ENABLED(CONFIG_X86)
|
|
+
|
|
+static bool has_reboot_by_cycle_timer_read_quirk(const struct fw_ohci *ohci)
|
|
+{
|
|
+ return !!(ohci->quirks & QUIRK_REBOOT_BY_CYCLE_TIMER_READ);
|
|
+}
|
|
+
|
|
+#define PCI_DEVICE_ID_ASMEDIA_ASM108X 0x1080
|
|
+
|
|
+static bool detect_vt630x_with_asm1083_on_amd_ryzen_machine(const struct pci_dev *pdev)
|
|
+{
|
|
+ const struct pci_dev *pcie_to_pci_bridge;
|
|
+
|
|
+ // Detect any type of AMD Ryzen machine.
|
|
+ if (!static_cpu_has(X86_FEATURE_ZEN))
|
|
+ return false;
|
|
+
|
|
+ // Detect VIA VT6306/6307/6308.
|
|
+ if (pdev->vendor != PCI_VENDOR_ID_VIA)
|
|
+ return false;
|
|
+ if (pdev->device != PCI_DEVICE_ID_VIA_VT630X)
|
|
+ return false;
|
|
+
|
|
+ // Detect Asmedia ASM1083/1085.
|
|
+ pcie_to_pci_bridge = pdev->bus->self;
|
|
+ if (pcie_to_pci_bridge->vendor != PCI_VENDOR_ID_ASMEDIA)
|
|
+ return false;
|
|
+ if (pcie_to_pci_bridge->device != PCI_DEVICE_ID_ASMEDIA_ASM108X)
|
|
+ return false;
|
|
+
|
|
+ return true;
|
|
+}
|
|
+
|
|
+#else
|
|
+#define has_reboot_by_cycle_timer_read_quirk(ohci) false
|
|
+#define detect_vt630x_with_asm1083_on_amd_ryzen_machine(pdev) false
|
|
+#endif
|
|
+
|
|
/* In case of multiple matches in ohci_quirks[], only the first one is used. */
|
|
static const struct {
|
|
unsigned short vendor, device, revision, flags;
|
|
@@ -1713,6 +1758,9 @@ static u32 get_cycle_time(struct fw_ohci *ohci)
|
|
s32 diff01, diff12;
|
|
int i;
|
|
|
|
+ if (has_reboot_by_cycle_timer_read_quirk(ohci))
|
|
+ return 0;
|
|
+
|
|
c2 = reg_read(ohci, OHCI1394_IsochronousCycleTimer);
|
|
|
|
if (ohci->quirks & QUIRK_CYCLE_TIMER) {
|
|
@@ -3615,6 +3663,9 @@ static int pci_probe(struct pci_dev *dev,
|
|
if (param_quirks)
|
|
ohci->quirks = param_quirks;
|
|
|
|
+ if (detect_vt630x_with_asm1083_on_amd_ryzen_machine(dev))
|
|
+ ohci->quirks |= QUIRK_REBOOT_BY_CYCLE_TIMER_READ;
|
|
+
|
|
/*
|
|
* Because dma_alloc_coherent() allocates at least one page,
|
|
* we save space by using a common buffer for the AR request/
|
|
diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c
|
|
index 431bda9165c3d..2775bcafe40f6 100644
|
|
--- a/drivers/firmware/arm_scmi/perf.c
|
|
+++ b/drivers/firmware/arm_scmi/perf.c
|
|
@@ -131,7 +131,7 @@ struct perf_dom_info {
|
|
u32 opp_count;
|
|
u32 sustained_freq_khz;
|
|
u32 sustained_perf_level;
|
|
- u32 mult_factor;
|
|
+ unsigned long mult_factor;
|
|
char name[SCMI_MAX_STR_SIZE];
|
|
struct scmi_opp opp[MAX_OPPS];
|
|
struct scmi_fc_info *fc_info;
|
|
@@ -223,8 +223,8 @@ scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
|
|
dom_info->mult_factor = 1000;
|
|
else
|
|
dom_info->mult_factor =
|
|
- (dom_info->sustained_freq_khz * 1000) /
|
|
- dom_info->sustained_perf_level;
|
|
+ (dom_info->sustained_freq_khz * 1000UL)
|
|
+ / dom_info->sustained_perf_level;
|
|
strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
|
|
}
|
|
|
|
diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
|
|
index 8a1b84aaaf717..a5352e5e2bd47 100644
|
|
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
|
|
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c
|
|
@@ -1976,15 +1976,8 @@ static int amdgpu_device_parse_gpu_info_fw(struct amdgpu_device *adev)
|
|
|
|
adev->firmware.gpu_info_fw = NULL;
|
|
|
|
- if (adev->mman.discovery_bin) {
|
|
- /*
|
|
- * FIXME: The bounding box is still needed by Navi12, so
|
|
- * temporarily read it from gpu_info firmware. Should be dropped
|
|
- * when DAL no longer needs it.
|
|
- */
|
|
- if (adev->asic_type != CHIP_NAVI12)
|
|
- return 0;
|
|
- }
|
|
+ if (adev->mman.discovery_bin)
|
|
+ return 0;
|
|
|
|
switch (adev->asic_type) {
|
|
default:
|
|
diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c
|
|
index 9d224bb2b3df6..ce893fe1c69f4 100644
|
|
--- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c
|
|
+++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/dcn20_fpu.c
|
|
@@ -438,7 +438,115 @@ struct _vcs_dpi_soc_bounding_box_st dcn2_0_nv14_soc = {
|
|
.use_urgent_burst_bw = 0
|
|
};
|
|
|
|
-struct _vcs_dpi_soc_bounding_box_st dcn2_0_nv12_soc = { 0 };
|
|
+struct _vcs_dpi_soc_bounding_box_st dcn2_0_nv12_soc = {
|
|
+ .clock_limits = {
|
|
+ {
|
|
+ .state = 0,
|
|
+ .dcfclk_mhz = 560.0,
|
|
+ .fabricclk_mhz = 560.0,
|
|
+ .dispclk_mhz = 513.0,
|
|
+ .dppclk_mhz = 513.0,
|
|
+ .phyclk_mhz = 540.0,
|
|
+ .socclk_mhz = 560.0,
|
|
+ .dscclk_mhz = 171.0,
|
|
+ .dram_speed_mts = 1069.0,
|
|
+ },
|
|
+ {
|
|
+ .state = 1,
|
|
+ .dcfclk_mhz = 694.0,
|
|
+ .fabricclk_mhz = 694.0,
|
|
+ .dispclk_mhz = 642.0,
|
|
+ .dppclk_mhz = 642.0,
|
|
+ .phyclk_mhz = 600.0,
|
|
+ .socclk_mhz = 694.0,
|
|
+ .dscclk_mhz = 214.0,
|
|
+ .dram_speed_mts = 1324.0,
|
|
+ },
|
|
+ {
|
|
+ .state = 2,
|
|
+ .dcfclk_mhz = 875.0,
|
|
+ .fabricclk_mhz = 875.0,
|
|
+ .dispclk_mhz = 734.0,
|
|
+ .dppclk_mhz = 734.0,
|
|
+ .phyclk_mhz = 810.0,
|
|
+ .socclk_mhz = 875.0,
|
|
+ .dscclk_mhz = 245.0,
|
|
+ .dram_speed_mts = 1670.0,
|
|
+ },
|
|
+ {
|
|
+ .state = 3,
|
|
+ .dcfclk_mhz = 1000.0,
|
|
+ .fabricclk_mhz = 1000.0,
|
|
+ .dispclk_mhz = 1100.0,
|
|
+ .dppclk_mhz = 1100.0,
|
|
+ .phyclk_mhz = 810.0,
|
|
+ .socclk_mhz = 1000.0,
|
|
+ .dscclk_mhz = 367.0,
|
|
+ .dram_speed_mts = 2000.0,
|
|
+ },
|
|
+ {
|
|
+ .state = 4,
|
|
+ .dcfclk_mhz = 1200.0,
|
|
+ .fabricclk_mhz = 1200.0,
|
|
+ .dispclk_mhz = 1284.0,
|
|
+ .dppclk_mhz = 1284.0,
|
|
+ .phyclk_mhz = 810.0,
|
|
+ .socclk_mhz = 1200.0,
|
|
+ .dscclk_mhz = 428.0,
|
|
+ .dram_speed_mts = 2000.0,
|
|
+ },
|
|
+ {
|
|
+ .state = 5,
|
|
+ .dcfclk_mhz = 1200.0,
|
|
+ .fabricclk_mhz = 1200.0,
|
|
+ .dispclk_mhz = 1284.0,
|
|
+ .dppclk_mhz = 1284.0,
|
|
+ .phyclk_mhz = 810.0,
|
|
+ .socclk_mhz = 1200.0,
|
|
+ .dscclk_mhz = 428.0,
|
|
+ .dram_speed_mts = 2000.0,
|
|
+ },
|
|
+ },
|
|
+
|
|
+ .num_states = 5,
|
|
+ .sr_exit_time_us = 1.9,
|
|
+ .sr_enter_plus_exit_time_us = 4.4,
|
|
+ .urgent_latency_us = 3.0,
|
|
+ .urgent_latency_pixel_data_only_us = 4.0,
|
|
+ .urgent_latency_pixel_mixed_with_vm_data_us = 4.0,
|
|
+ .urgent_latency_vm_data_only_us = 4.0,
|
|
+ .urgent_out_of_order_return_per_channel_pixel_only_bytes = 4096,
|
|
+ .urgent_out_of_order_return_per_channel_pixel_and_vm_bytes = 4096,
|
|
+ .urgent_out_of_order_return_per_channel_vm_only_bytes = 4096,
|
|
+ .pct_ideal_dram_sdp_bw_after_urgent_pixel_only = 40.0,
|
|
+ .pct_ideal_dram_sdp_bw_after_urgent_pixel_and_vm = 40.0,
|
|
+ .pct_ideal_dram_sdp_bw_after_urgent_vm_only = 40.0,
|
|
+ .max_avg_sdp_bw_use_normal_percent = 40.0,
|
|
+ .max_avg_dram_bw_use_normal_percent = 40.0,
|
|
+ .writeback_latency_us = 12.0,
|
|
+ .ideal_dram_bw_after_urgent_percent = 40.0,
|
|
+ .max_request_size_bytes = 256,
|
|
+ .dram_channel_width_bytes = 16,
|
|
+ .fabric_datapath_to_dcn_data_return_bytes = 64,
|
|
+ .dcn_downspread_percent = 0.5,
|
|
+ .downspread_percent = 0.5,
|
|
+ .dram_page_open_time_ns = 50.0,
|
|
+ .dram_rw_turnaround_time_ns = 17.5,
|
|
+ .dram_return_buffer_per_channel_bytes = 8192,
|
|
+ .round_trip_ping_latency_dcfclk_cycles = 131,
|
|
+ .urgent_out_of_order_return_per_channel_bytes = 4096,
|
|
+ .channel_interleave_bytes = 256,
|
|
+ .num_banks = 8,
|
|
+ .num_chans = 16,
|
|
+ .vmm_page_size_bytes = 4096,
|
|
+ .dram_clock_change_latency_us = 45.0,
|
|
+ .writeback_dram_clock_change_latency_us = 23.0,
|
|
+ .return_bus_width_bytes = 64,
|
|
+ .dispclk_dppclk_vco_speed_mhz = 3850,
|
|
+ .xfc_bus_transport_time_us = 20,
|
|
+ .xfc_xbuf_latency_tolerance_us = 50,
|
|
+ .use_urgent_burst_bw = 0,
|
|
+};
|
|
|
|
struct _vcs_dpi_ip_params_st dcn2_1_ip = {
|
|
.odm_capable = 1,
|
|
diff --git a/drivers/gpu/drm/bridge/ti-sn65dsi86.c b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
|
|
index 1b5c27ed27370..ff4d0564122a3 100644
|
|
--- a/drivers/gpu/drm/bridge/ti-sn65dsi86.c
|
|
+++ b/drivers/gpu/drm/bridge/ti-sn65dsi86.c
|
|
@@ -527,6 +527,7 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,
|
|
u32 request_val = AUX_CMD_REQ(msg->request);
|
|
u8 *buf = msg->buffer;
|
|
unsigned int len = msg->size;
|
|
+ unsigned int short_len;
|
|
unsigned int val;
|
|
int ret;
|
|
u8 addr_len[SN_AUX_LENGTH_REG + 1 - SN_AUX_ADDR_19_16_REG];
|
|
@@ -600,7 +601,8 @@ static ssize_t ti_sn_aux_transfer(struct drm_dp_aux *aux,
|
|
}
|
|
|
|
if (val & AUX_IRQ_STATUS_AUX_SHORT) {
|
|
- ret = regmap_read(pdata->regmap, SN_AUX_LENGTH_REG, &len);
|
|
+ ret = regmap_read(pdata->regmap, SN_AUX_LENGTH_REG, &short_len);
|
|
+ len = min(len, short_len);
|
|
if (ret)
|
|
goto exit;
|
|
} else if (val & AUX_IRQ_STATUS_NAT_I2C_FAIL) {
|
|
diff --git a/drivers/gpu/drm/i915/display/intel_dp.c b/drivers/gpu/drm/i915/display/intel_dp.c
|
|
index 5970f4149090f..4699c21102261 100644
|
|
--- a/drivers/gpu/drm/i915/display/intel_dp.c
|
|
+++ b/drivers/gpu/drm/i915/display/intel_dp.c
|
|
@@ -3707,7 +3707,7 @@ static void intel_dp_process_phy_request(struct intel_dp *intel_dp,
|
|
intel_dp->train_set, crtc_state->lane_count);
|
|
|
|
drm_dp_set_phy_test_pattern(&intel_dp->aux, data,
|
|
- link_status[DP_DPCD_REV]);
|
|
+ intel_dp->dpcd[DP_DPCD_REV]);
|
|
}
|
|
|
|
static u8 intel_dp_autotest_phy_pattern(struct intel_dp *intel_dp)
|
|
diff --git a/drivers/gpu/drm/mgag200/mgag200_drv.h b/drivers/gpu/drm/mgag200/mgag200_drv.h
|
|
index f0c2349404b46..aebd09e2d4087 100644
|
|
--- a/drivers/gpu/drm/mgag200/mgag200_drv.h
|
|
+++ b/drivers/gpu/drm/mgag200/mgag200_drv.h
|
|
@@ -390,6 +390,11 @@ void mgag200_primary_plane_helper_atomic_disable(struct drm_plane *plane,
|
|
.destroy = drm_plane_cleanup, \
|
|
DRM_GEM_SHADOW_PLANE_FUNCS
|
|
|
|
+void mgag200_crtc_set_gamma_linear(struct mga_device *mdev, const struct drm_format_info *format);
|
|
+void mgag200_crtc_set_gamma(struct mga_device *mdev,
|
|
+ const struct drm_format_info *format,
|
|
+ struct drm_color_lut *lut);
|
|
+
|
|
enum drm_mode_status mgag200_crtc_helper_mode_valid(struct drm_crtc *crtc,
|
|
const struct drm_display_mode *mode);
|
|
int mgag200_crtc_helper_atomic_check(struct drm_crtc *crtc, struct drm_atomic_state *new_state);
|
|
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200er.c b/drivers/gpu/drm/mgag200/mgag200_g200er.c
|
|
index bce267e0f7de3..8d4538b710477 100644
|
|
--- a/drivers/gpu/drm/mgag200/mgag200_g200er.c
|
|
+++ b/drivers/gpu/drm/mgag200/mgag200_g200er.c
|
|
@@ -202,6 +202,11 @@ static void mgag200_g200er_crtc_helper_atomic_enable(struct drm_crtc *crtc,
|
|
|
|
mgag200_g200er_reset_tagfifo(mdev);
|
|
|
|
+ if (crtc_state->gamma_lut)
|
|
+ mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data);
|
|
+ else
|
|
+ mgag200_crtc_set_gamma_linear(mdev, format);
|
|
+
|
|
mgag200_enable_display(mdev);
|
|
|
|
if (funcs->enable_vidrst)
|
|
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200ev.c b/drivers/gpu/drm/mgag200/mgag200_g200ev.c
|
|
index ac957f42abe18..56e6f986bff31 100644
|
|
--- a/drivers/gpu/drm/mgag200/mgag200_g200ev.c
|
|
+++ b/drivers/gpu/drm/mgag200/mgag200_g200ev.c
|
|
@@ -203,6 +203,11 @@ static void mgag200_g200ev_crtc_helper_atomic_enable(struct drm_crtc *crtc,
|
|
|
|
mgag200_g200ev_set_hiprilvl(mdev);
|
|
|
|
+ if (crtc_state->gamma_lut)
|
|
+ mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data);
|
|
+ else
|
|
+ mgag200_crtc_set_gamma_linear(mdev, format);
|
|
+
|
|
mgag200_enable_display(mdev);
|
|
|
|
if (funcs->enable_vidrst)
|
|
diff --git a/drivers/gpu/drm/mgag200/mgag200_g200se.c b/drivers/gpu/drm/mgag200/mgag200_g200se.c
|
|
index bd6e573c9a1a3..ff2b3c6622e7a 100644
|
|
--- a/drivers/gpu/drm/mgag200/mgag200_g200se.c
|
|
+++ b/drivers/gpu/drm/mgag200/mgag200_g200se.c
|
|
@@ -334,6 +334,11 @@ static void mgag200_g200se_crtc_helper_atomic_enable(struct drm_crtc *crtc,
|
|
|
|
mgag200_g200se_set_hiprilvl(mdev, adjusted_mode, format);
|
|
|
|
+ if (crtc_state->gamma_lut)
|
|
+ mgag200_crtc_set_gamma(mdev, format, crtc_state->gamma_lut->data);
|
|
+ else
|
|
+ mgag200_crtc_set_gamma_linear(mdev, format);
|
|
+
|
|
mgag200_enable_display(mdev);
|
|
|
|
if (funcs->enable_vidrst)
|
|
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
|
|
index ae90b260312a5..554adf05e0734 100644
|
|
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
|
|
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
|
|
@@ -28,8 +28,8 @@
|
|
* This file contains setup code for the CRTC.
|
|
*/
|
|
|
|
-static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev,
|
|
- const struct drm_format_info *format)
|
|
+void mgag200_crtc_set_gamma_linear(struct mga_device *mdev,
|
|
+ const struct drm_format_info *format)
|
|
{
|
|
int i;
|
|
|
|
@@ -65,9 +65,9 @@ static void mgag200_crtc_set_gamma_linear(struct mga_device *mdev,
|
|
}
|
|
}
|
|
|
|
-static void mgag200_crtc_set_gamma(struct mga_device *mdev,
|
|
- const struct drm_format_info *format,
|
|
- struct drm_color_lut *lut)
|
|
+void mgag200_crtc_set_gamma(struct mga_device *mdev,
|
|
+ const struct drm_format_info *format,
|
|
+ struct drm_color_lut *lut)
|
|
{
|
|
int i;
|
|
|
|
diff --git a/drivers/i2c/i2c-core.h b/drivers/i2c/i2c-core.h
|
|
index 05b8b8dfa9bdd..36587f38dff3d 100644
|
|
--- a/drivers/i2c/i2c-core.h
|
|
+++ b/drivers/i2c/i2c-core.h
|
|
@@ -3,6 +3,7 @@
|
|
* i2c-core.h - interfaces internal to the I2C framework
|
|
*/
|
|
|
|
+#include <linux/kconfig.h>
|
|
#include <linux/rwsem.h>
|
|
|
|
struct i2c_devinfo {
|
|
@@ -29,7 +30,8 @@ int i2c_dev_irq_from_resources(const struct resource *resources,
|
|
*/
|
|
static inline bool i2c_in_atomic_xfer_mode(void)
|
|
{
|
|
- return system_state > SYSTEM_RUNNING && !preemptible();
|
|
+ return system_state > SYSTEM_RUNNING &&
|
|
+ (IS_ENABLED(CONFIG_PREEMPT_COUNT) ? !preemptible() : irqs_disabled());
|
|
}
|
|
|
|
static inline int __i2c_lock_bus_helper(struct i2c_adapter *adap)
|
|
diff --git a/drivers/interconnect/qcom/sm8250.c b/drivers/interconnect/qcom/sm8250.c
|
|
index 9c2dd40d9a559..5cdb058fa0959 100644
|
|
--- a/drivers/interconnect/qcom/sm8250.c
|
|
+++ b/drivers/interconnect/qcom/sm8250.c
|
|
@@ -551,7 +551,6 @@ static struct platform_driver qnoc_driver = {
|
|
.driver = {
|
|
.name = "qnoc-sm8250",
|
|
.of_match_table = qnoc_of_match,
|
|
- .sync_state = icc_sync_state,
|
|
},
|
|
};
|
|
module_platform_driver(qnoc_driver);
|
|
diff --git a/drivers/media/platform/qcom/camss/camss-csid-gen2.c b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
|
|
index 904208f6f9546..0147cc062e1ae 100644
|
|
--- a/drivers/media/platform/qcom/camss/camss-csid-gen2.c
|
|
+++ b/drivers/media/platform/qcom/camss/camss-csid-gen2.c
|
|
@@ -334,13 +334,14 @@ static const struct csid_format csid_formats[] = {
|
|
},
|
|
};
|
|
|
|
-static void csid_configure_stream(struct csid_device *csid, u8 enable)
|
|
+static void __csid_configure_stream(struct csid_device *csid, u8 enable, u8 vc)
|
|
{
|
|
struct csid_testgen_config *tg = &csid->testgen;
|
|
u32 val;
|
|
u32 phy_sel = 0;
|
|
u8 lane_cnt = csid->phy.lane_cnt;
|
|
- struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_SRC];
|
|
+ /* Source pads matching RDI channels on hardware. Pad 1 -> RDI0, Pad 2 -> RDI1, etc. */
|
|
+ struct v4l2_mbus_framefmt *input_format = &csid->fmt[MSM_CSID_PAD_FIRST_SRC + vc];
|
|
const struct csid_format *format = csid_get_fmt_entry(csid->formats, csid->nformats,
|
|
input_format->code);
|
|
|
|
@@ -351,8 +352,19 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
|
|
phy_sel = csid->phy.csiphy_id;
|
|
|
|
if (enable) {
|
|
- u8 vc = 0; /* Virtual Channel 0 */
|
|
- u8 dt_id = vc * 4;
|
|
+ /*
|
|
+ * DT_ID is a two bit bitfield that is concatenated with
|
|
+ * the four least significant bits of the five bit VC
|
|
+ * bitfield to generate an internal CID value.
|
|
+ *
|
|
+ * CSID_RDI_CFG0(vc)
|
|
+ * DT_ID : 28:27
|
|
+ * VC : 26:22
|
|
+ * DT : 21:16
|
|
+ *
|
|
+ * CID : VC 3:0 << 2 | DT_ID 1:0
|
|
+ */
|
|
+ u8 dt_id = vc & 0x03;
|
|
|
|
if (tg->enabled) {
|
|
/* configure one DT, infinite frames */
|
|
@@ -392,42 +404,42 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
|
|
val |= format->data_type << RDI_CFG0_DATA_TYPE;
|
|
val |= vc << RDI_CFG0_VIRTUAL_CHANNEL;
|
|
val |= dt_id << RDI_CFG0_DT_ID;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_CFG0(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
|
|
|
|
/* CSID_TIMESTAMP_STB_POST_IRQ */
|
|
val = 2 << RDI_CFG1_TIMESTAMP_STB_SEL;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_CFG1(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_CFG1(vc));
|
|
|
|
val = 1;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PERIOD(vc));
|
|
|
|
val = 0;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_FRM_DROP_PATTERN(vc));
|
|
|
|
val = 1;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PERIOD(vc));
|
|
|
|
val = 0;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_IRQ_SUBSAMPLE_PATTERN(vc));
|
|
|
|
val = 1;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PERIOD(vc));
|
|
|
|
val = 0;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_PIX_DROP_PATTERN(vc));
|
|
|
|
val = 1;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PERIOD(vc));
|
|
|
|
val = 0;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_RPP_LINE_DROP_PATTERN(vc));
|
|
|
|
val = 0;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_CTRL(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc));
|
|
|
|
- val = readl_relaxed(csid->base + CSID_RDI_CFG0(0));
|
|
+ val = readl_relaxed(csid->base + CSID_RDI_CFG0(vc));
|
|
val |= 1 << RDI_CFG0_ENABLE;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_CFG0(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_CFG0(vc));
|
|
}
|
|
|
|
if (tg->enabled) {
|
|
@@ -446,6 +458,8 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
|
|
writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG0);
|
|
|
|
val = 1 << CSI2_RX_CFG1_PACKET_ECC_CORRECTION_EN;
|
|
+ if (vc > 3)
|
|
+ val |= 1 << CSI2_RX_CFG1_VC_MODE;
|
|
val |= 1 << CSI2_RX_CFG1_MISR_EN;
|
|
writel_relaxed(val, csid->base + CSID_CSI2_RX_CFG1);
|
|
|
|
@@ -453,7 +467,16 @@ static void csid_configure_stream(struct csid_device *csid, u8 enable)
|
|
val = HALT_CMD_RESUME_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD;
|
|
else
|
|
val = HALT_CMD_HALT_AT_FRAME_BOUNDARY << RDI_CTRL_HALT_CMD;
|
|
- writel_relaxed(val, csid->base + CSID_RDI_CTRL(0));
|
|
+ writel_relaxed(val, csid->base + CSID_RDI_CTRL(vc));
|
|
+}
|
|
+
|
|
+static void csid_configure_stream(struct csid_device *csid, u8 enable)
|
|
+{
|
|
+ u8 i;
|
|
+ /* Loop through all enabled VCs and configure stream for each */
|
|
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
|
|
+ if (csid->phy.en_vc & BIT(i))
|
|
+ __csid_configure_stream(csid, enable, i);
|
|
}
|
|
|
|
static int csid_configure_testgen_pattern(struct csid_device *csid, s32 val)
|
|
@@ -499,6 +522,7 @@ static irqreturn_t csid_isr(int irq, void *dev)
|
|
struct csid_device *csid = dev;
|
|
u32 val;
|
|
u8 reset_done;
|
|
+ int i;
|
|
|
|
val = readl_relaxed(csid->base + CSID_TOP_IRQ_STATUS);
|
|
writel_relaxed(val, csid->base + CSID_TOP_IRQ_CLEAR);
|
|
@@ -507,8 +531,12 @@ static irqreturn_t csid_isr(int irq, void *dev)
|
|
val = readl_relaxed(csid->base + CSID_CSI2_RX_IRQ_STATUS);
|
|
writel_relaxed(val, csid->base + CSID_CSI2_RX_IRQ_CLEAR);
|
|
|
|
- val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(0));
|
|
- writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(0));
|
|
+ /* Read and clear IRQ status for each enabled RDI channel */
|
|
+ for (i = 0; i < MSM_CSID_MAX_SRC_STREAMS; i++)
|
|
+ if (csid->phy.en_vc & BIT(i)) {
|
|
+ val = readl_relaxed(csid->base + CSID_CSI2_RDIN_IRQ_STATUS(i));
|
|
+ writel_relaxed(val, csid->base + CSID_CSI2_RDIN_IRQ_CLEAR(i));
|
|
+ }
|
|
|
|
val = 1 << IRQ_CMD_CLEAR;
|
|
writel_relaxed(val, csid->base + CSID_IRQ_CMD);
|
|
diff --git a/drivers/media/platform/qcom/camss/camss-csid.c b/drivers/media/platform/qcom/camss/camss-csid.c
|
|
index 88f188e0f7501..6360314f04a63 100644
|
|
--- a/drivers/media/platform/qcom/camss/camss-csid.c
|
|
+++ b/drivers/media/platform/qcom/camss/camss-csid.c
|
|
@@ -196,6 +196,8 @@ static int csid_set_power(struct v4l2_subdev *sd, int on)
|
|
return ret;
|
|
}
|
|
|
|
+ csid->phy.need_vc_update = true;
|
|
+
|
|
enable_irq(csid->irq);
|
|
|
|
ret = csid->ops->reset(csid);
|
|
@@ -249,7 +251,10 @@ static int csid_set_stream(struct v4l2_subdev *sd, int enable)
|
|
return -ENOLINK;
|
|
}
|
|
|
|
- csid->ops->configure_stream(csid, enable);
|
|
+ if (csid->phy.need_vc_update) {
|
|
+ csid->ops->configure_stream(csid, enable);
|
|
+ csid->phy.need_vc_update = false;
|
|
+ }
|
|
|
|
return 0;
|
|
}
|
|
@@ -460,6 +465,7 @@ static int csid_set_format(struct v4l2_subdev *sd,
|
|
{
|
|
struct csid_device *csid = v4l2_get_subdevdata(sd);
|
|
struct v4l2_mbus_framefmt *format;
|
|
+ int i;
|
|
|
|
format = __csid_get_format(csid, sd_state, fmt->pad, fmt->which);
|
|
if (format == NULL)
|
|
@@ -468,14 +474,14 @@ static int csid_set_format(struct v4l2_subdev *sd,
|
|
csid_try_format(csid, sd_state, fmt->pad, &fmt->format, fmt->which);
|
|
*format = fmt->format;
|
|
|
|
- /* Propagate the format from sink to source */
|
|
+ /* Propagate the format from sink to source pads */
|
|
if (fmt->pad == MSM_CSID_PAD_SINK) {
|
|
- format = __csid_get_format(csid, sd_state, MSM_CSID_PAD_SRC,
|
|
- fmt->which);
|
|
+ for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i) {
|
|
+ format = __csid_get_format(csid, sd_state, i, fmt->which);
|
|
|
|
- *format = fmt->format;
|
|
- csid_try_format(csid, sd_state, MSM_CSID_PAD_SRC, format,
|
|
- fmt->which);
|
|
+ *format = fmt->format;
|
|
+ csid_try_format(csid, sd_state, i, format, fmt->which);
|
|
+ }
|
|
}
|
|
|
|
return 0;
|
|
@@ -738,7 +744,6 @@ static int csid_link_setup(struct media_entity *entity,
|
|
struct csid_device *csid;
|
|
struct csiphy_device *csiphy;
|
|
struct csiphy_lanes_cfg *lane_cfg;
|
|
- struct v4l2_subdev_format format = { 0 };
|
|
|
|
sd = media_entity_to_v4l2_subdev(entity);
|
|
csid = v4l2_get_subdevdata(sd);
|
|
@@ -761,11 +766,22 @@ static int csid_link_setup(struct media_entity *entity,
|
|
lane_cfg = &csiphy->cfg.csi2->lane_cfg;
|
|
csid->phy.lane_cnt = lane_cfg->num_data;
|
|
csid->phy.lane_assign = csid_get_lane_assign(lane_cfg);
|
|
+ }
|
|
+ /* Decide which virtual channels to enable based on which source pads are enabled */
|
|
+ if (local->flags & MEDIA_PAD_FL_SOURCE) {
|
|
+ struct v4l2_subdev *sd = media_entity_to_v4l2_subdev(entity);
|
|
+ struct csid_device *csid = v4l2_get_subdevdata(sd);
|
|
+ struct device *dev = csid->camss->dev;
|
|
+
|
|
+ if (flags & MEDIA_LNK_FL_ENABLED)
|
|
+ csid->phy.en_vc |= BIT(local->index - 1);
|
|
+ else
|
|
+ csid->phy.en_vc &= ~BIT(local->index - 1);
|
|
|
|
- /* Reset format on source pad to sink pad format */
|
|
- format.pad = MSM_CSID_PAD_SRC;
|
|
- format.which = V4L2_SUBDEV_FORMAT_ACTIVE;
|
|
- csid_set_format(&csid->subdev, NULL, &format);
|
|
+ csid->phy.need_vc_update = true;
|
|
+
|
|
+ dev_dbg(dev, "%s: Enabled CSID virtual channels mask 0x%x\n",
|
|
+ __func__, csid->phy.en_vc);
|
|
}
|
|
|
|
return 0;
|
|
@@ -816,6 +832,7 @@ int msm_csid_register_entity(struct csid_device *csid,
|
|
struct v4l2_subdev *sd = &csid->subdev;
|
|
struct media_pad *pads = csid->pads;
|
|
struct device *dev = csid->camss->dev;
|
|
+ int i;
|
|
int ret;
|
|
|
|
v4l2_subdev_init(sd, &csid_v4l2_ops);
|
|
@@ -852,7 +869,8 @@ int msm_csid_register_entity(struct csid_device *csid,
|
|
}
|
|
|
|
pads[MSM_CSID_PAD_SINK].flags = MEDIA_PAD_FL_SINK;
|
|
- pads[MSM_CSID_PAD_SRC].flags = MEDIA_PAD_FL_SOURCE;
|
|
+ for (i = MSM_CSID_PAD_FIRST_SRC; i < MSM_CSID_PADS_NUM; ++i)
|
|
+ pads[i].flags = MEDIA_PAD_FL_SOURCE;
|
|
|
|
sd->entity.function = MEDIA_ENT_F_PROC_VIDEO_PIXEL_FORMATTER;
|
|
sd->entity.ops = &csid_media_ops;
|
|
diff --git a/drivers/media/platform/qcom/camss/camss-csid.h b/drivers/media/platform/qcom/camss/camss-csid.h
|
|
index f06040e44c515..d4b48432a0973 100644
|
|
--- a/drivers/media/platform/qcom/camss/camss-csid.h
|
|
+++ b/drivers/media/platform/qcom/camss/camss-csid.h
|
|
@@ -19,8 +19,13 @@
|
|
#include <media/v4l2-subdev.h>
|
|
|
|
#define MSM_CSID_PAD_SINK 0
|
|
-#define MSM_CSID_PAD_SRC 1
|
|
-#define MSM_CSID_PADS_NUM 2
|
|
+#define MSM_CSID_PAD_FIRST_SRC 1
|
|
+#define MSM_CSID_PADS_NUM 5
|
|
+
|
|
+#define MSM_CSID_PAD_SRC (MSM_CSID_PAD_FIRST_SRC)
|
|
+
|
|
+/* CSID hardware can demultiplex up to 4 outputs */
|
|
+#define MSM_CSID_MAX_SRC_STREAMS 4
|
|
|
|
#define DATA_TYPE_EMBEDDED_DATA_8BIT 0x12
|
|
#define DATA_TYPE_YUV420_8BIT 0x18
|
|
@@ -81,6 +86,8 @@ struct csid_phy_config {
|
|
u8 csiphy_id;
|
|
u8 lane_cnt;
|
|
u32 lane_assign;
|
|
+ u32 en_vc;
|
|
+ u8 need_vc_update;
|
|
};
|
|
|
|
struct csid_device;
|
|
diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c
|
|
index 770490234c872..e9ce53d200bc1 100644
|
|
--- a/drivers/mmc/core/block.c
|
|
+++ b/drivers/mmc/core/block.c
|
|
@@ -866,9 +866,10 @@ static const struct block_device_operations mmc_bdops = {
|
|
static int mmc_blk_part_switch_pre(struct mmc_card *card,
|
|
unsigned int part_type)
|
|
{
|
|
+ const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_RPMB;
|
|
int ret = 0;
|
|
|
|
- if (part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
|
|
+ if ((part_type & mask) == mask) {
|
|
if (card->ext_csd.cmdq_en) {
|
|
ret = mmc_cmdq_disable(card);
|
|
if (ret)
|
|
@@ -883,9 +884,10 @@ static int mmc_blk_part_switch_pre(struct mmc_card *card,
|
|
static int mmc_blk_part_switch_post(struct mmc_card *card,
|
|
unsigned int part_type)
|
|
{
|
|
+ const unsigned int mask = EXT_CSD_PART_CONFIG_ACC_RPMB;
|
|
int ret = 0;
|
|
|
|
- if (part_type == EXT_CSD_PART_CONFIG_ACC_RPMB) {
|
|
+ if ((part_type & mask) == mask) {
|
|
mmc_retune_unpause(card->host);
|
|
if (card->reenable_cmdq && !card->ext_csd.cmdq_en)
|
|
ret = mmc_cmdq_enable(card);
|
|
@@ -3180,4 +3182,3 @@ module_exit(mmc_blk_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_DESCRIPTION("Multimedia Card (MMC) block device driver");
|
|
-
|
|
diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c
|
|
index b89dca1f15e9c..25c152ef5d60e 100644
|
|
--- a/drivers/mmc/core/host.c
|
|
+++ b/drivers/mmc/core/host.c
|
|
@@ -670,6 +670,7 @@ EXPORT_SYMBOL(mmc_remove_host);
|
|
*/
|
|
void mmc_free_host(struct mmc_host *host)
|
|
{
|
|
+ cancel_delayed_work_sync(&host->detect);
|
|
mmc_pwrseq_free(host);
|
|
put_device(&host->class_dev);
|
|
}
|
|
diff --git a/drivers/mmc/host/meson-mx-sdhc-mmc.c b/drivers/mmc/host/meson-mx-sdhc-mmc.c
|
|
index da85c2f2acb83..c0e3b1634a88a 100644
|
|
--- a/drivers/mmc/host/meson-mx-sdhc-mmc.c
|
|
+++ b/drivers/mmc/host/meson-mx-sdhc-mmc.c
|
|
@@ -269,7 +269,7 @@ static int meson_mx_sdhc_enable_clks(struct mmc_host *mmc)
|
|
static int meson_mx_sdhc_set_clk(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
{
|
|
struct meson_mx_sdhc_host *host = mmc_priv(mmc);
|
|
- u32 rx_clk_phase;
|
|
+ u32 val, rx_clk_phase;
|
|
int ret;
|
|
|
|
meson_mx_sdhc_disable_clks(mmc);
|
|
@@ -290,27 +290,11 @@ static int meson_mx_sdhc_set_clk(struct mmc_host *mmc, struct mmc_ios *ios)
|
|
mmc->actual_clock = clk_get_rate(host->sd_clk);
|
|
|
|
/*
|
|
- * according to Amlogic the following latching points are
|
|
- * selected with empirical values, there is no (known) formula
|
|
- * to calculate these.
|
|
+ * Phase 90 should work in most cases. For data transmission,
|
|
+ * meson_mx_sdhc_execute_tuning() will find a accurate value
|
|
*/
|
|
- if (mmc->actual_clock > 100000000) {
|
|
- rx_clk_phase = 1;
|
|
- } else if (mmc->actual_clock > 45000000) {
|
|
- if (ios->signal_voltage == MMC_SIGNAL_VOLTAGE_330)
|
|
- rx_clk_phase = 15;
|
|
- else
|
|
- rx_clk_phase = 11;
|
|
- } else if (mmc->actual_clock >= 25000000) {
|
|
- rx_clk_phase = 15;
|
|
- } else if (mmc->actual_clock > 5000000) {
|
|
- rx_clk_phase = 23;
|
|
- } else if (mmc->actual_clock > 1000000) {
|
|
- rx_clk_phase = 55;
|
|
- } else {
|
|
- rx_clk_phase = 1061;
|
|
- }
|
|
-
|
|
+ regmap_read(host->regmap, MESON_SDHC_CLKC, &val);
|
|
+ rx_clk_phase = FIELD_GET(MESON_SDHC_CLKC_CLK_DIV, val) / 4;
|
|
regmap_update_bits(host->regmap, MESON_SDHC_CLK2,
|
|
MESON_SDHC_CLK2_RX_CLK_PHASE,
|
|
FIELD_PREP(MESON_SDHC_CLK2_RX_CLK_PHASE,
|
|
diff --git a/drivers/mmc/host/sdhci-sprd.c b/drivers/mmc/host/sdhci-sprd.c
|
|
index 2101b6e794c0e..66c1782823d89 100644
|
|
--- a/drivers/mmc/host/sdhci-sprd.c
|
|
+++ b/drivers/mmc/host/sdhci-sprd.c
|
|
@@ -228,15 +228,19 @@ static inline void _sdhci_sprd_set_clock(struct sdhci_host *host,
|
|
div = ((div & 0x300) >> 2) | ((div & 0xFF) << 8);
|
|
sdhci_enable_clk(host, div);
|
|
|
|
+ val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI);
|
|
+ mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN | SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN;
|
|
/* Enable CLK_AUTO when the clock is greater than 400K. */
|
|
if (clk > 400000) {
|
|
- val = sdhci_readl(host, SDHCI_SPRD_REG_32_BUSY_POSI);
|
|
- mask = SDHCI_SPRD_BIT_OUTR_CLK_AUTO_EN |
|
|
- SDHCI_SPRD_BIT_INNR_CLK_AUTO_EN;
|
|
if (mask != (val & mask)) {
|
|
val |= mask;
|
|
sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
|
|
}
|
|
+ } else {
|
|
+ if (val & mask) {
|
|
+ val &= ~mask;
|
|
+ sdhci_writel(host, val, SDHCI_SPRD_REG_32_BUSY_POSI);
|
|
+ }
|
|
}
|
|
}
|
|
|
|
diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
|
|
index 623cdeb29ed90..df4d88d35701b 100644
|
|
--- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c
|
|
+++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c
|
|
@@ -12081,6 +12081,8 @@ static void bnxt_sp_task(struct work_struct *work)
|
|
bnxt_cfg_ntp_filters(bp);
|
|
if (test_and_clear_bit(BNXT_HWRM_EXEC_FWD_REQ_SP_EVENT, &bp->sp_event))
|
|
bnxt_hwrm_exec_fwd_req(bp);
|
|
+ if (test_and_clear_bit(BNXT_HWRM_PF_UNLOAD_SP_EVENT, &bp->sp_event))
|
|
+ netdev_info(bp->dev, "Receive PF driver unload event!\n");
|
|
if (test_and_clear_bit(BNXT_PERIODIC_STATS_SP_EVENT, &bp->sp_event)) {
|
|
bnxt_hwrm_port_qstats(bp, 0);
|
|
bnxt_hwrm_port_qstats_ext(bp, 0);
|
|
@@ -13059,8 +13061,6 @@ static void bnxt_cfg_ntp_filters(struct bnxt *bp)
|
|
}
|
|
}
|
|
}
|
|
- if (test_and_clear_bit(BNXT_HWRM_PF_UNLOAD_SP_EVENT, &bp->sp_event))
|
|
- netdev_info(bp->dev, "Receive PF driver unload event!\n");
|
|
}
|
|
|
|
#else
|
|
diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
|
|
index 1ae082eb9e905..c2a9913082153 100644
|
|
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
|
|
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
|
|
@@ -2131,8 +2131,10 @@ static netdev_tx_t bcmgenet_xmit(struct sk_buff *skb, struct net_device *dev)
|
|
/* Note: if we ever change from DMA_TX_APPEND_CRC below we
|
|
* will need to restore software padding of "runt" packets
|
|
*/
|
|
+ len_stat |= DMA_TX_APPEND_CRC;
|
|
+
|
|
if (!i) {
|
|
- len_stat |= DMA_TX_APPEND_CRC | DMA_SOP;
|
|
+ len_stat |= DMA_SOP;
|
|
if (skb->ip_summed == CHECKSUM_PARTIAL)
|
|
len_stat |= DMA_TX_DO_CSUM;
|
|
}
|
|
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
|
index b58162ce81d87..de62eee58a00e 100644
|
|
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
|
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-eth.c
|
|
@@ -509,8 +509,6 @@ static struct sk_buff *dpaa2_eth_copybreak(struct dpaa2_eth_channel *ch,
|
|
|
|
memcpy(skb->data, fd_vaddr + fd_offset, fd_length);
|
|
|
|
- dpaa2_eth_recycle_buf(priv, ch, dpaa2_fd_get_addr(fd));
|
|
-
|
|
return skb;
|
|
}
|
|
|
|
@@ -528,6 +526,7 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
|
|
struct dpaa2_eth_drv_stats *percpu_extras;
|
|
struct device *dev = priv->net_dev->dev.parent;
|
|
struct dpaa2_fas *fas;
|
|
+ bool recycle_rx_buf = false;
|
|
void *buf_data;
|
|
u32 status = 0;
|
|
u32 xdp_act;
|
|
@@ -560,6 +559,8 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
|
|
dma_unmap_page(dev, addr, priv->rx_buf_size,
|
|
DMA_BIDIRECTIONAL);
|
|
skb = dpaa2_eth_build_linear_skb(ch, fd, vaddr);
|
|
+ } else {
|
|
+ recycle_rx_buf = true;
|
|
}
|
|
} else if (fd_format == dpaa2_fd_sg) {
|
|
WARN_ON(priv->xdp_prog);
|
|
@@ -607,6 +608,8 @@ static void dpaa2_eth_rx(struct dpaa2_eth_priv *priv,
|
|
|
|
list_add_tail(&skb->list, ch->rx_list);
|
|
|
|
+ if (recycle_rx_buf)
|
|
+ dpaa2_eth_recycle_buf(priv, ch, dpaa2_fd_get_addr(fd));
|
|
return;
|
|
|
|
err_build_skb:
|
|
diff --git a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
|
index eea7d7a07c007..59888826469b9 100644
|
|
--- a/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
|
+++ b/drivers/net/ethernet/freescale/dpaa2/dpaa2-ethtool.c
|
|
@@ -227,17 +227,8 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
|
|
struct ethtool_stats *stats,
|
|
u64 *data)
|
|
{
|
|
- int i = 0;
|
|
- int j, k, err;
|
|
- int num_cnt;
|
|
- union dpni_statistics dpni_stats;
|
|
- u32 fcnt, bcnt;
|
|
- u32 fcnt_rx_total = 0, fcnt_tx_total = 0;
|
|
- u32 bcnt_rx_total = 0, bcnt_tx_total = 0;
|
|
- u32 buf_cnt;
|
|
struct dpaa2_eth_priv *priv = netdev_priv(net_dev);
|
|
- struct dpaa2_eth_drv_stats *extras;
|
|
- struct dpaa2_eth_ch_stats *ch_stats;
|
|
+ union dpni_statistics dpni_stats;
|
|
int dpni_stats_page_size[DPNI_STATISTICS_CNT] = {
|
|
sizeof(dpni_stats.page_0),
|
|
sizeof(dpni_stats.page_1),
|
|
@@ -247,6 +238,13 @@ static void dpaa2_eth_get_ethtool_stats(struct net_device *net_dev,
|
|
sizeof(dpni_stats.page_5),
|
|
sizeof(dpni_stats.page_6),
|
|
};
|
|
+ u32 fcnt_rx_total = 0, fcnt_tx_total = 0;
|
|
+ u32 bcnt_rx_total = 0, bcnt_tx_total = 0;
|
|
+ struct dpaa2_eth_ch_stats *ch_stats;
|
|
+ struct dpaa2_eth_drv_stats *extras;
|
|
+ int j, k, err, num_cnt, i = 0;
|
|
+ u32 fcnt, bcnt;
|
|
+ u32 buf_cnt;
|
|
|
|
memset(data, 0,
|
|
sizeof(u64) * (DPAA2_ETH_NUM_STATS + DPAA2_ETH_NUM_EXTRA_STATS));
|
|
diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c
|
|
index b4157ff370a31..63d43ef86f9b9 100644
|
|
--- a/drivers/net/ethernet/intel/i40e/i40e_main.c
|
|
+++ b/drivers/net/ethernet/intel/i40e/i40e_main.c
|
|
@@ -104,12 +104,18 @@ static struct workqueue_struct *i40e_wq;
|
|
static void netdev_hw_addr_refcnt(struct i40e_mac_filter *f,
|
|
struct net_device *netdev, int delta)
|
|
{
|
|
+ struct netdev_hw_addr_list *ha_list;
|
|
struct netdev_hw_addr *ha;
|
|
|
|
if (!f || !netdev)
|
|
return;
|
|
|
|
- netdev_for_each_mc_addr(ha, netdev) {
|
|
+ if (is_unicast_ether_addr(f->macaddr) || is_link_local_ether_addr(f->macaddr))
|
|
+ ha_list = &netdev->uc;
|
|
+ else
|
|
+ ha_list = &netdev->mc;
|
|
+
|
|
+ netdev_hw_addr_list_for_each(ha, ha_list) {
|
|
if (ether_addr_equal(ha->addr, f->macaddr)) {
|
|
ha->refcount += delta;
|
|
if (ha->refcount <= 0)
|
|
@@ -16444,6 +16450,9 @@ static void i40e_pci_error_reset_done(struct pci_dev *pdev)
|
|
return;
|
|
|
|
i40e_reset_and_rebuild(pf, false, false);
|
|
+#ifdef CONFIG_PCI_IOV
|
|
+ i40e_restore_all_vfs_msi_state(pdev);
|
|
+#endif /* CONFIG_PCI_IOV */
|
|
}
|
|
|
|
/**
|
|
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
|
|
index cb925baf72ce0..c7d761426d6ce 100644
|
|
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
|
|
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.c
|
|
@@ -152,6 +152,32 @@ void i40e_vc_notify_reset(struct i40e_pf *pf)
|
|
(u8 *)&pfe, sizeof(struct virtchnl_pf_event));
|
|
}
|
|
|
|
+#ifdef CONFIG_PCI_IOV
|
|
+void i40e_restore_all_vfs_msi_state(struct pci_dev *pdev)
|
|
+{
|
|
+ u16 vf_id;
|
|
+ u16 pos;
|
|
+
|
|
+ /* Continue only if this is a PF */
|
|
+ if (!pdev->is_physfn)
|
|
+ return;
|
|
+
|
|
+ if (!pci_num_vf(pdev))
|
|
+ return;
|
|
+
|
|
+ pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_SRIOV);
|
|
+ if (pos) {
|
|
+ struct pci_dev *vf_dev = NULL;
|
|
+
|
|
+ pci_read_config_word(pdev, pos + PCI_SRIOV_VF_DID, &vf_id);
|
|
+ while ((vf_dev = pci_get_device(pdev->vendor, vf_id, vf_dev))) {
|
|
+ if (vf_dev->is_virtfn && vf_dev->physfn == pdev)
|
|
+ pci_restore_msi_state(vf_dev);
|
|
+ }
|
|
+ }
|
|
+}
|
|
+#endif /* CONFIG_PCI_IOV */
|
|
+
|
|
/**
|
|
* i40e_vc_notify_vf_reset
|
|
* @vf: pointer to the VF structure
|
|
@@ -3451,16 +3477,16 @@ static int i40e_validate_cloud_filter(struct i40e_vf *vf,
|
|
bool found = false;
|
|
int bkt;
|
|
|
|
- if (!tc_filter->action) {
|
|
+ if (tc_filter->action != VIRTCHNL_ACTION_TC_REDIRECT) {
|
|
dev_info(&pf->pdev->dev,
|
|
- "VF %d: Currently ADq doesn't support Drop Action\n",
|
|
- vf->vf_id);
|
|
+ "VF %d: ADQ doesn't support this action (%d)\n",
|
|
+ vf->vf_id, tc_filter->action);
|
|
goto err;
|
|
}
|
|
|
|
/* action_meta is TC number here to which the filter is applied */
|
|
if (!tc_filter->action_meta ||
|
|
- tc_filter->action_meta > I40E_MAX_VF_VSI) {
|
|
+ tc_filter->action_meta > vf->num_tc) {
|
|
dev_info(&pf->pdev->dev, "VF %d: Invalid TC number %u\n",
|
|
vf->vf_id, tc_filter->action_meta);
|
|
goto err;
|
|
diff --git a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
|
|
index 358bbdb587951..bd497cc5303a1 100644
|
|
--- a/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
|
|
+++ b/drivers/net/ethernet/intel/i40e/i40e_virtchnl_pf.h
|
|
@@ -135,6 +135,9 @@ int i40e_ndo_set_vf_spoofchk(struct net_device *netdev, int vf_id, bool enable);
|
|
|
|
void i40e_vc_notify_link_state(struct i40e_pf *pf);
|
|
void i40e_vc_notify_reset(struct i40e_pf *pf);
|
|
+#ifdef CONFIG_PCI_IOV
|
|
+void i40e_restore_all_vfs_msi_state(struct pci_dev *pdev);
|
|
+#endif /* CONFIG_PCI_IOV */
|
|
int i40e_get_vf_stats(struct net_device *netdev, int vf_id,
|
|
struct ifla_vf_stats *vf_stats);
|
|
|
|
diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c
|
|
index f0f39364819ac..ab46cfca4028d 100644
|
|
--- a/drivers/net/ethernet/intel/ice/ice_main.c
|
|
+++ b/drivers/net/ethernet/intel/ice/ice_main.c
|
|
@@ -2138,7 +2138,7 @@ static int ice_configure_phy(struct ice_vsi *vsi)
|
|
|
|
/* Ensure we have media as we cannot configure a medialess port */
|
|
if (!(phy->link_info.link_info & ICE_AQ_MEDIA_AVAILABLE))
|
|
- return -EPERM;
|
|
+ return -ENOMEDIUM;
|
|
|
|
ice_print_topo_conflict(vsi);
|
|
|
|
@@ -9065,8 +9065,14 @@ int ice_stop(struct net_device *netdev)
|
|
int link_err = ice_force_phys_link_state(vsi, false);
|
|
|
|
if (link_err) {
|
|
- netdev_err(vsi->netdev, "Failed to set physical link down, VSI %d error %d\n",
|
|
- vsi->vsi_num, link_err);
|
|
+ if (link_err == -ENOMEDIUM)
|
|
+ netdev_info(vsi->netdev, "Skipping link reconfig - no media attached, VSI %d\n",
|
|
+ vsi->vsi_num);
|
|
+ else
|
|
+ netdev_err(vsi->netdev, "Failed to set physical link down, VSI %d error %d\n",
|
|
+ vsi->vsi_num, link_err);
|
|
+
|
|
+ ice_vsi_close(vsi);
|
|
return -EIO;
|
|
}
|
|
}
|
|
diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h
|
|
index 43c05b41627f7..2a894ca49d93b 100644
|
|
--- a/drivers/net/ethernet/intel/igc/igc.h
|
|
+++ b/drivers/net/ethernet/intel/igc/igc.h
|
|
@@ -538,6 +538,7 @@ struct igc_nfc_filter {
|
|
u16 etype;
|
|
__be16 vlan_etype;
|
|
u16 vlan_tci;
|
|
+ u16 vlan_tci_mask;
|
|
u8 src_addr[ETH_ALEN];
|
|
u8 dst_addr[ETH_ALEN];
|
|
u8 user_data[8];
|
|
diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c
|
|
index 81897f7a90a91..2bee9cace5983 100644
|
|
--- a/drivers/net/ethernet/intel/igc/igc_ethtool.c
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c
|
|
@@ -957,6 +957,7 @@ static int igc_ethtool_set_coalesce(struct net_device *netdev,
|
|
}
|
|
|
|
#define ETHER_TYPE_FULL_MASK ((__force __be16)~0)
|
|
+#define VLAN_TCI_FULL_MASK ((__force __be16)~0)
|
|
static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,
|
|
struct ethtool_rxnfc *cmd)
|
|
{
|
|
@@ -979,10 +980,16 @@ static int igc_ethtool_get_nfc_rule(struct igc_adapter *adapter,
|
|
fsp->m_u.ether_spec.h_proto = ETHER_TYPE_FULL_MASK;
|
|
}
|
|
|
|
+ if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) {
|
|
+ fsp->flow_type |= FLOW_EXT;
|
|
+ fsp->h_ext.vlan_etype = rule->filter.vlan_etype;
|
|
+ fsp->m_ext.vlan_etype = ETHER_TYPE_FULL_MASK;
|
|
+ }
|
|
+
|
|
if (rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) {
|
|
fsp->flow_type |= FLOW_EXT;
|
|
fsp->h_ext.vlan_tci = htons(rule->filter.vlan_tci);
|
|
- fsp->m_ext.vlan_tci = htons(VLAN_PRIO_MASK);
|
|
+ fsp->m_ext.vlan_tci = htons(rule->filter.vlan_tci_mask);
|
|
}
|
|
|
|
if (rule->filter.match_flags & IGC_FILTER_FLAG_DST_MAC_ADDR) {
|
|
@@ -1217,6 +1224,7 @@ static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule,
|
|
|
|
if ((fsp->flow_type & FLOW_EXT) && fsp->m_ext.vlan_tci) {
|
|
rule->filter.vlan_tci = ntohs(fsp->h_ext.vlan_tci);
|
|
+ rule->filter.vlan_tci_mask = ntohs(fsp->m_ext.vlan_tci);
|
|
rule->filter.match_flags |= IGC_FILTER_FLAG_VLAN_TCI;
|
|
}
|
|
|
|
@@ -1254,11 +1262,19 @@ static void igc_ethtool_init_nfc_rule(struct igc_nfc_rule *rule,
|
|
memcpy(rule->filter.user_mask, fsp->m_ext.data, sizeof(fsp->m_ext.data));
|
|
}
|
|
|
|
- /* When multiple filter options or user data or vlan etype is set, use a
|
|
- * flex filter.
|
|
+ /* The i225/i226 has various different filters. Flex filters provide a
|
|
+ * way to match up to the first 128 bytes of a packet. Use them for:
|
|
+ * a) For specific user data
|
|
+ * b) For VLAN EtherType
|
|
+ * c) For full TCI match
|
|
+ * d) Or in case multiple filter criteria are set
|
|
+ *
|
|
+ * Otherwise, use the simple MAC, VLAN PRIO or EtherType filters.
|
|
*/
|
|
if ((rule->filter.match_flags & IGC_FILTER_FLAG_USER_DATA) ||
|
|
(rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_ETYPE) ||
|
|
+ ((rule->filter.match_flags & IGC_FILTER_FLAG_VLAN_TCI) &&
|
|
+ rule->filter.vlan_tci_mask == ntohs(VLAN_TCI_FULL_MASK)) ||
|
|
(rule->filter.match_flags & (rule->filter.match_flags - 1)))
|
|
rule->flex = true;
|
|
else
|
|
@@ -1328,6 +1344,26 @@ static int igc_ethtool_add_nfc_rule(struct igc_adapter *adapter,
|
|
return -EINVAL;
|
|
}
|
|
|
|
+ /* There are two ways to match the VLAN TCI:
|
|
+ * 1. Match on PCP field and use vlan prio filter for it
|
|
+ * 2. Match on complete TCI field and use flex filter for it
|
|
+ */
|
|
+ if ((fsp->flow_type & FLOW_EXT) &&
|
|
+ fsp->m_ext.vlan_tci &&
|
|
+ fsp->m_ext.vlan_tci != htons(VLAN_PRIO_MASK) &&
|
|
+ fsp->m_ext.vlan_tci != VLAN_TCI_FULL_MASK) {
|
|
+ netdev_dbg(netdev, "VLAN mask not supported\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
+ /* VLAN EtherType can only be matched by full mask. */
|
|
+ if ((fsp->flow_type & FLOW_EXT) &&
|
|
+ fsp->m_ext.vlan_etype &&
|
|
+ fsp->m_ext.vlan_etype != ETHER_TYPE_FULL_MASK) {
|
|
+ netdev_dbg(netdev, "VLAN EtherType mask not supported\n");
|
|
+ return -EOPNOTSUPP;
|
|
+ }
|
|
+
|
|
if (fsp->location >= IGC_MAX_RXNFC_RULES) {
|
|
netdev_dbg(netdev, "Invalid location\n");
|
|
return -EINVAL;
|
|
diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c
|
|
index 725db36e399d2..31ea0781b65ec 100644
|
|
--- a/drivers/net/ethernet/intel/igc/igc_tsn.c
|
|
+++ b/drivers/net/ethernet/intel/igc/igc_tsn.c
|
|
@@ -178,7 +178,7 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter)
|
|
wr32(IGC_TQAVCC(i), tqavcc);
|
|
|
|
wr32(IGC_TQAVHC(i),
|
|
- 0x80000000 + ring->hicredit * 0x7735);
|
|
+ 0x80000000 + ring->hicredit * 0x7736);
|
|
} else {
|
|
/* Disable any CBS for the queue */
|
|
txqctl &= ~(IGC_TXQCTL_QAV_SEL_MASK);
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
|
|
index 65c0373d34d12..90be87dc105d3 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.c
|
|
@@ -78,7 +78,7 @@ static bool is_dev_rpm(void *cgxd)
|
|
|
|
bool is_lmac_valid(struct cgx *cgx, int lmac_id)
|
|
{
|
|
- if (!cgx || lmac_id < 0 || lmac_id >= MAX_LMAC_PER_CGX)
|
|
+ if (!cgx || lmac_id < 0 || lmac_id >= cgx->max_lmac_per_mac)
|
|
return false;
|
|
return test_bit(lmac_id, &cgx->lmac_bmap);
|
|
}
|
|
@@ -90,7 +90,7 @@ static int get_sequence_id_of_lmac(struct cgx *cgx, int lmac_id)
|
|
{
|
|
int tmp, id = 0;
|
|
|
|
- for_each_set_bit(tmp, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) {
|
|
+ for_each_set_bit(tmp, &cgx->lmac_bmap, cgx->max_lmac_per_mac) {
|
|
if (tmp == lmac_id)
|
|
break;
|
|
id++;
|
|
@@ -121,7 +121,7 @@ u64 cgx_read(struct cgx *cgx, u64 lmac, u64 offset)
|
|
|
|
struct lmac *lmac_pdata(u8 lmac_id, struct cgx *cgx)
|
|
{
|
|
- if (!cgx || lmac_id >= MAX_LMAC_PER_CGX)
|
|
+ if (!cgx || lmac_id >= cgx->max_lmac_per_mac)
|
|
return NULL;
|
|
|
|
return cgx->lmac_idmap[lmac_id];
|
|
@@ -1410,7 +1410,7 @@ int cgx_get_fwdata_base(u64 *base)
|
|
if (!cgx)
|
|
return -ENXIO;
|
|
|
|
- first_lmac = find_first_bit(&cgx->lmac_bmap, MAX_LMAC_PER_CGX);
|
|
+ first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac);
|
|
req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FWD_BASE, req);
|
|
err = cgx_fwi_cmd_generic(req, &resp, cgx, first_lmac);
|
|
if (!err)
|
|
@@ -1499,7 +1499,7 @@ static int cgx_fwi_link_change(struct cgx *cgx, int lmac_id, bool enable)
|
|
|
|
static inline int cgx_fwi_read_version(u64 *resp, struct cgx *cgx)
|
|
{
|
|
- int first_lmac = find_first_bit(&cgx->lmac_bmap, MAX_LMAC_PER_CGX);
|
|
+ int first_lmac = find_first_bit(&cgx->lmac_bmap, cgx->max_lmac_per_mac);
|
|
u64 req = 0;
|
|
|
|
req = FIELD_SET(CMDREG_ID, CGX_CMD_GET_FW_VER, req);
|
|
@@ -1537,7 +1537,7 @@ static void cgx_lmac_linkup_work(struct work_struct *work)
|
|
int i, err;
|
|
|
|
/* Do Link up for all the enabled lmacs */
|
|
- for_each_set_bit(i, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) {
|
|
+ for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) {
|
|
err = cgx_fwi_link_change(cgx, i, true);
|
|
if (err)
|
|
dev_info(dev, "cgx port %d:%d Link up command failed\n",
|
|
@@ -1557,14 +1557,6 @@ int cgx_lmac_linkup_start(void *cgxd)
|
|
return 0;
|
|
}
|
|
|
|
-static void cgx_lmac_get_fifolen(struct cgx *cgx)
|
|
-{
|
|
- u64 cfg;
|
|
-
|
|
- cfg = cgx_read(cgx, 0, CGX_CONST);
|
|
- cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg);
|
|
-}
|
|
-
|
|
static int cgx_configure_interrupt(struct cgx *cgx, struct lmac *lmac,
|
|
int cnt, bool req_free)
|
|
{
|
|
@@ -1619,17 +1611,14 @@ static int cgx_lmac_init(struct cgx *cgx)
|
|
u64 lmac_list;
|
|
int i, err;
|
|
|
|
- cgx_lmac_get_fifolen(cgx);
|
|
-
|
|
- cgx->lmac_count = cgx->mac_ops->get_nr_lmacs(cgx);
|
|
/* lmac_list specifies which lmacs are enabled
|
|
* when bit n is set to 1, LMAC[n] is enabled
|
|
*/
|
|
if (cgx->mac_ops->non_contiguous_serdes_lane)
|
|
lmac_list = cgx_read(cgx, 0, CGXX_CMRX_RX_LMACS) & 0xFULL;
|
|
|
|
- if (cgx->lmac_count > MAX_LMAC_PER_CGX)
|
|
- cgx->lmac_count = MAX_LMAC_PER_CGX;
|
|
+ if (cgx->lmac_count > cgx->max_lmac_per_mac)
|
|
+ cgx->lmac_count = cgx->max_lmac_per_mac;
|
|
|
|
for (i = 0; i < cgx->lmac_count; i++) {
|
|
lmac = kzalloc(sizeof(struct lmac), GFP_KERNEL);
|
|
@@ -1707,7 +1696,7 @@ static int cgx_lmac_exit(struct cgx *cgx)
|
|
}
|
|
|
|
/* Free all lmac related resources */
|
|
- for_each_set_bit(i, &cgx->lmac_bmap, MAX_LMAC_PER_CGX) {
|
|
+ for_each_set_bit(i, &cgx->lmac_bmap, cgx->max_lmac_per_mac) {
|
|
lmac = cgx->lmac_idmap[i];
|
|
if (!lmac)
|
|
continue;
|
|
@@ -1723,6 +1712,12 @@ static int cgx_lmac_exit(struct cgx *cgx)
|
|
|
|
static void cgx_populate_features(struct cgx *cgx)
|
|
{
|
|
+ u64 cfg;
|
|
+
|
|
+ cfg = cgx_read(cgx, 0, CGX_CONST);
|
|
+ cgx->mac_ops->fifo_len = FIELD_GET(CGX_CONST_RXFIFO_SIZE, cfg);
|
|
+ cgx->max_lmac_per_mac = FIELD_GET(CGX_CONST_MAX_LMACS, cfg);
|
|
+
|
|
if (is_dev_rpm(cgx))
|
|
cgx->hw_features = (RVU_LMAC_FEAT_DMACF | RVU_MAC_RPM |
|
|
RVU_LMAC_FEAT_FC | RVU_LMAC_FEAT_PTP);
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
|
|
index 04338db38671b..09ddb00f63cc7 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/cgx.h
|
|
@@ -18,11 +18,8 @@
|
|
/* PCI BAR nos */
|
|
#define PCI_CFG_REG_BAR_NUM 0
|
|
|
|
-#define CGX_ID_MASK 0x7
|
|
-#define MAX_LMAC_PER_CGX 4
|
|
+#define CGX_ID_MASK 0xF
|
|
#define MAX_DMAC_ENTRIES_PER_CGX 32
|
|
-#define CGX_FIFO_LEN 65536 /* 64K for both Rx & Tx */
|
|
-#define CGX_OFFSET(x) ((x) * MAX_LMAC_PER_CGX)
|
|
|
|
/* Registers */
|
|
#define CGXX_CMRX_CFG 0x00
|
|
@@ -56,6 +53,7 @@
|
|
#define CGXX_SCRATCH1_REG 0x1058
|
|
#define CGX_CONST 0x2000
|
|
#define CGX_CONST_RXFIFO_SIZE GENMASK_ULL(23, 0)
|
|
+#define CGX_CONST_MAX_LMACS GENMASK_ULL(31, 24)
|
|
#define CGXX_SPUX_CONTROL1 0x10000
|
|
#define CGXX_SPUX_LNX_FEC_CORR_BLOCKS 0x10700
|
|
#define CGXX_SPUX_LNX_FEC_UNCORR_BLOCKS 0x10800
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h
|
|
index 52b6016789fa4..697cfec74aa1e 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/lmac_common.h
|
|
@@ -128,7 +128,10 @@ struct cgx {
|
|
struct pci_dev *pdev;
|
|
u8 cgx_id;
|
|
u8 lmac_count;
|
|
- struct lmac *lmac_idmap[MAX_LMAC_PER_CGX];
|
|
+ /* number of LMACs per MAC could be 4 or 8 */
|
|
+ u8 max_lmac_per_mac;
|
|
+#define MAX_LMAC_COUNT 8
|
|
+ struct lmac *lmac_idmap[MAX_LMAC_COUNT];
|
|
struct work_struct cgx_cmd_work;
|
|
struct workqueue_struct *cgx_cmd_workq;
|
|
struct list_head cgx_list;
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/npc.h b/drivers/net/ethernet/marvell/octeontx2/af/npc.h
|
|
index d027c23b8ef8e..aaff91bc7415a 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/npc.h
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/npc.h
|
|
@@ -514,7 +514,7 @@ struct npc_lt_def {
|
|
u8 ltype_mask;
|
|
u8 ltype_match;
|
|
u8 lid;
|
|
-};
|
|
+} __packed;
|
|
|
|
struct npc_lt_def_ipsec {
|
|
u8 ltype_mask;
|
|
@@ -522,7 +522,7 @@ struct npc_lt_def_ipsec {
|
|
u8 lid;
|
|
u8 spi_offset;
|
|
u8 spi_nz;
|
|
-};
|
|
+} __packed;
|
|
|
|
struct npc_lt_def_apad {
|
|
u8 ltype_mask;
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c
|
|
index a70e1153fa04b..6b4792a942d84 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/rpm.c
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rpm.c
|
|
@@ -283,6 +283,11 @@ void rpm_lmac_pause_frm_config(void *rpmd, int lmac_id, bool enable)
|
|
cfg = FIELD_SET(RPM_PFC_CLASS_MASK, 0, cfg);
|
|
rpm_write(rpm, lmac_id, RPMX_CMRX_PRT_CBFC_CTL, cfg);
|
|
|
|
+ /* Disable forward pause to driver */
|
|
+ cfg = rpm_read(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG);
|
|
+ cfg &= ~RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD;
|
|
+ rpm_write(rpm, lmac_id, RPMX_MTI_MAC100X_COMMAND_CONFIG, cfg);
|
|
+
|
|
/* Enable channel mask for all LMACS */
|
|
rpm_write(rpm, 0, RPMX_CMR_CHAN_MSK_OR, ~0ULL);
|
|
}
|
|
@@ -451,12 +456,10 @@ int rpm_lmac_pfc_config(void *rpmd, int lmac_id, u8 tx_pause, u8 rx_pause, u16 p
|
|
|
|
if (rx_pause) {
|
|
cfg &= ~(RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE |
|
|
- RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE |
|
|
- RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD);
|
|
+ RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE);
|
|
} else {
|
|
cfg |= (RPMX_MTI_MAC100X_COMMAND_CONFIG_RX_P_DISABLE |
|
|
- RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE |
|
|
- RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_FWD);
|
|
+ RPMX_MTI_MAC100X_COMMAND_CONFIG_PAUSE_IGNORE);
|
|
}
|
|
|
|
if (tx_pause) {
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
|
|
index 95a7bc396e8ea..0b76dfa979d4e 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.h
|
|
@@ -480,7 +480,7 @@ struct rvu {
|
|
u8 cgx_mapped_pfs;
|
|
u8 cgx_cnt_max; /* CGX port count max */
|
|
u8 *pf2cgxlmac_map; /* pf to cgx_lmac map */
|
|
- u16 *cgxlmac2pf_map; /* bitmap of mapped pfs for
|
|
+ u64 *cgxlmac2pf_map; /* bitmap of mapped pfs for
|
|
* every cgx lmac port
|
|
*/
|
|
unsigned long pf_notify_bmap; /* Flags for PF notification */
|
|
@@ -850,6 +850,7 @@ u32 rvu_cgx_get_fifolen(struct rvu *rvu);
|
|
void *rvu_first_cgx_pdata(struct rvu *rvu);
|
|
int cgxlmac_to_pf(struct rvu *rvu, int cgx_id, int lmac_id);
|
|
int rvu_cgx_config_tx(void *cgxd, int lmac_id, bool enable);
|
|
+int rvu_cgx_tx_enable(struct rvu *rvu, u16 pcifunc, bool enable);
|
|
int rvu_cgx_prio_flow_ctrl_cfg(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause,
|
|
u16 pfc_en);
|
|
int rvu_cgx_cfg_pause_frm(struct rvu *rvu, u16 pcifunc, u8 tx_pause, u8 rx_pause);
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
|
|
index c60b9580ca969..bcb4385d0621c 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_cgx.c
|
|
@@ -55,8 +55,9 @@ bool is_mac_feature_supported(struct rvu *rvu, int pf, int feature)
|
|
return (cgx_features_get(cgxd) & feature);
|
|
}
|
|
|
|
+#define CGX_OFFSET(x) ((x) * rvu->hw->lmac_per_cgx)
|
|
/* Returns bitmap of mapped PFs */
|
|
-static u16 cgxlmac_to_pfmap(struct rvu *rvu, u8 cgx_id, u8 lmac_id)
|
|
+static u64 cgxlmac_to_pfmap(struct rvu *rvu, u8 cgx_id, u8 lmac_id)
|
|
{
|
|
return rvu->cgxlmac2pf_map[CGX_OFFSET(cgx_id) + lmac_id];
|
|
}
|
|
@@ -71,7 +72,8 @@ int cgxlmac_to_pf(struct rvu *rvu, int cgx_id, int lmac_id)
|
|
if (!pfmap)
|
|
return -ENODEV;
|
|
else
|
|
- return find_first_bit(&pfmap, 16);
|
|
+ return find_first_bit(&pfmap,
|
|
+ rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx);
|
|
}
|
|
|
|
static u8 cgxlmac_id_to_bmap(u8 cgx_id, u8 lmac_id)
|
|
@@ -129,14 +131,14 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu)
|
|
if (!cgx_cnt_max)
|
|
return 0;
|
|
|
|
- if (cgx_cnt_max > 0xF || MAX_LMAC_PER_CGX > 0xF)
|
|
+ if (cgx_cnt_max > 0xF || rvu->hw->lmac_per_cgx > 0xF)
|
|
return -EINVAL;
|
|
|
|
/* Alloc map table
|
|
* An additional entry is required since PF id starts from 1 and
|
|
* hence entry at offset 0 is invalid.
|
|
*/
|
|
- size = (cgx_cnt_max * MAX_LMAC_PER_CGX + 1) * sizeof(u8);
|
|
+ size = (cgx_cnt_max * rvu->hw->lmac_per_cgx + 1) * sizeof(u8);
|
|
rvu->pf2cgxlmac_map = devm_kmalloc(rvu->dev, size, GFP_KERNEL);
|
|
if (!rvu->pf2cgxlmac_map)
|
|
return -ENOMEM;
|
|
@@ -145,9 +147,10 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu)
|
|
memset(rvu->pf2cgxlmac_map, 0xFF, size);
|
|
|
|
/* Reverse map table */
|
|
- rvu->cgxlmac2pf_map = devm_kzalloc(rvu->dev,
|
|
- cgx_cnt_max * MAX_LMAC_PER_CGX * sizeof(u16),
|
|
- GFP_KERNEL);
|
|
+ rvu->cgxlmac2pf_map =
|
|
+ devm_kzalloc(rvu->dev,
|
|
+ cgx_cnt_max * rvu->hw->lmac_per_cgx * sizeof(u64),
|
|
+ GFP_KERNEL);
|
|
if (!rvu->cgxlmac2pf_map)
|
|
return -ENOMEM;
|
|
|
|
@@ -156,7 +159,7 @@ static int rvu_map_cgx_lmac_pf(struct rvu *rvu)
|
|
if (!rvu_cgx_pdata(cgx, rvu))
|
|
continue;
|
|
lmac_bmap = cgx_get_lmac_bmap(rvu_cgx_pdata(cgx, rvu));
|
|
- for_each_set_bit(iter, &lmac_bmap, MAX_LMAC_PER_CGX) {
|
|
+ for_each_set_bit(iter, &lmac_bmap, rvu->hw->lmac_per_cgx) {
|
|
lmac = cgx_get_lmacid(rvu_cgx_pdata(cgx, rvu),
|
|
iter);
|
|
rvu->pf2cgxlmac_map[pf] = cgxlmac_id_to_bmap(cgx, lmac);
|
|
@@ -235,7 +238,8 @@ static void cgx_notify_pfs(struct cgx_link_event *event, struct rvu *rvu)
|
|
pfmap = cgxlmac_to_pfmap(rvu, event->cgx_id, event->lmac_id);
|
|
|
|
do {
|
|
- pfid = find_first_bit(&pfmap, 16);
|
|
+ pfid = find_first_bit(&pfmap,
|
|
+ rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx);
|
|
clear_bit(pfid, &pfmap);
|
|
|
|
/* check if notification is enabled */
|
|
@@ -310,7 +314,7 @@ static int cgx_lmac_event_handler_init(struct rvu *rvu)
|
|
if (!cgxd)
|
|
continue;
|
|
lmac_bmap = cgx_get_lmac_bmap(cgxd);
|
|
- for_each_set_bit(lmac, &lmac_bmap, MAX_LMAC_PER_CGX) {
|
|
+ for_each_set_bit(lmac, &lmac_bmap, rvu->hw->lmac_per_cgx) {
|
|
err = cgx_lmac_evh_register(&cb, cgxd, lmac);
|
|
if (err)
|
|
dev_err(rvu->dev,
|
|
@@ -396,7 +400,7 @@ int rvu_cgx_exit(struct rvu *rvu)
|
|
if (!cgxd)
|
|
continue;
|
|
lmac_bmap = cgx_get_lmac_bmap(cgxd);
|
|
- for_each_set_bit(lmac, &lmac_bmap, MAX_LMAC_PER_CGX)
|
|
+ for_each_set_bit(lmac, &lmac_bmap, rvu->hw->lmac_per_cgx)
|
|
cgx_lmac_evh_unregister(cgxd, lmac);
|
|
}
|
|
|
|
@@ -456,6 +460,23 @@ int rvu_cgx_config_rxtx(struct rvu *rvu, u16 pcifunc, bool start)
|
|
return mac_ops->mac_rx_tx_enable(cgxd, lmac_id, start);
|
|
}
|
|
|
|
+int rvu_cgx_tx_enable(struct rvu *rvu, u16 pcifunc, bool enable)
|
|
+{
|
|
+ int pf = rvu_get_pf(pcifunc);
|
|
+ struct mac_ops *mac_ops;
|
|
+ u8 cgx_id, lmac_id;
|
|
+ void *cgxd;
|
|
+
|
|
+ if (!is_cgx_config_permitted(rvu, pcifunc))
|
|
+ return LMAC_AF_ERR_PERM_DENIED;
|
|
+
|
|
+ rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
|
|
+ cgxd = rvu_cgx_pdata(cgx_id, rvu);
|
|
+ mac_ops = get_mac_ops(cgxd);
|
|
+
|
|
+ return mac_ops->mac_tx_enable(cgxd, lmac_id, enable);
|
|
+}
|
|
+
|
|
int rvu_cgx_config_tx(void *cgxd, int lmac_id, bool enable)
|
|
{
|
|
struct mac_ops *mac_ops;
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
|
|
index 5c9dc3f9262f5..cc5d342e026c7 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_debugfs.c
|
|
@@ -2618,7 +2618,7 @@ static void rvu_dbg_cgx_init(struct rvu *rvu)
|
|
rvu->rvu_dbg.cgx = debugfs_create_dir(dname,
|
|
rvu->rvu_dbg.cgx_root);
|
|
|
|
- for_each_set_bit(lmac_id, &lmac_bmap, MAX_LMAC_PER_CGX) {
|
|
+ for_each_set_bit(lmac_id, &lmac_bmap, rvu->hw->lmac_per_cgx) {
|
|
/* lmac debugfs dir */
|
|
sprintf(dname, "lmac%d", lmac_id);
|
|
rvu->rvu_dbg.lmac =
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
|
|
index 959f36efdc4a6..bb99302eab67a 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c
|
|
@@ -3923,90 +3923,18 @@ static void nix_find_link_frs(struct rvu *rvu,
|
|
req->minlen = minlen;
|
|
}
|
|
|
|
-static int
|
|
-nix_config_link_credits(struct rvu *rvu, int blkaddr, int link,
|
|
- u16 pcifunc, u64 tx_credits)
|
|
-{
|
|
- struct rvu_hwinfo *hw = rvu->hw;
|
|
- int pf = rvu_get_pf(pcifunc);
|
|
- u8 cgx_id = 0, lmac_id = 0;
|
|
- unsigned long poll_tmo;
|
|
- bool restore_tx_en = 0;
|
|
- struct nix_hw *nix_hw;
|
|
- u64 cfg, sw_xoff = 0;
|
|
- u32 schq = 0;
|
|
- u32 credits;
|
|
- int rc;
|
|
-
|
|
- nix_hw = get_nix_hw(rvu->hw, blkaddr);
|
|
- if (!nix_hw)
|
|
- return NIX_AF_ERR_INVALID_NIXBLK;
|
|
-
|
|
- if (tx_credits == nix_hw->tx_credits[link])
|
|
- return 0;
|
|
-
|
|
- /* Enable cgx tx if disabled for credits to be back */
|
|
- if (is_pf_cgxmapped(rvu, pf)) {
|
|
- rvu_get_cgx_lmac_id(rvu->pf2cgxlmac_map[pf], &cgx_id, &lmac_id);
|
|
- restore_tx_en = !rvu_cgx_config_tx(rvu_cgx_pdata(cgx_id, rvu),
|
|
- lmac_id, true);
|
|
- }
|
|
-
|
|
- mutex_lock(&rvu->rsrc_lock);
|
|
- /* Disable new traffic to link */
|
|
- if (hw->cap.nix_shaping) {
|
|
- schq = nix_get_tx_link(rvu, pcifunc);
|
|
- sw_xoff = rvu_read64(rvu, blkaddr, NIX_AF_TL1X_SW_XOFF(schq));
|
|
- rvu_write64(rvu, blkaddr,
|
|
- NIX_AF_TL1X_SW_XOFF(schq), BIT_ULL(0));
|
|
- }
|
|
-
|
|
- rc = NIX_AF_ERR_LINK_CREDITS;
|
|
- poll_tmo = jiffies + usecs_to_jiffies(200000);
|
|
- /* Wait for credits to return */
|
|
- do {
|
|
- if (time_after(jiffies, poll_tmo))
|
|
- goto exit;
|
|
- usleep_range(100, 200);
|
|
-
|
|
- cfg = rvu_read64(rvu, blkaddr,
|
|
- NIX_AF_TX_LINKX_NORM_CREDIT(link));
|
|
- credits = (cfg >> 12) & 0xFFFFFULL;
|
|
- } while (credits != nix_hw->tx_credits[link]);
|
|
-
|
|
- cfg &= ~(0xFFFFFULL << 12);
|
|
- cfg |= (tx_credits << 12);
|
|
- rvu_write64(rvu, blkaddr, NIX_AF_TX_LINKX_NORM_CREDIT(link), cfg);
|
|
- rc = 0;
|
|
-
|
|
- nix_hw->tx_credits[link] = tx_credits;
|
|
-
|
|
-exit:
|
|
- /* Enable traffic back */
|
|
- if (hw->cap.nix_shaping && !sw_xoff)
|
|
- rvu_write64(rvu, blkaddr, NIX_AF_TL1X_SW_XOFF(schq), 0);
|
|
-
|
|
- /* Restore state of cgx tx */
|
|
- if (restore_tx_en)
|
|
- rvu_cgx_config_tx(rvu_cgx_pdata(cgx_id, rvu), lmac_id, false);
|
|
-
|
|
- mutex_unlock(&rvu->rsrc_lock);
|
|
- return rc;
|
|
-}
|
|
-
|
|
int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req,
|
|
struct msg_rsp *rsp)
|
|
{
|
|
struct rvu_hwinfo *hw = rvu->hw;
|
|
u16 pcifunc = req->hdr.pcifunc;
|
|
int pf = rvu_get_pf(pcifunc);
|
|
- int blkaddr, schq, link = -1;
|
|
- struct nix_txsch *txsch;
|
|
- u64 cfg, lmac_fifo_len;
|
|
+ int blkaddr, link = -1;
|
|
struct nix_hw *nix_hw;
|
|
struct rvu_pfvf *pfvf;
|
|
u8 cgx = 0, lmac = 0;
|
|
u16 max_mtu;
|
|
+ u64 cfg;
|
|
|
|
blkaddr = rvu_get_blkaddr(rvu, BLKTYPE_NIX, pcifunc);
|
|
if (blkaddr < 0)
|
|
@@ -4027,25 +3955,6 @@ int rvu_mbox_handler_nix_set_hw_frs(struct rvu *rvu, struct nix_frs_cfg *req,
|
|
if (req->update_minlen && req->minlen < NIC_HW_MIN_FRS)
|
|
return NIX_AF_ERR_FRS_INVALID;
|
|
|
|
- /* Check if requester wants to update SMQ's */
|
|
- if (!req->update_smq)
|
|
- goto rx_frscfg;
|
|
-
|
|
- /* Update min/maxlen in each of the SMQ attached to this PF/VF */
|
|
- txsch = &nix_hw->txsch[NIX_TXSCH_LVL_SMQ];
|
|
- mutex_lock(&rvu->rsrc_lock);
|
|
- for (schq = 0; schq < txsch->schq.max; schq++) {
|
|
- if (TXSCH_MAP_FUNC(txsch->pfvf_map[schq]) != pcifunc)
|
|
- continue;
|
|
- cfg = rvu_read64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq));
|
|
- cfg = (cfg & ~(0xFFFFULL << 8)) | ((u64)req->maxlen << 8);
|
|
- if (req->update_minlen)
|
|
- cfg = (cfg & ~0x7FULL) | ((u64)req->minlen & 0x7F);
|
|
- rvu_write64(rvu, blkaddr, NIX_AF_SMQX_CFG(schq), cfg);
|
|
- }
|
|
- mutex_unlock(&rvu->rsrc_lock);
|
|
-
|
|
-rx_frscfg:
|
|
/* Check if config is for SDP link */
|
|
if (req->sdp_link) {
|
|
if (!hw->sdp_links)
|
|
@@ -4068,7 +3977,6 @@ rx_frscfg:
|
|
if (link < 0)
|
|
return NIX_AF_ERR_RX_LINK_INVALID;
|
|
|
|
-
|
|
linkcfg:
|
|
nix_find_link_frs(rvu, req, pcifunc);
|
|
|
|
@@ -4078,19 +3986,7 @@ linkcfg:
|
|
cfg = (cfg & ~0xFFFFULL) | req->minlen;
|
|
rvu_write64(rvu, blkaddr, NIX_AF_RX_LINKX_CFG(link), cfg);
|
|
|
|
- if (req->sdp_link || pf == 0)
|
|
- return 0;
|
|
-
|
|
- /* Update transmit credits for CGX links */
|
|
- lmac_fifo_len = rvu_cgx_get_lmac_fifolen(rvu, cgx, lmac);
|
|
- if (!lmac_fifo_len) {
|
|
- dev_err(rvu->dev,
|
|
- "%s: Failed to get CGX/RPM%d:LMAC%d FIFO size\n",
|
|
- __func__, cgx, lmac);
|
|
- return 0;
|
|
- }
|
|
- return nix_config_link_credits(rvu, blkaddr, link, pcifunc,
|
|
- (lmac_fifo_len - req->maxlen) / 16);
|
|
+ return 0;
|
|
}
|
|
|
|
int rvu_mbox_handler_nix_set_rx_cfg(struct rvu *rvu, struct nix_rx_cfg *req,
|
|
@@ -4183,7 +4079,7 @@ static void nix_link_config(struct rvu *rvu, int blkaddr,
|
|
|
|
/* Get LMAC id's from bitmap */
|
|
lmac_bmap = cgx_get_lmac_bmap(rvu_cgx_pdata(cgx, rvu));
|
|
- for_each_set_bit(iter, &lmac_bmap, MAX_LMAC_PER_CGX) {
|
|
+ for_each_set_bit(iter, &lmac_bmap, rvu->hw->lmac_per_cgx) {
|
|
lmac_fifo_len = rvu_cgx_get_lmac_fifolen(rvu, cgx, iter);
|
|
if (!lmac_fifo_len) {
|
|
dev_err(rvu->dev,
|
|
@@ -4610,7 +4506,13 @@ int rvu_mbox_handler_nix_lf_stop_rx(struct rvu *rvu, struct msg_req *req,
|
|
pfvf = rvu_get_pfvf(rvu, pcifunc);
|
|
clear_bit(NIXLF_INITIALIZED, &pfvf->flags);
|
|
|
|
- return rvu_cgx_start_stop_io(rvu, pcifunc, false);
|
|
+ err = rvu_cgx_start_stop_io(rvu, pcifunc, false);
|
|
+ if (err)
|
|
+ return err;
|
|
+
|
|
+ rvu_cgx_tx_enable(rvu, pcifunc, true);
|
|
+
|
|
+ return 0;
|
|
}
|
|
|
|
#define RX_SA_BASE GENMASK_ULL(52, 7)
|
|
diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c
|
|
index 34fa59575fa91..54e0dfdc9d984 100644
|
|
--- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c
|
|
+++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c
|
|
@@ -1999,7 +1999,9 @@ int rvu_npc_exact_init(struct rvu *rvu)
|
|
/* Install SDP drop rule */
|
|
drop_mcam_idx = &table->num_drop_rules;
|
|
|
|
- max_lmac_cnt = rvu->cgx_cnt_max * MAX_LMAC_PER_CGX + PF_CGXMAP_BASE;
|
|
+ max_lmac_cnt = rvu->cgx_cnt_max * rvu->hw->lmac_per_cgx +
|
|
+ PF_CGXMAP_BASE;
|
|
+
|
|
for (i = PF_CGXMAP_BASE; i < max_lmac_cnt; i++) {
|
|
if (rvu->pf2cgxlmac_map[i] == 0xFF)
|
|
continue;
|
|
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c
|
|
index d136360ac6a98..a6d3fc96e1685 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c
|
|
+++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.c
|
|
@@ -25,7 +25,7 @@
|
|
struct mlx5_irq {
|
|
struct atomic_notifier_head nh;
|
|
cpumask_var_t mask;
|
|
- char name[MLX5_MAX_IRQ_NAME];
|
|
+ char name[MLX5_MAX_IRQ_FORMATTED_NAME];
|
|
struct mlx5_irq_pool *pool;
|
|
int refcount;
|
|
u32 index;
|
|
@@ -236,8 +236,8 @@ struct mlx5_irq *mlx5_irq_alloc(struct mlx5_irq_pool *pool, int i,
|
|
else
|
|
irq_sf_set_name(pool, name, i);
|
|
ATOMIC_INIT_NOTIFIER_HEAD(&irq->nh);
|
|
- snprintf(irq->name, MLX5_MAX_IRQ_NAME,
|
|
- "%s@pci:%s", name, pci_name(dev->pdev));
|
|
+ snprintf(irq->name, MLX5_MAX_IRQ_FORMATTED_NAME,
|
|
+ MLX5_IRQ_NAME_FORMAT_STR, name, pci_name(dev->pdev));
|
|
err = request_irq(irq->irqn, irq_int_handler, 0, irq->name,
|
|
&irq->nh);
|
|
if (err) {
|
|
diff --git a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h
|
|
index 5c7e68bee43a0..4047179307c4a 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h
|
|
+++ b/drivers/net/ethernet/mellanox/mlx5/core/pci_irq.h
|
|
@@ -7,6 +7,9 @@
|
|
#include <linux/mlx5/driver.h>
|
|
|
|
#define MLX5_MAX_IRQ_NAME (32)
|
|
+#define MLX5_IRQ_NAME_FORMAT_STR ("%s@pci:%s")
|
|
+#define MLX5_MAX_IRQ_FORMATTED_NAME \
|
|
+ (MLX5_MAX_IRQ_NAME + sizeof(MLX5_IRQ_NAME_FORMAT_STR))
|
|
/* max irq_index is 2047, so four chars */
|
|
#define MLX5_MAX_IRQ_IDX_CHARS (4)
|
|
#define MLX5_EQ_REFS_PER_IRQ (2)
|
|
diff --git a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c
|
|
index 0d5a41a2ae010..227d01cace3f0 100644
|
|
--- a/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c
|
|
+++ b/drivers/net/ethernet/mellanox/mlxbf_gige/mlxbf_gige_rx.c
|
|
@@ -267,6 +267,13 @@ static bool mlxbf_gige_rx_packet(struct mlxbf_gige *priv, int *rx_pkts)
|
|
priv->stats.rx_truncate_errors++;
|
|
}
|
|
|
|
+ /* Read receive consumer index before replenish so that this routine
|
|
+ * returns accurate return value even if packet is received into
|
|
+ * just-replenished buffer prior to exiting this routine.
|
|
+ */
|
|
+ rx_ci = readq(priv->base + MLXBF_GIGE_RX_CQE_PACKET_CI);
|
|
+ rx_ci_rem = rx_ci % priv->rx_q_entries;
|
|
+
|
|
/* Let hardware know we've replenished one buffer */
|
|
rx_pi++;
|
|
|
|
@@ -279,8 +286,6 @@ static bool mlxbf_gige_rx_packet(struct mlxbf_gige *priv, int *rx_pkts)
|
|
rx_pi_rem = rx_pi % priv->rx_q_entries;
|
|
if (rx_pi_rem == 0)
|
|
priv->valid_polarity ^= 1;
|
|
- rx_ci = readq(priv->base + MLXBF_GIGE_RX_CQE_PACKET_CI);
|
|
- rx_ci_rem = rx_ci % priv->rx_q_entries;
|
|
|
|
if (skb)
|
|
netif_receive_skb(skb);
|
|
diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c
|
|
index 0d57ffcedf0c6..fc78bc959ded8 100644
|
|
--- a/drivers/net/ethernet/qlogic/qla3xxx.c
|
|
+++ b/drivers/net/ethernet/qlogic/qla3xxx.c
|
|
@@ -2591,6 +2591,7 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev)
|
|
|
|
if (qdev->lrg_buf_q_alloc_virt_addr == NULL) {
|
|
netdev_err(qdev->ndev, "lBufQ failed\n");
|
|
+ kfree(qdev->lrg_buf);
|
|
return -ENOMEM;
|
|
}
|
|
qdev->lrg_buf_q_virt_addr = qdev->lrg_buf_q_alloc_virt_addr;
|
|
@@ -2615,6 +2616,7 @@ static int ql_alloc_buffer_queues(struct ql3_adapter *qdev)
|
|
qdev->lrg_buf_q_alloc_size,
|
|
qdev->lrg_buf_q_alloc_virt_addr,
|
|
qdev->lrg_buf_q_alloc_phy_addr);
|
|
+ kfree(qdev->lrg_buf);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
diff --git a/drivers/net/ethernet/realtek/r8169_main.c b/drivers/net/ethernet/realtek/r8169_main.c
|
|
index d22457f2cf9cf..06663c11ca96d 100644
|
|
--- a/drivers/net/ethernet/realtek/r8169_main.c
|
|
+++ b/drivers/net/ethernet/realtek/r8169_main.c
|
|
@@ -1145,7 +1145,7 @@ static void rtl8168ep_driver_start(struct rtl8169_private *tp)
|
|
{
|
|
r8168ep_ocp_write(tp, 0x01, 0x180, OOB_CMD_DRIVER_START);
|
|
r8168ep_ocp_write(tp, 0x01, 0x30, r8168ep_ocp_read(tp, 0x30) | 0x01);
|
|
- rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 10);
|
|
+ rtl_loop_wait_high(tp, &rtl_ep_ocp_read_cond, 10000, 30);
|
|
}
|
|
|
|
static void rtl8168_driver_start(struct rtl8169_private *tp)
|
|
diff --git a/drivers/net/ethernet/renesas/ravb_main.c b/drivers/net/ethernet/renesas/ravb_main.c
|
|
index 68cb5616ef991..c2c56a5289caf 100644
|
|
--- a/drivers/net/ethernet/renesas/ravb_main.c
|
|
+++ b/drivers/net/ethernet/renesas/ravb_main.c
|
|
@@ -68,16 +68,27 @@ int ravb_wait(struct net_device *ndev, enum ravb_reg reg, u32 mask, u32 value)
|
|
return -ETIMEDOUT;
|
|
}
|
|
|
|
-static int ravb_config(struct net_device *ndev)
|
|
+static int ravb_set_opmode(struct net_device *ndev, u32 opmode)
|
|
{
|
|
+ u32 csr_ops = 1U << (opmode & CCC_OPC);
|
|
+ u32 ccc_mask = CCC_OPC;
|
|
int error;
|
|
|
|
- /* Set config mode */
|
|
- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG);
|
|
- /* Check if the operating mode is changed to the config mode */
|
|
- error = ravb_wait(ndev, CSR, CSR_OPS, CSR_OPS_CONFIG);
|
|
- if (error)
|
|
- netdev_err(ndev, "failed to switch device to config mode\n");
|
|
+ /* If gPTP active in config mode is supported it needs to be configured
|
|
+ * along with CSEL and operating mode in the same access. This is a
|
|
+ * hardware limitation.
|
|
+ */
|
|
+ if (opmode & CCC_GAC)
|
|
+ ccc_mask |= CCC_GAC | CCC_CSEL;
|
|
+
|
|
+ /* Set operating mode */
|
|
+ ravb_modify(ndev, CCC, ccc_mask, opmode);
|
|
+ /* Check if the operating mode is changed to the requested one */
|
|
+ error = ravb_wait(ndev, CSR, CSR_OPS, csr_ops);
|
|
+ if (error) {
|
|
+ netdev_err(ndev, "failed to switch device to requested mode (%u)\n",
|
|
+ opmode & CCC_OPC);
|
|
+ }
|
|
|
|
return error;
|
|
}
|
|
@@ -675,7 +686,7 @@ static int ravb_dmac_init(struct net_device *ndev)
|
|
int error;
|
|
|
|
/* Set CONFIG mode */
|
|
- error = ravb_config(ndev);
|
|
+ error = ravb_set_opmode(ndev, CCC_OPC_CONFIG);
|
|
if (error)
|
|
return error;
|
|
|
|
@@ -684,9 +695,7 @@ static int ravb_dmac_init(struct net_device *ndev)
|
|
return error;
|
|
|
|
/* Setting the control will start the AVB-DMAC process. */
|
|
- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_OPERATION);
|
|
-
|
|
- return 0;
|
|
+ return ravb_set_opmode(ndev, CCC_OPC_OPERATION);
|
|
}
|
|
|
|
static void ravb_get_tx_tstamp(struct net_device *ndev)
|
|
@@ -1048,7 +1057,7 @@ static int ravb_stop_dma(struct net_device *ndev)
|
|
return error;
|
|
|
|
/* Stop AVB-DMAC process */
|
|
- return ravb_config(ndev);
|
|
+ return ravb_set_opmode(ndev, CCC_OPC_CONFIG);
|
|
}
|
|
|
|
/* E-MAC interrupt handler */
|
|
@@ -2576,21 +2585,25 @@ static int ravb_set_gti(struct net_device *ndev)
|
|
return 0;
|
|
}
|
|
|
|
-static void ravb_set_config_mode(struct net_device *ndev)
|
|
+static int ravb_set_config_mode(struct net_device *ndev)
|
|
{
|
|
struct ravb_private *priv = netdev_priv(ndev);
|
|
const struct ravb_hw_info *info = priv->info;
|
|
+ int error;
|
|
|
|
if (info->gptp) {
|
|
- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG);
|
|
+ error = ravb_set_opmode(ndev, CCC_OPC_CONFIG);
|
|
+ if (error)
|
|
+ return error;
|
|
/* Set CSEL value */
|
|
ravb_modify(ndev, CCC, CCC_CSEL, CCC_CSEL_HPB);
|
|
} else if (info->ccc_gac) {
|
|
- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG |
|
|
- CCC_GAC | CCC_CSEL_HPB);
|
|
+ error = ravb_set_opmode(ndev, CCC_OPC_CONFIG | CCC_GAC | CCC_CSEL_HPB);
|
|
} else {
|
|
- ravb_modify(ndev, CCC, CCC_OPC, CCC_OPC_CONFIG);
|
|
+ error = ravb_set_opmode(ndev, CCC_OPC_CONFIG);
|
|
}
|
|
+
|
|
+ return error;
|
|
}
|
|
|
|
/* Set tx and rx clock internal delay modes */
|
|
@@ -2810,7 +2823,9 @@ static int ravb_probe(struct platform_device *pdev)
|
|
ndev->ethtool_ops = &ravb_ethtool_ops;
|
|
|
|
/* Set AVB config mode */
|
|
- ravb_set_config_mode(ndev);
|
|
+ error = ravb_set_config_mode(ndev);
|
|
+ if (error)
|
|
+ goto out_disable_gptp_clk;
|
|
|
|
if (info->gptp || info->ccc_gac) {
|
|
/* Set GTI value */
|
|
@@ -2933,8 +2948,7 @@ static int ravb_remove(struct platform_device *pdev)
|
|
dma_free_coherent(ndev->dev.parent, priv->desc_bat_size, priv->desc_bat,
|
|
priv->desc_bat_dma);
|
|
|
|
- /* Set reset mode */
|
|
- ravb_write(ndev, CCC_OPC_RESET, CCC);
|
|
+ ravb_set_opmode(ndev, CCC_OPC_RESET);
|
|
|
|
clk_disable_unprepare(priv->gptp_clk);
|
|
clk_disable_unprepare(priv->refclk);
|
|
@@ -3018,8 +3032,11 @@ static int __maybe_unused ravb_resume(struct device *dev)
|
|
int ret = 0;
|
|
|
|
/* If WoL is enabled set reset mode to rearm the WoL logic */
|
|
- if (priv->wol_enabled)
|
|
- ravb_write(ndev, CCC_OPC_RESET, CCC);
|
|
+ if (priv->wol_enabled) {
|
|
+ ret = ravb_set_opmode(ndev, CCC_OPC_RESET);
|
|
+ if (ret)
|
|
+ return ret;
|
|
+ }
|
|
|
|
/* All register have been reset to default values.
|
|
* Restore all registers which where setup at probe time and
|
|
@@ -3027,7 +3044,9 @@ static int __maybe_unused ravb_resume(struct device *dev)
|
|
*/
|
|
|
|
/* Set AVB config mode */
|
|
- ravb_set_config_mode(ndev);
|
|
+ ret = ravb_set_config_mode(ndev);
|
|
+ if (ret)
|
|
+ return ret;
|
|
|
|
if (info->gptp || info->ccc_gac) {
|
|
/* Set GTI value */
|
|
diff --git a/drivers/net/ethernet/sfc/rx_common.c b/drivers/net/ethernet/sfc/rx_common.c
|
|
index 9220afeddee81..3f290791df1c4 100644
|
|
--- a/drivers/net/ethernet/sfc/rx_common.c
|
|
+++ b/drivers/net/ethernet/sfc/rx_common.c
|
|
@@ -820,8 +820,10 @@ int efx_probe_filters(struct efx_nic *efx)
|
|
}
|
|
|
|
if (!success) {
|
|
- efx_for_each_channel(channel, efx)
|
|
+ efx_for_each_channel(channel, efx) {
|
|
kfree(channel->rps_flow_id);
|
|
+ channel->rps_flow_id = NULL;
|
|
+ }
|
|
efx->type->filter_table_remove(efx);
|
|
rc = -ENOMEM;
|
|
goto out_unlock;
|
|
diff --git a/drivers/net/gtp.c b/drivers/net/gtp.c
|
|
index 477b4d4f860bd..bace989591f75 100644
|
|
--- a/drivers/net/gtp.c
|
|
+++ b/drivers/net/gtp.c
|
|
@@ -629,7 +629,7 @@ static void __gtp_encap_destroy(struct sock *sk)
|
|
gtp->sk0 = NULL;
|
|
else
|
|
gtp->sk1u = NULL;
|
|
- udp_sk(sk)->encap_type = 0;
|
|
+ WRITE_ONCE(udp_sk(sk)->encap_type, 0);
|
|
rcu_assign_sk_user_data(sk, NULL);
|
|
release_sock(sk);
|
|
sock_put(sk);
|
|
@@ -681,7 +681,7 @@ static int gtp_encap_recv(struct sock *sk, struct sk_buff *skb)
|
|
|
|
netdev_dbg(gtp->dev, "encap_recv sk=%p\n", sk);
|
|
|
|
- switch (udp_sk(sk)->encap_type) {
|
|
+ switch (READ_ONCE(udp_sk(sk)->encap_type)) {
|
|
case UDP_ENCAP_GTP0:
|
|
netdev_dbg(gtp->dev, "received GTP0 packet\n");
|
|
ret = gtp0_udp_encap_recv(gtp, skb);
|
|
diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
|
|
index 3777c7e2e6fc0..e47bb125048d4 100644
|
|
--- a/drivers/net/usb/ax88172a.c
|
|
+++ b/drivers/net/usb/ax88172a.c
|
|
@@ -161,7 +161,9 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
|
|
u8 buf[ETH_ALEN];
|
|
struct ax88172a_private *priv;
|
|
|
|
- usbnet_get_endpoints(dev, intf);
|
|
+ ret = usbnet_get_endpoints(dev, intf);
|
|
+ if (ret)
|
|
+ return ret;
|
|
|
|
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
|
if (!priv)
|
|
diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
|
|
index 157d1f31c4871..c5a306b01fe20 100644
|
|
--- a/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
|
|
+++ b/drivers/net/wireless/intel/iwlwifi/iwl-prph.h
|
|
@@ -348,8 +348,8 @@
|
|
#define RFIC_REG_RD 0xAD0470
|
|
#define WFPM_CTRL_REG 0xA03030
|
|
#define WFPM_OTP_CFG1_ADDR 0x00a03098
|
|
-#define WFPM_OTP_CFG1_IS_JACKET_BIT BIT(4)
|
|
-#define WFPM_OTP_CFG1_IS_CDB_BIT BIT(5)
|
|
+#define WFPM_OTP_CFG1_IS_JACKET_BIT BIT(5)
|
|
+#define WFPM_OTP_CFG1_IS_CDB_BIT BIT(4)
|
|
|
|
#define WFPM_GP2 0xA030B4
|
|
|
|
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
|
|
index 69b95ad5993b0..2ec4ee8ab317c 100644
|
|
--- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
|
|
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h
|
|
@@ -745,7 +745,7 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans)
|
|
}
|
|
}
|
|
|
|
-void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans);
|
|
+void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq);
|
|
|
|
static inline bool iwl_is_rfkill_set(struct iwl_trans *trans)
|
|
{
|
|
@@ -792,7 +792,7 @@ static inline bool iwl_pcie_dbg_on(struct iwl_trans *trans)
|
|
return (trans->dbg.dest_tlv || iwl_trans_dbg_ini_valid(trans));
|
|
}
|
|
|
|
-void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state);
|
|
+void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq);
|
|
void iwl_trans_pcie_dump_regs(struct iwl_trans *trans);
|
|
|
|
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
|
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
|
|
index 90a46faaaffdf..57a11ee05bc36 100644
|
|
--- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
|
|
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c
|
|
@@ -1781,7 +1781,7 @@ static u32 iwl_pcie_int_cause_ict(struct iwl_trans *trans)
|
|
return inta;
|
|
}
|
|
|
|
-void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans)
|
|
+void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq)
|
|
{
|
|
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
struct isr_statistics *isr_stats = &trans_pcie->isr_stats;
|
|
@@ -1805,7 +1805,7 @@ void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans)
|
|
isr_stats->rfkill++;
|
|
|
|
if (prev != report)
|
|
- iwl_trans_pcie_rf_kill(trans, report);
|
|
+ iwl_trans_pcie_rf_kill(trans, report, from_irq);
|
|
mutex_unlock(&trans_pcie->mutex);
|
|
|
|
if (hw_rfkill) {
|
|
@@ -1945,7 +1945,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
|
|
|
|
/* HW RF KILL switch toggled */
|
|
if (inta & CSR_INT_BIT_RF_KILL) {
|
|
- iwl_pcie_handle_rfkill_irq(trans);
|
|
+ iwl_pcie_handle_rfkill_irq(trans, true);
|
|
handled |= CSR_INT_BIT_RF_KILL;
|
|
}
|
|
|
|
@@ -2362,7 +2362,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id)
|
|
|
|
/* HW RF KILL switch toggled */
|
|
if (inta_hw & MSIX_HW_INT_CAUSES_REG_RF_KILL)
|
|
- iwl_pcie_handle_rfkill_irq(trans);
|
|
+ iwl_pcie_handle_rfkill_irq(trans, true);
|
|
|
|
if (inta_hw & MSIX_HW_INT_CAUSES_REG_HW_ERR) {
|
|
IWL_ERR(trans,
|
|
diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
|
|
index 796972f224326..c7ed35b3dd8d5 100644
|
|
--- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
|
|
+++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c
|
|
@@ -1080,7 +1080,7 @@ bool iwl_pcie_check_hw_rf_kill(struct iwl_trans *trans)
|
|
report = test_bit(STATUS_RFKILL_OPMODE, &trans->status);
|
|
|
|
if (prev != report)
|
|
- iwl_trans_pcie_rf_kill(trans, report);
|
|
+ iwl_trans_pcie_rf_kill(trans, report, false);
|
|
|
|
return hw_rfkill;
|
|
}
|
|
@@ -1234,7 +1234,7 @@ static void iwl_pcie_init_msix(struct iwl_trans_pcie *trans_pcie)
|
|
trans_pcie->hw_mask = trans_pcie->hw_init_mask;
|
|
}
|
|
|
|
-static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans)
|
|
+static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq)
|
|
{
|
|
struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
|
|
@@ -1261,7 +1261,8 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans)
|
|
if (test_and_clear_bit(STATUS_DEVICE_ENABLED, &trans->status)) {
|
|
IWL_DEBUG_INFO(trans,
|
|
"DEVICE_ENABLED bit was set and is now cleared\n");
|
|
- iwl_pcie_synchronize_irqs(trans);
|
|
+ if (!from_irq)
|
|
+ iwl_pcie_synchronize_irqs(trans);
|
|
iwl_pcie_rx_napi_sync(trans);
|
|
iwl_pcie_tx_stop(trans);
|
|
iwl_pcie_rx_stop(trans);
|
|
@@ -1451,7 +1452,7 @@ void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans,
|
|
clear_bit(STATUS_RFKILL_OPMODE, &trans->status);
|
|
}
|
|
if (hw_rfkill != was_in_rfkill)
|
|
- iwl_trans_pcie_rf_kill(trans, hw_rfkill);
|
|
+ iwl_trans_pcie_rf_kill(trans, hw_rfkill, false);
|
|
}
|
|
|
|
static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
|
|
@@ -1466,12 +1467,12 @@ static void iwl_trans_pcie_stop_device(struct iwl_trans *trans)
|
|
mutex_lock(&trans_pcie->mutex);
|
|
trans_pcie->opmode_down = true;
|
|
was_in_rfkill = test_bit(STATUS_RFKILL_OPMODE, &trans->status);
|
|
- _iwl_trans_pcie_stop_device(trans);
|
|
+ _iwl_trans_pcie_stop_device(trans, false);
|
|
iwl_trans_pcie_handle_stop_rfkill(trans, was_in_rfkill);
|
|
mutex_unlock(&trans_pcie->mutex);
|
|
}
|
|
|
|
-void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
|
|
+void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq)
|
|
{
|
|
struct iwl_trans_pcie __maybe_unused *trans_pcie =
|
|
IWL_TRANS_GET_PCIE_TRANS(trans);
|
|
@@ -1484,7 +1485,7 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state)
|
|
if (trans->trans_cfg->gen2)
|
|
_iwl_trans_pcie_gen2_stop_device(trans);
|
|
else
|
|
- _iwl_trans_pcie_stop_device(trans);
|
|
+ _iwl_trans_pcie_stop_device(trans, from_irq);
|
|
}
|
|
}
|
|
|
|
@@ -2815,7 +2816,7 @@ static ssize_t iwl_dbgfs_rfkill_write(struct file *file,
|
|
IWL_WARN(trans, "changing debug rfkill %d->%d\n",
|
|
trans_pcie->debug_rfkill, new_value);
|
|
trans_pcie->debug_rfkill = new_value;
|
|
- iwl_pcie_handle_rfkill_irq(trans);
|
|
+ iwl_pcie_handle_rfkill_irq(trans, false);
|
|
|
|
return count;
|
|
}
|
|
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
|
|
index 8df156c28aade..5368a37154cf9 100644
|
|
--- a/drivers/pci/pci.c
|
|
+++ b/drivers/pci/pci.c
|
|
@@ -1302,6 +1302,9 @@ static int pci_set_full_power_state(struct pci_dev *dev)
|
|
pci_restore_bars(dev);
|
|
}
|
|
|
|
+ if (dev->bus->self)
|
|
+ pcie_aspm_pm_state_change(dev->bus->self);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
@@ -1396,6 +1399,9 @@ static int pci_set_low_power_state(struct pci_dev *dev, pci_power_t state)
|
|
pci_power_name(dev->current_state),
|
|
pci_power_name(state));
|
|
|
|
+ if (dev->bus->self)
|
|
+ pcie_aspm_pm_state_change(dev->bus->self);
|
|
+
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
|
|
index ffccb03933e27..ed6d75d138c7a 100644
|
|
--- a/drivers/pci/pci.h
|
|
+++ b/drivers/pci/pci.h
|
|
@@ -561,10 +561,12 @@ bool pcie_wait_for_link(struct pci_dev *pdev, bool active);
|
|
#ifdef CONFIG_PCIEASPM
|
|
void pcie_aspm_init_link_state(struct pci_dev *pdev);
|
|
void pcie_aspm_exit_link_state(struct pci_dev *pdev);
|
|
+void pcie_aspm_pm_state_change(struct pci_dev *pdev);
|
|
void pcie_aspm_powersave_config_link(struct pci_dev *pdev);
|
|
#else
|
|
static inline void pcie_aspm_init_link_state(struct pci_dev *pdev) { }
|
|
static inline void pcie_aspm_exit_link_state(struct pci_dev *pdev) { }
|
|
+static inline void pcie_aspm_pm_state_change(struct pci_dev *pdev) { }
|
|
static inline void pcie_aspm_powersave_config_link(struct pci_dev *pdev) { }
|
|
#endif
|
|
|
|
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
|
|
index 5d1756f53ba84..25736d408e88e 100644
|
|
--- a/drivers/pci/pcie/aspm.c
|
|
+++ b/drivers/pci/pcie/aspm.c
|
|
@@ -1055,6 +1055,25 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev)
|
|
up_read(&pci_bus_sem);
|
|
}
|
|
|
|
+/* @pdev: the root port or switch downstream port */
|
|
+void pcie_aspm_pm_state_change(struct pci_dev *pdev)
|
|
+{
|
|
+ struct pcie_link_state *link = pdev->link_state;
|
|
+
|
|
+ if (aspm_disabled || !link)
|
|
+ return;
|
|
+ /*
|
|
+ * Devices changed PM state, we should recheck if latency
|
|
+ * meets all functions' requirement
|
|
+ */
|
|
+ down_read(&pci_bus_sem);
|
|
+ mutex_lock(&aspm_lock);
|
|
+ pcie_update_aspm_capable(link->root);
|
|
+ pcie_config_aspm_path(link);
|
|
+ mutex_unlock(&aspm_lock);
|
|
+ up_read(&pci_bus_sem);
|
|
+}
|
|
+
|
|
void pcie_aspm_powersave_config_link(struct pci_dev *pdev)
|
|
{
|
|
struct pcie_link_state *link = pdev->link_state;
|
|
diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c
|
|
index b194e71f07bfc..aa51cb72cbba5 100644
|
|
--- a/drivers/video/fbdev/imsttfb.c
|
|
+++ b/drivers/video/fbdev/imsttfb.c
|
|
@@ -1419,7 +1419,6 @@ static int init_imstt(struct fb_info *info)
|
|
if ((info->var.xres * info->var.yres) * (info->var.bits_per_pixel >> 3) > info->fix.smem_len
|
|
|| !(compute_imstt_regvals(par, info->var.xres, info->var.yres))) {
|
|
printk("imsttfb: %ux%ux%u not supported\n", info->var.xres, info->var.yres, info->var.bits_per_pixel);
|
|
- framebuffer_release(info);
|
|
return -ENODEV;
|
|
}
|
|
|
|
@@ -1452,10 +1451,11 @@ static int init_imstt(struct fb_info *info)
|
|
FBINFO_HWACCEL_FILLRECT |
|
|
FBINFO_HWACCEL_YPAN;
|
|
|
|
- fb_alloc_cmap(&info->cmap, 0, 0);
|
|
+ if (fb_alloc_cmap(&info->cmap, 0, 0))
|
|
+ return -ENODEV;
|
|
|
|
if (register_framebuffer(info) < 0) {
|
|
- framebuffer_release(info);
|
|
+ fb_dealloc_cmap(&info->cmap);
|
|
return -ENODEV;
|
|
}
|
|
|
|
diff --git a/fs/9p/cache.c b/fs/9p/cache.c
|
|
index cebba4eaa0b57..12c0ae29f1857 100644
|
|
--- a/fs/9p/cache.c
|
|
+++ b/fs/9p/cache.c
|
|
@@ -68,6 +68,8 @@ void v9fs_cache_inode_get_cookie(struct inode *inode)
|
|
&path, sizeof(path),
|
|
&version, sizeof(version),
|
|
i_size_read(&v9inode->netfs.inode));
|
|
+ if (v9inode->netfs.cache)
|
|
+ mapping_set_release_always(inode->i_mapping);
|
|
|
|
p9_debug(P9_DEBUG_FSC, "inode %p get cookie %p\n",
|
|
inode, v9fs_inode_cookie(v9inode));
|
|
diff --git a/fs/afs/internal.h b/fs/afs/internal.h
|
|
index fcbb598d8c85d..a25fdc3e52310 100644
|
|
--- a/fs/afs/internal.h
|
|
+++ b/fs/afs/internal.h
|
|
@@ -682,6 +682,8 @@ static inline void afs_vnode_set_cache(struct afs_vnode *vnode,
|
|
{
|
|
#ifdef CONFIG_AFS_FSCACHE
|
|
vnode->netfs.cache = cookie;
|
|
+ if (cookie)
|
|
+ mapping_set_release_always(vnode->netfs.inode.i_mapping);
|
|
#endif
|
|
}
|
|
|
|
diff --git a/fs/btrfs/delalloc-space.c b/fs/btrfs/delalloc-space.c
|
|
index 0b62ce77053f5..f2bc5563c0f92 100644
|
|
--- a/fs/btrfs/delalloc-space.c
|
|
+++ b/fs/btrfs/delalloc-space.c
|
|
@@ -197,7 +197,7 @@ void btrfs_free_reserved_data_space(struct btrfs_inode *inode,
|
|
start = round_down(start, fs_info->sectorsize);
|
|
|
|
btrfs_free_reserved_data_space_noquota(fs_info, len);
|
|
- btrfs_qgroup_free_data(inode, reserved, start, len);
|
|
+ btrfs_qgroup_free_data(inode, reserved, start, len, NULL);
|
|
}
|
|
|
|
/**
|
|
diff --git a/fs/btrfs/file-item.c b/fs/btrfs/file-item.c
|
|
index b14d2da9b26d3..14478da875313 100644
|
|
--- a/fs/btrfs/file-item.c
|
|
+++ b/fs/btrfs/file-item.c
|
|
@@ -602,7 +602,7 @@ int btrfs_lookup_csums_range(struct btrfs_root *root, u64 start, u64 end,
|
|
}
|
|
|
|
sums->bytenr = start;
|
|
- sums->len = (int)size;
|
|
+ sums->len = size;
|
|
|
|
offset = (start - key.offset) >> fs_info->sectorsize_bits;
|
|
offset *= csum_size;
|
|
diff --git a/fs/btrfs/file.c b/fs/btrfs/file.c
|
|
index 0a46fff3dd067..1783a0fbf1665 100644
|
|
--- a/fs/btrfs/file.c
|
|
+++ b/fs/btrfs/file.c
|
|
@@ -3191,7 +3191,7 @@ static long btrfs_fallocate(struct file *file, int mode,
|
|
qgroup_reserved -= range->len;
|
|
} else if (qgroup_reserved > 0) {
|
|
btrfs_qgroup_free_data(BTRFS_I(inode), data_reserved,
|
|
- range->start, range->len);
|
|
+ range->start, range->len, NULL);
|
|
qgroup_reserved -= range->len;
|
|
}
|
|
list_del(&range->list);
|
|
diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c
|
|
index 81eac121c6b23..9a7d77c410e22 100644
|
|
--- a/fs/btrfs/inode.c
|
|
+++ b/fs/btrfs/inode.c
|
|
@@ -466,7 +466,7 @@ out:
|
|
* And at reserve time, it's always aligned to page size, so
|
|
* just free one page here.
|
|
*/
|
|
- btrfs_qgroup_free_data(inode, NULL, 0, PAGE_SIZE);
|
|
+ btrfs_qgroup_free_data(inode, NULL, 0, PAGE_SIZE, NULL);
|
|
btrfs_free_path(path);
|
|
btrfs_end_transaction(trans);
|
|
return ret;
|
|
@@ -5372,7 +5372,7 @@ static void evict_inode_truncate_pages(struct inode *inode)
|
|
*/
|
|
if (state_flags & EXTENT_DELALLOC)
|
|
btrfs_qgroup_free_data(BTRFS_I(inode), NULL, start,
|
|
- end - start + 1);
|
|
+ end - start + 1, NULL);
|
|
|
|
clear_extent_bit(io_tree, start, end,
|
|
EXTENT_CLEAR_ALL_BITS | EXTENT_DO_ACCOUNTING,
|
|
@@ -8440,7 +8440,7 @@ next:
|
|
* reserved data space.
|
|
* Since the IO will never happen for this page.
|
|
*/
|
|
- btrfs_qgroup_free_data(inode, NULL, cur, range_end + 1 - cur);
|
|
+ btrfs_qgroup_free_data(inode, NULL, cur, range_end + 1 - cur, NULL);
|
|
if (!inode_evicting) {
|
|
clear_extent_bit(tree, cur, range_end, EXTENT_LOCKED |
|
|
EXTENT_DELALLOC | EXTENT_UPTODATE |
|
|
@@ -9902,7 +9902,7 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
|
|
struct btrfs_path *path;
|
|
u64 start = ins->objectid;
|
|
u64 len = ins->offset;
|
|
- int qgroup_released;
|
|
+ u64 qgroup_released = 0;
|
|
int ret;
|
|
|
|
memset(&stack_fi, 0, sizeof(stack_fi));
|
|
@@ -9915,9 +9915,9 @@ static struct btrfs_trans_handle *insert_prealloc_file_extent(
|
|
btrfs_set_stack_file_extent_compression(&stack_fi, BTRFS_COMPRESS_NONE);
|
|
/* Encryption and other encoding is reserved and all 0 */
|
|
|
|
- qgroup_released = btrfs_qgroup_release_data(inode, file_offset, len);
|
|
- if (qgroup_released < 0)
|
|
- return ERR_PTR(qgroup_released);
|
|
+ ret = btrfs_qgroup_release_data(inode, file_offset, len, &qgroup_released);
|
|
+ if (ret < 0)
|
|
+ return ERR_PTR(ret);
|
|
|
|
if (trans) {
|
|
ret = insert_reserved_file_extent(trans, inode,
|
|
@@ -10903,7 +10903,7 @@ out_delalloc_release:
|
|
btrfs_delalloc_release_metadata(inode, disk_num_bytes, ret < 0);
|
|
out_qgroup_free_data:
|
|
if (ret < 0)
|
|
- btrfs_qgroup_free_data(inode, data_reserved, start, num_bytes);
|
|
+ btrfs_qgroup_free_data(inode, data_reserved, start, num_bytes, NULL);
|
|
out_free_data_space:
|
|
/*
|
|
* If btrfs_reserve_extent() succeeded, then we already decremented
|
|
diff --git a/fs/btrfs/ordered-data.c b/fs/btrfs/ordered-data.c
|
|
index 0321753c16b9f..1b2af4785c0e2 100644
|
|
--- a/fs/btrfs/ordered-data.c
|
|
+++ b/fs/btrfs/ordered-data.c
|
|
@@ -172,11 +172,12 @@ int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset,
|
|
struct rb_node *node;
|
|
struct btrfs_ordered_extent *entry;
|
|
int ret;
|
|
+ u64 qgroup_rsv = 0;
|
|
|
|
if (flags &
|
|
((1 << BTRFS_ORDERED_NOCOW) | (1 << BTRFS_ORDERED_PREALLOC))) {
|
|
/* For nocow write, we can release the qgroup rsv right now */
|
|
- ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes);
|
|
+ ret = btrfs_qgroup_free_data(inode, NULL, file_offset, num_bytes, &qgroup_rsv);
|
|
if (ret < 0)
|
|
return ret;
|
|
ret = 0;
|
|
@@ -185,7 +186,7 @@ int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset,
|
|
* The ordered extent has reserved qgroup space, release now
|
|
* and pass the reserved number for qgroup_record to free.
|
|
*/
|
|
- ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes);
|
|
+ ret = btrfs_qgroup_release_data(inode, file_offset, num_bytes, &qgroup_rsv);
|
|
if (ret < 0)
|
|
return ret;
|
|
}
|
|
@@ -203,7 +204,7 @@ int btrfs_add_ordered_extent(struct btrfs_inode *inode, u64 file_offset,
|
|
entry->inode = igrab(&inode->vfs_inode);
|
|
entry->compress_type = compress_type;
|
|
entry->truncated_len = (u64)-1;
|
|
- entry->qgroup_rsv = ret;
|
|
+ entry->qgroup_rsv = qgroup_rsv;
|
|
entry->physical = (u64)-1;
|
|
|
|
ASSERT((flags & ~BTRFS_ORDERED_TYPE_FLAGS) == 0);
|
|
diff --git a/fs/btrfs/ordered-data.h b/fs/btrfs/ordered-data.h
|
|
index f59f2dbdb25ed..cc3ca4bb9bd54 100644
|
|
--- a/fs/btrfs/ordered-data.h
|
|
+++ b/fs/btrfs/ordered-data.h
|
|
@@ -20,7 +20,7 @@ struct btrfs_ordered_sum {
|
|
/*
|
|
* this is the length in bytes covered by the sums array below.
|
|
*/
|
|
- int len;
|
|
+ u32 len;
|
|
struct list_head list;
|
|
/* last field is a variable length array of csums */
|
|
u8 sums[];
|
|
diff --git a/fs/btrfs/qgroup.c b/fs/btrfs/qgroup.c
|
|
index 26cabffd59710..96ec9ccc2ef61 100644
|
|
--- a/fs/btrfs/qgroup.c
|
|
+++ b/fs/btrfs/qgroup.c
|
|
@@ -3833,13 +3833,14 @@ int btrfs_qgroup_reserve_data(struct btrfs_inode *inode,
|
|
|
|
/* Free ranges specified by @reserved, normally in error path */
|
|
static int qgroup_free_reserved_data(struct btrfs_inode *inode,
|
|
- struct extent_changeset *reserved, u64 start, u64 len)
|
|
+ struct extent_changeset *reserved,
|
|
+ u64 start, u64 len, u64 *freed_ret)
|
|
{
|
|
struct btrfs_root *root = inode->root;
|
|
struct ulist_node *unode;
|
|
struct ulist_iterator uiter;
|
|
struct extent_changeset changeset;
|
|
- int freed = 0;
|
|
+ u64 freed = 0;
|
|
int ret;
|
|
|
|
extent_changeset_init(&changeset);
|
|
@@ -3880,7 +3881,9 @@ static int qgroup_free_reserved_data(struct btrfs_inode *inode,
|
|
}
|
|
btrfs_qgroup_free_refroot(root->fs_info, root->root_key.objectid, freed,
|
|
BTRFS_QGROUP_RSV_DATA);
|
|
- ret = freed;
|
|
+ if (freed_ret)
|
|
+ *freed_ret = freed;
|
|
+ ret = 0;
|
|
out:
|
|
extent_changeset_release(&changeset);
|
|
return ret;
|
|
@@ -3888,7 +3891,7 @@ out:
|
|
|
|
static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
|
|
struct extent_changeset *reserved, u64 start, u64 len,
|
|
- int free)
|
|
+ u64 *released, int free)
|
|
{
|
|
struct extent_changeset changeset;
|
|
int trace_op = QGROUP_RELEASE;
|
|
@@ -3900,7 +3903,7 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
|
|
/* In release case, we shouldn't have @reserved */
|
|
WARN_ON(!free && reserved);
|
|
if (free && reserved)
|
|
- return qgroup_free_reserved_data(inode, reserved, start, len);
|
|
+ return qgroup_free_reserved_data(inode, reserved, start, len, released);
|
|
extent_changeset_init(&changeset);
|
|
ret = clear_record_extent_bits(&inode->io_tree, start, start + len -1,
|
|
EXTENT_QGROUP_RESERVED, &changeset);
|
|
@@ -3915,7 +3918,8 @@ static int __btrfs_qgroup_release_data(struct btrfs_inode *inode,
|
|
btrfs_qgroup_free_refroot(inode->root->fs_info,
|
|
inode->root->root_key.objectid,
|
|
changeset.bytes_changed, BTRFS_QGROUP_RSV_DATA);
|
|
- ret = changeset.bytes_changed;
|
|
+ if (released)
|
|
+ *released = changeset.bytes_changed;
|
|
out:
|
|
extent_changeset_release(&changeset);
|
|
return ret;
|
|
@@ -3934,9 +3938,10 @@ out:
|
|
* NOTE: This function may sleep for memory allocation.
|
|
*/
|
|
int btrfs_qgroup_free_data(struct btrfs_inode *inode,
|
|
- struct extent_changeset *reserved, u64 start, u64 len)
|
|
+ struct extent_changeset *reserved,
|
|
+ u64 start, u64 len, u64 *freed)
|
|
{
|
|
- return __btrfs_qgroup_release_data(inode, reserved, start, len, 1);
|
|
+ return __btrfs_qgroup_release_data(inode, reserved, start, len, freed, 1);
|
|
}
|
|
|
|
/*
|
|
@@ -3954,9 +3959,9 @@ int btrfs_qgroup_free_data(struct btrfs_inode *inode,
|
|
*
|
|
* NOTE: This function may sleep for memory allocation.
|
|
*/
|
|
-int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len)
|
|
+int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len, u64 *released)
|
|
{
|
|
- return __btrfs_qgroup_release_data(inode, NULL, start, len, 0);
|
|
+ return __btrfs_qgroup_release_data(inode, NULL, start, len, released, 0);
|
|
}
|
|
|
|
static void add_root_meta_rsv(struct btrfs_root *root, int num_bytes,
|
|
diff --git a/fs/btrfs/qgroup.h b/fs/btrfs/qgroup.h
|
|
index 578c77e94200f..c382923f7628e 100644
|
|
--- a/fs/btrfs/qgroup.h
|
|
+++ b/fs/btrfs/qgroup.h
|
|
@@ -360,10 +360,10 @@ int btrfs_verify_qgroup_counts(struct btrfs_fs_info *fs_info, u64 qgroupid,
|
|
/* New io_tree based accurate qgroup reserve API */
|
|
int btrfs_qgroup_reserve_data(struct btrfs_inode *inode,
|
|
struct extent_changeset **reserved, u64 start, u64 len);
|
|
-int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len);
|
|
+int btrfs_qgroup_release_data(struct btrfs_inode *inode, u64 start, u64 len, u64 *released);
|
|
int btrfs_qgroup_free_data(struct btrfs_inode *inode,
|
|
struct extent_changeset *reserved, u64 start,
|
|
- u64 len);
|
|
+ u64 len, u64 *freed);
|
|
int btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
|
|
enum btrfs_qgroup_rsv_type type, bool enforce);
|
|
int __btrfs_qgroup_reserve_meta(struct btrfs_root *root, int num_bytes,
|
|
diff --git a/fs/cachefiles/namei.c b/fs/cachefiles/namei.c
|
|
index 03ca8f2f657ab..50b2ee163af60 100644
|
|
--- a/fs/cachefiles/namei.c
|
|
+++ b/fs/cachefiles/namei.c
|
|
@@ -584,6 +584,8 @@ static bool cachefiles_open_file(struct cachefiles_object *object,
|
|
if (ret < 0)
|
|
goto check_failed;
|
|
|
|
+ clear_bit(FSCACHE_COOKIE_NO_DATA_TO_READ, &object->cookie->flags);
|
|
+
|
|
object->file = file;
|
|
|
|
/* Always update the atime on an object we've just looked up (this is
|
|
diff --git a/fs/ceph/cache.c b/fs/ceph/cache.c
|
|
index 177d8e8d73fe4..de1dee46d3df7 100644
|
|
--- a/fs/ceph/cache.c
|
|
+++ b/fs/ceph/cache.c
|
|
@@ -36,6 +36,8 @@ void ceph_fscache_register_inode_cookie(struct inode *inode)
|
|
&ci->i_vino, sizeof(ci->i_vino),
|
|
&ci->i_version, sizeof(ci->i_version),
|
|
i_size_read(inode));
|
|
+ if (ci->netfs.cache)
|
|
+ mapping_set_release_always(inode->i_mapping);
|
|
}
|
|
|
|
void ceph_fscache_unregister_inode_cookie(struct ceph_inode_info *ci)
|
|
diff --git a/fs/ext4/move_extent.c b/fs/ext4/move_extent.c
|
|
index 044e34cd835c1..dedc9d445f243 100644
|
|
--- a/fs/ext4/move_extent.c
|
|
+++ b/fs/ext4/move_extent.c
|
|
@@ -253,6 +253,7 @@ move_extent_per_page(struct file *o_filp, struct inode *donor_inode,
|
|
{
|
|
struct inode *orig_inode = file_inode(o_filp);
|
|
struct page *pagep[2] = {NULL, NULL};
|
|
+ struct folio *folio[2] = {NULL, NULL};
|
|
handle_t *handle;
|
|
ext4_lblk_t orig_blk_offset, donor_blk_offset;
|
|
unsigned long blocksize = orig_inode->i_sb->s_blocksize;
|
|
@@ -313,6 +314,13 @@ again:
|
|
* hold page's lock, if it is still the case data copy is not
|
|
* necessary, just swap data blocks between orig and donor.
|
|
*/
|
|
+ folio[0] = page_folio(pagep[0]);
|
|
+ folio[1] = page_folio(pagep[1]);
|
|
+
|
|
+ VM_BUG_ON_FOLIO(folio_test_large(folio[0]), folio[0]);
|
|
+ VM_BUG_ON_FOLIO(folio_test_large(folio[1]), folio[1]);
|
|
+ VM_BUG_ON_FOLIO(folio_nr_pages(folio[0]) != folio_nr_pages(folio[1]), folio[1]);
|
|
+
|
|
if (unwritten) {
|
|
ext4_double_down_write_data_sem(orig_inode, donor_inode);
|
|
/* If any of extents in range became initialized we have to
|
|
@@ -331,10 +339,8 @@ again:
|
|
ext4_double_up_write_data_sem(orig_inode, donor_inode);
|
|
goto data_copy;
|
|
}
|
|
- if ((page_has_private(pagep[0]) &&
|
|
- !try_to_release_page(pagep[0], 0)) ||
|
|
- (page_has_private(pagep[1]) &&
|
|
- !try_to_release_page(pagep[1], 0))) {
|
|
+ if (!filemap_release_folio(folio[0], 0) ||
|
|
+ !filemap_release_folio(folio[1], 0)) {
|
|
*err = -EBUSY;
|
|
goto drop_data_sem;
|
|
}
|
|
@@ -344,19 +350,19 @@ again:
|
|
block_len_in_page, 1, err);
|
|
drop_data_sem:
|
|
ext4_double_up_write_data_sem(orig_inode, donor_inode);
|
|
- goto unlock_pages;
|
|
+ goto unlock_folios;
|
|
}
|
|
data_copy:
|
|
- *err = mext_page_mkuptodate(pagep[0], from, from + replaced_size);
|
|
+ *err = mext_page_mkuptodate(&folio[0]->page, from, from + replaced_size);
|
|
if (*err)
|
|
- goto unlock_pages;
|
|
+ goto unlock_folios;
|
|
|
|
/* At this point all buffers in range are uptodate, old mapping layout
|
|
* is no longer required, try to drop it now. */
|
|
- if ((page_has_private(pagep[0]) && !try_to_release_page(pagep[0], 0)) ||
|
|
- (page_has_private(pagep[1]) && !try_to_release_page(pagep[1], 0))) {
|
|
+ if (!filemap_release_folio(folio[0], 0) ||
|
|
+ !filemap_release_folio(folio[1], 0)) {
|
|
*err = -EBUSY;
|
|
- goto unlock_pages;
|
|
+ goto unlock_folios;
|
|
}
|
|
ext4_double_down_write_data_sem(orig_inode, donor_inode);
|
|
replaced_count = ext4_swap_extents(handle, orig_inode, donor_inode,
|
|
@@ -369,13 +375,13 @@ data_copy:
|
|
replaced_size =
|
|
block_len_in_page << orig_inode->i_blkbits;
|
|
} else
|
|
- goto unlock_pages;
|
|
+ goto unlock_folios;
|
|
}
|
|
/* Perform all necessary steps similar write_begin()/write_end()
|
|
* but keeping in mind that i_size will not change */
|
|
- if (!page_has_buffers(pagep[0]))
|
|
- create_empty_buffers(pagep[0], 1 << orig_inode->i_blkbits, 0);
|
|
- bh = page_buffers(pagep[0]);
|
|
+ if (!folio_buffers(folio[0]))
|
|
+ create_empty_buffers(&folio[0]->page, 1 << orig_inode->i_blkbits, 0);
|
|
+ bh = folio_buffers(folio[0]);
|
|
for (i = 0; i < data_offset_in_page; i++)
|
|
bh = bh->b_this_page;
|
|
for (i = 0; i < block_len_in_page; i++) {
|
|
@@ -385,7 +391,7 @@ data_copy:
|
|
bh = bh->b_this_page;
|
|
}
|
|
if (!*err)
|
|
- *err = block_commit_write(pagep[0], from, from + replaced_size);
|
|
+ *err = block_commit_write(&folio[0]->page, from, from + replaced_size);
|
|
|
|
if (unlikely(*err < 0))
|
|
goto repair_branches;
|
|
@@ -395,11 +401,11 @@ data_copy:
|
|
*err = ext4_jbd2_inode_add_write(handle, orig_inode,
|
|
(loff_t)orig_page_offset << PAGE_SHIFT, replaced_size);
|
|
|
|
-unlock_pages:
|
|
- unlock_page(pagep[0]);
|
|
- put_page(pagep[0]);
|
|
- unlock_page(pagep[1]);
|
|
- put_page(pagep[1]);
|
|
+unlock_folios:
|
|
+ folio_unlock(folio[0]);
|
|
+ folio_put(folio[0]);
|
|
+ folio_unlock(folio[1]);
|
|
+ folio_put(folio[1]);
|
|
stop_journal:
|
|
ext4_journal_stop(handle);
|
|
if (*err == -ENOSPC &&
|
|
@@ -430,7 +436,7 @@ repair_branches:
|
|
*err = -EIO;
|
|
}
|
|
replaced_count = 0;
|
|
- goto unlock_pages;
|
|
+ goto unlock_folios;
|
|
}
|
|
|
|
/**
|
|
diff --git a/fs/f2fs/checkpoint.c b/fs/f2fs/checkpoint.c
|
|
index 5df04ed010cae..eb4d69f53337f 100644
|
|
--- a/fs/f2fs/checkpoint.c
|
|
+++ b/fs/f2fs/checkpoint.c
|
|
@@ -984,7 +984,7 @@ int f2fs_get_valid_checkpoint(struct f2fs_sb_info *sbi)
|
|
|
|
cp_blk_no = le32_to_cpu(fsb->cp_blkaddr);
|
|
if (cur_page == cp2)
|
|
- cp_blk_no += 1 << le32_to_cpu(fsb->log_blocks_per_seg);
|
|
+ cp_blk_no += BIT(le32_to_cpu(fsb->log_blocks_per_seg));
|
|
|
|
for (i = 1; i < cp_blks; i++) {
|
|
void *sit_bitmap_ptr;
|
|
diff --git a/fs/f2fs/compress.c b/fs/f2fs/compress.c
|
|
index 11d9dce994dbe..4cb58e8d699e2 100644
|
|
--- a/fs/f2fs/compress.c
|
|
+++ b/fs/f2fs/compress.c
|
|
@@ -241,7 +241,7 @@ static int lz4_init_compress_ctx(struct compress_ctx *cc)
|
|
unsigned int size = LZ4_MEM_COMPRESS;
|
|
|
|
#ifdef CONFIG_F2FS_FS_LZ4HC
|
|
- if (F2FS_I(cc->inode)->i_compress_flag >> COMPRESS_LEVEL_OFFSET)
|
|
+ if (F2FS_I(cc->inode)->i_compress_level)
|
|
size = LZ4HC_MEM_COMPRESS;
|
|
#endif
|
|
|
|
@@ -267,8 +267,7 @@ static void lz4_destroy_compress_ctx(struct compress_ctx *cc)
|
|
#ifdef CONFIG_F2FS_FS_LZ4HC
|
|
static int lz4hc_compress_pages(struct compress_ctx *cc)
|
|
{
|
|
- unsigned char level = F2FS_I(cc->inode)->i_compress_flag >>
|
|
- COMPRESS_LEVEL_OFFSET;
|
|
+ unsigned char level = F2FS_I(cc->inode)->i_compress_level;
|
|
int len;
|
|
|
|
if (level)
|
|
@@ -332,17 +331,15 @@ static const struct f2fs_compress_ops f2fs_lz4_ops = {
|
|
#endif
|
|
|
|
#ifdef CONFIG_F2FS_FS_ZSTD
|
|
-#define F2FS_ZSTD_DEFAULT_CLEVEL 1
|
|
-
|
|
static int zstd_init_compress_ctx(struct compress_ctx *cc)
|
|
{
|
|
zstd_parameters params;
|
|
zstd_cstream *stream;
|
|
void *workspace;
|
|
unsigned int workspace_size;
|
|
- unsigned char level = F2FS_I(cc->inode)->i_compress_flag >>
|
|
- COMPRESS_LEVEL_OFFSET;
|
|
+ unsigned char level = F2FS_I(cc->inode)->i_compress_level;
|
|
|
|
+ /* Need to remain this for backward compatibility */
|
|
if (!level)
|
|
level = F2FS_ZSTD_DEFAULT_CLEVEL;
|
|
|
|
@@ -675,7 +672,7 @@ static int f2fs_compress_pages(struct compress_ctx *cc)
|
|
|
|
cc->cbuf->clen = cpu_to_le32(cc->clen);
|
|
|
|
- if (fi->i_compress_flag & 1 << COMPRESS_CHKSUM)
|
|
+ if (fi->i_compress_flag & BIT(COMPRESS_CHKSUM))
|
|
chksum = f2fs_crc32(F2FS_I_SB(cc->inode),
|
|
cc->cbuf->cdata, cc->clen);
|
|
cc->cbuf->chksum = cpu_to_le32(chksum);
|
|
@@ -773,7 +770,7 @@ void f2fs_decompress_cluster(struct decompress_io_ctx *dic, bool in_task)
|
|
|
|
ret = cops->decompress_pages(dic);
|
|
|
|
- if (!ret && (fi->i_compress_flag & 1 << COMPRESS_CHKSUM)) {
|
|
+ if (!ret && (fi->i_compress_flag & BIT(COMPRESS_CHKSUM))) {
|
|
u32 provided = le32_to_cpu(dic->cbuf->chksum);
|
|
u32 calculated = f2fs_crc32(sbi, dic->cbuf->cdata, dic->clen);
|
|
|
|
diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c
|
|
index ea05710ca9bdf..3666c1fd77a64 100644
|
|
--- a/fs/f2fs/data.c
|
|
+++ b/fs/f2fs/data.c
|
|
@@ -95,17 +95,17 @@ static enum count_type __read_io_type(struct page *page)
|
|
/* postprocessing steps for read bios */
|
|
enum bio_post_read_step {
|
|
#ifdef CONFIG_FS_ENCRYPTION
|
|
- STEP_DECRYPT = 1 << 0,
|
|
+ STEP_DECRYPT = BIT(0),
|
|
#else
|
|
STEP_DECRYPT = 0, /* compile out the decryption-related code */
|
|
#endif
|
|
#ifdef CONFIG_F2FS_FS_COMPRESSION
|
|
- STEP_DECOMPRESS = 1 << 1,
|
|
+ STEP_DECOMPRESS = BIT(1),
|
|
#else
|
|
STEP_DECOMPRESS = 0, /* compile out the decompression-related code */
|
|
#endif
|
|
#ifdef CONFIG_FS_VERITY
|
|
- STEP_VERITY = 1 << 2,
|
|
+ STEP_VERITY = BIT(2),
|
|
#else
|
|
STEP_VERITY = 0, /* compile out the verity-related code */
|
|
#endif
|
|
@@ -409,7 +409,7 @@ int f2fs_target_device_index(struct f2fs_sb_info *sbi, block_t blkaddr)
|
|
|
|
static blk_opf_t f2fs_io_flags(struct f2fs_io_info *fio)
|
|
{
|
|
- unsigned int temp_mask = (1 << NR_TEMP_TYPE) - 1;
|
|
+ unsigned int temp_mask = GENMASK(NR_TEMP_TYPE - 1, 0);
|
|
unsigned int fua_flag, meta_flag, io_flag;
|
|
blk_opf_t op_flags = 0;
|
|
|
|
@@ -431,9 +431,9 @@ static blk_opf_t f2fs_io_flags(struct f2fs_io_info *fio)
|
|
* 5 | 4 | 3 | 2 | 1 | 0 |
|
|
* Cold | Warm | Hot | Cold | Warm | Hot |
|
|
*/
|
|
- if ((1 << fio->temp) & meta_flag)
|
|
+ if (BIT(fio->temp) & meta_flag)
|
|
op_flags |= REQ_META;
|
|
- if ((1 << fio->temp) & fua_flag)
|
|
+ if (BIT(fio->temp) & fua_flag)
|
|
op_flags |= REQ_FUA;
|
|
return op_flags;
|
|
}
|
|
diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c
|
|
index 8373eba3a1337..510736d2ae110 100644
|
|
--- a/fs/f2fs/dir.c
|
|
+++ b/fs/f2fs/dir.c
|
|
@@ -29,7 +29,7 @@ static unsigned long dir_blocks(struct inode *inode)
|
|
static unsigned int dir_buckets(unsigned int level, int dir_level)
|
|
{
|
|
if (level + dir_level < MAX_DIR_HASH_DEPTH / 2)
|
|
- return 1 << (level + dir_level);
|
|
+ return BIT(level + dir_level);
|
|
else
|
|
return MAX_DIR_BUCKETS;
|
|
}
|
|
diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h
|
|
index f56abb39601ac..5c76ba764b71f 100644
|
|
--- a/fs/f2fs/f2fs.h
|
|
+++ b/fs/f2fs/f2fs.h
|
|
@@ -64,7 +64,7 @@ enum {
|
|
};
|
|
|
|
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
|
-#define F2FS_ALL_FAULT_TYPE ((1 << FAULT_MAX) - 1)
|
|
+#define F2FS_ALL_FAULT_TYPE (GENMASK(FAULT_MAX - 1, 0))
|
|
|
|
struct f2fs_fault_info {
|
|
atomic_t inject_ops;
|
|
@@ -73,7 +73,7 @@ struct f2fs_fault_info {
|
|
};
|
|
|
|
extern const char *f2fs_fault_name[FAULT_MAX];
|
|
-#define IS_FAULT_SET(fi, type) ((fi)->inject_type & (1 << (type)))
|
|
+#define IS_FAULT_SET(fi, type) ((fi)->inject_type & BIT(type))
|
|
#endif
|
|
|
|
/*
|
|
@@ -840,7 +840,7 @@ struct f2fs_inode_info {
|
|
unsigned char i_compress_algorithm; /* algorithm type */
|
|
unsigned char i_log_cluster_size; /* log of cluster size */
|
|
unsigned char i_compress_level; /* compress level (lz4hc,zstd) */
|
|
- unsigned short i_compress_flag; /* compress flag */
|
|
+ unsigned char i_compress_flag; /* compress flag */
|
|
unsigned int i_cluster_size; /* cluster size */
|
|
|
|
unsigned int atomic_write_cnt;
|
|
@@ -1412,7 +1412,7 @@ static inline void set_page_private_##name(struct page *page) \
|
|
static inline void clear_page_private_##name(struct page *page) \
|
|
{ \
|
|
clear_bit(PAGE_PRIVATE_##flagname, &page_private(page)); \
|
|
- if (page_private(page) == 1 << PAGE_PRIVATE_NOT_POINTER) { \
|
|
+ if (page_private(page) == BIT(PAGE_PRIVATE_NOT_POINTER)) { \
|
|
set_page_private(page, 0); \
|
|
if (PagePrivate(page)) { \
|
|
ClearPagePrivate(page); \
|
|
@@ -1462,8 +1462,8 @@ static inline void set_page_private_data(struct page *page, unsigned long data)
|
|
|
|
static inline void clear_page_private_data(struct page *page)
|
|
{
|
|
- page_private(page) &= (1 << PAGE_PRIVATE_MAX) - 1;
|
|
- if (page_private(page) == 1 << PAGE_PRIVATE_NOT_POINTER) {
|
|
+ page_private(page) &= GENMASK(PAGE_PRIVATE_MAX - 1, 0);
|
|
+ if (page_private(page) == BIT(PAGE_PRIVATE_NOT_POINTER)) {
|
|
set_page_private(page, 0);
|
|
if (PagePrivate(page)) {
|
|
ClearPagePrivate(page);
|
|
@@ -1501,6 +1501,8 @@ struct compress_data {
|
|
|
|
#define F2FS_COMPRESSED_PAGE_MAGIC 0xF5F2C000
|
|
|
|
+#define F2FS_ZSTD_DEFAULT_CLEVEL 1
|
|
+
|
|
#define COMPRESS_LEVEL_OFFSET 8
|
|
|
|
/* compress context */
|
|
@@ -2882,7 +2884,7 @@ static inline int f2fs_test_bit(unsigned int nr, char *addr)
|
|
int mask;
|
|
|
|
addr += (nr >> 3);
|
|
- mask = 1 << (7 - (nr & 0x07));
|
|
+ mask = BIT(7 - (nr & 0x07));
|
|
return mask & *addr;
|
|
}
|
|
|
|
@@ -2891,7 +2893,7 @@ static inline void f2fs_set_bit(unsigned int nr, char *addr)
|
|
int mask;
|
|
|
|
addr += (nr >> 3);
|
|
- mask = 1 << (7 - (nr & 0x07));
|
|
+ mask = BIT(7 - (nr & 0x07));
|
|
*addr |= mask;
|
|
}
|
|
|
|
@@ -2900,7 +2902,7 @@ static inline void f2fs_clear_bit(unsigned int nr, char *addr)
|
|
int mask;
|
|
|
|
addr += (nr >> 3);
|
|
- mask = 1 << (7 - (nr & 0x07));
|
|
+ mask = BIT(7 - (nr & 0x07));
|
|
*addr &= ~mask;
|
|
}
|
|
|
|
@@ -2910,7 +2912,7 @@ static inline int f2fs_test_and_set_bit(unsigned int nr, char *addr)
|
|
int ret;
|
|
|
|
addr += (nr >> 3);
|
|
- mask = 1 << (7 - (nr & 0x07));
|
|
+ mask = BIT(7 - (nr & 0x07));
|
|
ret = mask & *addr;
|
|
*addr |= mask;
|
|
return ret;
|
|
@@ -2922,7 +2924,7 @@ static inline int f2fs_test_and_clear_bit(unsigned int nr, char *addr)
|
|
int ret;
|
|
|
|
addr += (nr >> 3);
|
|
- mask = 1 << (7 - (nr & 0x07));
|
|
+ mask = BIT(7 - (nr & 0x07));
|
|
ret = mask & *addr;
|
|
*addr &= ~mask;
|
|
return ret;
|
|
@@ -2933,7 +2935,7 @@ static inline void f2fs_change_bit(unsigned int nr, char *addr)
|
|
int mask;
|
|
|
|
addr += (nr >> 3);
|
|
- mask = 1 << (7 - (nr & 0x07));
|
|
+ mask = BIT(7 - (nr & 0x07));
|
|
*addr ^= mask;
|
|
}
|
|
|
|
@@ -4333,15 +4335,14 @@ static inline int set_compress_context(struct inode *inode)
|
|
F2FS_OPTION(sbi).compress_log_size;
|
|
F2FS_I(inode)->i_compress_flag =
|
|
F2FS_OPTION(sbi).compress_chksum ?
|
|
- 1 << COMPRESS_CHKSUM : 0;
|
|
+ BIT(COMPRESS_CHKSUM) : 0;
|
|
F2FS_I(inode)->i_cluster_size =
|
|
- 1 << F2FS_I(inode)->i_log_cluster_size;
|
|
+ BIT(F2FS_I(inode)->i_log_cluster_size);
|
|
if ((F2FS_I(inode)->i_compress_algorithm == COMPRESS_LZ4 ||
|
|
F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD) &&
|
|
F2FS_OPTION(sbi).compress_level)
|
|
- F2FS_I(inode)->i_compress_flag |=
|
|
- F2FS_OPTION(sbi).compress_level <<
|
|
- COMPRESS_LEVEL_OFFSET;
|
|
+ F2FS_I(inode)->i_compress_level =
|
|
+ F2FS_OPTION(sbi).compress_level;
|
|
F2FS_I(inode)->i_flags |= F2FS_COMPR_FL;
|
|
set_inode_flag(inode, FI_COMPRESSED_FILE);
|
|
stat_inc_compr_inode(inode);
|
|
diff --git a/fs/f2fs/file.c b/fs/f2fs/file.c
|
|
index d0c17366ebf48..9b9fb3c57ec6c 100644
|
|
--- a/fs/f2fs/file.c
|
|
+++ b/fs/f2fs/file.c
|
|
@@ -3983,7 +3983,16 @@ static int f2fs_ioc_set_compress_option(struct file *filp, unsigned long arg)
|
|
|
|
F2FS_I(inode)->i_compress_algorithm = option.algorithm;
|
|
F2FS_I(inode)->i_log_cluster_size = option.log_cluster_size;
|
|
- F2FS_I(inode)->i_cluster_size = 1 << option.log_cluster_size;
|
|
+ F2FS_I(inode)->i_cluster_size = BIT(option.log_cluster_size);
|
|
+ /* Set default level */
|
|
+ if (F2FS_I(inode)->i_compress_algorithm == COMPRESS_ZSTD)
|
|
+ F2FS_I(inode)->i_compress_level = F2FS_ZSTD_DEFAULT_CLEVEL;
|
|
+ else
|
|
+ F2FS_I(inode)->i_compress_level = 0;
|
|
+ /* Adjust mount option level */
|
|
+ if (option.algorithm == F2FS_OPTION(sbi).compress_algorithm &&
|
|
+ F2FS_OPTION(sbi).compress_level)
|
|
+ F2FS_I(inode)->i_compress_level = F2FS_OPTION(sbi).compress_level;
|
|
f2fs_mark_inode_dirty_sync(inode, true);
|
|
|
|
if (!f2fs_is_compress_backend_ready(inode))
|
|
diff --git a/fs/f2fs/inode.c b/fs/f2fs/inode.c
|
|
index 1fc7760499f10..0010579f17368 100644
|
|
--- a/fs/f2fs/inode.c
|
|
+++ b/fs/f2fs/inode.c
|
|
@@ -450,12 +450,18 @@ static int do_read_inode(struct inode *inode)
|
|
(fi->i_flags & F2FS_COMPR_FL)) {
|
|
if (F2FS_FITS_IN_INODE(ri, fi->i_extra_isize,
|
|
i_log_cluster_size)) {
|
|
+ unsigned short compress_flag;
|
|
+
|
|
atomic_set(&fi->i_compr_blocks,
|
|
le64_to_cpu(ri->i_compr_blocks));
|
|
fi->i_compress_algorithm = ri->i_compress_algorithm;
|
|
fi->i_log_cluster_size = ri->i_log_cluster_size;
|
|
- fi->i_compress_flag = le16_to_cpu(ri->i_compress_flag);
|
|
- fi->i_cluster_size = 1 << fi->i_log_cluster_size;
|
|
+ compress_flag = le16_to_cpu(ri->i_compress_flag);
|
|
+ fi->i_compress_level = compress_flag >>
|
|
+ COMPRESS_LEVEL_OFFSET;
|
|
+ fi->i_compress_flag = compress_flag &
|
|
+ GENMASK(COMPRESS_LEVEL_OFFSET - 1, 0);
|
|
+ fi->i_cluster_size = BIT(fi->i_log_cluster_size);
|
|
set_inode_flag(inode, FI_COMPRESSED_FILE);
|
|
}
|
|
}
|
|
@@ -675,13 +681,17 @@ void f2fs_update_inode(struct inode *inode, struct page *node_page)
|
|
if (f2fs_sb_has_compression(F2FS_I_SB(inode)) &&
|
|
F2FS_FITS_IN_INODE(ri, F2FS_I(inode)->i_extra_isize,
|
|
i_log_cluster_size)) {
|
|
+ unsigned short compress_flag;
|
|
+
|
|
ri->i_compr_blocks =
|
|
cpu_to_le64(atomic_read(
|
|
&F2FS_I(inode)->i_compr_blocks));
|
|
ri->i_compress_algorithm =
|
|
F2FS_I(inode)->i_compress_algorithm;
|
|
- ri->i_compress_flag =
|
|
- cpu_to_le16(F2FS_I(inode)->i_compress_flag);
|
|
+ compress_flag = F2FS_I(inode)->i_compress_flag |
|
|
+ F2FS_I(inode)->i_compress_level <<
|
|
+ COMPRESS_LEVEL_OFFSET;
|
|
+ ri->i_compress_flag = cpu_to_le16(compress_flag);
|
|
ri->i_log_cluster_size =
|
|
F2FS_I(inode)->i_log_cluster_size;
|
|
}
|
|
diff --git a/fs/f2fs/node.h b/fs/f2fs/node.h
|
|
index 0aa48704c77a0..7068f3ac036a5 100644
|
|
--- a/fs/f2fs/node.h
|
|
+++ b/fs/f2fs/node.h
|
|
@@ -93,17 +93,15 @@ static inline void copy_node_info(struct node_info *dst,
|
|
static inline void set_nat_flag(struct nat_entry *ne,
|
|
unsigned int type, bool set)
|
|
{
|
|
- unsigned char mask = 0x01 << type;
|
|
if (set)
|
|
- ne->ni.flag |= mask;
|
|
+ ne->ni.flag |= BIT(type);
|
|
else
|
|
- ne->ni.flag &= ~mask;
|
|
+ ne->ni.flag &= ~BIT(type);
|
|
}
|
|
|
|
static inline bool get_nat_flag(struct nat_entry *ne, unsigned int type)
|
|
{
|
|
- unsigned char mask = 0x01 << type;
|
|
- return ne->ni.flag & mask;
|
|
+ return ne->ni.flag & BIT(type);
|
|
}
|
|
|
|
static inline void nat_reset_flag(struct nat_entry *ne)
|
|
@@ -224,7 +222,7 @@ static inline pgoff_t next_nat_addr(struct f2fs_sb_info *sbi,
|
|
struct f2fs_nm_info *nm_i = NM_I(sbi);
|
|
|
|
block_addr -= nm_i->nat_blkaddr;
|
|
- block_addr ^= 1 << sbi->log_blocks_per_seg;
|
|
+ block_addr ^= BIT(sbi->log_blocks_per_seg);
|
|
return block_addr + nm_i->nat_blkaddr;
|
|
}
|
|
|
|
@@ -394,7 +392,7 @@ static inline nid_t get_nid(struct page *p, int off, bool i)
|
|
static inline int is_node(struct page *page, int type)
|
|
{
|
|
struct f2fs_node *rn = F2FS_NODE(page);
|
|
- return le32_to_cpu(rn->footer.flag) & (1 << type);
|
|
+ return le32_to_cpu(rn->footer.flag) & BIT(type);
|
|
}
|
|
|
|
#define is_cold_node(page) is_node(page, COLD_BIT_SHIFT)
|
|
@@ -407,9 +405,9 @@ static inline void set_cold_node(struct page *page, bool is_dir)
|
|
unsigned int flag = le32_to_cpu(rn->footer.flag);
|
|
|
|
if (is_dir)
|
|
- flag &= ~(0x1 << COLD_BIT_SHIFT);
|
|
+ flag &= ~BIT(COLD_BIT_SHIFT);
|
|
else
|
|
- flag |= (0x1 << COLD_BIT_SHIFT);
|
|
+ flag |= BIT(COLD_BIT_SHIFT);
|
|
rn->footer.flag = cpu_to_le32(flag);
|
|
}
|
|
|
|
@@ -418,9 +416,9 @@ static inline void set_mark(struct page *page, int mark, int type)
|
|
struct f2fs_node *rn = F2FS_NODE(page);
|
|
unsigned int flag = le32_to_cpu(rn->footer.flag);
|
|
if (mark)
|
|
- flag |= (0x1 << type);
|
|
+ flag |= BIT(type);
|
|
else
|
|
- flag &= ~(0x1 << type);
|
|
+ flag &= ~BIT(type);
|
|
rn->footer.flag = cpu_to_le32(flag);
|
|
|
|
#ifdef CONFIG_F2FS_CHECK_FS
|
|
diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c
|
|
index 1ba85ef97cbd3..3805162dcef2b 100644
|
|
--- a/fs/f2fs/super.c
|
|
+++ b/fs/f2fs/super.c
|
|
@@ -613,14 +613,12 @@ static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str)
|
|
{
|
|
#ifdef CONFIG_F2FS_FS_LZ4HC
|
|
unsigned int level;
|
|
-#endif
|
|
|
|
if (strlen(str) == 3) {
|
|
F2FS_OPTION(sbi).compress_level = 0;
|
|
return 0;
|
|
}
|
|
|
|
-#ifdef CONFIG_F2FS_FS_LZ4HC
|
|
str += 3;
|
|
|
|
if (str[0] != ':') {
|
|
@@ -638,6 +636,10 @@ static int f2fs_set_lz4hc_level(struct f2fs_sb_info *sbi, const char *str)
|
|
F2FS_OPTION(sbi).compress_level = level;
|
|
return 0;
|
|
#else
|
|
+ if (strlen(str) == 3) {
|
|
+ F2FS_OPTION(sbi).compress_level = 0;
|
|
+ return 0;
|
|
+ }
|
|
f2fs_info(sbi, "kernel doesn't support lz4hc compression");
|
|
return -EINVAL;
|
|
#endif
|
|
@@ -651,7 +653,7 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
|
|
int len = 4;
|
|
|
|
if (strlen(str) == len) {
|
|
- F2FS_OPTION(sbi).compress_level = 0;
|
|
+ F2FS_OPTION(sbi).compress_level = F2FS_ZSTD_DEFAULT_CLEVEL;
|
|
return 0;
|
|
}
|
|
|
|
@@ -664,7 +666,7 @@ static int f2fs_set_zstd_level(struct f2fs_sb_info *sbi, const char *str)
|
|
if (kstrtouint(str + 1, 10, &level))
|
|
return -EINVAL;
|
|
|
|
- if (!level || level > zstd_max_clevel()) {
|
|
+ if (level < zstd_min_clevel() || level > zstd_max_clevel()) {
|
|
f2fs_info(sbi, "invalid zstd compress level: %d", level);
|
|
return -EINVAL;
|
|
}
|
|
@@ -898,8 +900,8 @@ static int parse_options(struct super_block *sb, char *options, bool is_remount)
|
|
if (args->from && match_int(args, &arg))
|
|
return -EINVAL;
|
|
if (arg <= 0 || arg > __ilog2_u32(BIO_MAX_VECS)) {
|
|
- f2fs_warn(sbi, "Not support %d, larger than %d",
|
|
- 1 << arg, BIO_MAX_VECS);
|
|
+ f2fs_warn(sbi, "Not support %ld, larger than %d",
|
|
+ BIT(arg), BIO_MAX_VECS);
|
|
return -EINVAL;
|
|
}
|
|
F2FS_OPTION(sbi).write_io_size_bits = arg;
|
|
@@ -1340,7 +1342,7 @@ default_check:
|
|
#endif
|
|
|
|
if (F2FS_IO_SIZE_BITS(sbi) && !f2fs_lfs_mode(sbi)) {
|
|
- f2fs_err(sbi, "Should set mode=lfs with %uKB-sized IO",
|
|
+ f2fs_err(sbi, "Should set mode=lfs with %luKB-sized IO",
|
|
F2FS_IO_SIZE_KB(sbi));
|
|
return -EINVAL;
|
|
}
|
|
@@ -3356,7 +3358,7 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi,
|
|
total_sections = le32_to_cpu(raw_super->section_count);
|
|
|
|
/* blocks_per_seg should be 512, given the above check */
|
|
- blocks_per_seg = 1 << le32_to_cpu(raw_super->log_blocks_per_seg);
|
|
+ blocks_per_seg = BIT(le32_to_cpu(raw_super->log_blocks_per_seg));
|
|
|
|
if (segment_count > F2FS_MAX_SEGMENT ||
|
|
segment_count < F2FS_MIN_SEGMENTS) {
|
|
@@ -3625,9 +3627,9 @@ static void init_sb_info(struct f2fs_sb_info *sbi)
|
|
sbi->log_sectors_per_block =
|
|
le32_to_cpu(raw_super->log_sectors_per_block);
|
|
sbi->log_blocksize = le32_to_cpu(raw_super->log_blocksize);
|
|
- sbi->blocksize = 1 << sbi->log_blocksize;
|
|
+ sbi->blocksize = BIT(sbi->log_blocksize);
|
|
sbi->log_blocks_per_seg = le32_to_cpu(raw_super->log_blocks_per_seg);
|
|
- sbi->blocks_per_seg = 1 << sbi->log_blocks_per_seg;
|
|
+ sbi->blocks_per_seg = BIT(sbi->log_blocks_per_seg);
|
|
sbi->segs_per_sec = le32_to_cpu(raw_super->segs_per_sec);
|
|
sbi->secs_per_zone = le32_to_cpu(raw_super->secs_per_zone);
|
|
sbi->total_sections = le32_to_cpu(raw_super->section_count);
|
|
@@ -3883,7 +3885,7 @@ void f2fs_handle_stop(struct f2fs_sb_info *sbi, unsigned char reason)
|
|
|
|
f2fs_down_write(&sbi->sb_lock);
|
|
|
|
- if (raw_super->s_stop_reason[reason] < ((1 << BITS_PER_BYTE) - 1))
|
|
+ if (raw_super->s_stop_reason[reason] < GENMASK(BITS_PER_BYTE - 1, 0))
|
|
raw_super->s_stop_reason[reason]++;
|
|
|
|
err = f2fs_commit_super(sbi, false);
|
|
@@ -4033,7 +4035,7 @@ static int f2fs_scan_devices(struct f2fs_sb_info *sbi)
|
|
FDEV(i).start_blk, FDEV(i).end_blk);
|
|
}
|
|
f2fs_info(sbi,
|
|
- "IO Block Size: %8d KB", F2FS_IO_SIZE_KB(sbi));
|
|
+ "IO Block Size: %8ld KB", F2FS_IO_SIZE_KB(sbi));
|
|
return 0;
|
|
}
|
|
|
|
diff --git a/fs/f2fs/sysfs.c b/fs/f2fs/sysfs.c
|
|
index 3d68bfa75cf2a..751a108e612ff 100644
|
|
--- a/fs/f2fs/sysfs.c
|
|
+++ b/fs/f2fs/sysfs.c
|
|
@@ -451,7 +451,7 @@ out:
|
|
if (ret < 0)
|
|
return ret;
|
|
#ifdef CONFIG_F2FS_FAULT_INJECTION
|
|
- if (a->struct_type == FAULT_INFO_TYPE && t >= (1 << FAULT_MAX))
|
|
+ if (a->struct_type == FAULT_INFO_TYPE && t >= BIT(FAULT_MAX))
|
|
return -EINVAL;
|
|
if (a->struct_type == FAULT_INFO_RATE && t >= UINT_MAX)
|
|
return -EINVAL;
|
|
diff --git a/fs/inode.c b/fs/inode.c
|
|
index 73ad1b0d47758..8cfda7a6d5900 100644
|
|
--- a/fs/inode.c
|
|
+++ b/fs/inode.c
|
|
@@ -215,6 +215,8 @@ int inode_init_always(struct super_block *sb, struct inode *inode)
|
|
lockdep_set_class_and_name(&mapping->invalidate_lock,
|
|
&sb->s_type->invalidate_lock_key,
|
|
"mapping.invalidate_lock");
|
|
+ if (sb->s_iflags & SB_I_STABLE_WRITES)
|
|
+ mapping_set_stable_writes(mapping);
|
|
inode->i_private = NULL;
|
|
inode->i_mapping = mapping;
|
|
INIT_HLIST_HEAD(&inode->i_dentry); /* buggered by rcu freeing */
|
|
diff --git a/fs/nfs/fscache.c b/fs/nfs/fscache.c
|
|
index e731c00a9fcbc..d3c938dd2b12a 100644
|
|
--- a/fs/nfs/fscache.c
|
|
+++ b/fs/nfs/fscache.c
|
|
@@ -176,6 +176,9 @@ void nfs_fscache_init_inode(struct inode *inode)
|
|
&auxdata, /* aux_data */
|
|
sizeof(auxdata),
|
|
i_size_read(inode));
|
|
+
|
|
+ if (netfs_inode(inode)->cache)
|
|
+ mapping_set_release_always(inode->i_mapping);
|
|
}
|
|
|
|
/*
|
|
diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
|
|
index 512ac9dea9787..7f1aea4c11b9c 100644
|
|
--- a/fs/smb/client/cifsglob.h
|
|
+++ b/fs/smb/client/cifsglob.h
|
|
@@ -972,7 +972,6 @@ release_iface(struct kref *ref)
|
|
struct cifs_server_iface *iface = container_of(ref,
|
|
struct cifs_server_iface,
|
|
refcount);
|
|
- list_del_init(&iface->iface_head);
|
|
kfree(iface);
|
|
}
|
|
|
|
diff --git a/fs/smb/client/connect.c b/fs/smb/client/connect.c
|
|
index f725a119ce312..49fdc6dfdcf8d 100644
|
|
--- a/fs/smb/client/connect.c
|
|
+++ b/fs/smb/client/connect.c
|
|
@@ -258,10 +258,13 @@ cifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
|
|
spin_lock(&cifs_tcp_ses_lock);
|
|
list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) {
|
|
/* check if iface is still active */
|
|
- if (!cifs_chan_is_iface_active(ses, server))
|
|
+ spin_lock(&ses->chan_lock);
|
|
+ if (!cifs_chan_is_iface_active(ses, server)) {
|
|
+ spin_unlock(&ses->chan_lock);
|
|
cifs_chan_update_iface(ses, server);
|
|
+ spin_lock(&ses->chan_lock);
|
|
+ }
|
|
|
|
- spin_lock(&ses->chan_lock);
|
|
if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) {
|
|
spin_unlock(&ses->chan_lock);
|
|
continue;
|
|
diff --git a/fs/smb/client/fscache.c b/fs/smb/client/fscache.c
|
|
index e73625b5d0cc6..f64bad513ba6d 100644
|
|
--- a/fs/smb/client/fscache.c
|
|
+++ b/fs/smb/client/fscache.c
|
|
@@ -108,6 +108,8 @@ void cifs_fscache_get_inode_cookie(struct inode *inode)
|
|
&cifsi->uniqueid, sizeof(cifsi->uniqueid),
|
|
&cd, sizeof(cd),
|
|
i_size_read(&cifsi->netfs.inode));
|
|
+ if (cifsi->netfs.cache)
|
|
+ mapping_set_release_always(inode->i_mapping);
|
|
}
|
|
|
|
void cifs_fscache_unuse_inode_cookie(struct inode *inode, bool update)
|
|
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
|
|
index 7be51f9d2fa18..5343898bac8a6 100644
|
|
--- a/fs/smb/client/inode.c
|
|
+++ b/fs/smb/client/inode.c
|
|
@@ -264,7 +264,7 @@ cifs_unix_basic_to_fattr(struct cifs_fattr *fattr, FILE_UNIX_BASIC_INFO *info,
|
|
fattr->cf_dtype = DT_REG;
|
|
break;
|
|
case UNIX_SYMLINK:
|
|
- fattr->cf_mode |= S_IFLNK;
|
|
+ fattr->cf_mode |= S_IFLNK | cifs_sb->ctx->file_mode;
|
|
fattr->cf_dtype = DT_LNK;
|
|
break;
|
|
case UNIX_DIR:
|
|
diff --git a/fs/smb/client/smb2file.c b/fs/smb/client/smb2file.c
|
|
index ba6cc50af390f..a7475bc05cac0 100644
|
|
--- a/fs/smb/client/smb2file.c
|
|
+++ b/fs/smb/client/smb2file.c
|
|
@@ -34,7 +34,7 @@ static struct smb2_symlink_err_rsp *symlink_data(const struct kvec *iov)
|
|
len = (u32)err->ErrorContextCount * (offsetof(struct smb2_error_context_rsp,
|
|
ErrorContextData) +
|
|
sizeof(struct smb2_symlink_err_rsp));
|
|
- if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err))
|
|
+ if (le32_to_cpu(err->ByteCount) < len || iov->iov_len < len + sizeof(*err) + 1)
|
|
return ERR_PTR(-EINVAL);
|
|
|
|
p = (struct smb2_error_context_rsp *)err->ErrorData;
|
|
diff --git a/fs/smb/client/smb2misc.c b/fs/smb/client/smb2misc.c
|
|
index 88942b1fb4318..fdf7a7f188c5f 100644
|
|
--- a/fs/smb/client/smb2misc.c
|
|
+++ b/fs/smb/client/smb2misc.c
|
|
@@ -113,7 +113,7 @@ static __u32 get_neg_ctxt_len(struct smb2_hdr *hdr, __u32 len,
|
|
} else if (nc_offset + 1 == non_ctxlen) {
|
|
cifs_dbg(FYI, "no SPNEGO security blob in negprot rsp\n");
|
|
size_of_pad_before_neg_ctxts = 0;
|
|
- } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE)
|
|
+ } else if (non_ctxlen == SMB311_NEGPROT_BASE_SIZE + 1)
|
|
/* has padding, but no SPNEGO blob */
|
|
size_of_pad_before_neg_ctxts = nc_offset - non_ctxlen + 1;
|
|
else
|
|
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
|
|
index df03d80ab6d5f..4596d2dfdec3a 100644
|
|
--- a/fs/smb/client/smb2ops.c
|
|
+++ b/fs/smb/client/smb2ops.c
|
|
@@ -588,16 +588,12 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
|
|
}
|
|
|
|
/*
|
|
- * Go through iface_list and do kref_put to remove
|
|
- * any unused ifaces. ifaces in use will be removed
|
|
- * when the last user calls a kref_put on it
|
|
+ * Go through iface_list and mark them as inactive
|
|
*/
|
|
list_for_each_entry_safe(iface, niface, &ses->iface_list,
|
|
- iface_head) {
|
|
+ iface_head)
|
|
iface->is_active = 0;
|
|
- kref_put(&iface->refcount, release_iface);
|
|
- ses->iface_count--;
|
|
- }
|
|
+
|
|
spin_unlock(&ses->iface_lock);
|
|
|
|
/*
|
|
@@ -672,10 +668,7 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
|
|
iface_head) {
|
|
ret = iface_cmp(iface, &tmp_iface);
|
|
if (!ret) {
|
|
- /* just get a ref so that it doesn't get picked/freed */
|
|
iface->is_active = 1;
|
|
- kref_get(&iface->refcount);
|
|
- ses->iface_count++;
|
|
spin_unlock(&ses->iface_lock);
|
|
goto next_iface;
|
|
} else if (ret < 0) {
|
|
@@ -742,6 +735,20 @@ next_iface:
|
|
}
|
|
|
|
out:
|
|
+ /*
|
|
+ * Go through the list again and put the inactive entries
|
|
+ */
|
|
+ spin_lock(&ses->iface_lock);
|
|
+ list_for_each_entry_safe(iface, niface, &ses->iface_list,
|
|
+ iface_head) {
|
|
+ if (!iface->is_active) {
|
|
+ list_del(&iface->iface_head);
|
|
+ kref_put(&iface->refcount, release_iface);
|
|
+ ses->iface_count--;
|
|
+ }
|
|
+ }
|
|
+ spin_unlock(&ses->iface_lock);
|
|
+
|
|
return rc;
|
|
}
|
|
|
|
@@ -778,9 +785,14 @@ SMB3_request_interfaces(const unsigned int xid, struct cifs_tcon *tcon, bool in_
|
|
goto out;
|
|
|
|
/* check if iface is still active */
|
|
+ spin_lock(&ses->chan_lock);
|
|
pserver = ses->chans[0].server;
|
|
- if (pserver && !cifs_chan_is_iface_active(ses, pserver))
|
|
+ if (pserver && !cifs_chan_is_iface_active(ses, pserver)) {
|
|
+ spin_unlock(&ses->chan_lock);
|
|
cifs_chan_update_iface(ses, pserver);
|
|
+ spin_lock(&ses->chan_lock);
|
|
+ }
|
|
+ spin_unlock(&ses->chan_lock);
|
|
|
|
out:
|
|
kfree(out_buf);
|
|
@@ -5752,7 +5764,7 @@ struct smb_version_values smb20_values = {
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.header_preamble_size = 0,
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -5774,7 +5786,7 @@ struct smb_version_values smb21_values = {
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.header_preamble_size = 0,
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -5795,7 +5807,7 @@ struct smb_version_values smb3any_values = {
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.header_preamble_size = 0,
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -5816,7 +5828,7 @@ struct smb_version_values smbdefault_values = {
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.header_preamble_size = 0,
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -5837,7 +5849,7 @@ struct smb_version_values smb30_values = {
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.header_preamble_size = 0,
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -5858,7 +5870,7 @@ struct smb_version_values smb302_values = {
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.header_preamble_size = 0,
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -5879,7 +5891,7 @@ struct smb_version_values smb311_values = {
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.header_preamble_size = 0,
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
|
|
index 05ff8a457a3d7..2dfbf1b23cfa0 100644
|
|
--- a/fs/smb/client/smb2pdu.c
|
|
+++ b/fs/smb/client/smb2pdu.c
|
|
@@ -1386,7 +1386,7 @@ SMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
|
|
|
|
/* Testing shows that buffer offset must be at location of Buffer[0] */
|
|
req->SecurityBufferOffset =
|
|
- cpu_to_le16(sizeof(struct smb2_sess_setup_req) - 1 /* pad */);
|
|
+ cpu_to_le16(sizeof(struct smb2_sess_setup_req));
|
|
req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
|
|
|
|
memset(&rqst, 0, sizeof(struct smb_rqst));
|
|
@@ -1905,8 +1905,7 @@ SMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
|
|
iov[0].iov_len = total_len - 1;
|
|
|
|
/* Testing shows that buffer offset must be at location of Buffer[0] */
|
|
- req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req)
|
|
- - 1 /* pad */);
|
|
+ req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req));
|
|
req->PathLength = cpu_to_le16(unc_path_len - 2);
|
|
iov[1].iov_base = unc_path;
|
|
iov[1].iov_len = unc_path_len;
|
|
@@ -3796,7 +3795,7 @@ SMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
|
|
ses->Suid, (u8)watch_tree, completion_filter);
|
|
/* validate that notify information is plausible */
|
|
if ((rsp_iov.iov_base == NULL) ||
|
|
- (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp)))
|
|
+ (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp) + 1))
|
|
goto cnotify_exit;
|
|
|
|
smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base;
|
|
@@ -5009,7 +5008,7 @@ int SMB2_query_directory_init(const unsigned int xid,
|
|
memcpy(bufptr, &asteriks, len);
|
|
|
|
req->FileNameOffset =
|
|
- cpu_to_le16(sizeof(struct smb2_query_directory_req) - 1);
|
|
+ cpu_to_le16(sizeof(struct smb2_query_directory_req));
|
|
req->FileNameLength = cpu_to_le16(len);
|
|
/*
|
|
* BB could be 30 bytes or so longer if we used SMB2 specific
|
|
@@ -5205,8 +5204,7 @@ SMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
|
|
req->VolatileFileId = volatile_fid;
|
|
req->AdditionalInformation = cpu_to_le32(additional_info);
|
|
|
|
- req->BufferOffset =
|
|
- cpu_to_le16(sizeof(struct smb2_set_info_req) - 1);
|
|
+ req->BufferOffset = cpu_to_le16(sizeof(struct smb2_set_info_req));
|
|
req->BufferLength = cpu_to_le32(*size);
|
|
|
|
memcpy(req->Buffer, *data, *size);
|
|
@@ -5440,9 +5438,9 @@ build_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon,
|
|
req->VolatileFileId = volatile_fid;
|
|
/* 1 for pad */
|
|
req->InputBufferOffset =
|
|
- cpu_to_le16(sizeof(struct smb2_query_info_req) - 1);
|
|
+ cpu_to_le16(sizeof(struct smb2_query_info_req));
|
|
req->OutputBufferLength = cpu_to_le32(
|
|
- outbuf_len + sizeof(struct smb2_query_info_rsp) - 1);
|
|
+ outbuf_len + sizeof(struct smb2_query_info_rsp));
|
|
|
|
iov->iov_base = (char *)req;
|
|
iov->iov_len = total_len;
|
|
diff --git a/fs/smb/client/smb2pdu.h b/fs/smb/client/smb2pdu.h
|
|
index 1237bb86e93a8..a5773a06aba8e 100644
|
|
--- a/fs/smb/client/smb2pdu.h
|
|
+++ b/fs/smb/client/smb2pdu.h
|
|
@@ -57,7 +57,7 @@ struct smb2_rdma_crypto_transform {
|
|
#define COMPOUND_FID 0xFFFFFFFFFFFFFFFFULL
|
|
|
|
#define SMB2_SYMLINK_STRUCT_SIZE \
|
|
- (sizeof(struct smb2_err_rsp) - 1 + sizeof(struct smb2_symlink_err_rsp))
|
|
+ (sizeof(struct smb2_err_rsp) + sizeof(struct smb2_symlink_err_rsp))
|
|
|
|
#define SYMLINK_ERROR_TAG 0x4c4d5953
|
|
|
|
diff --git a/fs/smb/common/smb2pdu.h b/fs/smb/common/smb2pdu.h
|
|
index 07549957b3099..5593bb49954c6 100644
|
|
--- a/fs/smb/common/smb2pdu.h
|
|
+++ b/fs/smb/common/smb2pdu.h
|
|
@@ -189,7 +189,7 @@ struct smb2_err_rsp {
|
|
__u8 ErrorContextCount;
|
|
__u8 Reserved;
|
|
__le32 ByteCount; /* even if zero, at least one byte follows */
|
|
- __u8 ErrorData[1]; /* variable length */
|
|
+ __u8 ErrorData[]; /* variable length */
|
|
} __packed;
|
|
|
|
#define SMB3_AES_CCM_NONCE 11
|
|
@@ -330,7 +330,7 @@ struct smb2_tree_connect_req {
|
|
__le16 Flags; /* Flags in SMB3.1.1 */
|
|
__le16 PathOffset;
|
|
__le16 PathLength;
|
|
- __u8 Buffer[1]; /* variable length */
|
|
+ __u8 Buffer[]; /* variable length */
|
|
} __packed;
|
|
|
|
/* Possible ShareType values */
|
|
@@ -617,7 +617,7 @@ struct smb2_negotiate_rsp {
|
|
__le16 SecurityBufferOffset;
|
|
__le16 SecurityBufferLength;
|
|
__le32 NegotiateContextOffset; /* Pre:SMB3.1.1 was reserved/ignored */
|
|
- __u8 Buffer[1]; /* variable length GSS security buffer */
|
|
+ __u8 Buffer[]; /* variable length GSS security buffer */
|
|
} __packed;
|
|
|
|
|
|
@@ -638,7 +638,7 @@ struct smb2_sess_setup_req {
|
|
__le16 SecurityBufferOffset;
|
|
__le16 SecurityBufferLength;
|
|
__le64 PreviousSessionId;
|
|
- __u8 Buffer[1]; /* variable length GSS security buffer */
|
|
+ __u8 Buffer[]; /* variable length GSS security buffer */
|
|
} __packed;
|
|
|
|
/* Currently defined SessionFlags */
|
|
@@ -655,7 +655,7 @@ struct smb2_sess_setup_rsp {
|
|
__le16 SessionFlags;
|
|
__le16 SecurityBufferOffset;
|
|
__le16 SecurityBufferLength;
|
|
- __u8 Buffer[1]; /* variable length GSS security buffer */
|
|
+ __u8 Buffer[]; /* variable length GSS security buffer */
|
|
} __packed;
|
|
|
|
|
|
@@ -737,7 +737,7 @@ struct smb2_read_req {
|
|
__le32 RemainingBytes;
|
|
__le16 ReadChannelInfoOffset;
|
|
__le16 ReadChannelInfoLength;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
/* Read flags */
|
|
@@ -752,7 +752,7 @@ struct smb2_read_rsp {
|
|
__le32 DataLength;
|
|
__le32 DataRemaining;
|
|
__le32 Flags;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
|
|
@@ -776,7 +776,7 @@ struct smb2_write_req {
|
|
__le16 WriteChannelInfoOffset;
|
|
__le16 WriteChannelInfoLength;
|
|
__le32 Flags;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
struct smb2_write_rsp {
|
|
@@ -787,7 +787,7 @@ struct smb2_write_rsp {
|
|
__le32 DataLength;
|
|
__le32 DataRemaining;
|
|
__u32 Reserved2;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
|
|
@@ -834,7 +834,10 @@ struct smb2_lock_req {
|
|
__u64 PersistentFileId;
|
|
__u64 VolatileFileId;
|
|
/* Followed by at least one */
|
|
- struct smb2_lock_element locks[1];
|
|
+ union {
|
|
+ struct smb2_lock_element lock;
|
|
+ DECLARE_FLEX_ARRAY(struct smb2_lock_element, locks);
|
|
+ };
|
|
} __packed;
|
|
|
|
struct smb2_lock_rsp {
|
|
@@ -888,7 +891,7 @@ struct smb2_query_directory_req {
|
|
__le16 FileNameOffset;
|
|
__le16 FileNameLength;
|
|
__le32 OutputBufferLength;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
struct smb2_query_directory_rsp {
|
|
@@ -896,7 +899,7 @@ struct smb2_query_directory_rsp {
|
|
__le16 StructureSize; /* Must be 9 */
|
|
__le16 OutputBufferOffset;
|
|
__le32 OutputBufferLength;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
/*
|
|
@@ -919,7 +922,7 @@ struct smb2_set_info_req {
|
|
__le32 AdditionalInformation;
|
|
__u64 PersistentFileId;
|
|
__u64 VolatileFileId;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
struct smb2_set_info_rsp {
|
|
@@ -974,7 +977,7 @@ struct smb2_change_notify_rsp {
|
|
__le16 StructureSize; /* Must be 9 */
|
|
__le16 OutputBufferOffset;
|
|
__le32 OutputBufferLength;
|
|
- __u8 Buffer[1]; /* array of file notify structs */
|
|
+ __u8 Buffer[]; /* array of file notify structs */
|
|
} __packed;
|
|
|
|
|
|
@@ -1180,7 +1183,7 @@ struct smb2_create_rsp {
|
|
__u64 VolatileFileId;
|
|
__le32 CreateContextsOffset;
|
|
__le32 CreateContextsLength;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
struct create_posix {
|
|
@@ -1524,7 +1527,7 @@ struct smb2_query_info_req {
|
|
__le32 Flags;
|
|
__u64 PersistentFileId;
|
|
__u64 VolatileFileId;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
struct smb2_query_info_rsp {
|
|
@@ -1532,7 +1535,7 @@ struct smb2_query_info_rsp {
|
|
__le16 StructureSize; /* Must be 9 */
|
|
__le16 OutputBufferOffset;
|
|
__le32 OutputBufferLength;
|
|
- __u8 Buffer[1];
|
|
+ __u8 Buffer[];
|
|
} __packed;
|
|
|
|
/*
|
|
@@ -1593,7 +1596,10 @@ struct smb2_file_all_info { /* data block encoding of response to level 18 */
|
|
__le32 Mode;
|
|
__le32 AlignmentRequirement;
|
|
__le32 FileNameLength;
|
|
- char FileName[1];
|
|
+ union {
|
|
+ char __pad; /* Legacy structure padding */
|
|
+ DECLARE_FLEX_ARRAY(char, FileName);
|
|
+ };
|
|
} __packed; /* level 18 Query */
|
|
|
|
struct smb2_file_eof_info { /* encoding of request for level 10 */
|
|
diff --git a/fs/smb/server/smb2ops.c b/fs/smb/server/smb2ops.c
|
|
index 535402629655e..27a9dce3e03ab 100644
|
|
--- a/fs/smb/server/smb2ops.c
|
|
+++ b/fs/smb/server/smb2ops.c
|
|
@@ -26,7 +26,7 @@ static struct smb_version_values smb21_server_values = {
|
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -52,7 +52,7 @@ static struct smb_version_values smb30_server_values = {
|
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -79,7 +79,7 @@ static struct smb_version_values smb302_server_values = {
|
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
@@ -106,7 +106,7 @@ static struct smb_version_values smb311_server_values = {
|
|
.unlock_lock_type = SMB2_LOCKFLAG_UNLOCK,
|
|
.header_size = sizeof(struct smb2_hdr),
|
|
.max_header_size = MAX_SMB2_HDR_SIZE,
|
|
- .read_rsp_size = sizeof(struct smb2_read_rsp) - 1,
|
|
+ .read_rsp_size = sizeof(struct smb2_read_rsp),
|
|
.lock_cmd = SMB2_LOCK,
|
|
.cap_unix = 0,
|
|
.cap_nt_find = SMB2_NT_FIND,
|
|
diff --git a/fs/smb/server/smb2pdu.c b/fs/smb/server/smb2pdu.c
|
|
index ea48dd06d4da3..6e5ed0ac578a6 100644
|
|
--- a/fs/smb/server/smb2pdu.c
|
|
+++ b/fs/smb/server/smb2pdu.c
|
|
@@ -294,8 +294,7 @@ int init_smb2_neg_rsp(struct ksmbd_work *work)
|
|
if (server_conf.signing == KSMBD_CONFIG_OPT_MANDATORY)
|
|
rsp->SecurityMode |= SMB2_NEGOTIATE_SIGNING_REQUIRED_LE;
|
|
err = ksmbd_iov_pin_rsp(work, rsp,
|
|
- sizeof(struct smb2_negotiate_rsp) -
|
|
- sizeof(rsp->Buffer) + AUTH_GSS_LENGTH);
|
|
+ sizeof(struct smb2_negotiate_rsp) + AUTH_GSS_LENGTH);
|
|
if (err)
|
|
return err;
|
|
conn->use_spnego = true;
|
|
@@ -1263,9 +1262,8 @@ err_out:
|
|
|
|
if (!rc)
|
|
rc = ksmbd_iov_pin_rsp(work, rsp,
|
|
- sizeof(struct smb2_negotiate_rsp) -
|
|
- sizeof(rsp->Buffer) +
|
|
- AUTH_GSS_LENGTH + neg_ctxt_len);
|
|
+ sizeof(struct smb2_negotiate_rsp) +
|
|
+ AUTH_GSS_LENGTH + neg_ctxt_len);
|
|
if (rc < 0)
|
|
smb2_set_err_rsp(work);
|
|
return rc;
|
|
diff --git a/fs/splice.c b/fs/splice.c
|
|
index 5969b7a1d353a..d0230cf8ec571 100644
|
|
--- a/fs/splice.c
|
|
+++ b/fs/splice.c
|
|
@@ -65,8 +65,7 @@ static bool page_cache_pipe_buf_try_steal(struct pipe_inode_info *pipe,
|
|
*/
|
|
folio_wait_writeback(folio);
|
|
|
|
- if (folio_has_private(folio) &&
|
|
- !filemap_release_folio(folio, GFP_KERNEL))
|
|
+ if (!filemap_release_folio(folio, GFP_KERNEL))
|
|
goto out_unlock;
|
|
|
|
/*
|
|
@@ -764,6 +763,17 @@ static long do_splice_from(struct pipe_inode_info *pipe, struct file *out,
|
|
return out->f_op->splice_write(pipe, out, ppos, len, flags);
|
|
}
|
|
|
|
+/*
|
|
+ * Indicate to the caller that there was a premature EOF when reading from the
|
|
+ * source and the caller didn't indicate they would be sending more data after
|
|
+ * this.
|
|
+ */
|
|
+static void do_splice_eof(struct splice_desc *sd)
|
|
+{
|
|
+ if (sd->splice_eof)
|
|
+ sd->splice_eof(sd);
|
|
+}
|
|
+
|
|
/*
|
|
* Attempt to initiate a splice from a file to a pipe.
|
|
*/
|
|
@@ -864,7 +874,7 @@ ssize_t splice_direct_to_actor(struct file *in, struct splice_desc *sd,
|
|
|
|
ret = do_splice_to(in, &pos, pipe, len, flags);
|
|
if (unlikely(ret <= 0))
|
|
- goto out_release;
|
|
+ goto read_failure;
|
|
|
|
read_len = ret;
|
|
sd->total_len = read_len;
|
|
@@ -904,6 +914,15 @@ done:
|
|
file_accessed(in);
|
|
return bytes;
|
|
|
|
+read_failure:
|
|
+ /*
|
|
+ * If the user did *not* set SPLICE_F_MORE *and* we didn't hit that
|
|
+ * "use all of len" case that cleared SPLICE_F_MORE, *and* we did a
|
|
+ * "->splice_in()" that returned EOF (ie zero) *and* we have sent at
|
|
+ * least 1 byte *then* we will also do the ->splice_eof() call.
|
|
+ */
|
|
+ if (ret == 0 && !more && len > 0 && bytes)
|
|
+ do_splice_eof(sd);
|
|
out_release:
|
|
/*
|
|
* If we did an incomplete transfer we must release
|
|
@@ -932,6 +951,14 @@ static int direct_splice_actor(struct pipe_inode_info *pipe,
|
|
sd->flags);
|
|
}
|
|
|
|
+static void direct_file_splice_eof(struct splice_desc *sd)
|
|
+{
|
|
+ struct file *file = sd->u.file;
|
|
+
|
|
+ if (file->f_op->splice_eof)
|
|
+ file->f_op->splice_eof(file);
|
|
+}
|
|
+
|
|
/**
|
|
* do_splice_direct - splices data directly between two files
|
|
* @in: file to splice from
|
|
@@ -957,6 +984,7 @@ long do_splice_direct(struct file *in, loff_t *ppos, struct file *out,
|
|
.flags = flags,
|
|
.pos = *ppos,
|
|
.u.file = out,
|
|
+ .splice_eof = direct_file_splice_eof,
|
|
.opos = opos,
|
|
};
|
|
long ret;
|
|
diff --git a/include/linux/bpf.h b/include/linux/bpf.h
|
|
index 3ce9e39ecdb85..ba22cf4f5fc0e 100644
|
|
--- a/include/linux/bpf.h
|
|
+++ b/include/linux/bpf.h
|
|
@@ -702,10 +702,14 @@ bpf_ctx_record_field_size(struct bpf_insn_access_aux *aux, u32 size)
|
|
aux->ctx_field_size = size;
|
|
}
|
|
|
|
+static bool bpf_is_ldimm64(const struct bpf_insn *insn)
|
|
+{
|
|
+ return insn->code == (BPF_LD | BPF_IMM | BPF_DW);
|
|
+}
|
|
+
|
|
static inline bool bpf_pseudo_func(const struct bpf_insn *insn)
|
|
{
|
|
- return insn->code == (BPF_LD | BPF_IMM | BPF_DW) &&
|
|
- insn->src_reg == BPF_PSEUDO_FUNC;
|
|
+ return bpf_is_ldimm64(insn) && insn->src_reg == BPF_PSEUDO_FUNC;
|
|
}
|
|
|
|
struct bpf_prog_ops {
|
|
@@ -825,6 +829,11 @@ struct btf_func_model {
|
|
*/
|
|
#define BPF_TRAMP_F_SHARE_IPMODIFY BIT(6)
|
|
|
|
+/* Indicate that current trampoline is in a tail call context. Then, it has to
|
|
+ * cache and restore tail_call_cnt to avoid infinite tail call loop.
|
|
+ */
|
|
+#define BPF_TRAMP_F_TAIL_CALL_CTX BIT(7)
|
|
+
|
|
/* Each call __bpf_prog_enter + call bpf_func + call __bpf_prog_exit is ~50
|
|
* bytes on x86.
|
|
*/
|
|
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
|
|
index 1a32baa78ce26..f080ccf27d256 100644
|
|
--- a/include/linux/bpf_verifier.h
|
|
+++ b/include/linux/bpf_verifier.h
|
|
@@ -429,6 +429,7 @@ struct bpf_insn_aux_data {
|
|
/* below fields are initialized once */
|
|
unsigned int orig_idx; /* original instruction index */
|
|
bool prune_point;
|
|
+ bool jmp_point;
|
|
};
|
|
|
|
#define MAX_USED_MAPS 64 /* max number of maps accessed by one eBPF program */
|
|
diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h
|
|
index ee0d75d9a302d..1e0df607e40c4 100644
|
|
--- a/include/linux/f2fs_fs.h
|
|
+++ b/include/linux/f2fs_fs.h
|
|
@@ -40,9 +40,8 @@
|
|
|
|
#define F2FS_ENC_UTF8_12_1 1
|
|
|
|
-#define F2FS_IO_SIZE(sbi) (1 << F2FS_OPTION(sbi).write_io_size_bits) /* Blocks */
|
|
-#define F2FS_IO_SIZE_KB(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 2)) /* KB */
|
|
-#define F2FS_IO_SIZE_BYTES(sbi) (1 << (F2FS_OPTION(sbi).write_io_size_bits + 12)) /* B */
|
|
+#define F2FS_IO_SIZE(sbi) BIT(F2FS_OPTION(sbi).write_io_size_bits) /* Blocks */
|
|
+#define F2FS_IO_SIZE_KB(sbi) BIT(F2FS_OPTION(sbi).write_io_size_bits + 2) /* KB */
|
|
#define F2FS_IO_SIZE_BITS(sbi) (F2FS_OPTION(sbi).write_io_size_bits) /* power of 2 */
|
|
#define F2FS_IO_SIZE_MASK(sbi) (F2FS_IO_SIZE(sbi) - 1)
|
|
#define F2FS_IO_ALIGNED(sbi) (F2FS_IO_SIZE(sbi) > 1)
|
|
@@ -340,7 +339,7 @@ enum {
|
|
OFFSET_BIT_SHIFT
|
|
};
|
|
|
|
-#define OFFSET_BIT_MASK (0x07) /* (0x01 << OFFSET_BIT_SHIFT) - 1 */
|
|
+#define OFFSET_BIT_MASK GENMASK(OFFSET_BIT_SHIFT - 1, 0)
|
|
|
|
struct node_footer {
|
|
__le32 nid; /* node id */
|
|
@@ -545,7 +544,7 @@ typedef __le32 f2fs_hash_t;
|
|
#define MAX_DIR_HASH_DEPTH 63
|
|
|
|
/* MAX buckets in one level of dir */
|
|
-#define MAX_DIR_BUCKETS (1 << ((MAX_DIR_HASH_DEPTH / 2) - 1))
|
|
+#define MAX_DIR_BUCKETS BIT((MAX_DIR_HASH_DEPTH / 2) - 1)
|
|
|
|
/*
|
|
* space utilization of regular dentry and inline dentry (w/o extra reservation)
|
|
diff --git a/include/linux/fs.h b/include/linux/fs.h
|
|
index b6af6abc7a77f..4a1911dcf834b 100644
|
|
--- a/include/linux/fs.h
|
|
+++ b/include/linux/fs.h
|
|
@@ -2177,6 +2177,7 @@ struct file_operations {
|
|
int (*flock) (struct file *, int, struct file_lock *);
|
|
ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
|
|
ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
|
|
+ void (*splice_eof)(struct file *file);
|
|
int (*setlease)(struct file *, long, struct file_lock **, void **);
|
|
long (*fallocate)(struct file *file, int mode, loff_t offset,
|
|
loff_t len);
|
|
diff --git a/include/linux/group_cpus.h b/include/linux/group_cpus.h
|
|
new file mode 100644
|
|
index 0000000000000..e42807ec61f6e
|
|
--- /dev/null
|
|
+++ b/include/linux/group_cpus.h
|
|
@@ -0,0 +1,14 @@
|
|
+/* SPDX-License-Identifier: GPL-2.0-only */
|
|
+/*
|
|
+ * Copyright (C) 2016 Thomas Gleixner.
|
|
+ * Copyright (C) 2016-2017 Christoph Hellwig.
|
|
+ */
|
|
+
|
|
+#ifndef __LINUX_GROUP_CPUS_H
|
|
+#define __LINUX_GROUP_CPUS_H
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/cpu.h>
|
|
+
|
|
+struct cpumask *group_cpus_evenly(unsigned int numgrps);
|
|
+
|
|
+#endif
|
|
diff --git a/include/linux/net.h b/include/linux/net.h
|
|
index 18d942bbdf6e0..25baca60f6cba 100644
|
|
--- a/include/linux/net.h
|
|
+++ b/include/linux/net.h
|
|
@@ -209,6 +209,7 @@ struct proto_ops {
|
|
int offset, size_t size, int flags);
|
|
ssize_t (*splice_read)(struct socket *sock, loff_t *ppos,
|
|
struct pipe_inode_info *pipe, size_t len, unsigned int flags);
|
|
+ void (*splice_eof)(struct socket *sock);
|
|
int (*set_peek_off)(struct sock *sk, int val);
|
|
int (*peek_len)(struct socket *sock);
|
|
|
|
diff --git a/include/linux/pagemap.h b/include/linux/pagemap.h
|
|
index 03307b72de6c6..1be5a1fa6a3a8 100644
|
|
--- a/include/linux/pagemap.h
|
|
+++ b/include/linux/pagemap.h
|
|
@@ -199,6 +199,9 @@ enum mapping_flags {
|
|
/* writeback related tags are not used */
|
|
AS_NO_WRITEBACK_TAGS = 5,
|
|
AS_LARGE_FOLIO_SUPPORT = 6,
|
|
+ AS_RELEASE_ALWAYS, /* Call ->release_folio(), even if no private data */
|
|
+ AS_STABLE_WRITES, /* must wait for writeback before modifying
|
|
+ folio contents */
|
|
};
|
|
|
|
/**
|
|
@@ -269,6 +272,36 @@ static inline int mapping_use_writeback_tags(struct address_space *mapping)
|
|
return !test_bit(AS_NO_WRITEBACK_TAGS, &mapping->flags);
|
|
}
|
|
|
|
+static inline bool mapping_release_always(const struct address_space *mapping)
|
|
+{
|
|
+ return test_bit(AS_RELEASE_ALWAYS, &mapping->flags);
|
|
+}
|
|
+
|
|
+static inline void mapping_set_release_always(struct address_space *mapping)
|
|
+{
|
|
+ set_bit(AS_RELEASE_ALWAYS, &mapping->flags);
|
|
+}
|
|
+
|
|
+static inline void mapping_clear_release_always(struct address_space *mapping)
|
|
+{
|
|
+ clear_bit(AS_RELEASE_ALWAYS, &mapping->flags);
|
|
+}
|
|
+
|
|
+static inline bool mapping_stable_writes(const struct address_space *mapping)
|
|
+{
|
|
+ return test_bit(AS_STABLE_WRITES, &mapping->flags);
|
|
+}
|
|
+
|
|
+static inline void mapping_set_stable_writes(struct address_space *mapping)
|
|
+{
|
|
+ set_bit(AS_STABLE_WRITES, &mapping->flags);
|
|
+}
|
|
+
|
|
+static inline void mapping_clear_stable_writes(struct address_space *mapping)
|
|
+{
|
|
+ clear_bit(AS_STABLE_WRITES, &mapping->flags);
|
|
+}
|
|
+
|
|
static inline gfp_t mapping_gfp_mask(struct address_space * mapping)
|
|
{
|
|
return mapping->gfp_mask;
|
|
diff --git a/include/linux/skmsg.h b/include/linux/skmsg.h
|
|
index c1637515a8a41..c953b8c0d2f43 100644
|
|
--- a/include/linux/skmsg.h
|
|
+++ b/include/linux/skmsg.h
|
|
@@ -106,6 +106,7 @@ struct sk_psock {
|
|
struct mutex work_mutex;
|
|
struct sk_psock_work_state work_state;
|
|
struct delayed_work work;
|
|
+ struct sock *sk_pair;
|
|
struct rcu_work rwork;
|
|
};
|
|
|
|
diff --git a/include/linux/socket.h b/include/linux/socket.h
|
|
index 1db29aab8f9c3..b3c58042bd254 100644
|
|
--- a/include/linux/socket.h
|
|
+++ b/include/linux/socket.h
|
|
@@ -324,6 +324,7 @@ struct ucred {
|
|
*/
|
|
|
|
#define MSG_ZEROCOPY 0x4000000 /* Use user data in kernel path */
|
|
+#define MSG_SPLICE_PAGES 0x8000000 /* Splice the pages from the iterator in sendmsg() */
|
|
#define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */
|
|
#define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exec for file
|
|
descriptor received through
|
|
@@ -334,6 +335,8 @@ struct ucred {
|
|
#define MSG_CMSG_COMPAT 0 /* We never have 32 bit fixups */
|
|
#endif
|
|
|
|
+/* Flags to be cleared on entry by sendmsg and sendmmsg syscalls */
|
|
+#define MSG_INTERNAL_SENDMSG_FLAGS (MSG_SPLICE_PAGES)
|
|
|
|
/* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
|
|
#define SOL_IP 0
|
|
diff --git a/include/linux/splice.h b/include/linux/splice.h
|
|
index a55179fd60fc3..41a70687be853 100644
|
|
--- a/include/linux/splice.h
|
|
+++ b/include/linux/splice.h
|
|
@@ -38,6 +38,7 @@ struct splice_desc {
|
|
struct file *file; /* file to read/write */
|
|
void *data; /* cookie */
|
|
} u;
|
|
+ void (*splice_eof)(struct splice_desc *sd); /* Unexpected EOF handler */
|
|
loff_t pos; /* file position */
|
|
loff_t *opos; /* sendfile: output position */
|
|
size_t num_spliced; /* number of bytes already spliced */
|
|
diff --git a/include/linux/udp.h b/include/linux/udp.h
|
|
index e96da4157d04d..efd9ab6df3797 100644
|
|
--- a/include/linux/udp.h
|
|
+++ b/include/linux/udp.h
|
|
@@ -30,25 +30,33 @@ static inline u32 udp_hashfn(const struct net *net, u32 num, u32 mask)
|
|
return (num + net_hash_mix(net)) & mask;
|
|
}
|
|
|
|
+enum {
|
|
+ UDP_FLAGS_CORK, /* Cork is required */
|
|
+ UDP_FLAGS_NO_CHECK6_TX, /* Send zero UDP6 checksums on TX? */
|
|
+ UDP_FLAGS_NO_CHECK6_RX, /* Allow zero UDP6 checksums on RX? */
|
|
+ UDP_FLAGS_GRO_ENABLED, /* Request GRO aggregation */
|
|
+ UDP_FLAGS_ACCEPT_FRAGLIST,
|
|
+ UDP_FLAGS_ACCEPT_L4,
|
|
+ UDP_FLAGS_ENCAP_ENABLED, /* This socket enabled encap */
|
|
+};
|
|
+
|
|
struct udp_sock {
|
|
/* inet_sock has to be the first member */
|
|
struct inet_sock inet;
|
|
#define udp_port_hash inet.sk.__sk_common.skc_u16hashes[0]
|
|
#define udp_portaddr_hash inet.sk.__sk_common.skc_u16hashes[1]
|
|
#define udp_portaddr_node inet.sk.__sk_common.skc_portaddr_node
|
|
+
|
|
+ unsigned long udp_flags;
|
|
+
|
|
int pending; /* Any pending frames ? */
|
|
- unsigned int corkflag; /* Cork is required */
|
|
__u8 encap_type; /* Is this an Encapsulation socket? */
|
|
- unsigned char no_check6_tx:1,/* Send zero UDP6 checksums on TX? */
|
|
- no_check6_rx:1,/* Allow zero UDP6 checksums on RX? */
|
|
- encap_enabled:1, /* This socket enabled encap
|
|
- * processing; UDP tunnels and
|
|
- * different encapsulation layer set
|
|
- * this
|
|
- */
|
|
- gro_enabled:1, /* Request GRO aggregation */
|
|
- accept_udp_l4:1,
|
|
- accept_udp_fraglist:1;
|
|
+
|
|
+/* indicator bits used by pcflag: */
|
|
+#define UDPLITE_BIT 0x1 /* set by udplite proto init function */
|
|
+#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */
|
|
+#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */
|
|
+ __u8 pcflag; /* marks socket as UDP-Lite if > 0 */
|
|
/*
|
|
* Following member retains the information to create a UDP header
|
|
* when the socket is uncorked.
|
|
@@ -60,12 +68,6 @@ struct udp_sock {
|
|
*/
|
|
__u16 pcslen;
|
|
__u16 pcrlen;
|
|
-/* indicator bits used by pcflag: */
|
|
-#define UDPLITE_BIT 0x1 /* set by udplite proto init function */
|
|
-#define UDPLITE_SEND_CC 0x2 /* set via udplite setsockopt */
|
|
-#define UDPLITE_RECV_CC 0x4 /* set via udplite setsocktopt */
|
|
- __u8 pcflag; /* marks socket as UDP-Lite if > 0 */
|
|
- __u8 unused[3];
|
|
/*
|
|
* For encapsulation sockets.
|
|
*/
|
|
@@ -89,6 +91,17 @@ struct udp_sock {
|
|
int forward_deficit;
|
|
};
|
|
|
|
+#define udp_test_bit(nr, sk) \
|
|
+ test_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags)
|
|
+#define udp_set_bit(nr, sk) \
|
|
+ set_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags)
|
|
+#define udp_test_and_set_bit(nr, sk) \
|
|
+ test_and_set_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags)
|
|
+#define udp_clear_bit(nr, sk) \
|
|
+ clear_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags)
|
|
+#define udp_assign_bit(nr, sk, val) \
|
|
+ assign_bit(UDP_FLAGS_##nr, &udp_sk(sk)->udp_flags, val)
|
|
+
|
|
#define UDP_MAX_SEGMENTS (1 << 6UL)
|
|
|
|
static inline struct udp_sock *udp_sk(const struct sock *sk)
|
|
@@ -98,22 +111,22 @@ static inline struct udp_sock *udp_sk(const struct sock *sk)
|
|
|
|
static inline void udp_set_no_check6_tx(struct sock *sk, bool val)
|
|
{
|
|
- udp_sk(sk)->no_check6_tx = val;
|
|
+ udp_assign_bit(NO_CHECK6_TX, sk, val);
|
|
}
|
|
|
|
static inline void udp_set_no_check6_rx(struct sock *sk, bool val)
|
|
{
|
|
- udp_sk(sk)->no_check6_rx = val;
|
|
+ udp_assign_bit(NO_CHECK6_RX, sk, val);
|
|
}
|
|
|
|
-static inline bool udp_get_no_check6_tx(struct sock *sk)
|
|
+static inline bool udp_get_no_check6_tx(const struct sock *sk)
|
|
{
|
|
- return udp_sk(sk)->no_check6_tx;
|
|
+ return udp_test_bit(NO_CHECK6_TX, sk);
|
|
}
|
|
|
|
-static inline bool udp_get_no_check6_rx(struct sock *sk)
|
|
+static inline bool udp_get_no_check6_rx(const struct sock *sk)
|
|
{
|
|
- return udp_sk(sk)->no_check6_rx;
|
|
+ return udp_test_bit(NO_CHECK6_RX, sk);
|
|
}
|
|
|
|
static inline void udp_cmsg_recv(struct msghdr *msg, struct sock *sk,
|
|
@@ -132,10 +145,12 @@ static inline bool udp_unexpected_gso(struct sock *sk, struct sk_buff *skb)
|
|
if (!skb_is_gso(skb))
|
|
return false;
|
|
|
|
- if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 && !udp_sk(sk)->accept_udp_l4)
|
|
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP_L4 &&
|
|
+ !udp_test_bit(ACCEPT_L4, sk))
|
|
return true;
|
|
|
|
- if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST && !udp_sk(sk)->accept_udp_fraglist)
|
|
+ if (skb_shinfo(skb)->gso_type & SKB_GSO_FRAGLIST &&
|
|
+ !udp_test_bit(ACCEPT_FRAGLIST, sk))
|
|
return true;
|
|
|
|
return false;
|
|
@@ -143,8 +158,8 @@ static inline bool udp_unexpected_gso(struct sock *sk, struct sk_buff *skb)
|
|
|
|
static inline void udp_allow_gso(struct sock *sk)
|
|
{
|
|
- udp_sk(sk)->accept_udp_l4 = 1;
|
|
- udp_sk(sk)->accept_udp_fraglist = 1;
|
|
+ udp_set_bit(ACCEPT_L4, sk);
|
|
+ udp_set_bit(ACCEPT_FRAGLIST, sk);
|
|
}
|
|
|
|
#define udp_portaddr_for_each_entry(__sk, list) \
|
|
diff --git a/include/net/af_unix.h b/include/net/af_unix.h
|
|
index 480fa579787e5..55ca217c626b7 100644
|
|
--- a/include/net/af_unix.h
|
|
+++ b/include/net/af_unix.h
|
|
@@ -77,6 +77,7 @@ static inline struct unix_sock *unix_sk(const struct sock *sk)
|
|
{
|
|
return (struct unix_sock *)sk;
|
|
}
|
|
+#define unix_peer(sk) (unix_sk(sk)->peer)
|
|
|
|
#define peer_wait peer_wq.wait
|
|
|
|
diff --git a/include/net/inet_common.h b/include/net/inet_common.h
|
|
index cec453c18f1d6..4673bbfd2811f 100644
|
|
--- a/include/net/inet_common.h
|
|
+++ b/include/net/inet_common.h
|
|
@@ -33,6 +33,7 @@ int inet_accept(struct socket *sock, struct socket *newsock, int flags,
|
|
bool kern);
|
|
int inet_send_prepare(struct sock *sk);
|
|
int inet_sendmsg(struct socket *sock, struct msghdr *msg, size_t size);
|
|
+void inet_splice_eof(struct socket *sock);
|
|
ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
|
|
size_t size, int flags);
|
|
int inet_recvmsg(struct socket *sock, struct msghdr *msg, size_t size,
|
|
diff --git a/include/net/ip.h b/include/net/ip.h
|
|
index c286344628dba..c83c09c65623f 100644
|
|
--- a/include/net/ip.h
|
|
+++ b/include/net/ip.h
|
|
@@ -95,7 +95,7 @@ static inline void ipcm_init_sk(struct ipcm_cookie *ipcm,
|
|
ipcm_init(ipcm);
|
|
|
|
ipcm->sockc.mark = READ_ONCE(inet->sk.sk_mark);
|
|
- ipcm->sockc.tsflags = inet->sk.sk_tsflags;
|
|
+ ipcm->sockc.tsflags = READ_ONCE(inet->sk.sk_tsflags);
|
|
ipcm->oif = READ_ONCE(inet->sk.sk_bound_dev_if);
|
|
ipcm->addr = inet->inet_saddr;
|
|
ipcm->protocol = inet->inet_num;
|
|
diff --git a/include/net/netfilter/nf_conntrack_act_ct.h b/include/net/netfilter/nf_conntrack_act_ct.h
|
|
index 078d3c52c03f9..e5f2f0b73a9a0 100644
|
|
--- a/include/net/netfilter/nf_conntrack_act_ct.h
|
|
+++ b/include/net/netfilter/nf_conntrack_act_ct.h
|
|
@@ -20,7 +20,22 @@ static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_find(const struct nf
|
|
#endif
|
|
}
|
|
|
|
-static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct nf_conn *ct)
|
|
+static inline void nf_conn_act_ct_ext_fill(struct sk_buff *skb, struct nf_conn *ct,
|
|
+ enum ip_conntrack_info ctinfo)
|
|
+{
|
|
+#if IS_ENABLED(CONFIG_NET_ACT_CT)
|
|
+ struct nf_conn_act_ct_ext *act_ct_ext;
|
|
+
|
|
+ act_ct_ext = nf_conn_act_ct_ext_find(ct);
|
|
+ if (dev_net(skb->dev) == &init_net && act_ct_ext)
|
|
+ act_ct_ext->ifindex[CTINFO2DIR(ctinfo)] = skb->dev->ifindex;
|
|
+#endif
|
|
+}
|
|
+
|
|
+static inline struct
|
|
+nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct sk_buff *skb,
|
|
+ struct nf_conn *ct,
|
|
+ enum ip_conntrack_info ctinfo)
|
|
{
|
|
#if IS_ENABLED(CONFIG_NET_ACT_CT)
|
|
struct nf_conn_act_ct_ext *act_ct = nf_ct_ext_find(ct, NF_CT_EXT_ACT_CT);
|
|
@@ -29,22 +44,11 @@ static inline struct nf_conn_act_ct_ext *nf_conn_act_ct_ext_add(struct nf_conn *
|
|
return act_ct;
|
|
|
|
act_ct = nf_ct_ext_add(ct, NF_CT_EXT_ACT_CT, GFP_ATOMIC);
|
|
+ nf_conn_act_ct_ext_fill(skb, ct, ctinfo);
|
|
return act_ct;
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
-static inline void nf_conn_act_ct_ext_fill(struct sk_buff *skb, struct nf_conn *ct,
|
|
- enum ip_conntrack_info ctinfo)
|
|
-{
|
|
-#if IS_ENABLED(CONFIG_NET_ACT_CT)
|
|
- struct nf_conn_act_ct_ext *act_ct_ext;
|
|
-
|
|
- act_ct_ext = nf_conn_act_ct_ext_find(ct);
|
|
- if (dev_net(skb->dev) == &init_net && act_ct_ext)
|
|
- act_ct_ext->ifindex[CTINFO2DIR(ctinfo)] = skb->dev->ifindex;
|
|
-#endif
|
|
-}
|
|
-
|
|
#endif /* _NF_CONNTRACK_ACT_CT_H */
|
|
diff --git a/include/net/netfilter/nf_flow_table.h b/include/net/netfilter/nf_flow_table.h
|
|
index cd982f4a0f50c..dde4dd9c4012c 100644
|
|
--- a/include/net/netfilter/nf_flow_table.h
|
|
+++ b/include/net/netfilter/nf_flow_table.h
|
|
@@ -53,14 +53,17 @@ struct nf_flowtable_type {
|
|
struct list_head list;
|
|
int family;
|
|
int (*init)(struct nf_flowtable *ft);
|
|
+ bool (*gc)(const struct flow_offload *flow);
|
|
int (*setup)(struct nf_flowtable *ft,
|
|
struct net_device *dev,
|
|
enum flow_block_command cmd);
|
|
int (*action)(struct net *net,
|
|
- const struct flow_offload *flow,
|
|
+ struct flow_offload *flow,
|
|
enum flow_offload_tuple_dir dir,
|
|
struct nf_flow_rule *flow_rule);
|
|
void (*free)(struct nf_flowtable *ft);
|
|
+ void (*get)(struct nf_flowtable *ft);
|
|
+ void (*put)(struct nf_flowtable *ft);
|
|
nf_hookfn *hook;
|
|
struct module *owner;
|
|
};
|
|
@@ -164,6 +167,8 @@ enum nf_flow_flags {
|
|
NF_FLOW_HW_DYING,
|
|
NF_FLOW_HW_DEAD,
|
|
NF_FLOW_HW_PENDING,
|
|
+ NF_FLOW_HW_BIDIRECTIONAL,
|
|
+ NF_FLOW_HW_ESTABLISHED,
|
|
};
|
|
|
|
enum flow_offload_type {
|
|
@@ -237,6 +242,11 @@ nf_flow_table_offload_add_cb(struct nf_flowtable *flow_table,
|
|
}
|
|
|
|
list_add_tail(&block_cb->list, &block->cb_list);
|
|
+ up_write(&flow_table->flow_block_lock);
|
|
+
|
|
+ if (flow_table->type->get)
|
|
+ flow_table->type->get(flow_table);
|
|
+ return 0;
|
|
|
|
unlock:
|
|
up_write(&flow_table->flow_block_lock);
|
|
@@ -259,6 +269,9 @@ nf_flow_table_offload_del_cb(struct nf_flowtable *flow_table,
|
|
WARN_ON(true);
|
|
}
|
|
up_write(&flow_table->flow_block_lock);
|
|
+
|
|
+ if (flow_table->type->put)
|
|
+ flow_table->type->put(flow_table);
|
|
}
|
|
|
|
int flow_offload_route_init(struct flow_offload *flow,
|
|
@@ -266,7 +279,7 @@ int flow_offload_route_init(struct flow_offload *flow,
|
|
|
|
int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow);
|
|
void flow_offload_refresh(struct nf_flowtable *flow_table,
|
|
- struct flow_offload *flow);
|
|
+ struct flow_offload *flow, bool force);
|
|
|
|
struct flow_offload_tuple_rhash *flow_offload_lookup(struct nf_flowtable *flow_table,
|
|
struct flow_offload_tuple *tuple);
|
|
@@ -312,10 +325,10 @@ void nf_flow_table_offload_flush_cleanup(struct nf_flowtable *flowtable);
|
|
int nf_flow_table_offload_setup(struct nf_flowtable *flowtable,
|
|
struct net_device *dev,
|
|
enum flow_block_command cmd);
|
|
-int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
|
|
+int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
|
|
enum flow_offload_tuple_dir dir,
|
|
struct nf_flow_rule *flow_rule);
|
|
-int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow,
|
|
+int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow,
|
|
enum flow_offload_tuple_dir dir,
|
|
struct nf_flow_rule *flow_rule);
|
|
|
|
diff --git a/include/net/netfilter/nf_tables_ipv4.h b/include/net/netfilter/nf_tables_ipv4.h
|
|
index c4a6147b0ef8c..5225d2bd1a6e9 100644
|
|
--- a/include/net/netfilter/nf_tables_ipv4.h
|
|
+++ b/include/net/netfilter/nf_tables_ipv4.h
|
|
@@ -29,8 +29,8 @@ static inline int __nft_set_pktinfo_ipv4_validate(struct nft_pktinfo *pkt)
|
|
if (iph->ihl < 5 || iph->version != 4)
|
|
return -1;
|
|
|
|
- len = ntohs(iph->tot_len);
|
|
- thoff = iph->ihl * 4;
|
|
+ len = iph_totlen(pkt->skb, iph);
|
|
+ thoff = skb_network_offset(pkt->skb) + (iph->ihl * 4);
|
|
if (pkt->skb->len < len)
|
|
return -1;
|
|
else if (len < thoff)
|
|
@@ -62,7 +62,7 @@ static inline int nft_set_pktinfo_ipv4_ingress(struct nft_pktinfo *pkt)
|
|
if (iph->ihl < 5 || iph->version != 4)
|
|
goto inhdr_error;
|
|
|
|
- len = ntohs(iph->tot_len);
|
|
+ len = iph_totlen(pkt->skb, iph);
|
|
thoff = iph->ihl * 4;
|
|
if (pkt->skb->len < len) {
|
|
__IP_INC_STATS(nft_net(pkt), IPSTATS_MIB_INTRUNCATEDPKTS);
|
|
diff --git a/include/net/sock.h b/include/net/sock.h
|
|
index b6027b01c2455..6b51e85ae69e3 100644
|
|
--- a/include/net/sock.h
|
|
+++ b/include/net/sock.h
|
|
@@ -1279,6 +1279,7 @@ struct proto {
|
|
size_t len, int flags, int *addr_len);
|
|
int (*sendpage)(struct sock *sk, struct page *page,
|
|
int offset, size_t size, int flags);
|
|
+ void (*splice_eof)(struct socket *sock);
|
|
int (*bind)(struct sock *sk,
|
|
struct sockaddr *addr, int addr_len);
|
|
int (*bind_add)(struct sock *sk,
|
|
@@ -1928,7 +1929,9 @@ struct sockcm_cookie {
|
|
static inline void sockcm_init(struct sockcm_cookie *sockc,
|
|
const struct sock *sk)
|
|
{
|
|
- *sockc = (struct sockcm_cookie) { .tsflags = sk->sk_tsflags };
|
|
+ *sockc = (struct sockcm_cookie) {
|
|
+ .tsflags = READ_ONCE(sk->sk_tsflags)
|
|
+ };
|
|
}
|
|
|
|
int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
|
|
@@ -2741,9 +2744,9 @@ void __sock_recv_wifi_status(struct msghdr *msg, struct sock *sk,
|
|
static inline void
|
|
sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
|
|
{
|
|
- ktime_t kt = skb->tstamp;
|
|
struct skb_shared_hwtstamps *hwtstamps = skb_hwtstamps(skb);
|
|
-
|
|
+ u32 tsflags = READ_ONCE(sk->sk_tsflags);
|
|
+ ktime_t kt = skb->tstamp;
|
|
/*
|
|
* generate control messages if
|
|
* - receive time stamping in software requested
|
|
@@ -2751,10 +2754,10 @@ sock_recv_timestamp(struct msghdr *msg, struct sock *sk, struct sk_buff *skb)
|
|
* - hardware time stamps available and wanted
|
|
*/
|
|
if (sock_flag(sk, SOCK_RCVTSTAMP) ||
|
|
- (sk->sk_tsflags & SOF_TIMESTAMPING_RX_SOFTWARE) ||
|
|
- (kt && sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) ||
|
|
+ (tsflags & SOF_TIMESTAMPING_RX_SOFTWARE) ||
|
|
+ (kt && tsflags & SOF_TIMESTAMPING_SOFTWARE) ||
|
|
(hwtstamps->hwtstamp &&
|
|
- (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE)))
|
|
+ (tsflags & SOF_TIMESTAMPING_RAW_HARDWARE)))
|
|
__sock_recv_timestamp(msg, sk, skb);
|
|
else
|
|
sock_write_timestamp(sk, kt);
|
|
@@ -2776,7 +2779,8 @@ static inline void sock_recv_cmsgs(struct msghdr *msg, struct sock *sk,
|
|
#define TSFLAGS_ANY (SOF_TIMESTAMPING_SOFTWARE | \
|
|
SOF_TIMESTAMPING_RAW_HARDWARE)
|
|
|
|
- if (sk->sk_flags & FLAGS_RECV_CMSGS || sk->sk_tsflags & TSFLAGS_ANY)
|
|
+ if (sk->sk_flags & FLAGS_RECV_CMSGS ||
|
|
+ READ_ONCE(sk->sk_tsflags) & TSFLAGS_ANY)
|
|
__sock_recv_cmsgs(msg, sk, skb);
|
|
else if (unlikely(sock_flag(sk, SOCK_TIMESTAMP)))
|
|
sock_write_timestamp(sk, skb->tstamp);
|
|
@@ -2825,6 +2829,11 @@ static inline bool sk_is_tcp(const struct sock *sk)
|
|
return sk->sk_type == SOCK_STREAM && sk->sk_protocol == IPPROTO_TCP;
|
|
}
|
|
|
|
+static inline bool sk_is_stream_unix(const struct sock *sk)
|
|
+{
|
|
+ return sk->sk_family == AF_UNIX && sk->sk_type == SOCK_STREAM;
|
|
+}
|
|
+
|
|
/**
|
|
* sk_eat_skb - Release a skb if it is no longer needed
|
|
* @sk: socket to eat this skb from
|
|
diff --git a/include/net/tcp.h b/include/net/tcp.h
|
|
index c3d56b337f358..4c838f7290dd9 100644
|
|
--- a/include/net/tcp.h
|
|
+++ b/include/net/tcp.h
|
|
@@ -332,6 +332,7 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size);
|
|
int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size);
|
|
int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, int *copied,
|
|
size_t size, struct ubuf_info *uarg);
|
|
+void tcp_splice_eof(struct socket *sock);
|
|
int tcp_sendpage(struct sock *sk, struct page *page, int offset, size_t size,
|
|
int flags);
|
|
int tcp_sendpage_locked(struct sock *sk, struct page *page, int offset,
|
|
diff --git a/include/net/udp.h b/include/net/udp.h
|
|
index fee053bcd17c6..fa4cdbe55552c 100644
|
|
--- a/include/net/udp.h
|
|
+++ b/include/net/udp.h
|
|
@@ -269,6 +269,7 @@ int udp_get_port(struct sock *sk, unsigned short snum,
|
|
int udp_err(struct sk_buff *, u32);
|
|
int udp_abort(struct sock *sk, int err);
|
|
int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len);
|
|
+void udp_splice_eof(struct socket *sock);
|
|
int udp_push_pending_frames(struct sock *sk);
|
|
void udp_flush_pending_frames(struct sock *sk);
|
|
int udp_cmsg_send(struct sock *sk, struct msghdr *msg, u16 *gso_size);
|
|
diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h
|
|
index 72394f441dad8..e5f81710b18f4 100644
|
|
--- a/include/net/udp_tunnel.h
|
|
+++ b/include/net/udp_tunnel.h
|
|
@@ -174,16 +174,13 @@ static inline int udp_tunnel_handle_offloads(struct sk_buff *skb, bool udp_csum)
|
|
}
|
|
#endif
|
|
|
|
-static inline void udp_tunnel_encap_enable(struct socket *sock)
|
|
+static inline void udp_tunnel_encap_enable(struct sock *sk)
|
|
{
|
|
- struct udp_sock *up = udp_sk(sock->sk);
|
|
-
|
|
- if (up->encap_enabled)
|
|
+ if (udp_test_and_set_bit(ENCAP_ENABLED, sk))
|
|
return;
|
|
|
|
- up->encap_enabled = 1;
|
|
#if IS_ENABLED(CONFIG_IPV6)
|
|
- if (sock->sk->sk_family == PF_INET6)
|
|
+ if (READ_ONCE(sk->sk_family) == PF_INET6)
|
|
ipv6_stub->udpv6_encap_enable();
|
|
#endif
|
|
udp_encap_enable();
|
|
diff --git a/io_uring/net.c b/io_uring/net.c
|
|
index 57c626cb4d1a5..67f09a40bcb21 100644
|
|
--- a/io_uring/net.c
|
|
+++ b/io_uring/net.c
|
|
@@ -389,6 +389,7 @@ int io_send(struct io_kiocb *req, unsigned int issue_flags)
|
|
if (flags & MSG_WAITALL)
|
|
min_ret = iov_iter_count(&msg.msg_iter);
|
|
|
|
+ flags &= ~MSG_INTERNAL_SENDMSG_FLAGS;
|
|
msg.msg_flags = flags;
|
|
ret = sock_sendmsg(sock, &msg);
|
|
if (ret < min_ret) {
|
|
@@ -1137,6 +1138,7 @@ int io_send_zc(struct io_kiocb *req, unsigned int issue_flags)
|
|
msg_flags |= MSG_DONTWAIT;
|
|
if (msg_flags & MSG_WAITALL)
|
|
min_ret = iov_iter_count(&msg.msg_iter);
|
|
+ msg_flags &= ~MSG_INTERNAL_SENDMSG_FLAGS;
|
|
|
|
msg.msg_flags = msg_flags;
|
|
msg.msg_ubuf = &io_notif_to_data(zc->notif)->uarg;
|
|
diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c
|
|
index 7225cb67c0d3a..76bf1de261152 100644
|
|
--- a/kernel/bpf/core.c
|
|
+++ b/kernel/bpf/core.c
|
|
@@ -365,9 +365,18 @@ static int bpf_adj_delta_to_imm(struct bpf_insn *insn, u32 pos, s32 end_old,
|
|
static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, s32 end_old,
|
|
s32 end_new, s32 curr, const bool probe_pass)
|
|
{
|
|
- const s32 off_min = S16_MIN, off_max = S16_MAX;
|
|
+ s64 off_min, off_max, off;
|
|
s32 delta = end_new - end_old;
|
|
- s32 off = insn->off;
|
|
+
|
|
+ if (insn->code == (BPF_JMP32 | BPF_JA)) {
|
|
+ off = insn->imm;
|
|
+ off_min = S32_MIN;
|
|
+ off_max = S32_MAX;
|
|
+ } else {
|
|
+ off = insn->off;
|
|
+ off_min = S16_MIN;
|
|
+ off_max = S16_MAX;
|
|
+ }
|
|
|
|
if (curr < pos && curr + off + 1 >= end_old)
|
|
off += delta;
|
|
@@ -375,8 +384,12 @@ static int bpf_adj_delta_to_off(struct bpf_insn *insn, u32 pos, s32 end_old,
|
|
off -= delta;
|
|
if (off < off_min || off > off_max)
|
|
return -ERANGE;
|
|
- if (!probe_pass)
|
|
- insn->off = off;
|
|
+ if (!probe_pass) {
|
|
+ if (insn->code == (BPF_JMP32 | BPF_JA))
|
|
+ insn->imm = off;
|
|
+ else
|
|
+ insn->off = off;
|
|
+ }
|
|
return 0;
|
|
}
|
|
|
|
@@ -1586,6 +1599,7 @@ EXPORT_SYMBOL_GPL(__bpf_call_base);
|
|
INSN_3(JMP, JSLE, K), \
|
|
INSN_3(JMP, JSET, K), \
|
|
INSN_2(JMP, JA), \
|
|
+ INSN_2(JMP32, JA), \
|
|
/* Store instructions. */ \
|
|
/* Register based. */ \
|
|
INSN_3(STX, MEM, B), \
|
|
@@ -1862,6 +1876,9 @@ out:
|
|
JMP_JA:
|
|
insn += insn->off;
|
|
CONT;
|
|
+ JMP32_JA:
|
|
+ insn += insn->imm;
|
|
+ CONT;
|
|
JMP_EXIT:
|
|
return BPF_R0;
|
|
/* JMP */
|
|
diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
|
|
index c4381dfcd6b09..748ac86169941 100644
|
|
--- a/kernel/bpf/trampoline.c
|
|
+++ b/kernel/bpf/trampoline.c
|
|
@@ -443,8 +443,8 @@ static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mut
|
|
goto out;
|
|
}
|
|
|
|
- /* clear all bits except SHARE_IPMODIFY */
|
|
- tr->flags &= BPF_TRAMP_F_SHARE_IPMODIFY;
|
|
+ /* clear all bits except SHARE_IPMODIFY and TAIL_CALL_CTX */
|
|
+ tr->flags &= (BPF_TRAMP_F_SHARE_IPMODIFY | BPF_TRAMP_F_TAIL_CALL_CTX);
|
|
|
|
if (tlinks[BPF_TRAMP_FEXIT].nr_links ||
|
|
tlinks[BPF_TRAMP_MODIFY_RETURN].nr_links) {
|
|
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
|
|
index 12d360d80c149..142e10d49fd81 100644
|
|
--- a/kernel/bpf/verifier.c
|
|
+++ b/kernel/bpf/verifier.c
|
|
@@ -2254,7 +2254,10 @@ static int check_subprogs(struct bpf_verifier_env *env)
|
|
goto next;
|
|
if (BPF_OP(code) == BPF_EXIT || BPF_OP(code) == BPF_CALL)
|
|
goto next;
|
|
- off = i + insn[i].off + 1;
|
|
+ if (code == (BPF_JMP32 | BPF_JA))
|
|
+ off = i + insn[i].imm + 1;
|
|
+ else
|
|
+ off = i + insn[i].off + 1;
|
|
if (off < subprog_start || off >= subprog_end) {
|
|
verbose(env, "jump out of range from insn %d to %d\n", i, off);
|
|
return -EINVAL;
|
|
@@ -2266,6 +2269,7 @@ next:
|
|
* or unconditional jump back
|
|
*/
|
|
if (code != (BPF_JMP | BPF_EXIT) &&
|
|
+ code != (BPF_JMP32 | BPF_JA) &&
|
|
code != (BPF_JMP | BPF_JA)) {
|
|
verbose(env, "last insn is not an exit or jmp\n");
|
|
return -EINVAL;
|
|
@@ -2512,6 +2516,16 @@ static int check_reg_arg(struct bpf_verifier_env *env, u32 regno,
|
|
return 0;
|
|
}
|
|
|
|
+static void mark_jmp_point(struct bpf_verifier_env *env, int idx)
|
|
+{
|
|
+ env->insn_aux_data[idx].jmp_point = true;
|
|
+}
|
|
+
|
|
+static bool is_jmp_point(struct bpf_verifier_env *env, int insn_idx)
|
|
+{
|
|
+ return env->insn_aux_data[insn_idx].jmp_point;
|
|
+}
|
|
+
|
|
/* for any branch, call, exit record the history of jmps in the given state */
|
|
static int push_jmp_history(struct bpf_verifier_env *env,
|
|
struct bpf_verifier_state *cur)
|
|
@@ -2520,6 +2534,9 @@ static int push_jmp_history(struct bpf_verifier_env *env,
|
|
struct bpf_idx_pair *p;
|
|
size_t alloc_size;
|
|
|
|
+ if (!is_jmp_point(env, env->insn_idx))
|
|
+ return 0;
|
|
+
|
|
cnt++;
|
|
alloc_size = kmalloc_size_roundup(size_mul(cnt, sizeof(*p)));
|
|
p = krealloc(cur->jmp_history, alloc_size, GFP_USER);
|
|
@@ -2534,12 +2551,29 @@ static int push_jmp_history(struct bpf_verifier_env *env,
|
|
|
|
/* Backtrack one insn at a time. If idx is not at the top of recorded
|
|
* history then previous instruction came from straight line execution.
|
|
+ * Return -ENOENT if we exhausted all instructions within given state.
|
|
+ *
|
|
+ * It's legal to have a bit of a looping with the same starting and ending
|
|
+ * insn index within the same state, e.g.: 3->4->5->3, so just because current
|
|
+ * instruction index is the same as state's first_idx doesn't mean we are
|
|
+ * done. If there is still some jump history left, we should keep going. We
|
|
+ * need to take into account that we might have a jump history between given
|
|
+ * state's parent and itself, due to checkpointing. In this case, we'll have
|
|
+ * history entry recording a jump from last instruction of parent state and
|
|
+ * first instruction of given state.
|
|
*/
|
|
static int get_prev_insn_idx(struct bpf_verifier_state *st, int i,
|
|
u32 *history)
|
|
{
|
|
u32 cnt = *history;
|
|
|
|
+ if (i == st->first_insn_idx) {
|
|
+ if (cnt == 0)
|
|
+ return -ENOENT;
|
|
+ if (cnt == 1 && st->jmp_history[0].idx == i)
|
|
+ return -ENOENT;
|
|
+ }
|
|
+
|
|
if (cnt && st->jmp_history[cnt - 1].idx == i) {
|
|
i = st->jmp_history[cnt - 1].prev_idx;
|
|
(*history)--;
|
|
@@ -3035,9 +3069,9 @@ static int __mark_chain_precision(struct bpf_verifier_env *env, int frame, int r
|
|
* Nothing to be tracked further in the parent state.
|
|
*/
|
|
return 0;
|
|
- if (i == first_idx)
|
|
- break;
|
|
i = get_prev_insn_idx(st, i, &history);
|
|
+ if (i == -ENOENT)
|
|
+ break;
|
|
if (i >= env->prog->len) {
|
|
/* This can happen if backtracking reached insn 0
|
|
* and there are still reg_mask or stack_mask
|
|
@@ -11000,11 +11034,16 @@ static struct bpf_verifier_state_list **explored_state(
|
|
return &env->explored_states[(idx ^ state->callsite) % state_htab_size(env)];
|
|
}
|
|
|
|
-static void init_explored_state(struct bpf_verifier_env *env, int idx)
|
|
+static void mark_prune_point(struct bpf_verifier_env *env, int idx)
|
|
{
|
|
env->insn_aux_data[idx].prune_point = true;
|
|
}
|
|
|
|
+static bool is_prune_point(struct bpf_verifier_env *env, int insn_idx)
|
|
+{
|
|
+ return env->insn_aux_data[insn_idx].prune_point;
|
|
+}
|
|
+
|
|
enum {
|
|
DONE_EXPLORING = 0,
|
|
KEEP_EXPLORING = 1,
|
|
@@ -11033,9 +11072,11 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env,
|
|
return -EINVAL;
|
|
}
|
|
|
|
- if (e == BRANCH)
|
|
+ if (e == BRANCH) {
|
|
/* mark branch target for state pruning */
|
|
- init_explored_state(env, w);
|
|
+ mark_prune_point(env, w);
|
|
+ mark_jmp_point(env, w);
|
|
+ }
|
|
|
|
if (insn_state[w] == 0) {
|
|
/* tree-edge */
|
|
@@ -11062,21 +11103,23 @@ static int push_insn(int t, int w, int e, struct bpf_verifier_env *env,
|
|
return DONE_EXPLORING;
|
|
}
|
|
|
|
-static int visit_func_call_insn(int t, int insn_cnt,
|
|
- struct bpf_insn *insns,
|
|
+static int visit_func_call_insn(int t, struct bpf_insn *insns,
|
|
struct bpf_verifier_env *env,
|
|
bool visit_callee)
|
|
{
|
|
- int ret;
|
|
+ int ret, insn_sz;
|
|
|
|
- ret = push_insn(t, t + 1, FALLTHROUGH, env, false);
|
|
+ insn_sz = bpf_is_ldimm64(&insns[t]) ? 2 : 1;
|
|
+ ret = push_insn(t, t + insn_sz, FALLTHROUGH, env, false);
|
|
if (ret)
|
|
return ret;
|
|
|
|
- if (t + 1 < insn_cnt)
|
|
- init_explored_state(env, t + 1);
|
|
+ mark_prune_point(env, t + insn_sz);
|
|
+ /* when we exit from subprog, we need to record non-linear history */
|
|
+ mark_jmp_point(env, t + insn_sz);
|
|
+
|
|
if (visit_callee) {
|
|
- init_explored_state(env, t);
|
|
+ mark_prune_point(env, t);
|
|
ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env,
|
|
/* It's ok to allow recursion from CFG point of
|
|
* view. __check_func_call() will do the actual
|
|
@@ -11092,65 +11135,64 @@ static int visit_func_call_insn(int t, int insn_cnt,
|
|
* DONE_EXPLORING - the instruction was fully explored
|
|
* KEEP_EXPLORING - there is still work to be done before it is fully explored
|
|
*/
|
|
-static int visit_insn(int t, int insn_cnt, struct bpf_verifier_env *env)
|
|
+static int visit_insn(int t, struct bpf_verifier_env *env)
|
|
{
|
|
- struct bpf_insn *insns = env->prog->insnsi;
|
|
- int ret;
|
|
+ struct bpf_insn *insns = env->prog->insnsi, *insn = &insns[t];
|
|
+ int ret, off, insn_sz;
|
|
|
|
- if (bpf_pseudo_func(insns + t))
|
|
- return visit_func_call_insn(t, insn_cnt, insns, env, true);
|
|
+ if (bpf_pseudo_func(insn))
|
|
+ return visit_func_call_insn(t, insns, env, true);
|
|
|
|
/* All non-branch instructions have a single fall-through edge. */
|
|
- if (BPF_CLASS(insns[t].code) != BPF_JMP &&
|
|
- BPF_CLASS(insns[t].code) != BPF_JMP32)
|
|
- return push_insn(t, t + 1, FALLTHROUGH, env, false);
|
|
+ if (BPF_CLASS(insn->code) != BPF_JMP &&
|
|
+ BPF_CLASS(insn->code) != BPF_JMP32) {
|
|
+ insn_sz = bpf_is_ldimm64(insn) ? 2 : 1;
|
|
+ return push_insn(t, t + insn_sz, FALLTHROUGH, env, false);
|
|
+ }
|
|
|
|
- switch (BPF_OP(insns[t].code)) {
|
|
+ switch (BPF_OP(insn->code)) {
|
|
case BPF_EXIT:
|
|
return DONE_EXPLORING;
|
|
|
|
case BPF_CALL:
|
|
- if (insns[t].imm == BPF_FUNC_timer_set_callback)
|
|
- /* Mark this call insn to trigger is_state_visited() check
|
|
- * before call itself is processed by __check_func_call().
|
|
- * Otherwise new async state will be pushed for further
|
|
- * exploration.
|
|
+ if (insn->imm == BPF_FUNC_timer_set_callback)
|
|
+ /* Mark this call insn as a prune point to trigger
|
|
+ * is_state_visited() check before call itself is
|
|
+ * processed by __check_func_call(). Otherwise new
|
|
+ * async state will be pushed for further exploration.
|
|
*/
|
|
- init_explored_state(env, t);
|
|
- return visit_func_call_insn(t, insn_cnt, insns, env,
|
|
- insns[t].src_reg == BPF_PSEUDO_CALL);
|
|
+ mark_prune_point(env, t);
|
|
+ return visit_func_call_insn(t, insns, env, insn->src_reg == BPF_PSEUDO_CALL);
|
|
|
|
case BPF_JA:
|
|
- if (BPF_SRC(insns[t].code) != BPF_K)
|
|
+ if (BPF_SRC(insn->code) != BPF_K)
|
|
return -EINVAL;
|
|
|
|
+ if (BPF_CLASS(insn->code) == BPF_JMP)
|
|
+ off = insn->off;
|
|
+ else
|
|
+ off = insn->imm;
|
|
+
|
|
/* unconditional jump with single edge */
|
|
- ret = push_insn(t, t + insns[t].off + 1, FALLTHROUGH, env,
|
|
+ ret = push_insn(t, t + off + 1, FALLTHROUGH, env,
|
|
true);
|
|
if (ret)
|
|
return ret;
|
|
|
|
- /* unconditional jmp is not a good pruning point,
|
|
- * but it's marked, since backtracking needs
|
|
- * to record jmp history in is_state_visited().
|
|
- */
|
|
- init_explored_state(env, t + insns[t].off + 1);
|
|
- /* tell verifier to check for equivalent states
|
|
- * after every call and jump
|
|
- */
|
|
- if (t + 1 < insn_cnt)
|
|
- init_explored_state(env, t + 1);
|
|
+ mark_prune_point(env, t + off + 1);
|
|
+ mark_jmp_point(env, t + off + 1);
|
|
|
|
return ret;
|
|
|
|
default:
|
|
/* conditional jump with two edges */
|
|
- init_explored_state(env, t);
|
|
+ mark_prune_point(env, t);
|
|
+
|
|
ret = push_insn(t, t + 1, FALLTHROUGH, env, true);
|
|
if (ret)
|
|
return ret;
|
|
|
|
- return push_insn(t, t + insns[t].off + 1, BRANCH, env, true);
|
|
+ return push_insn(t, t + insn->off + 1, BRANCH, env, true);
|
|
}
|
|
}
|
|
|
|
@@ -11181,7 +11223,7 @@ static int check_cfg(struct bpf_verifier_env *env)
|
|
while (env->cfg.cur_stack > 0) {
|
|
int t = insn_stack[env->cfg.cur_stack - 1];
|
|
|
|
- ret = visit_insn(t, insn_cnt, env);
|
|
+ ret = visit_insn(t, env);
|
|
switch (ret) {
|
|
case DONE_EXPLORING:
|
|
insn_state[t] = EXPLORED;
|
|
@@ -11205,11 +11247,21 @@ static int check_cfg(struct bpf_verifier_env *env)
|
|
}
|
|
|
|
for (i = 0; i < insn_cnt; i++) {
|
|
+ struct bpf_insn *insn = &env->prog->insnsi[i];
|
|
+
|
|
if (insn_state[i] != EXPLORED) {
|
|
verbose(env, "unreachable insn %d\n", i);
|
|
ret = -EINVAL;
|
|
goto err_free;
|
|
}
|
|
+ if (bpf_is_ldimm64(insn)) {
|
|
+ if (insn_state[i + 1] != 0) {
|
|
+ verbose(env, "jump into the middle of ldimm64 insn %d\n", i);
|
|
+ ret = -EINVAL;
|
|
+ goto err_free;
|
|
+ }
|
|
+ i++; /* skip second half of ldimm64 */
|
|
+ }
|
|
}
|
|
ret = 0; /* cfg looks good */
|
|
|
|
@@ -12178,11 +12230,11 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
|
|
bool add_new_state = env->test_state_freq ? true : false;
|
|
|
|
cur->last_insn_idx = env->prev_insn_idx;
|
|
- if (!env->insn_aux_data[insn_idx].prune_point)
|
|
+ if (!is_prune_point(env, insn_idx))
|
|
/* this 'insn_idx' instruction wasn't marked, so we will not
|
|
* be doing state search here
|
|
*/
|
|
- return 0;
|
|
+ return push_jmp_history(env, cur);
|
|
|
|
/* bpf progs typically have pruning point every 4 instructions
|
|
* http://vger.kernel.org/bpfconf2019.html#session-1
|
|
@@ -12674,15 +12726,18 @@ static int do_check(struct bpf_verifier_env *env)
|
|
return err;
|
|
} else if (opcode == BPF_JA) {
|
|
if (BPF_SRC(insn->code) != BPF_K ||
|
|
- insn->imm != 0 ||
|
|
insn->src_reg != BPF_REG_0 ||
|
|
insn->dst_reg != BPF_REG_0 ||
|
|
- class == BPF_JMP32) {
|
|
+ (class == BPF_JMP && insn->imm != 0) ||
|
|
+ (class == BPF_JMP32 && insn->off != 0)) {
|
|
verbose(env, "BPF_JA uses reserved fields\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
- env->insn_idx += insn->off + 1;
|
|
+ if (class == BPF_JMP)
|
|
+ env->insn_idx += insn->off + 1;
|
|
+ else
|
|
+ env->insn_idx += insn->imm + 1;
|
|
continue;
|
|
|
|
} else if (opcode == BPF_EXIT) {
|
|
@@ -13508,13 +13563,13 @@ static bool insn_is_cond_jump(u8 code)
|
|
{
|
|
u8 op;
|
|
|
|
+ op = BPF_OP(code);
|
|
if (BPF_CLASS(code) == BPF_JMP32)
|
|
- return true;
|
|
+ return op != BPF_JA;
|
|
|
|
if (BPF_CLASS(code) != BPF_JMP)
|
|
return false;
|
|
|
|
- op = BPF_OP(code);
|
|
return op != BPF_JA && op != BPF_EXIT && op != BPF_CALL;
|
|
}
|
|
|
|
@@ -15442,6 +15497,9 @@ static int check_attach_btf_id(struct bpf_verifier_env *env)
|
|
if (!tr)
|
|
return -ENOMEM;
|
|
|
|
+ if (tgt_prog && tgt_prog->aux->tail_call_reachable)
|
|
+ tr->flags = BPF_TRAMP_F_TAIL_CALL_CTX;
|
|
+
|
|
prog->aux->dst_trampoline = tr;
|
|
return 0;
|
|
}
|
|
diff --git a/kernel/cpu.c b/kernel/cpu.c
|
|
index 551468d9c5a85..e6f0101941ed8 100644
|
|
--- a/kernel/cpu.c
|
|
+++ b/kernel/cpu.c
|
|
@@ -446,9 +446,31 @@ static int __init smt_cmdline_disable(char *str)
|
|
}
|
|
early_param("nosmt", smt_cmdline_disable);
|
|
|
|
-static inline bool cpu_smt_allowed(unsigned int cpu)
|
|
+/*
|
|
+ * For Archicture supporting partial SMT states check if the thread is allowed.
|
|
+ * Otherwise this has already been checked through cpu_smt_max_threads when
|
|
+ * setting the SMT level.
|
|
+ */
|
|
+static inline bool cpu_smt_thread_allowed(unsigned int cpu)
|
|
{
|
|
- if (cpu_smt_control == CPU_SMT_ENABLED)
|
|
+#ifdef CONFIG_SMT_NUM_THREADS_DYNAMIC
|
|
+ return topology_smt_thread_allowed(cpu);
|
|
+#else
|
|
+ return true;
|
|
+#endif
|
|
+}
|
|
+
|
|
+static inline bool cpu_bootable(unsigned int cpu)
|
|
+{
|
|
+ if (cpu_smt_control == CPU_SMT_ENABLED && cpu_smt_thread_allowed(cpu))
|
|
+ return true;
|
|
+
|
|
+ /* All CPUs are bootable if controls are not configured */
|
|
+ if (cpu_smt_control == CPU_SMT_NOT_IMPLEMENTED)
|
|
+ return true;
|
|
+
|
|
+ /* All CPUs are bootable if CPU is not SMT capable */
|
|
+ if (cpu_smt_control == CPU_SMT_NOT_SUPPORTED)
|
|
return true;
|
|
|
|
if (topology_is_primary_thread(cpu))
|
|
@@ -471,7 +493,7 @@ bool cpu_smt_possible(void)
|
|
}
|
|
EXPORT_SYMBOL_GPL(cpu_smt_possible);
|
|
#else
|
|
-static inline bool cpu_smt_allowed(unsigned int cpu) { return true; }
|
|
+static inline bool cpu_bootable(unsigned int cpu) { return true; }
|
|
#endif
|
|
|
|
static inline enum cpuhp_state
|
|
@@ -574,10 +596,10 @@ static int bringup_wait_for_ap(unsigned int cpu)
|
|
* SMT soft disabling on X86 requires to bring the CPU out of the
|
|
* BIOS 'wait for SIPI' state in order to set the CR4.MCE bit. The
|
|
* CPU marked itself as booted_once in notify_cpu_starting() so the
|
|
- * cpu_smt_allowed() check will now return false if this is not the
|
|
+ * cpu_bootable() check will now return false if this is not the
|
|
* primary sibling.
|
|
*/
|
|
- if (!cpu_smt_allowed(cpu))
|
|
+ if (!cpu_bootable(cpu))
|
|
return -ECANCELED;
|
|
|
|
if (st->target <= CPUHP_AP_ONLINE_IDLE)
|
|
@@ -1464,7 +1486,7 @@ static int cpu_up(unsigned int cpu, enum cpuhp_state target)
|
|
err = -EBUSY;
|
|
goto out;
|
|
}
|
|
- if (!cpu_smt_allowed(cpu)) {
|
|
+ if (!cpu_bootable(cpu)) {
|
|
err = -EPERM;
|
|
goto out;
|
|
}
|
|
@@ -2294,6 +2316,12 @@ int cpuhp_smt_disable(enum cpuhp_smt_control ctrlval)
|
|
for_each_online_cpu(cpu) {
|
|
if (topology_is_primary_thread(cpu))
|
|
continue;
|
|
+ /*
|
|
+ * Disable can be called with CPU_SMT_ENABLED when changing
|
|
+ * from a higher to lower number of SMT threads per core.
|
|
+ */
|
|
+ if (ctrlval == CPU_SMT_ENABLED && cpu_smt_thread_allowed(cpu))
|
|
+ continue;
|
|
ret = cpu_down_maps_locked(cpu, CPUHP_OFFLINE);
|
|
if (ret)
|
|
break;
|
|
@@ -2328,6 +2356,8 @@ int cpuhp_smt_enable(void)
|
|
/* Skip online CPUs and CPUs on offline nodes */
|
|
if (cpu_online(cpu) || !node_online(cpu_to_node(cpu)))
|
|
continue;
|
|
+ if (!cpu_smt_thread_allowed(cpu))
|
|
+ continue;
|
|
ret = _cpu_up(cpu, 0, CPUHP_ONLINE);
|
|
if (ret)
|
|
break;
|
|
diff --git a/kernel/irq/affinity.c b/kernel/irq/affinity.c
|
|
index d9a5c1d65a79d..44a4eba80315c 100644
|
|
--- a/kernel/irq/affinity.c
|
|
+++ b/kernel/irq/affinity.c
|
|
@@ -7,398 +7,7 @@
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/cpu.h>
|
|
-#include <linux/sort.h>
|
|
-
|
|
-static void irq_spread_init_one(struct cpumask *irqmsk, struct cpumask *nmsk,
|
|
- unsigned int cpus_per_vec)
|
|
-{
|
|
- const struct cpumask *siblmsk;
|
|
- int cpu, sibl;
|
|
-
|
|
- for ( ; cpus_per_vec > 0; ) {
|
|
- cpu = cpumask_first(nmsk);
|
|
-
|
|
- /* Should not happen, but I'm too lazy to think about it */
|
|
- if (cpu >= nr_cpu_ids)
|
|
- return;
|
|
-
|
|
- cpumask_clear_cpu(cpu, nmsk);
|
|
- cpumask_set_cpu(cpu, irqmsk);
|
|
- cpus_per_vec--;
|
|
-
|
|
- /* If the cpu has siblings, use them first */
|
|
- siblmsk = topology_sibling_cpumask(cpu);
|
|
- for (sibl = -1; cpus_per_vec > 0; ) {
|
|
- sibl = cpumask_next(sibl, siblmsk);
|
|
- if (sibl >= nr_cpu_ids)
|
|
- break;
|
|
- if (!cpumask_test_and_clear_cpu(sibl, nmsk))
|
|
- continue;
|
|
- cpumask_set_cpu(sibl, irqmsk);
|
|
- cpus_per_vec--;
|
|
- }
|
|
- }
|
|
-}
|
|
-
|
|
-static cpumask_var_t *alloc_node_to_cpumask(void)
|
|
-{
|
|
- cpumask_var_t *masks;
|
|
- int node;
|
|
-
|
|
- masks = kcalloc(nr_node_ids, sizeof(cpumask_var_t), GFP_KERNEL);
|
|
- if (!masks)
|
|
- return NULL;
|
|
-
|
|
- for (node = 0; node < nr_node_ids; node++) {
|
|
- if (!zalloc_cpumask_var(&masks[node], GFP_KERNEL))
|
|
- goto out_unwind;
|
|
- }
|
|
-
|
|
- return masks;
|
|
-
|
|
-out_unwind:
|
|
- while (--node >= 0)
|
|
- free_cpumask_var(masks[node]);
|
|
- kfree(masks);
|
|
- return NULL;
|
|
-}
|
|
-
|
|
-static void free_node_to_cpumask(cpumask_var_t *masks)
|
|
-{
|
|
- int node;
|
|
-
|
|
- for (node = 0; node < nr_node_ids; node++)
|
|
- free_cpumask_var(masks[node]);
|
|
- kfree(masks);
|
|
-}
|
|
-
|
|
-static void build_node_to_cpumask(cpumask_var_t *masks)
|
|
-{
|
|
- int cpu;
|
|
-
|
|
- for_each_possible_cpu(cpu)
|
|
- cpumask_set_cpu(cpu, masks[cpu_to_node(cpu)]);
|
|
-}
|
|
-
|
|
-static int get_nodes_in_cpumask(cpumask_var_t *node_to_cpumask,
|
|
- const struct cpumask *mask, nodemask_t *nodemsk)
|
|
-{
|
|
- int n, nodes = 0;
|
|
-
|
|
- /* Calculate the number of nodes in the supplied affinity mask */
|
|
- for_each_node(n) {
|
|
- if (cpumask_intersects(mask, node_to_cpumask[n])) {
|
|
- node_set(n, *nodemsk);
|
|
- nodes++;
|
|
- }
|
|
- }
|
|
- return nodes;
|
|
-}
|
|
-
|
|
-struct node_vectors {
|
|
- unsigned id;
|
|
-
|
|
- union {
|
|
- unsigned nvectors;
|
|
- unsigned ncpus;
|
|
- };
|
|
-};
|
|
-
|
|
-static int ncpus_cmp_func(const void *l, const void *r)
|
|
-{
|
|
- const struct node_vectors *ln = l;
|
|
- const struct node_vectors *rn = r;
|
|
-
|
|
- return ln->ncpus - rn->ncpus;
|
|
-}
|
|
-
|
|
-/*
|
|
- * Allocate vector number for each node, so that for each node:
|
|
- *
|
|
- * 1) the allocated number is >= 1
|
|
- *
|
|
- * 2) the allocated numbver is <= active CPU number of this node
|
|
- *
|
|
- * The actual allocated total vectors may be less than @numvecs when
|
|
- * active total CPU number is less than @numvecs.
|
|
- *
|
|
- * Active CPUs means the CPUs in '@cpu_mask AND @node_to_cpumask[]'
|
|
- * for each node.
|
|
- */
|
|
-static void alloc_nodes_vectors(unsigned int numvecs,
|
|
- cpumask_var_t *node_to_cpumask,
|
|
- const struct cpumask *cpu_mask,
|
|
- const nodemask_t nodemsk,
|
|
- struct cpumask *nmsk,
|
|
- struct node_vectors *node_vectors)
|
|
-{
|
|
- unsigned n, remaining_ncpus = 0;
|
|
-
|
|
- for (n = 0; n < nr_node_ids; n++) {
|
|
- node_vectors[n].id = n;
|
|
- node_vectors[n].ncpus = UINT_MAX;
|
|
- }
|
|
-
|
|
- for_each_node_mask(n, nodemsk) {
|
|
- unsigned ncpus;
|
|
-
|
|
- cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]);
|
|
- ncpus = cpumask_weight(nmsk);
|
|
-
|
|
- if (!ncpus)
|
|
- continue;
|
|
- remaining_ncpus += ncpus;
|
|
- node_vectors[n].ncpus = ncpus;
|
|
- }
|
|
-
|
|
- numvecs = min_t(unsigned, remaining_ncpus, numvecs);
|
|
-
|
|
- sort(node_vectors, nr_node_ids, sizeof(node_vectors[0]),
|
|
- ncpus_cmp_func, NULL);
|
|
-
|
|
- /*
|
|
- * Allocate vectors for each node according to the ratio of this
|
|
- * node's nr_cpus to remaining un-assigned ncpus. 'numvecs' is
|
|
- * bigger than number of active numa nodes. Always start the
|
|
- * allocation from the node with minimized nr_cpus.
|
|
- *
|
|
- * This way guarantees that each active node gets allocated at
|
|
- * least one vector, and the theory is simple: over-allocation
|
|
- * is only done when this node is assigned by one vector, so
|
|
- * other nodes will be allocated >= 1 vector, since 'numvecs' is
|
|
- * bigger than number of numa nodes.
|
|
- *
|
|
- * One perfect invariant is that number of allocated vectors for
|
|
- * each node is <= CPU count of this node:
|
|
- *
|
|
- * 1) suppose there are two nodes: A and B
|
|
- * ncpu(X) is CPU count of node X
|
|
- * vecs(X) is the vector count allocated to node X via this
|
|
- * algorithm
|
|
- *
|
|
- * ncpu(A) <= ncpu(B)
|
|
- * ncpu(A) + ncpu(B) = N
|
|
- * vecs(A) + vecs(B) = V
|
|
- *
|
|
- * vecs(A) = max(1, round_down(V * ncpu(A) / N))
|
|
- * vecs(B) = V - vecs(A)
|
|
- *
|
|
- * both N and V are integer, and 2 <= V <= N, suppose
|
|
- * V = N - delta, and 0 <= delta <= N - 2
|
|
- *
|
|
- * 2) obviously vecs(A) <= ncpu(A) because:
|
|
- *
|
|
- * if vecs(A) is 1, then vecs(A) <= ncpu(A) given
|
|
- * ncpu(A) >= 1
|
|
- *
|
|
- * otherwise,
|
|
- * vecs(A) <= V * ncpu(A) / N <= ncpu(A), given V <= N
|
|
- *
|
|
- * 3) prove how vecs(B) <= ncpu(B):
|
|
- *
|
|
- * if round_down(V * ncpu(A) / N) == 0, vecs(B) won't be
|
|
- * over-allocated, so vecs(B) <= ncpu(B),
|
|
- *
|
|
- * otherwise:
|
|
- *
|
|
- * vecs(A) =
|
|
- * round_down(V * ncpu(A) / N) =
|
|
- * round_down((N - delta) * ncpu(A) / N) =
|
|
- * round_down((N * ncpu(A) - delta * ncpu(A)) / N) >=
|
|
- * round_down((N * ncpu(A) - delta * N) / N) =
|
|
- * cpu(A) - delta
|
|
- *
|
|
- * then:
|
|
- *
|
|
- * vecs(A) - V >= ncpu(A) - delta - V
|
|
- * =>
|
|
- * V - vecs(A) <= V + delta - ncpu(A)
|
|
- * =>
|
|
- * vecs(B) <= N - ncpu(A)
|
|
- * =>
|
|
- * vecs(B) <= cpu(B)
|
|
- *
|
|
- * For nodes >= 3, it can be thought as one node and another big
|
|
- * node given that is exactly what this algorithm is implemented,
|
|
- * and we always re-calculate 'remaining_ncpus' & 'numvecs', and
|
|
- * finally for each node X: vecs(X) <= ncpu(X).
|
|
- *
|
|
- */
|
|
- for (n = 0; n < nr_node_ids; n++) {
|
|
- unsigned nvectors, ncpus;
|
|
-
|
|
- if (node_vectors[n].ncpus == UINT_MAX)
|
|
- continue;
|
|
-
|
|
- WARN_ON_ONCE(numvecs == 0);
|
|
-
|
|
- ncpus = node_vectors[n].ncpus;
|
|
- nvectors = max_t(unsigned, 1,
|
|
- numvecs * ncpus / remaining_ncpus);
|
|
- WARN_ON_ONCE(nvectors > ncpus);
|
|
-
|
|
- node_vectors[n].nvectors = nvectors;
|
|
-
|
|
- remaining_ncpus -= ncpus;
|
|
- numvecs -= nvectors;
|
|
- }
|
|
-}
|
|
-
|
|
-static int __irq_build_affinity_masks(unsigned int startvec,
|
|
- unsigned int numvecs,
|
|
- unsigned int firstvec,
|
|
- cpumask_var_t *node_to_cpumask,
|
|
- const struct cpumask *cpu_mask,
|
|
- struct cpumask *nmsk,
|
|
- struct irq_affinity_desc *masks)
|
|
-{
|
|
- unsigned int i, n, nodes, cpus_per_vec, extra_vecs, done = 0;
|
|
- unsigned int last_affv = firstvec + numvecs;
|
|
- unsigned int curvec = startvec;
|
|
- nodemask_t nodemsk = NODE_MASK_NONE;
|
|
- struct node_vectors *node_vectors;
|
|
-
|
|
- if (cpumask_empty(cpu_mask))
|
|
- return 0;
|
|
-
|
|
- nodes = get_nodes_in_cpumask(node_to_cpumask, cpu_mask, &nodemsk);
|
|
-
|
|
- /*
|
|
- * If the number of nodes in the mask is greater than or equal the
|
|
- * number of vectors we just spread the vectors across the nodes.
|
|
- */
|
|
- if (numvecs <= nodes) {
|
|
- for_each_node_mask(n, nodemsk) {
|
|
- /* Ensure that only CPUs which are in both masks are set */
|
|
- cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]);
|
|
- cpumask_or(&masks[curvec].mask, &masks[curvec].mask, nmsk);
|
|
- if (++curvec == last_affv)
|
|
- curvec = firstvec;
|
|
- }
|
|
- return numvecs;
|
|
- }
|
|
-
|
|
- node_vectors = kcalloc(nr_node_ids,
|
|
- sizeof(struct node_vectors),
|
|
- GFP_KERNEL);
|
|
- if (!node_vectors)
|
|
- return -ENOMEM;
|
|
-
|
|
- /* allocate vector number for each node */
|
|
- alloc_nodes_vectors(numvecs, node_to_cpumask, cpu_mask,
|
|
- nodemsk, nmsk, node_vectors);
|
|
-
|
|
- for (i = 0; i < nr_node_ids; i++) {
|
|
- unsigned int ncpus, v;
|
|
- struct node_vectors *nv = &node_vectors[i];
|
|
-
|
|
- if (nv->nvectors == UINT_MAX)
|
|
- continue;
|
|
-
|
|
- /* Get the cpus on this node which are in the mask */
|
|
- cpumask_and(nmsk, cpu_mask, node_to_cpumask[nv->id]);
|
|
- ncpus = cpumask_weight(nmsk);
|
|
- if (!ncpus)
|
|
- continue;
|
|
-
|
|
- WARN_ON_ONCE(nv->nvectors > ncpus);
|
|
-
|
|
- /* Account for rounding errors */
|
|
- extra_vecs = ncpus - nv->nvectors * (ncpus / nv->nvectors);
|
|
-
|
|
- /* Spread allocated vectors on CPUs of the current node */
|
|
- for (v = 0; v < nv->nvectors; v++, curvec++) {
|
|
- cpus_per_vec = ncpus / nv->nvectors;
|
|
-
|
|
- /* Account for extra vectors to compensate rounding errors */
|
|
- if (extra_vecs) {
|
|
- cpus_per_vec++;
|
|
- --extra_vecs;
|
|
- }
|
|
-
|
|
- /*
|
|
- * wrapping has to be considered given 'startvec'
|
|
- * may start anywhere
|
|
- */
|
|
- if (curvec >= last_affv)
|
|
- curvec = firstvec;
|
|
- irq_spread_init_one(&masks[curvec].mask, nmsk,
|
|
- cpus_per_vec);
|
|
- }
|
|
- done += nv->nvectors;
|
|
- }
|
|
- kfree(node_vectors);
|
|
- return done;
|
|
-}
|
|
-
|
|
-/*
|
|
- * build affinity in two stages:
|
|
- * 1) spread present CPU on these vectors
|
|
- * 2) spread other possible CPUs on these vectors
|
|
- */
|
|
-static int irq_build_affinity_masks(unsigned int startvec, unsigned int numvecs,
|
|
- unsigned int firstvec,
|
|
- struct irq_affinity_desc *masks)
|
|
-{
|
|
- unsigned int curvec = startvec, nr_present = 0, nr_others = 0;
|
|
- cpumask_var_t *node_to_cpumask;
|
|
- cpumask_var_t nmsk, npresmsk;
|
|
- int ret = -ENOMEM;
|
|
-
|
|
- if (!zalloc_cpumask_var(&nmsk, GFP_KERNEL))
|
|
- return ret;
|
|
-
|
|
- if (!zalloc_cpumask_var(&npresmsk, GFP_KERNEL))
|
|
- goto fail_nmsk;
|
|
-
|
|
- node_to_cpumask = alloc_node_to_cpumask();
|
|
- if (!node_to_cpumask)
|
|
- goto fail_npresmsk;
|
|
-
|
|
- /* Stabilize the cpumasks */
|
|
- cpus_read_lock();
|
|
- build_node_to_cpumask(node_to_cpumask);
|
|
-
|
|
- /* Spread on present CPUs starting from affd->pre_vectors */
|
|
- ret = __irq_build_affinity_masks(curvec, numvecs, firstvec,
|
|
- node_to_cpumask, cpu_present_mask,
|
|
- nmsk, masks);
|
|
- if (ret < 0)
|
|
- goto fail_build_affinity;
|
|
- nr_present = ret;
|
|
-
|
|
- /*
|
|
- * Spread on non present CPUs starting from the next vector to be
|
|
- * handled. If the spreading of present CPUs already exhausted the
|
|
- * vector space, assign the non present CPUs to the already spread
|
|
- * out vectors.
|
|
- */
|
|
- if (nr_present >= numvecs)
|
|
- curvec = firstvec;
|
|
- else
|
|
- curvec = firstvec + nr_present;
|
|
- cpumask_andnot(npresmsk, cpu_possible_mask, cpu_present_mask);
|
|
- ret = __irq_build_affinity_masks(curvec, numvecs, firstvec,
|
|
- node_to_cpumask, npresmsk, nmsk,
|
|
- masks);
|
|
- if (ret >= 0)
|
|
- nr_others = ret;
|
|
-
|
|
- fail_build_affinity:
|
|
- cpus_read_unlock();
|
|
-
|
|
- if (ret >= 0)
|
|
- WARN_ON(nr_present + nr_others < numvecs);
|
|
-
|
|
- free_node_to_cpumask(node_to_cpumask);
|
|
-
|
|
- fail_npresmsk:
|
|
- free_cpumask_var(npresmsk);
|
|
-
|
|
- fail_nmsk:
|
|
- free_cpumask_var(nmsk);
|
|
- return ret < 0 ? ret : 0;
|
|
-}
|
|
+#include <linux/group_cpus.h>
|
|
|
|
static void default_calc_sets(struct irq_affinity *affd, unsigned int affvecs)
|
|
{
|
|
@@ -461,14 +70,18 @@ irq_create_affinity_masks(unsigned int nvecs, struct irq_affinity *affd)
|
|
*/
|
|
for (i = 0, usedvecs = 0; i < affd->nr_sets; i++) {
|
|
unsigned int this_vecs = affd->set_size[i];
|
|
- int ret;
|
|
+ int j;
|
|
+ struct cpumask *result = group_cpus_evenly(this_vecs);
|
|
|
|
- ret = irq_build_affinity_masks(curvec, this_vecs,
|
|
- curvec, masks);
|
|
- if (ret) {
|
|
+ if (!result) {
|
|
kfree(masks);
|
|
return NULL;
|
|
}
|
|
+
|
|
+ for (j = 0; j < this_vecs; j++)
|
|
+ cpumask_copy(&masks[curvec + j].mask, &result[j]);
|
|
+ kfree(result);
|
|
+
|
|
curvec += this_vecs;
|
|
usedvecs += this_vecs;
|
|
}
|
|
diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
|
|
index 8fdf076720384..929dcbc04d29c 100644
|
|
--- a/kernel/rcu/srcutree.c
|
|
+++ b/kernel/rcu/srcutree.c
|
|
@@ -1100,10 +1100,37 @@ static unsigned long srcu_gp_start_if_needed(struct srcu_struct *ssp,
|
|
spin_lock_irqsave_sdp_contention(sdp, &flags);
|
|
if (rhp)
|
|
rcu_segcblist_enqueue(&sdp->srcu_cblist, rhp);
|
|
+ /*
|
|
+ * The snapshot for acceleration must be taken _before_ the read of the
|
|
+ * current gp sequence used for advancing, otherwise advancing may fail
|
|
+ * and acceleration may then fail too.
|
|
+ *
|
|
+ * This could happen if:
|
|
+ *
|
|
+ * 1) The RCU_WAIT_TAIL segment has callbacks (gp_num = X + 4) and the
|
|
+ * RCU_NEXT_READY_TAIL also has callbacks (gp_num = X + 8).
|
|
+ *
|
|
+ * 2) The grace period for RCU_WAIT_TAIL is seen as started but not
|
|
+ * completed so rcu_seq_current() returns X + SRCU_STATE_SCAN1.
|
|
+ *
|
|
+ * 3) This value is passed to rcu_segcblist_advance() which can't move
|
|
+ * any segment forward and fails.
|
|
+ *
|
|
+ * 4) srcu_gp_start_if_needed() still proceeds with callback acceleration.
|
|
+ * But then the call to rcu_seq_snap() observes the grace period for the
|
|
+ * RCU_WAIT_TAIL segment as completed and the subsequent one for the
|
|
+ * RCU_NEXT_READY_TAIL segment as started (ie: X + 4 + SRCU_STATE_SCAN1)
|
|
+ * so it returns a snapshot of the next grace period, which is X + 12.
|
|
+ *
|
|
+ * 5) The value of X + 12 is passed to rcu_segcblist_accelerate() but the
|
|
+ * freshly enqueued callback in RCU_NEXT_TAIL can't move to
|
|
+ * RCU_NEXT_READY_TAIL which already has callbacks for a previous grace
|
|
+ * period (gp_num = X + 8). So acceleration fails.
|
|
+ */
|
|
+ s = rcu_seq_snap(&ssp->srcu_gp_seq);
|
|
rcu_segcblist_advance(&sdp->srcu_cblist,
|
|
rcu_seq_current(&ssp->srcu_gp_seq));
|
|
- s = rcu_seq_snap(&ssp->srcu_gp_seq);
|
|
- (void)rcu_segcblist_accelerate(&sdp->srcu_cblist, s);
|
|
+ WARN_ON_ONCE(!rcu_segcblist_accelerate(&sdp->srcu_cblist, s) && rhp);
|
|
if (ULONG_CMP_LT(sdp->srcu_gp_seq_needed, s)) {
|
|
sdp->srcu_gp_seq_needed = s;
|
|
needgp = true;
|
|
diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
|
|
index 06d52525407b8..71cad4f1323c6 100644
|
|
--- a/kernel/trace/ring_buffer.c
|
|
+++ b/kernel/trace/ring_buffer.c
|
|
@@ -646,8 +646,8 @@ static inline bool __rb_time_read(rb_time_t *t, u64 *ret, unsigned long *cnt)
|
|
|
|
*cnt = rb_time_cnt(top);
|
|
|
|
- /* If top and msb counts don't match, this interrupted a write */
|
|
- if (*cnt != rb_time_cnt(msb))
|
|
+ /* If top, msb or bottom counts don't match, this interrupted a write */
|
|
+ if (*cnt != rb_time_cnt(msb) || *cnt != rb_time_cnt(bottom))
|
|
return false;
|
|
|
|
/* The shift to msb will lose its cnt bits */
|
|
diff --git a/lib/Makefile b/lib/Makefile
|
|
index 5ffe72ec99797..6f1611d053e6a 100644
|
|
--- a/lib/Makefile
|
|
+++ b/lib/Makefile
|
|
@@ -361,6 +361,8 @@ obj-$(CONFIG_SBITMAP) += sbitmap.o
|
|
|
|
obj-$(CONFIG_PARMAN) += parman.o
|
|
|
|
+obj-y += group_cpus.o
|
|
+
|
|
# GCC library routines
|
|
obj-$(CONFIG_GENERIC_LIB_ASHLDI3) += ashldi3.o
|
|
obj-$(CONFIG_GENERIC_LIB_ASHRDI3) += ashrdi3.o
|
|
diff --git a/lib/group_cpus.c b/lib/group_cpus.c
|
|
new file mode 100644
|
|
index 0000000000000..0292611901b8b
|
|
--- /dev/null
|
|
+++ b/lib/group_cpus.c
|
|
@@ -0,0 +1,438 @@
|
|
+// SPDX-License-Identifier: GPL-2.0
|
|
+/*
|
|
+ * Copyright (C) 2016 Thomas Gleixner.
|
|
+ * Copyright (C) 2016-2017 Christoph Hellwig.
|
|
+ */
|
|
+#include <linux/kernel.h>
|
|
+#include <linux/slab.h>
|
|
+#include <linux/cpu.h>
|
|
+#include <linux/sort.h>
|
|
+#include <linux/group_cpus.h>
|
|
+
|
|
+#ifdef CONFIG_SMP
|
|
+
|
|
+static void grp_spread_init_one(struct cpumask *irqmsk, struct cpumask *nmsk,
|
|
+ unsigned int cpus_per_grp)
|
|
+{
|
|
+ const struct cpumask *siblmsk;
|
|
+ int cpu, sibl;
|
|
+
|
|
+ for ( ; cpus_per_grp > 0; ) {
|
|
+ cpu = cpumask_first(nmsk);
|
|
+
|
|
+ /* Should not happen, but I'm too lazy to think about it */
|
|
+ if (cpu >= nr_cpu_ids)
|
|
+ return;
|
|
+
|
|
+ cpumask_clear_cpu(cpu, nmsk);
|
|
+ cpumask_set_cpu(cpu, irqmsk);
|
|
+ cpus_per_grp--;
|
|
+
|
|
+ /* If the cpu has siblings, use them first */
|
|
+ siblmsk = topology_sibling_cpumask(cpu);
|
|
+ for (sibl = -1; cpus_per_grp > 0; ) {
|
|
+ sibl = cpumask_next(sibl, siblmsk);
|
|
+ if (sibl >= nr_cpu_ids)
|
|
+ break;
|
|
+ if (!cpumask_test_and_clear_cpu(sibl, nmsk))
|
|
+ continue;
|
|
+ cpumask_set_cpu(sibl, irqmsk);
|
|
+ cpus_per_grp--;
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
+static cpumask_var_t *alloc_node_to_cpumask(void)
|
|
+{
|
|
+ cpumask_var_t *masks;
|
|
+ int node;
|
|
+
|
|
+ masks = kcalloc(nr_node_ids, sizeof(cpumask_var_t), GFP_KERNEL);
|
|
+ if (!masks)
|
|
+ return NULL;
|
|
+
|
|
+ for (node = 0; node < nr_node_ids; node++) {
|
|
+ if (!zalloc_cpumask_var(&masks[node], GFP_KERNEL))
|
|
+ goto out_unwind;
|
|
+ }
|
|
+
|
|
+ return masks;
|
|
+
|
|
+out_unwind:
|
|
+ while (--node >= 0)
|
|
+ free_cpumask_var(masks[node]);
|
|
+ kfree(masks);
|
|
+ return NULL;
|
|
+}
|
|
+
|
|
+static void free_node_to_cpumask(cpumask_var_t *masks)
|
|
+{
|
|
+ int node;
|
|
+
|
|
+ for (node = 0; node < nr_node_ids; node++)
|
|
+ free_cpumask_var(masks[node]);
|
|
+ kfree(masks);
|
|
+}
|
|
+
|
|
+static void build_node_to_cpumask(cpumask_var_t *masks)
|
|
+{
|
|
+ int cpu;
|
|
+
|
|
+ for_each_possible_cpu(cpu)
|
|
+ cpumask_set_cpu(cpu, masks[cpu_to_node(cpu)]);
|
|
+}
|
|
+
|
|
+static int get_nodes_in_cpumask(cpumask_var_t *node_to_cpumask,
|
|
+ const struct cpumask *mask, nodemask_t *nodemsk)
|
|
+{
|
|
+ int n, nodes = 0;
|
|
+
|
|
+ /* Calculate the number of nodes in the supplied affinity mask */
|
|
+ for_each_node(n) {
|
|
+ if (cpumask_intersects(mask, node_to_cpumask[n])) {
|
|
+ node_set(n, *nodemsk);
|
|
+ nodes++;
|
|
+ }
|
|
+ }
|
|
+ return nodes;
|
|
+}
|
|
+
|
|
+struct node_groups {
|
|
+ unsigned id;
|
|
+
|
|
+ union {
|
|
+ unsigned ngroups;
|
|
+ unsigned ncpus;
|
|
+ };
|
|
+};
|
|
+
|
|
+static int ncpus_cmp_func(const void *l, const void *r)
|
|
+{
|
|
+ const struct node_groups *ln = l;
|
|
+ const struct node_groups *rn = r;
|
|
+
|
|
+ return ln->ncpus - rn->ncpus;
|
|
+}
|
|
+
|
|
+/*
|
|
+ * Allocate group number for each node, so that for each node:
|
|
+ *
|
|
+ * 1) the allocated number is >= 1
|
|
+ *
|
|
+ * 2) the allocated number is <= active CPU number of this node
|
|
+ *
|
|
+ * The actual allocated total groups may be less than @numgrps when
|
|
+ * active total CPU number is less than @numgrps.
|
|
+ *
|
|
+ * Active CPUs means the CPUs in '@cpu_mask AND @node_to_cpumask[]'
|
|
+ * for each node.
|
|
+ */
|
|
+static void alloc_nodes_groups(unsigned int numgrps,
|
|
+ cpumask_var_t *node_to_cpumask,
|
|
+ const struct cpumask *cpu_mask,
|
|
+ const nodemask_t nodemsk,
|
|
+ struct cpumask *nmsk,
|
|
+ struct node_groups *node_groups)
|
|
+{
|
|
+ unsigned n, remaining_ncpus = 0;
|
|
+
|
|
+ for (n = 0; n < nr_node_ids; n++) {
|
|
+ node_groups[n].id = n;
|
|
+ node_groups[n].ncpus = UINT_MAX;
|
|
+ }
|
|
+
|
|
+ for_each_node_mask(n, nodemsk) {
|
|
+ unsigned ncpus;
|
|
+
|
|
+ cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]);
|
|
+ ncpus = cpumask_weight(nmsk);
|
|
+
|
|
+ if (!ncpus)
|
|
+ continue;
|
|
+ remaining_ncpus += ncpus;
|
|
+ node_groups[n].ncpus = ncpus;
|
|
+ }
|
|
+
|
|
+ numgrps = min_t(unsigned, remaining_ncpus, numgrps);
|
|
+
|
|
+ sort(node_groups, nr_node_ids, sizeof(node_groups[0]),
|
|
+ ncpus_cmp_func, NULL);
|
|
+
|
|
+ /*
|
|
+ * Allocate groups for each node according to the ratio of this
|
|
+ * node's nr_cpus to remaining un-assigned ncpus. 'numgrps' is
|
|
+ * bigger than number of active numa nodes. Always start the
|
|
+ * allocation from the node with minimized nr_cpus.
|
|
+ *
|
|
+ * This way guarantees that each active node gets allocated at
|
|
+ * least one group, and the theory is simple: over-allocation
|
|
+ * is only done when this node is assigned by one group, so
|
|
+ * other nodes will be allocated >= 1 groups, since 'numgrps' is
|
|
+ * bigger than number of numa nodes.
|
|
+ *
|
|
+ * One perfect invariant is that number of allocated groups for
|
|
+ * each node is <= CPU count of this node:
|
|
+ *
|
|
+ * 1) suppose there are two nodes: A and B
|
|
+ * ncpu(X) is CPU count of node X
|
|
+ * grps(X) is the group count allocated to node X via this
|
|
+ * algorithm
|
|
+ *
|
|
+ * ncpu(A) <= ncpu(B)
|
|
+ * ncpu(A) + ncpu(B) = N
|
|
+ * grps(A) + grps(B) = G
|
|
+ *
|
|
+ * grps(A) = max(1, round_down(G * ncpu(A) / N))
|
|
+ * grps(B) = G - grps(A)
|
|
+ *
|
|
+ * both N and G are integer, and 2 <= G <= N, suppose
|
|
+ * G = N - delta, and 0 <= delta <= N - 2
|
|
+ *
|
|
+ * 2) obviously grps(A) <= ncpu(A) because:
|
|
+ *
|
|
+ * if grps(A) is 1, then grps(A) <= ncpu(A) given
|
|
+ * ncpu(A) >= 1
|
|
+ *
|
|
+ * otherwise,
|
|
+ * grps(A) <= G * ncpu(A) / N <= ncpu(A), given G <= N
|
|
+ *
|
|
+ * 3) prove how grps(B) <= ncpu(B):
|
|
+ *
|
|
+ * if round_down(G * ncpu(A) / N) == 0, vecs(B) won't be
|
|
+ * over-allocated, so grps(B) <= ncpu(B),
|
|
+ *
|
|
+ * otherwise:
|
|
+ *
|
|
+ * grps(A) =
|
|
+ * round_down(G * ncpu(A) / N) =
|
|
+ * round_down((N - delta) * ncpu(A) / N) =
|
|
+ * round_down((N * ncpu(A) - delta * ncpu(A)) / N) >=
|
|
+ * round_down((N * ncpu(A) - delta * N) / N) =
|
|
+ * cpu(A) - delta
|
|
+ *
|
|
+ * then:
|
|
+ *
|
|
+ * grps(A) - G >= ncpu(A) - delta - G
|
|
+ * =>
|
|
+ * G - grps(A) <= G + delta - ncpu(A)
|
|
+ * =>
|
|
+ * grps(B) <= N - ncpu(A)
|
|
+ * =>
|
|
+ * grps(B) <= cpu(B)
|
|
+ *
|
|
+ * For nodes >= 3, it can be thought as one node and another big
|
|
+ * node given that is exactly what this algorithm is implemented,
|
|
+ * and we always re-calculate 'remaining_ncpus' & 'numgrps', and
|
|
+ * finally for each node X: grps(X) <= ncpu(X).
|
|
+ *
|
|
+ */
|
|
+ for (n = 0; n < nr_node_ids; n++) {
|
|
+ unsigned ngroups, ncpus;
|
|
+
|
|
+ if (node_groups[n].ncpus == UINT_MAX)
|
|
+ continue;
|
|
+
|
|
+ WARN_ON_ONCE(numgrps == 0);
|
|
+
|
|
+ ncpus = node_groups[n].ncpus;
|
|
+ ngroups = max_t(unsigned, 1,
|
|
+ numgrps * ncpus / remaining_ncpus);
|
|
+ WARN_ON_ONCE(ngroups > ncpus);
|
|
+
|
|
+ node_groups[n].ngroups = ngroups;
|
|
+
|
|
+ remaining_ncpus -= ncpus;
|
|
+ numgrps -= ngroups;
|
|
+ }
|
|
+}
|
|
+
|
|
+static int __group_cpus_evenly(unsigned int startgrp, unsigned int numgrps,
|
|
+ cpumask_var_t *node_to_cpumask,
|
|
+ const struct cpumask *cpu_mask,
|
|
+ struct cpumask *nmsk, struct cpumask *masks)
|
|
+{
|
|
+ unsigned int i, n, nodes, cpus_per_grp, extra_grps, done = 0;
|
|
+ unsigned int last_grp = numgrps;
|
|
+ unsigned int curgrp = startgrp;
|
|
+ nodemask_t nodemsk = NODE_MASK_NONE;
|
|
+ struct node_groups *node_groups;
|
|
+
|
|
+ if (cpumask_empty(cpu_mask))
|
|
+ return 0;
|
|
+
|
|
+ nodes = get_nodes_in_cpumask(node_to_cpumask, cpu_mask, &nodemsk);
|
|
+
|
|
+ /*
|
|
+ * If the number of nodes in the mask is greater than or equal the
|
|
+ * number of groups we just spread the groups across the nodes.
|
|
+ */
|
|
+ if (numgrps <= nodes) {
|
|
+ for_each_node_mask(n, nodemsk) {
|
|
+ /* Ensure that only CPUs which are in both masks are set */
|
|
+ cpumask_and(nmsk, cpu_mask, node_to_cpumask[n]);
|
|
+ cpumask_or(&masks[curgrp], &masks[curgrp], nmsk);
|
|
+ if (++curgrp == last_grp)
|
|
+ curgrp = 0;
|
|
+ }
|
|
+ return numgrps;
|
|
+ }
|
|
+
|
|
+ node_groups = kcalloc(nr_node_ids,
|
|
+ sizeof(struct node_groups),
|
|
+ GFP_KERNEL);
|
|
+ if (!node_groups)
|
|
+ return -ENOMEM;
|
|
+
|
|
+ /* allocate group number for each node */
|
|
+ alloc_nodes_groups(numgrps, node_to_cpumask, cpu_mask,
|
|
+ nodemsk, nmsk, node_groups);
|
|
+ for (i = 0; i < nr_node_ids; i++) {
|
|
+ unsigned int ncpus, v;
|
|
+ struct node_groups *nv = &node_groups[i];
|
|
+
|
|
+ if (nv->ngroups == UINT_MAX)
|
|
+ continue;
|
|
+
|
|
+ /* Get the cpus on this node which are in the mask */
|
|
+ cpumask_and(nmsk, cpu_mask, node_to_cpumask[nv->id]);
|
|
+ ncpus = cpumask_weight(nmsk);
|
|
+ if (!ncpus)
|
|
+ continue;
|
|
+
|
|
+ WARN_ON_ONCE(nv->ngroups > ncpus);
|
|
+
|
|
+ /* Account for rounding errors */
|
|
+ extra_grps = ncpus - nv->ngroups * (ncpus / nv->ngroups);
|
|
+
|
|
+ /* Spread allocated groups on CPUs of the current node */
|
|
+ for (v = 0; v < nv->ngroups; v++, curgrp++) {
|
|
+ cpus_per_grp = ncpus / nv->ngroups;
|
|
+
|
|
+ /* Account for extra groups to compensate rounding errors */
|
|
+ if (extra_grps) {
|
|
+ cpus_per_grp++;
|
|
+ --extra_grps;
|
|
+ }
|
|
+
|
|
+ /*
|
|
+ * wrapping has to be considered given 'startgrp'
|
|
+ * may start anywhere
|
|
+ */
|
|
+ if (curgrp >= last_grp)
|
|
+ curgrp = 0;
|
|
+ grp_spread_init_one(&masks[curgrp], nmsk,
|
|
+ cpus_per_grp);
|
|
+ }
|
|
+ done += nv->ngroups;
|
|
+ }
|
|
+ kfree(node_groups);
|
|
+ return done;
|
|
+}
|
|
+
|
|
+/**
|
|
+ * group_cpus_evenly - Group all CPUs evenly per NUMA/CPU locality
|
|
+ * @numgrps: number of groups
|
|
+ *
|
|
+ * Return: cpumask array if successful, NULL otherwise. And each element
|
|
+ * includes CPUs assigned to this group
|
|
+ *
|
|
+ * Try to put close CPUs from viewpoint of CPU and NUMA locality into
|
|
+ * same group, and run two-stage grouping:
|
|
+ * 1) allocate present CPUs on these groups evenly first
|
|
+ * 2) allocate other possible CPUs on these groups evenly
|
|
+ *
|
|
+ * We guarantee in the resulted grouping that all CPUs are covered, and
|
|
+ * no same CPU is assigned to multiple groups
|
|
+ */
|
|
+struct cpumask *group_cpus_evenly(unsigned int numgrps)
|
|
+{
|
|
+ unsigned int curgrp = 0, nr_present = 0, nr_others = 0;
|
|
+ cpumask_var_t *node_to_cpumask;
|
|
+ cpumask_var_t nmsk, npresmsk;
|
|
+ int ret = -ENOMEM;
|
|
+ struct cpumask *masks = NULL;
|
|
+
|
|
+ if (!zalloc_cpumask_var(&nmsk, GFP_KERNEL))
|
|
+ return NULL;
|
|
+
|
|
+ if (!zalloc_cpumask_var(&npresmsk, GFP_KERNEL))
|
|
+ goto fail_nmsk;
|
|
+
|
|
+ node_to_cpumask = alloc_node_to_cpumask();
|
|
+ if (!node_to_cpumask)
|
|
+ goto fail_npresmsk;
|
|
+
|
|
+ masks = kcalloc(numgrps, sizeof(*masks), GFP_KERNEL);
|
|
+ if (!masks)
|
|
+ goto fail_node_to_cpumask;
|
|
+
|
|
+ build_node_to_cpumask(node_to_cpumask);
|
|
+
|
|
+ /*
|
|
+ * Make a local cache of 'cpu_present_mask', so the two stages
|
|
+ * spread can observe consistent 'cpu_present_mask' without holding
|
|
+ * cpu hotplug lock, then we can reduce deadlock risk with cpu
|
|
+ * hotplug code.
|
|
+ *
|
|
+ * Here CPU hotplug may happen when reading `cpu_present_mask`, and
|
|
+ * we can live with the case because it only affects that hotplug
|
|
+ * CPU is handled in the 1st or 2nd stage, and either way is correct
|
|
+ * from API user viewpoint since 2-stage spread is sort of
|
|
+ * optimization.
|
|
+ */
|
|
+ cpumask_copy(npresmsk, data_race(cpu_present_mask));
|
|
+
|
|
+ /* grouping present CPUs first */
|
|
+ ret = __group_cpus_evenly(curgrp, numgrps, node_to_cpumask,
|
|
+ npresmsk, nmsk, masks);
|
|
+ if (ret < 0)
|
|
+ goto fail_build_affinity;
|
|
+ nr_present = ret;
|
|
+
|
|
+ /*
|
|
+ * Allocate non present CPUs starting from the next group to be
|
|
+ * handled. If the grouping of present CPUs already exhausted the
|
|
+ * group space, assign the non present CPUs to the already
|
|
+ * allocated out groups.
|
|
+ */
|
|
+ if (nr_present >= numgrps)
|
|
+ curgrp = 0;
|
|
+ else
|
|
+ curgrp = nr_present;
|
|
+ cpumask_andnot(npresmsk, cpu_possible_mask, npresmsk);
|
|
+ ret = __group_cpus_evenly(curgrp, numgrps, node_to_cpumask,
|
|
+ npresmsk, nmsk, masks);
|
|
+ if (ret >= 0)
|
|
+ nr_others = ret;
|
|
+
|
|
+ fail_build_affinity:
|
|
+ if (ret >= 0)
|
|
+ WARN_ON(nr_present + nr_others < numgrps);
|
|
+
|
|
+ fail_node_to_cpumask:
|
|
+ free_node_to_cpumask(node_to_cpumask);
|
|
+
|
|
+ fail_npresmsk:
|
|
+ free_cpumask_var(npresmsk);
|
|
+
|
|
+ fail_nmsk:
|
|
+ free_cpumask_var(nmsk);
|
|
+ if (ret < 0) {
|
|
+ kfree(masks);
|
|
+ return NULL;
|
|
+ }
|
|
+ return masks;
|
|
+}
|
|
+#else /* CONFIG_SMP */
|
|
+struct cpumask *group_cpus_evenly(unsigned int numgrps)
|
|
+{
|
|
+ struct cpumask *masks = kcalloc(numgrps, sizeof(*masks), GFP_KERNEL);
|
|
+
|
|
+ if (!masks)
|
|
+ return NULL;
|
|
+
|
|
+ /* assign all CPUs(cpu 0) to the 1st group only */
|
|
+ cpumask_copy(&masks[0], cpu_possible_mask);
|
|
+ return masks;
|
|
+}
|
|
+#endif /* CONFIG_SMP */
|
|
diff --git a/mm/filemap.c b/mm/filemap.c
|
|
index 10fe6430693bd..2809b1174f04e 100644
|
|
--- a/mm/filemap.c
|
|
+++ b/mm/filemap.c
|
|
@@ -4005,6 +4005,8 @@ bool filemap_release_folio(struct folio *folio, gfp_t gfp)
|
|
struct address_space * const mapping = folio->mapping;
|
|
|
|
BUG_ON(!folio_test_locked(folio));
|
|
+ if (!folio_needs_release(folio))
|
|
+ return true;
|
|
if (folio_test_writeback(folio))
|
|
return false;
|
|
|
|
diff --git a/mm/huge_memory.c b/mm/huge_memory.c
|
|
index 2753fb54cdf38..59577946735b1 100644
|
|
--- a/mm/huge_memory.c
|
|
+++ b/mm/huge_memory.c
|
|
@@ -2694,8 +2694,7 @@ int split_huge_page_to_list(struct page *page, struct list_head *list)
|
|
gfp = current_gfp_context(mapping_gfp_mask(mapping) &
|
|
GFP_RECLAIM_MASK);
|
|
|
|
- if (folio_test_private(folio) &&
|
|
- !filemap_release_folio(folio, gfp)) {
|
|
+ if (!filemap_release_folio(folio, gfp)) {
|
|
ret = -EBUSY;
|
|
goto out;
|
|
}
|
|
diff --git a/mm/internal.h b/mm/internal.h
|
|
index 6b7ef495b56d3..d01130efce5fb 100644
|
|
--- a/mm/internal.h
|
|
+++ b/mm/internal.h
|
|
@@ -163,6 +163,17 @@ static inline void set_page_refcounted(struct page *page)
|
|
set_page_count(page, 1);
|
|
}
|
|
|
|
+/*
|
|
+ * Return true if a folio needs ->release_folio() calling upon it.
|
|
+ */
|
|
+static inline bool folio_needs_release(struct folio *folio)
|
|
+{
|
|
+ struct address_space *mapping = folio_mapping(folio);
|
|
+
|
|
+ return folio_has_private(folio) ||
|
|
+ (mapping && mapping_release_always(mapping));
|
|
+}
|
|
+
|
|
extern unsigned long highest_memmap_pfn;
|
|
|
|
/*
|
|
diff --git a/mm/khugepaged.c b/mm/khugepaged.c
|
|
index ef72d3df4b65b..65bd0b105266a 100644
|
|
--- a/mm/khugepaged.c
|
|
+++ b/mm/khugepaged.c
|
|
@@ -1818,6 +1818,7 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
|
|
xas_set(&xas, start);
|
|
for (index = start; index < end; index++) {
|
|
struct page *page = xas_next(&xas);
|
|
+ struct folio *folio;
|
|
|
|
VM_BUG_ON(index != xas.xa_index);
|
|
if (is_shmem) {
|
|
@@ -1844,8 +1845,6 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
|
|
}
|
|
|
|
if (xa_is_value(page) || !PageUptodate(page)) {
|
|
- struct folio *folio;
|
|
-
|
|
xas_unlock_irq(&xas);
|
|
/* swap in or instantiate fallocated page */
|
|
if (shmem_get_folio(mapping->host, index,
|
|
@@ -1933,13 +1932,15 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
|
|
goto out_unlock;
|
|
}
|
|
|
|
- if (page_mapping(page) != mapping) {
|
|
+ folio = page_folio(page);
|
|
+
|
|
+ if (folio_mapping(folio) != mapping) {
|
|
result = SCAN_TRUNCATED;
|
|
goto out_unlock;
|
|
}
|
|
|
|
- if (!is_shmem && (PageDirty(page) ||
|
|
- PageWriteback(page))) {
|
|
+ if (!is_shmem && (folio_test_dirty(folio) ||
|
|
+ folio_test_writeback(folio))) {
|
|
/*
|
|
* khugepaged only works on read-only fd, so this
|
|
* page is dirty because it hasn't been flushed
|
|
@@ -1949,20 +1950,19 @@ static int collapse_file(struct mm_struct *mm, unsigned long addr,
|
|
goto out_unlock;
|
|
}
|
|
|
|
- if (isolate_lru_page(page)) {
|
|
+ if (folio_isolate_lru(folio)) {
|
|
result = SCAN_DEL_PAGE_LRU;
|
|
goto out_unlock;
|
|
}
|
|
|
|
- if (page_has_private(page) &&
|
|
- !try_to_release_page(page, GFP_KERNEL)) {
|
|
+ if (!filemap_release_folio(folio, GFP_KERNEL)) {
|
|
result = SCAN_PAGE_HAS_PRIVATE;
|
|
- putback_lru_page(page);
|
|
+ folio_putback_lru(folio);
|
|
goto out_unlock;
|
|
}
|
|
|
|
- if (page_mapped(page))
|
|
- try_to_unmap(page_folio(page),
|
|
+ if (folio_mapped(folio))
|
|
+ try_to_unmap(folio,
|
|
TTU_IGNORE_MLOCK | TTU_BATCH_FLUSH);
|
|
|
|
xas_lock_irq(&xas);
|
|
diff --git a/mm/memory-failure.c b/mm/memory-failure.c
|
|
index ebd717157c813..5b846ed5dcbe9 100644
|
|
--- a/mm/memory-failure.c
|
|
+++ b/mm/memory-failure.c
|
|
@@ -827,16 +827,15 @@ static int truncate_error_page(struct page *p, unsigned long pfn,
|
|
int ret = MF_FAILED;
|
|
|
|
if (mapping->a_ops->error_remove_page) {
|
|
+ struct folio *folio = page_folio(p);
|
|
int err = mapping->a_ops->error_remove_page(mapping, p);
|
|
|
|
- if (err != 0) {
|
|
+ if (err != 0)
|
|
pr_info("%#lx: Failed to punch page: %d\n", pfn, err);
|
|
- } else if (page_has_private(p) &&
|
|
- !try_to_release_page(p, GFP_NOIO)) {
|
|
+ else if (!filemap_release_folio(folio, GFP_NOIO))
|
|
pr_info("%#lx: failed to release buffers\n", pfn);
|
|
- } else {
|
|
+ else
|
|
ret = MF_RECOVERED;
|
|
- }
|
|
} else {
|
|
/*
|
|
* If the file system doesn't support it just invalidate
|
|
diff --git a/mm/memory.c b/mm/memory.c
|
|
index 0d1b3ee8fcd7a..fc8b264ec0cac 100644
|
|
--- a/mm/memory.c
|
|
+++ b/mm/memory.c
|
|
@@ -3617,8 +3617,8 @@ EXPORT_SYMBOL_GPL(unmap_mapping_pages);
|
|
void unmap_mapping_range(struct address_space *mapping,
|
|
loff_t const holebegin, loff_t const holelen, int even_cows)
|
|
{
|
|
- pgoff_t hba = holebegin >> PAGE_SHIFT;
|
|
- pgoff_t hlen = (holelen + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
+ pgoff_t hba = (pgoff_t)(holebegin) >> PAGE_SHIFT;
|
|
+ pgoff_t hlen = ((pgoff_t)(holelen) + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
|
/* Check for overflow. */
|
|
if (sizeof(holelen) > sizeof(hlen)) {
|
|
diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c
|
|
index bd2570b4f9b7b..3b9d3a4b43869 100644
|
|
--- a/mm/memory_hotplug.c
|
|
+++ b/mm/memory_hotplug.c
|
|
@@ -1069,6 +1069,9 @@ void mhp_deinit_memmap_on_memory(unsigned long pfn, unsigned long nr_pages)
|
|
kasan_remove_zero_shadow(__va(PFN_PHYS(pfn)), PFN_PHYS(nr_pages));
|
|
}
|
|
|
|
+/*
|
|
+ * Must be called with mem_hotplug_lock in write mode.
|
|
+ */
|
|
int __ref online_pages(unsigned long pfn, unsigned long nr_pages,
|
|
struct zone *zone, struct memory_group *group)
|
|
{
|
|
@@ -1089,7 +1092,6 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages,
|
|
!IS_ALIGNED(pfn + nr_pages, PAGES_PER_SECTION)))
|
|
return -EINVAL;
|
|
|
|
- mem_hotplug_begin();
|
|
|
|
/* associate pfn range with the zone */
|
|
move_pfn_range_to_zone(zone, pfn, nr_pages, NULL, MIGRATE_ISOLATE);
|
|
@@ -1148,7 +1150,6 @@ int __ref online_pages(unsigned long pfn, unsigned long nr_pages,
|
|
writeback_set_ratelimit();
|
|
|
|
memory_notify(MEM_ONLINE, &arg);
|
|
- mem_hotplug_done();
|
|
return 0;
|
|
|
|
failed_addition:
|
|
@@ -1157,7 +1158,6 @@ failed_addition:
|
|
(((unsigned long long) pfn + nr_pages) << PAGE_SHIFT) - 1);
|
|
memory_notify(MEM_CANCEL_ONLINE, &arg);
|
|
remove_pfn_range_from_zone(zone, pfn, nr_pages);
|
|
- mem_hotplug_done();
|
|
return ret;
|
|
}
|
|
|
|
@@ -1382,7 +1382,7 @@ int __ref add_memory_resource(int nid, struct resource *res, mhp_t mhp_flags)
|
|
ret = create_memory_block_devices(start, size, mhp_altmap.alloc,
|
|
group);
|
|
if (ret) {
|
|
- arch_remove_memory(start, size, NULL);
|
|
+ arch_remove_memory(start, size, params.altmap);
|
|
goto error;
|
|
}
|
|
|
|
@@ -1787,6 +1787,9 @@ static int count_system_ram_pages_cb(unsigned long start_pfn,
|
|
return 0;
|
|
}
|
|
|
|
+/*
|
|
+ * Must be called with mem_hotplug_lock in write mode.
|
|
+ */
|
|
int __ref offline_pages(unsigned long start_pfn, unsigned long nr_pages,
|
|
struct zone *zone, struct memory_group *group)
|
|
{
|
|
@@ -1809,8 +1812,6 @@ int __ref offline_pages(unsigned long start_pfn, unsigned long nr_pages,
|
|
!IS_ALIGNED(start_pfn + nr_pages, PAGES_PER_SECTION)))
|
|
return -EINVAL;
|
|
|
|
- mem_hotplug_begin();
|
|
-
|
|
/*
|
|
* Don't allow to offline memory blocks that contain holes.
|
|
* Consequently, memory blocks with holes can never get onlined
|
|
@@ -1946,7 +1947,6 @@ int __ref offline_pages(unsigned long start_pfn, unsigned long nr_pages,
|
|
|
|
memory_notify(MEM_OFFLINE, &arg);
|
|
remove_pfn_range_from_zone(zone, start_pfn, nr_pages);
|
|
- mem_hotplug_done();
|
|
return 0;
|
|
|
|
failed_removal_isolated:
|
|
@@ -1961,7 +1961,6 @@ failed_removal:
|
|
(unsigned long long) start_pfn << PAGE_SHIFT,
|
|
((unsigned long long) end_pfn << PAGE_SHIFT) - 1,
|
|
reason);
|
|
- mem_hotplug_done();
|
|
return ret;
|
|
}
|
|
|
|
diff --git a/mm/migrate.c b/mm/migrate.c
|
|
index 91bd69c61148e..c93dd6a31c31a 100644
|
|
--- a/mm/migrate.c
|
|
+++ b/mm/migrate.c
|
|
@@ -914,8 +914,7 @@ static int fallback_migrate_folio(struct address_space *mapping,
|
|
* Buffers may be managed in a filesystem specific way.
|
|
* We must have no buffers or drop them.
|
|
*/
|
|
- if (folio_test_private(src) &&
|
|
- !filemap_release_folio(src, GFP_KERNEL))
|
|
+ if (!filemap_release_folio(src, GFP_KERNEL))
|
|
return mode == MIGRATE_SYNC ? -EAGAIN : -EBUSY;
|
|
|
|
return migrate_folio(mapping, dst, src, mode);
|
|
diff --git a/mm/page-writeback.c b/mm/page-writeback.c
|
|
index 7e9d8d857ecca..de5f69921b946 100644
|
|
--- a/mm/page-writeback.c
|
|
+++ b/mm/page-writeback.c
|
|
@@ -3078,7 +3078,7 @@ EXPORT_SYMBOL_GPL(folio_wait_writeback_killable);
|
|
*/
|
|
void folio_wait_stable(struct folio *folio)
|
|
{
|
|
- if (folio_inode(folio)->i_sb->s_iflags & SB_I_STABLE_WRITES)
|
|
+ if (mapping_stable_writes(folio_mapping(folio)))
|
|
folio_wait_writeback(folio);
|
|
}
|
|
EXPORT_SYMBOL_GPL(folio_wait_stable);
|
|
diff --git a/mm/truncate.c b/mm/truncate.c
|
|
index c0be77e5c0083..0d4dd233f5187 100644
|
|
--- a/mm/truncate.c
|
|
+++ b/mm/truncate.c
|
|
@@ -19,7 +19,6 @@
|
|
#include <linux/highmem.h>
|
|
#include <linux/pagevec.h>
|
|
#include <linux/task_io_accounting_ops.h>
|
|
-#include <linux/buffer_head.h> /* grr. try_to_release_page */
|
|
#include <linux/shmem_fs.h>
|
|
#include <linux/rmap.h>
|
|
#include "internal.h"
|
|
@@ -276,7 +275,7 @@ static long mapping_evict_folio(struct address_space *mapping,
|
|
if (folio_ref_count(folio) >
|
|
folio_nr_pages(folio) + folio_has_private(folio) + 1)
|
|
return 0;
|
|
- if (folio_has_private(folio) && !filemap_release_folio(folio, 0))
|
|
+ if (!filemap_release_folio(folio, 0))
|
|
return 0;
|
|
|
|
return remove_mapping(mapping, folio);
|
|
@@ -581,8 +580,7 @@ static int invalidate_complete_folio2(struct address_space *mapping,
|
|
if (folio->mapping != mapping)
|
|
return 0;
|
|
|
|
- if (folio_has_private(folio) &&
|
|
- !filemap_release_folio(folio, GFP_KERNEL))
|
|
+ if (!filemap_release_folio(folio, GFP_KERNEL))
|
|
return 0;
|
|
|
|
spin_lock(&mapping->host->i_lock);
|
|
diff --git a/mm/vmscan.c b/mm/vmscan.c
|
|
index 3f090faa6377f..9f3cfb7caa48d 100644
|
|
--- a/mm/vmscan.c
|
|
+++ b/mm/vmscan.c
|
|
@@ -1992,7 +1992,7 @@ retry:
|
|
* (refcount == 1) it can be freed. Otherwise, leave
|
|
* the folio on the LRU so it is swappable.
|
|
*/
|
|
- if (folio_has_private(folio)) {
|
|
+ if (folio_needs_release(folio)) {
|
|
if (!filemap_release_folio(folio, sc->gfp_mask))
|
|
goto activate_locked;
|
|
if (!mapping && folio_ref_count(folio) == 1) {
|
|
@@ -2618,9 +2618,9 @@ static void shrink_active_list(unsigned long nr_to_scan,
|
|
}
|
|
|
|
if (unlikely(buffer_heads_over_limit)) {
|
|
- if (folio_test_private(folio) && folio_trylock(folio)) {
|
|
- if (folio_test_private(folio))
|
|
- filemap_release_folio(folio, 0);
|
|
+ if (folio_needs_release(folio) &&
|
|
+ folio_trylock(folio)) {
|
|
+ filemap_release_folio(folio, 0);
|
|
folio_unlock(folio);
|
|
}
|
|
}
|
|
diff --git a/net/can/j1939/socket.c b/net/can/j1939/socket.c
|
|
index 9c828067b4481..b0be23559243c 100644
|
|
--- a/net/can/j1939/socket.c
|
|
+++ b/net/can/j1939/socket.c
|
|
@@ -974,6 +974,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk,
|
|
struct sock_exterr_skb *serr;
|
|
struct sk_buff *skb;
|
|
char *state = "UNK";
|
|
+ u32 tsflags;
|
|
int err;
|
|
|
|
jsk = j1939_sk(sk);
|
|
@@ -981,13 +982,14 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk,
|
|
if (!(jsk->state & J1939_SOCK_ERRQUEUE))
|
|
return;
|
|
|
|
+ tsflags = READ_ONCE(sk->sk_tsflags);
|
|
switch (type) {
|
|
case J1939_ERRQUEUE_TX_ACK:
|
|
- if (!(sk->sk_tsflags & SOF_TIMESTAMPING_TX_ACK))
|
|
+ if (!(tsflags & SOF_TIMESTAMPING_TX_ACK))
|
|
return;
|
|
break;
|
|
case J1939_ERRQUEUE_TX_SCHED:
|
|
- if (!(sk->sk_tsflags & SOF_TIMESTAMPING_TX_SCHED))
|
|
+ if (!(tsflags & SOF_TIMESTAMPING_TX_SCHED))
|
|
return;
|
|
break;
|
|
case J1939_ERRQUEUE_TX_ABORT:
|
|
@@ -997,7 +999,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk,
|
|
case J1939_ERRQUEUE_RX_DPO:
|
|
fallthrough;
|
|
case J1939_ERRQUEUE_RX_ABORT:
|
|
- if (!(sk->sk_tsflags & SOF_TIMESTAMPING_RX_SOFTWARE))
|
|
+ if (!(tsflags & SOF_TIMESTAMPING_RX_SOFTWARE))
|
|
return;
|
|
break;
|
|
default:
|
|
@@ -1054,7 +1056,7 @@ static void __j1939_sk_errqueue(struct j1939_session *session, struct sock *sk,
|
|
}
|
|
|
|
serr->opt_stats = true;
|
|
- if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)
|
|
+ if (tsflags & SOF_TIMESTAMPING_OPT_ID)
|
|
serr->ee.ee_data = session->tskey;
|
|
|
|
netdev_dbg(session->priv->ndev, "%s: 0x%p tskey: %i, state: %s\n",
|
|
diff --git a/net/can/raw.c b/net/can/raw.c
|
|
index 8c104339d538d..488320738e319 100644
|
|
--- a/net/can/raw.c
|
|
+++ b/net/can/raw.c
|
|
@@ -881,6 +881,7 @@ static int raw_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
|
|
|
skb->dev = dev;
|
|
skb->priority = sk->sk_priority;
|
|
+ skb->mark = sk->sk_mark;
|
|
skb->tstamp = sockc.transmit_time;
|
|
|
|
skb_setup_tx_timestamp(skb, sockc.tsflags);
|
|
diff --git a/net/core/skbuff.c b/net/core/skbuff.c
|
|
index 73b1e0e53534e..8a819d0a7bfb0 100644
|
|
--- a/net/core/skbuff.c
|
|
+++ b/net/core/skbuff.c
|
|
@@ -4913,7 +4913,7 @@ static void __skb_complete_tx_timestamp(struct sk_buff *skb,
|
|
serr->ee.ee_info = tstype;
|
|
serr->opt_stats = opt_stats;
|
|
serr->header.h4.iif = skb->dev ? skb->dev->ifindex : 0;
|
|
- if (sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID) {
|
|
+ if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID) {
|
|
serr->ee.ee_data = skb_shinfo(skb)->tskey;
|
|
if (sk_is_tcp(sk))
|
|
serr->ee.ee_data -= atomic_read(&sk->sk_tskey);
|
|
@@ -4969,21 +4969,23 @@ void __skb_tstamp_tx(struct sk_buff *orig_skb,
|
|
{
|
|
struct sk_buff *skb;
|
|
bool tsonly, opt_stats = false;
|
|
+ u32 tsflags;
|
|
|
|
if (!sk)
|
|
return;
|
|
|
|
- if (!hwtstamps && !(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) &&
|
|
+ tsflags = READ_ONCE(sk->sk_tsflags);
|
|
+ if (!hwtstamps && !(tsflags & SOF_TIMESTAMPING_OPT_TX_SWHW) &&
|
|
skb_shinfo(orig_skb)->tx_flags & SKBTX_IN_PROGRESS)
|
|
return;
|
|
|
|
- tsonly = sk->sk_tsflags & SOF_TIMESTAMPING_OPT_TSONLY;
|
|
+ tsonly = tsflags & SOF_TIMESTAMPING_OPT_TSONLY;
|
|
if (!skb_may_tx_timestamp(sk, tsonly))
|
|
return;
|
|
|
|
if (tsonly) {
|
|
#ifdef CONFIG_INET
|
|
- if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_STATS) &&
|
|
+ if ((tsflags & SOF_TIMESTAMPING_OPT_STATS) &&
|
|
sk_is_tcp(sk)) {
|
|
skb = tcp_get_timestamping_opt_stats(sk, orig_skb,
|
|
ack_skb);
|
|
diff --git a/net/core/skmsg.c b/net/core/skmsg.c
|
|
index a5c1f67dc96ec..3818035ea0021 100644
|
|
--- a/net/core/skmsg.c
|
|
+++ b/net/core/skmsg.c
|
|
@@ -825,6 +825,8 @@ static void sk_psock_destroy(struct work_struct *work)
|
|
|
|
if (psock->sk_redir)
|
|
sock_put(psock->sk_redir);
|
|
+ if (psock->sk_pair)
|
|
+ sock_put(psock->sk_pair);
|
|
sock_put(psock->sk);
|
|
kfree(psock);
|
|
}
|
|
diff --git a/net/core/sock.c b/net/core/sock.c
|
|
index 4305e55dbfba4..c50a14a02edd4 100644
|
|
--- a/net/core/sock.c
|
|
+++ b/net/core/sock.c
|
|
@@ -890,7 +890,7 @@ static int sock_timestamping_bind_phc(struct sock *sk, int phc_index)
|
|
if (!match)
|
|
return -EINVAL;
|
|
|
|
- sk->sk_bind_phc = phc_index;
|
|
+ WRITE_ONCE(sk->sk_bind_phc, phc_index);
|
|
|
|
return 0;
|
|
}
|
|
@@ -926,7 +926,7 @@ int sock_set_timestamping(struct sock *sk, int optname,
|
|
return ret;
|
|
}
|
|
|
|
- sk->sk_tsflags = val;
|
|
+ WRITE_ONCE(sk->sk_tsflags, val);
|
|
sock_valbool_flag(sk, SOCK_TSTAMP_NEW, optname == SO_TIMESTAMPING_NEW);
|
|
|
|
if (val & SOF_TIMESTAMPING_RX_SOFTWARE)
|
|
@@ -1704,9 +1704,16 @@ int sk_getsockopt(struct sock *sk, int level, int optname,
|
|
break;
|
|
|
|
case SO_TIMESTAMPING_OLD:
|
|
+ case SO_TIMESTAMPING_NEW:
|
|
lv = sizeof(v.timestamping);
|
|
- v.timestamping.flags = sk->sk_tsflags;
|
|
- v.timestamping.bind_phc = sk->sk_bind_phc;
|
|
+ /* For the later-added case SO_TIMESTAMPING_NEW: Be strict about only
|
|
+ * returning the flags when they were set through the same option.
|
|
+ * Don't change the beviour for the old case SO_TIMESTAMPING_OLD.
|
|
+ */
|
|
+ if (optname == SO_TIMESTAMPING_OLD || sock_flag(sk, SOCK_TSTAMP_NEW)) {
|
|
+ v.timestamping.flags = READ_ONCE(sk->sk_tsflags);
|
|
+ v.timestamping.bind_phc = READ_ONCE(sk->sk_bind_phc);
|
|
+ }
|
|
break;
|
|
|
|
case SO_RCVTIMEO_OLD:
|
|
@@ -2764,6 +2771,7 @@ int __sock_cmsg_send(struct sock *sk, struct msghdr *msg, struct cmsghdr *cmsg,
|
|
sockc->mark = *(u32 *)CMSG_DATA(cmsg);
|
|
break;
|
|
case SO_TIMESTAMPING_OLD:
|
|
+ case SO_TIMESTAMPING_NEW:
|
|
if (cmsg->cmsg_len != CMSG_LEN(sizeof(u32)))
|
|
return -EINVAL;
|
|
|
|
diff --git a/net/core/sock_map.c b/net/core/sock_map.c
|
|
index 38e01f82f2ef3..91140bc0541f3 100644
|
|
--- a/net/core/sock_map.c
|
|
+++ b/net/core/sock_map.c
|
|
@@ -538,6 +538,8 @@ static bool sock_map_sk_state_allowed(const struct sock *sk)
|
|
{
|
|
if (sk_is_tcp(sk))
|
|
return (1 << sk->sk_state) & (TCPF_ESTABLISHED | TCPF_LISTEN);
|
|
+ if (sk_is_stream_unix(sk))
|
|
+ return (1 << sk->sk_state) & TCPF_ESTABLISHED;
|
|
return true;
|
|
}
|
|
|
|
diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c
|
|
index 03f8f33dc134c..8324e9f970668 100644
|
|
--- a/net/dns_resolver/dns_key.c
|
|
+++ b/net/dns_resolver/dns_key.c
|
|
@@ -91,8 +91,6 @@ const struct cred *dns_resolver_cache;
|
|
static int
|
|
dns_resolver_preparse(struct key_preparsed_payload *prep)
|
|
{
|
|
- const struct dns_server_list_v1_header *v1;
|
|
- const struct dns_payload_header *bin;
|
|
struct user_key_payload *upayload;
|
|
unsigned long derrno;
|
|
int ret;
|
|
@@ -103,27 +101,28 @@ dns_resolver_preparse(struct key_preparsed_payload *prep)
|
|
return -EINVAL;
|
|
|
|
if (data[0] == 0) {
|
|
+ const struct dns_server_list_v1_header *v1;
|
|
+
|
|
/* It may be a server list. */
|
|
- if (datalen <= sizeof(*bin))
|
|
+ if (datalen <= sizeof(*v1))
|
|
return -EINVAL;
|
|
|
|
- bin = (const struct dns_payload_header *)data;
|
|
- kenter("[%u,%u],%u", bin->content, bin->version, datalen);
|
|
- if (bin->content != DNS_PAYLOAD_IS_SERVER_LIST) {
|
|
+ v1 = (const struct dns_server_list_v1_header *)data;
|
|
+ kenter("[%u,%u],%u", v1->hdr.content, v1->hdr.version, datalen);
|
|
+ if (v1->hdr.content != DNS_PAYLOAD_IS_SERVER_LIST) {
|
|
pr_warn_ratelimited(
|
|
"dns_resolver: Unsupported content type (%u)\n",
|
|
- bin->content);
|
|
+ v1->hdr.content);
|
|
return -EINVAL;
|
|
}
|
|
|
|
- if (bin->version != 1) {
|
|
+ if (v1->hdr.version != 1) {
|
|
pr_warn_ratelimited(
|
|
"dns_resolver: Unsupported server list version (%u)\n",
|
|
- bin->version);
|
|
+ v1->hdr.version);
|
|
return -EINVAL;
|
|
}
|
|
|
|
- v1 = (const struct dns_server_list_v1_header *)bin;
|
|
if ((v1->status != DNS_LOOKUP_GOOD &&
|
|
v1->status != DNS_LOOKUP_GOOD_WITH_BAD)) {
|
|
if (prep->expiry == TIME64_MAX)
|
|
diff --git a/net/ethtool/netlink.c b/net/ethtool/netlink.c
|
|
index 1a4c11356c96c..fc4ccecf9495c 100644
|
|
--- a/net/ethtool/netlink.c
|
|
+++ b/net/ethtool/netlink.c
|
|
@@ -509,7 +509,7 @@ lock_and_cont:
|
|
cont:
|
|
idx++;
|
|
}
|
|
-
|
|
+ ret = 0;
|
|
}
|
|
rtnl_unlock();
|
|
|
|
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
|
|
index 5d379df90c826..347c3768df6e8 100644
|
|
--- a/net/ipv4/af_inet.c
|
|
+++ b/net/ipv4/af_inet.c
|
|
@@ -838,6 +838,21 @@ int inet_sendmsg(struct socket *sock, struct msghdr *msg, size_t size)
|
|
}
|
|
EXPORT_SYMBOL(inet_sendmsg);
|
|
|
|
+void inet_splice_eof(struct socket *sock)
|
|
+{
|
|
+ const struct proto *prot;
|
|
+ struct sock *sk = sock->sk;
|
|
+
|
|
+ if (unlikely(inet_send_prepare(sk)))
|
|
+ return;
|
|
+
|
|
+ /* IPV6_ADDRFORM can change sk->sk_prot under us. */
|
|
+ prot = READ_ONCE(sk->sk_prot);
|
|
+ if (prot->splice_eof)
|
|
+ prot->splice_eof(sock);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(inet_splice_eof);
|
|
+
|
|
ssize_t inet_sendpage(struct socket *sock, struct page *page, int offset,
|
|
size_t size, int flags)
|
|
{
|
|
@@ -1057,6 +1072,7 @@ const struct proto_ops inet_stream_ops = {
|
|
#ifdef CONFIG_MMU
|
|
.mmap = tcp_mmap,
|
|
#endif
|
|
+ .splice_eof = inet_splice_eof,
|
|
.sendpage = inet_sendpage,
|
|
.splice_read = tcp_splice_read,
|
|
.read_sock = tcp_read_sock,
|
|
@@ -1091,6 +1107,7 @@ const struct proto_ops inet_dgram_ops = {
|
|
.read_skb = udp_read_skb,
|
|
.recvmsg = inet_recvmsg,
|
|
.mmap = sock_no_mmap,
|
|
+ .splice_eof = inet_splice_eof,
|
|
.sendpage = inet_sendpage,
|
|
.set_peek_off = sk_set_peek_off,
|
|
#ifdef CONFIG_COMPAT
|
|
@@ -1122,6 +1139,7 @@ static const struct proto_ops inet_sockraw_ops = {
|
|
.sendmsg = inet_sendmsg,
|
|
.recvmsg = inet_recvmsg,
|
|
.mmap = sock_no_mmap,
|
|
+ .splice_eof = inet_splice_eof,
|
|
.sendpage = inet_sendpage,
|
|
#ifdef CONFIG_COMPAT
|
|
.compat_ioctl = inet_compat_ioctl,
|
|
diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c
|
|
index 493c679ea54f3..e19ef88ae181f 100644
|
|
--- a/net/ipv4/ip_output.c
|
|
+++ b/net/ipv4/ip_output.c
|
|
@@ -990,8 +990,8 @@ static int __ip_append_data(struct sock *sk,
|
|
mtu = cork->gso_size ? IP_MAX_MTU : cork->fragsize;
|
|
paged = !!cork->gso_size;
|
|
|
|
- if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP &&
|
|
- sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)
|
|
+ if (cork->tx_flags & SKBTX_ANY_TSTAMP &&
|
|
+ READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID)
|
|
tskey = atomic_inc_return(&sk->sk_tskey) - 1;
|
|
|
|
hh_len = LL_RESERVED_SPACE(rt->dst.dev);
|
|
diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c
|
|
index 63aa52becd880..c1fb7580ea581 100644
|
|
--- a/net/ipv4/ip_sockglue.c
|
|
+++ b/net/ipv4/ip_sockglue.c
|
|
@@ -509,7 +509,7 @@ static bool ipv4_datagram_support_cmsg(const struct sock *sk,
|
|
* or without payload (SOF_TIMESTAMPING_OPT_TSONLY).
|
|
*/
|
|
info = PKTINFO_SKB_CB(skb);
|
|
- if (!(sk->sk_tsflags & SOF_TIMESTAMPING_OPT_CMSG) ||
|
|
+ if (!(READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_CMSG) ||
|
|
!info->ipi_ifindex)
|
|
return false;
|
|
|
|
diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c
|
|
index 58409ea2da0af..0b7844a8d5711 100644
|
|
--- a/net/ipv4/tcp.c
|
|
+++ b/net/ipv4/tcp.c
|
|
@@ -1492,6 +1492,22 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
|
|
}
|
|
EXPORT_SYMBOL(tcp_sendmsg);
|
|
|
|
+void tcp_splice_eof(struct socket *sock)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+ struct tcp_sock *tp = tcp_sk(sk);
|
|
+ int mss_now, size_goal;
|
|
+
|
|
+ if (!tcp_write_queue_tail(sk))
|
|
+ return;
|
|
+
|
|
+ lock_sock(sk);
|
|
+ mss_now = tcp_send_mss(sk, &size_goal, 0);
|
|
+ tcp_push(sk, 0, mss_now, tp->nonagle, size_goal);
|
|
+ release_sock(sk);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(tcp_splice_eof);
|
|
+
|
|
/*
|
|
* Handle reading urgent data. BSD has very simple semantics for
|
|
* this, no blocking and very strange errors 8)
|
|
@@ -2359,14 +2375,14 @@ void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk,
|
|
}
|
|
}
|
|
|
|
- if (sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE)
|
|
+ if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_SOFTWARE)
|
|
has_timestamping = true;
|
|
else
|
|
tss->ts[0] = (struct timespec64) {0};
|
|
}
|
|
|
|
if (tss->ts[2].tv_sec || tss->ts[2].tv_nsec) {
|
|
- if (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE)
|
|
+ if (READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_RAW_HARDWARE)
|
|
has_timestamping = true;
|
|
else
|
|
tss->ts[2] = (struct timespec64) {0};
|
|
diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c
|
|
index 7ebbbe561e402..be2c807eed15d 100644
|
|
--- a/net/ipv4/tcp_ipv4.c
|
|
+++ b/net/ipv4/tcp_ipv4.c
|
|
@@ -3067,6 +3067,7 @@ struct proto tcp_prot = {
|
|
.keepalive = tcp_set_keepalive,
|
|
.recvmsg = tcp_recvmsg,
|
|
.sendmsg = tcp_sendmsg,
|
|
+ .splice_eof = tcp_splice_eof,
|
|
.sendpage = tcp_sendpage,
|
|
.backlog_rcv = tcp_v4_do_rcv,
|
|
.release_cb = tcp_release_cb,
|
|
diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c
|
|
index 65abc92a81bd0..5672d9a86c5d2 100644
|
|
--- a/net/ipv4/udp.c
|
|
+++ b/net/ipv4/udp.c
|
|
@@ -733,7 +733,7 @@ int __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable)
|
|
iph->saddr, uh->source, skb->dev->ifindex,
|
|
inet_sdif(skb), udptable, NULL);
|
|
|
|
- if (!sk || udp_sk(sk)->encap_type) {
|
|
+ if (!sk || READ_ONCE(udp_sk(sk)->encap_type)) {
|
|
/* No socket for error: try tunnels before discarding */
|
|
if (static_branch_unlikely(&udp_encap_needed_key)) {
|
|
sk = __udp4_lib_err_encap(net, iph, uh, udptable, sk, skb,
|
|
@@ -1068,7 +1068,7 @@ int udp_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
__be16 dport;
|
|
u8 tos;
|
|
int err, is_udplite = IS_UDPLITE(sk);
|
|
- int corkreq = READ_ONCE(up->corkflag) || msg->msg_flags&MSG_MORE;
|
|
+ int corkreq = udp_test_bit(CORK, sk) || msg->msg_flags & MSG_MORE;
|
|
int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
|
|
struct sk_buff *skb;
|
|
struct ip_options_data opt_copy;
|
|
@@ -1332,57 +1332,33 @@ do_confirm:
|
|
}
|
|
EXPORT_SYMBOL(udp_sendmsg);
|
|
|
|
-int udp_sendpage(struct sock *sk, struct page *page, int offset,
|
|
- size_t size, int flags)
|
|
+void udp_splice_eof(struct socket *sock)
|
|
{
|
|
- struct inet_sock *inet = inet_sk(sk);
|
|
+ struct sock *sk = sock->sk;
|
|
struct udp_sock *up = udp_sk(sk);
|
|
- int ret;
|
|
|
|
- if (flags & MSG_SENDPAGE_NOTLAST)
|
|
- flags |= MSG_MORE;
|
|
-
|
|
- if (!up->pending) {
|
|
- struct msghdr msg = { .msg_flags = flags|MSG_MORE };
|
|
-
|
|
- /* Call udp_sendmsg to specify destination address which
|
|
- * sendpage interface can't pass.
|
|
- * This will succeed only when the socket is connected.
|
|
- */
|
|
- ret = udp_sendmsg(sk, &msg, 0);
|
|
- if (ret < 0)
|
|
- return ret;
|
|
- }
|
|
+ if (!up->pending || udp_test_bit(CORK, sk))
|
|
+ return;
|
|
|
|
lock_sock(sk);
|
|
+ if (up->pending && !udp_test_bit(CORK, sk))
|
|
+ udp_push_pending_frames(sk);
|
|
+ release_sock(sk);
|
|
+}
|
|
+EXPORT_SYMBOL_GPL(udp_splice_eof);
|
|
|
|
- if (unlikely(!up->pending)) {
|
|
- release_sock(sk);
|
|
-
|
|
- net_dbg_ratelimited("cork failed\n");
|
|
- return -EINVAL;
|
|
- }
|
|
+int udp_sendpage(struct sock *sk, struct page *page, int offset,
|
|
+ size_t size, int flags)
|
|
+{
|
|
+ struct bio_vec bvec;
|
|
+ struct msghdr msg = { .msg_flags = flags | MSG_SPLICE_PAGES };
|
|
|
|
- ret = ip_append_page(sk, &inet->cork.fl.u.ip4,
|
|
- page, offset, size, flags);
|
|
- if (ret == -EOPNOTSUPP) {
|
|
- release_sock(sk);
|
|
- return sock_no_sendpage(sk->sk_socket, page, offset,
|
|
- size, flags);
|
|
- }
|
|
- if (ret < 0) {
|
|
- udp_flush_pending_frames(sk);
|
|
- goto out;
|
|
- }
|
|
+ if (flags & MSG_SENDPAGE_NOTLAST)
|
|
+ msg.msg_flags |= MSG_MORE;
|
|
|
|
- up->len += size;
|
|
- if (!(READ_ONCE(up->corkflag) || (flags&MSG_MORE)))
|
|
- ret = udp_push_pending_frames(sk);
|
|
- if (!ret)
|
|
- ret = size;
|
|
-out:
|
|
- release_sock(sk);
|
|
- return ret;
|
|
+ bvec_set_page(&bvec, page, size, offset);
|
|
+ iov_iter_bvec(&msg.msg_iter, ITER_SOURCE, &bvec, 1, size);
|
|
+ return udp_sendmsg(sk, &msg, size);
|
|
}
|
|
|
|
#define UDP_SKB_IS_STATELESS 0x80000000
|
|
@@ -1925,7 +1901,7 @@ try_again:
|
|
(struct sockaddr *)sin);
|
|
}
|
|
|
|
- if (udp_sk(sk)->gro_enabled)
|
|
+ if (udp_test_bit(GRO_ENABLED, sk))
|
|
udp_cmsg_recv(msg, sk, skb);
|
|
|
|
if (inet->cmsg_flags)
|
|
@@ -2138,7 +2114,8 @@ static int udp_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
|
|
}
|
|
nf_reset_ct(skb);
|
|
|
|
- if (static_branch_unlikely(&udp_encap_needed_key) && up->encap_type) {
|
|
+ if (static_branch_unlikely(&udp_encap_needed_key) &&
|
|
+ READ_ONCE(up->encap_type)) {
|
|
int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
|
|
|
|
/*
|
|
@@ -2669,7 +2646,7 @@ void udp_destroy_sock(struct sock *sk)
|
|
if (encap_destroy)
|
|
encap_destroy(sk);
|
|
}
|
|
- if (up->encap_enabled)
|
|
+ if (udp_test_bit(ENCAP_ENABLED, sk))
|
|
static_branch_dec(&udp_encap_needed_key);
|
|
}
|
|
}
|
|
@@ -2697,9 +2674,9 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
|
|
switch (optname) {
|
|
case UDP_CORK:
|
|
if (val != 0) {
|
|
- WRITE_ONCE(up->corkflag, 1);
|
|
+ udp_set_bit(CORK, sk);
|
|
} else {
|
|
- WRITE_ONCE(up->corkflag, 0);
|
|
+ udp_clear_bit(CORK, sk);
|
|
lock_sock(sk);
|
|
push_pending_frames(sk);
|
|
release_sock(sk);
|
|
@@ -2723,10 +2700,8 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
|
|
#endif
|
|
fallthrough;
|
|
case UDP_ENCAP_L2TPINUDP:
|
|
- up->encap_type = val;
|
|
- lock_sock(sk);
|
|
- udp_tunnel_encap_enable(sk->sk_socket);
|
|
- release_sock(sk);
|
|
+ WRITE_ONCE(up->encap_type, val);
|
|
+ udp_tunnel_encap_enable(sk);
|
|
break;
|
|
default:
|
|
err = -ENOPROTOOPT;
|
|
@@ -2735,11 +2710,11 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
|
|
break;
|
|
|
|
case UDP_NO_CHECK6_TX:
|
|
- up->no_check6_tx = valbool;
|
|
+ udp_set_no_check6_tx(sk, valbool);
|
|
break;
|
|
|
|
case UDP_NO_CHECK6_RX:
|
|
- up->no_check6_rx = valbool;
|
|
+ udp_set_no_check6_rx(sk, valbool);
|
|
break;
|
|
|
|
case UDP_SEGMENT:
|
|
@@ -2749,14 +2724,12 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname,
|
|
break;
|
|
|
|
case UDP_GRO:
|
|
- lock_sock(sk);
|
|
|
|
/* when enabling GRO, accept the related GSO packet type */
|
|
if (valbool)
|
|
- udp_tunnel_encap_enable(sk->sk_socket);
|
|
- up->gro_enabled = valbool;
|
|
- up->accept_udp_l4 = valbool;
|
|
- release_sock(sk);
|
|
+ udp_tunnel_encap_enable(sk);
|
|
+ udp_assign_bit(GRO_ENABLED, sk, valbool);
|
|
+ udp_assign_bit(ACCEPT_L4, sk, valbool);
|
|
break;
|
|
|
|
/*
|
|
@@ -2824,19 +2797,19 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
|
|
|
|
switch (optname) {
|
|
case UDP_CORK:
|
|
- val = READ_ONCE(up->corkflag);
|
|
+ val = udp_test_bit(CORK, sk);
|
|
break;
|
|
|
|
case UDP_ENCAP:
|
|
- val = up->encap_type;
|
|
+ val = READ_ONCE(up->encap_type);
|
|
break;
|
|
|
|
case UDP_NO_CHECK6_TX:
|
|
- val = up->no_check6_tx;
|
|
+ val = udp_get_no_check6_tx(sk);
|
|
break;
|
|
|
|
case UDP_NO_CHECK6_RX:
|
|
- val = up->no_check6_rx;
|
|
+ val = udp_get_no_check6_rx(sk);
|
|
break;
|
|
|
|
case UDP_SEGMENT:
|
|
@@ -2844,7 +2817,7 @@ int udp_lib_getsockopt(struct sock *sk, int level, int optname,
|
|
break;
|
|
|
|
case UDP_GRO:
|
|
- val = up->gro_enabled;
|
|
+ val = udp_test_bit(GRO_ENABLED, sk);
|
|
break;
|
|
|
|
/* The following two cannot be changed on UDP sockets, the return is
|
|
@@ -2946,6 +2919,7 @@ struct proto udp_prot = {
|
|
.getsockopt = udp_getsockopt,
|
|
.sendmsg = udp_sendmsg,
|
|
.recvmsg = udp_recvmsg,
|
|
+ .splice_eof = udp_splice_eof,
|
|
.sendpage = udp_sendpage,
|
|
.release_cb = ip4_datagram_release_cb,
|
|
.hash = udp_lib_hash,
|
|
diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c
|
|
index 6d1a4bec2614d..8096576fd9bde 100644
|
|
--- a/net/ipv4/udp_offload.c
|
|
+++ b/net/ipv4/udp_offload.c
|
|
@@ -549,10 +549,10 @@ struct sk_buff *udp_gro_receive(struct list_head *head, struct sk_buff *skb,
|
|
NAPI_GRO_CB(skb)->is_flist = 0;
|
|
if (!sk || !udp_sk(sk)->gro_receive) {
|
|
if (skb->dev->features & NETIF_F_GRO_FRAGLIST)
|
|
- NAPI_GRO_CB(skb)->is_flist = sk ? !udp_sk(sk)->gro_enabled : 1;
|
|
+ NAPI_GRO_CB(skb)->is_flist = sk ? !udp_test_bit(GRO_ENABLED, sk) : 1;
|
|
|
|
if ((!sk && (skb->dev->features & NETIF_F_GRO_UDP_FWD)) ||
|
|
- (sk && udp_sk(sk)->gro_enabled) || NAPI_GRO_CB(skb)->is_flist)
|
|
+ (sk && udp_test_bit(GRO_ENABLED, sk)) || NAPI_GRO_CB(skb)->is_flist)
|
|
return call_gro_receive(udp_gro_receive_segment, head, skb);
|
|
|
|
/* no GRO, be sure flush the current packet */
|
|
diff --git a/net/ipv4/udp_tunnel_core.c b/net/ipv4/udp_tunnel_core.c
|
|
index 5f8104cf082d0..732e21b75ba28 100644
|
|
--- a/net/ipv4/udp_tunnel_core.c
|
|
+++ b/net/ipv4/udp_tunnel_core.c
|
|
@@ -78,7 +78,7 @@ void setup_udp_tunnel_sock(struct net *net, struct socket *sock,
|
|
udp_sk(sk)->gro_receive = cfg->gro_receive;
|
|
udp_sk(sk)->gro_complete = cfg->gro_complete;
|
|
|
|
- udp_tunnel_encap_enable(sock);
|
|
+ udp_tunnel_encap_enable(sk);
|
|
}
|
|
EXPORT_SYMBOL_GPL(setup_udp_tunnel_sock);
|
|
|
|
diff --git a/net/ipv4/xfrm4_input.c b/net/ipv4/xfrm4_input.c
|
|
index eac206a290d05..183f6dc372429 100644
|
|
--- a/net/ipv4/xfrm4_input.c
|
|
+++ b/net/ipv4/xfrm4_input.c
|
|
@@ -85,11 +85,11 @@ int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
|
|
struct udphdr *uh;
|
|
struct iphdr *iph;
|
|
int iphlen, len;
|
|
-
|
|
__u8 *udpdata;
|
|
__be32 *udpdata32;
|
|
- __u16 encap_type = up->encap_type;
|
|
+ u16 encap_type;
|
|
|
|
+ encap_type = READ_ONCE(up->encap_type);
|
|
/* if this is not encapsulated socket, then just return now */
|
|
if (!encap_type)
|
|
return 1;
|
|
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
|
|
index b5309ae87fd79..a2f29ca516000 100644
|
|
--- a/net/ipv6/af_inet6.c
|
|
+++ b/net/ipv6/af_inet6.c
|
|
@@ -711,6 +711,7 @@ const struct proto_ops inet6_stream_ops = {
|
|
#ifdef CONFIG_MMU
|
|
.mmap = tcp_mmap,
|
|
#endif
|
|
+ .splice_eof = inet_splice_eof,
|
|
.sendpage = inet_sendpage,
|
|
.sendmsg_locked = tcp_sendmsg_locked,
|
|
.sendpage_locked = tcp_sendpage_locked,
|
|
diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
|
|
index 3c2b2a85de367..e9ae084d038d1 100644
|
|
--- a/net/ipv6/ip6_output.c
|
|
+++ b/net/ipv6/ip6_output.c
|
|
@@ -1506,8 +1506,8 @@ static int __ip6_append_data(struct sock *sk,
|
|
mtu = cork->gso_size ? IP6_MAX_MTU : cork->fragsize;
|
|
orig_mtu = mtu;
|
|
|
|
- if (cork->tx_flags & SKBTX_ANY_SW_TSTAMP &&
|
|
- sk->sk_tsflags & SOF_TIMESTAMPING_OPT_ID)
|
|
+ if (cork->tx_flags & SKBTX_ANY_TSTAMP &&
|
|
+ READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_OPT_ID)
|
|
tskey = atomic_inc_return(&sk->sk_tskey) - 1;
|
|
|
|
hh_len = LL_RESERVED_SPACE(rt->dst.dev);
|
|
diff --git a/net/ipv6/ping.c b/net/ipv6/ping.c
|
|
index 4d5a27dd9a4b2..a5d7d1915ba7e 100644
|
|
--- a/net/ipv6/ping.c
|
|
+++ b/net/ipv6/ping.c
|
|
@@ -119,7 +119,7 @@ static int ping_v6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
return -EINVAL;
|
|
|
|
ipcm6_init_sk(&ipc6, np);
|
|
- ipc6.sockc.tsflags = sk->sk_tsflags;
|
|
+ ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags);
|
|
ipc6.sockc.mark = READ_ONCE(sk->sk_mark);
|
|
|
|
fl6.flowi6_oif = oif;
|
|
diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c
|
|
index df3abd9e5237c..dc31752a7edcc 100644
|
|
--- a/net/ipv6/raw.c
|
|
+++ b/net/ipv6/raw.c
|
|
@@ -776,7 +776,7 @@ static int rawv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
fl6.flowi6_uid = sk->sk_uid;
|
|
|
|
ipcm6_init(&ipc6);
|
|
- ipc6.sockc.tsflags = sk->sk_tsflags;
|
|
+ ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags);
|
|
ipc6.sockc.mark = fl6.flowi6_mark;
|
|
|
|
if (sin6) {
|
|
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c
|
|
index 7be89dcfd5fc5..ba9a22db5805c 100644
|
|
--- a/net/ipv6/tcp_ipv6.c
|
|
+++ b/net/ipv6/tcp_ipv6.c
|
|
@@ -2158,6 +2158,7 @@ struct proto tcpv6_prot = {
|
|
.keepalive = tcp_set_keepalive,
|
|
.recvmsg = tcp_recvmsg,
|
|
.sendmsg = tcp_sendmsg,
|
|
+ .splice_eof = tcp_splice_eof,
|
|
.sendpage = tcp_sendpage,
|
|
.backlog_rcv = tcp_v6_do_rcv,
|
|
.release_cb = tcp_release_cb,
|
|
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
|
|
index 64b36c2ba774a..961106eda69d0 100644
|
|
--- a/net/ipv6/udp.c
|
|
+++ b/net/ipv6/udp.c
|
|
@@ -440,7 +440,7 @@ try_again:
|
|
(struct sockaddr *)sin6);
|
|
}
|
|
|
|
- if (udp_sk(sk)->gro_enabled)
|
|
+ if (udp_test_bit(GRO_ENABLED, sk))
|
|
udp_cmsg_recv(msg, sk, skb);
|
|
|
|
if (np->rxopt.all)
|
|
@@ -598,7 +598,7 @@ int __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt,
|
|
sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source,
|
|
inet6_iif(skb), inet6_sdif(skb), udptable, NULL);
|
|
|
|
- if (!sk || udp_sk(sk)->encap_type) {
|
|
+ if (!sk || READ_ONCE(udp_sk(sk)->encap_type)) {
|
|
/* No socket for error: try tunnels before discarding */
|
|
if (static_branch_unlikely(&udpv6_encap_needed_key)) {
|
|
sk = __udp6_lib_err_encap(net, hdr, offset, uh,
|
|
@@ -712,7 +712,8 @@ static int udpv6_queue_rcv_one_skb(struct sock *sk, struct sk_buff *skb)
|
|
}
|
|
nf_reset_ct(skb);
|
|
|
|
- if (static_branch_unlikely(&udpv6_encap_needed_key) && up->encap_type) {
|
|
+ if (static_branch_unlikely(&udpv6_encap_needed_key) &&
|
|
+ READ_ONCE(up->encap_type)) {
|
|
int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);
|
|
|
|
/*
|
|
@@ -882,7 +883,7 @@ start_lookup:
|
|
/* If zero checksum and no_check is not on for
|
|
* the socket then skip it.
|
|
*/
|
|
- if (!uh->check && !udp_sk(sk)->no_check6_rx)
|
|
+ if (!uh->check && !udp_get_no_check6_rx(sk))
|
|
continue;
|
|
if (!first) {
|
|
first = sk;
|
|
@@ -1000,7 +1001,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
|
|
if (unlikely(rcu_dereference(sk->sk_rx_dst) != dst))
|
|
udp6_sk_rx_dst_set(sk, dst);
|
|
|
|
- if (!uh->check && !udp_sk(sk)->no_check6_rx) {
|
|
+ if (!uh->check && !udp_get_no_check6_rx(sk)) {
|
|
if (refcounted)
|
|
sock_put(sk);
|
|
goto report_csum_error;
|
|
@@ -1022,7 +1023,7 @@ int __udp6_lib_rcv(struct sk_buff *skb, struct udp_table *udptable,
|
|
/* Unicast */
|
|
sk = __udp6_lib_lookup_skb(skb, uh->source, uh->dest, udptable);
|
|
if (sk) {
|
|
- if (!uh->check && !udp_sk(sk)->no_check6_rx)
|
|
+ if (!uh->check && !udp_get_no_check6_rx(sk))
|
|
goto report_csum_error;
|
|
return udp6_unicast_rcv_skb(sk, skb, uh);
|
|
}
|
|
@@ -1260,7 +1261,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6,
|
|
kfree_skb(skb);
|
|
return -EINVAL;
|
|
}
|
|
- if (udp_sk(sk)->no_check6_tx) {
|
|
+ if (udp_get_no_check6_tx(sk)) {
|
|
kfree_skb(skb);
|
|
return -EINVAL;
|
|
}
|
|
@@ -1281,7 +1282,7 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6,
|
|
|
|
if (is_udplite)
|
|
csum = udplite_csum(skb);
|
|
- else if (udp_sk(sk)->no_check6_tx) { /* UDP csum disabled */
|
|
+ else if (udp_get_no_check6_tx(sk)) { /* UDP csum disabled */
|
|
skb->ip_summed = CHECKSUM_NONE;
|
|
goto send;
|
|
} else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */
|
|
@@ -1351,14 +1352,14 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)
|
|
int addr_len = msg->msg_namelen;
|
|
bool connected = false;
|
|
int ulen = len;
|
|
- int corkreq = READ_ONCE(up->corkflag) || msg->msg_flags&MSG_MORE;
|
|
+ int corkreq = udp_test_bit(CORK, sk) || msg->msg_flags & MSG_MORE;
|
|
int err;
|
|
int is_udplite = IS_UDPLITE(sk);
|
|
int (*getfrag)(void *, char *, int, int, int, struct sk_buff *);
|
|
|
|
ipcm6_init(&ipc6);
|
|
ipc6.gso_size = READ_ONCE(up->gso_size);
|
|
- ipc6.sockc.tsflags = sk->sk_tsflags;
|
|
+ ipc6.sockc.tsflags = READ_ONCE(sk->sk_tsflags);
|
|
ipc6.sockc.mark = READ_ONCE(sk->sk_mark);
|
|
|
|
/* destination address check */
|
|
@@ -1657,6 +1658,20 @@ do_confirm:
|
|
goto out;
|
|
}
|
|
|
|
+static void udpv6_splice_eof(struct socket *sock)
|
|
+{
|
|
+ struct sock *sk = sock->sk;
|
|
+ struct udp_sock *up = udp_sk(sk);
|
|
+
|
|
+ if (!up->pending || udp_test_bit(CORK, sk))
|
|
+ return;
|
|
+
|
|
+ lock_sock(sk);
|
|
+ if (up->pending && !udp_test_bit(CORK, sk))
|
|
+ udp_v6_push_pending_frames(sk);
|
|
+ release_sock(sk);
|
|
+}
|
|
+
|
|
void udpv6_destroy_sock(struct sock *sk)
|
|
{
|
|
struct udp_sock *up = udp_sk(sk);
|
|
@@ -1674,7 +1689,7 @@ void udpv6_destroy_sock(struct sock *sk)
|
|
if (encap_destroy)
|
|
encap_destroy(sk);
|
|
}
|
|
- if (up->encap_enabled) {
|
|
+ if (udp_test_bit(ENCAP_ENABLED, sk)) {
|
|
static_branch_dec(&udpv6_encap_needed_key);
|
|
udp_encap_disable();
|
|
}
|
|
@@ -1768,6 +1783,7 @@ struct proto udpv6_prot = {
|
|
.getsockopt = udpv6_getsockopt,
|
|
.sendmsg = udpv6_sendmsg,
|
|
.recvmsg = udpv6_recvmsg,
|
|
+ .splice_eof = udpv6_splice_eof,
|
|
.release_cb = ip6_datagram_release_cb,
|
|
.hash = udp_lib_hash,
|
|
.unhash = udp_lib_unhash,
|
|
diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c
|
|
index 4907ab241d6be..4156387248e40 100644
|
|
--- a/net/ipv6/xfrm6_input.c
|
|
+++ b/net/ipv6/xfrm6_input.c
|
|
@@ -81,14 +81,14 @@ int xfrm6_udp_encap_rcv(struct sock *sk, struct sk_buff *skb)
|
|
struct ipv6hdr *ip6h;
|
|
int len;
|
|
int ip6hlen = sizeof(struct ipv6hdr);
|
|
-
|
|
__u8 *udpdata;
|
|
__be32 *udpdata32;
|
|
- __u16 encap_type = up->encap_type;
|
|
+ u16 encap_type;
|
|
|
|
if (skb->protocol == htons(ETH_P_IP))
|
|
return xfrm4_udp_encap_rcv(sk, skb);
|
|
|
|
+ encap_type = READ_ONCE(up->encap_type);
|
|
/* if this is not encapsulated socket, then just return now */
|
|
if (!encap_type)
|
|
return 1;
|
|
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c
|
|
index 03608d3ded4b8..8d21ff25f1602 100644
|
|
--- a/net/l2tp/l2tp_core.c
|
|
+++ b/net/l2tp/l2tp_core.c
|
|
@@ -1139,9 +1139,9 @@ static void l2tp_tunnel_destruct(struct sock *sk)
|
|
switch (tunnel->encap) {
|
|
case L2TP_ENCAPTYPE_UDP:
|
|
/* No longer an encapsulation socket. See net/ipv4/udp.c */
|
|
- (udp_sk(sk))->encap_type = 0;
|
|
- (udp_sk(sk))->encap_rcv = NULL;
|
|
- (udp_sk(sk))->encap_destroy = NULL;
|
|
+ WRITE_ONCE(udp_sk(sk)->encap_type, 0);
|
|
+ udp_sk(sk)->encap_rcv = NULL;
|
|
+ udp_sk(sk)->encap_destroy = NULL;
|
|
break;
|
|
case L2TP_ENCAPTYPE_IP:
|
|
break;
|
|
diff --git a/net/mptcp/subflow.c b/net/mptcp/subflow.c
|
|
index d611783c2601f..8ed7769cae836 100644
|
|
--- a/net/mptcp/subflow.c
|
|
+++ b/net/mptcp/subflow.c
|
|
@@ -1899,6 +1899,17 @@ static void tcp_release_cb_override(struct sock *ssk)
|
|
tcp_release_cb(ssk);
|
|
}
|
|
|
|
+static int tcp_abort_override(struct sock *ssk, int err)
|
|
+{
|
|
+ /* closing a listener subflow requires a great deal of care.
|
|
+ * keep it simple and just prevent such operation
|
|
+ */
|
|
+ if (inet_sk_state_load(ssk) == TCP_LISTEN)
|
|
+ return -EINVAL;
|
|
+
|
|
+ return tcp_abort(ssk, err);
|
|
+}
|
|
+
|
|
static struct tcp_ulp_ops subflow_ulp_ops __read_mostly = {
|
|
.name = "mptcp",
|
|
.owner = THIS_MODULE,
|
|
@@ -1942,6 +1953,7 @@ void __init mptcp_subflow_init(void)
|
|
|
|
tcp_prot_override = tcp_prot;
|
|
tcp_prot_override.release_cb = tcp_release_cb_override;
|
|
+ tcp_prot_override.diag_destroy = tcp_abort_override;
|
|
|
|
#if IS_ENABLED(CONFIG_MPTCP_IPV6)
|
|
/* In struct mptcp_subflow_request_sock, we assume the TCP request sock
|
|
@@ -1977,6 +1989,7 @@ void __init mptcp_subflow_init(void)
|
|
|
|
tcpv6_prot_override = tcpv6_prot;
|
|
tcpv6_prot_override.release_cb = tcp_release_cb_override;
|
|
+ tcpv6_prot_override.diag_destroy = tcp_abort_override;
|
|
#endif
|
|
|
|
mptcp_diag_subflow_init(&subflow_ulp_ops);
|
|
diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c
|
|
index 7243079ef3546..b452eb3ddcecb 100644
|
|
--- a/net/netfilter/ipvs/ip_vs_xmit.c
|
|
+++ b/net/netfilter/ipvs/ip_vs_xmit.c
|
|
@@ -994,7 +994,7 @@ ip_vs_prepare_tunneled_skb(struct sk_buff *skb, int skb_af,
|
|
old_dsfield = ipv4_get_dsfield(old_iph);
|
|
*ttl = old_iph->ttl;
|
|
if (payload_len)
|
|
- *payload_len = ntohs(old_iph->tot_len);
|
|
+ *payload_len = skb_ip_totlen(skb);
|
|
}
|
|
|
|
/* Implement full-functionality option for ECN encapsulation */
|
|
diff --git a/net/netfilter/nf_flow_table_core.c b/net/netfilter/nf_flow_table_core.c
|
|
index 81c26a96c30bb..c1d99cb370b44 100644
|
|
--- a/net/netfilter/nf_flow_table_core.c
|
|
+++ b/net/netfilter/nf_flow_table_core.c
|
|
@@ -314,12 +314,12 @@ int flow_offload_add(struct nf_flowtable *flow_table, struct flow_offload *flow)
|
|
EXPORT_SYMBOL_GPL(flow_offload_add);
|
|
|
|
void flow_offload_refresh(struct nf_flowtable *flow_table,
|
|
- struct flow_offload *flow)
|
|
+ struct flow_offload *flow, bool force)
|
|
{
|
|
u32 timeout;
|
|
|
|
timeout = nf_flowtable_time_stamp + flow_offload_get_timeout(flow);
|
|
- if (timeout - READ_ONCE(flow->timeout) > HZ)
|
|
+ if (force || timeout - READ_ONCE(flow->timeout) > HZ)
|
|
WRITE_ONCE(flow->timeout, timeout);
|
|
else
|
|
return;
|
|
@@ -416,11 +416,18 @@ nf_flow_table_iterate(struct nf_flowtable *flow_table,
|
|
return err;
|
|
}
|
|
|
|
+static bool nf_flow_custom_gc(struct nf_flowtable *flow_table,
|
|
+ const struct flow_offload *flow)
|
|
+{
|
|
+ return flow_table->type->gc && flow_table->type->gc(flow);
|
|
+}
|
|
+
|
|
static void nf_flow_offload_gc_step(struct nf_flowtable *flow_table,
|
|
struct flow_offload *flow, void *data)
|
|
{
|
|
if (nf_flow_has_expired(flow) ||
|
|
- nf_ct_is_dying(flow->ct))
|
|
+ nf_ct_is_dying(flow->ct) ||
|
|
+ nf_flow_custom_gc(flow_table, flow))
|
|
flow_offload_teardown(flow);
|
|
|
|
if (test_bit(NF_FLOW_TEARDOWN, &flow->flags)) {
|
|
diff --git a/net/netfilter/nf_flow_table_inet.c b/net/netfilter/nf_flow_table_inet.c
|
|
index 0ccabf3fa6aa3..9505f9d188ff2 100644
|
|
--- a/net/netfilter/nf_flow_table_inet.c
|
|
+++ b/net/netfilter/nf_flow_table_inet.c
|
|
@@ -39,7 +39,7 @@ nf_flow_offload_inet_hook(void *priv, struct sk_buff *skb,
|
|
}
|
|
|
|
static int nf_flow_rule_route_inet(struct net *net,
|
|
- const struct flow_offload *flow,
|
|
+ struct flow_offload *flow,
|
|
enum flow_offload_tuple_dir dir,
|
|
struct nf_flow_rule *flow_rule)
|
|
{
|
|
diff --git a/net/netfilter/nf_flow_table_ip.c b/net/netfilter/nf_flow_table_ip.c
|
|
index b350fe9d00b0b..6feaac9ab05c8 100644
|
|
--- a/net/netfilter/nf_flow_table_ip.c
|
|
+++ b/net/netfilter/nf_flow_table_ip.c
|
|
@@ -384,7 +384,7 @@ nf_flow_offload_ip_hook(void *priv, struct sk_buff *skb,
|
|
if (skb_try_make_writable(skb, thoff + hdrsize))
|
|
return NF_DROP;
|
|
|
|
- flow_offload_refresh(flow_table, flow);
|
|
+ flow_offload_refresh(flow_table, flow, false);
|
|
|
|
nf_flow_encap_pop(skb, tuplehash);
|
|
thoff -= offset;
|
|
@@ -646,7 +646,7 @@ nf_flow_offload_ipv6_hook(void *priv, struct sk_buff *skb,
|
|
if (skb_try_make_writable(skb, thoff + hdrsize))
|
|
return NF_DROP;
|
|
|
|
- flow_offload_refresh(flow_table, flow);
|
|
+ flow_offload_refresh(flow_table, flow, false);
|
|
|
|
nf_flow_encap_pop(skb, tuplehash);
|
|
|
|
diff --git a/net/netfilter/nf_flow_table_offload.c b/net/netfilter/nf_flow_table_offload.c
|
|
index 4d9b99abe37d6..1c26f03fc6617 100644
|
|
--- a/net/netfilter/nf_flow_table_offload.c
|
|
+++ b/net/netfilter/nf_flow_table_offload.c
|
|
@@ -679,7 +679,7 @@ nf_flow_rule_route_common(struct net *net, const struct flow_offload *flow,
|
|
return 0;
|
|
}
|
|
|
|
-int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
|
|
+int nf_flow_rule_route_ipv4(struct net *net, struct flow_offload *flow,
|
|
enum flow_offload_tuple_dir dir,
|
|
struct nf_flow_rule *flow_rule)
|
|
{
|
|
@@ -704,7 +704,7 @@ int nf_flow_rule_route_ipv4(struct net *net, const struct flow_offload *flow,
|
|
}
|
|
EXPORT_SYMBOL_GPL(nf_flow_rule_route_ipv4);
|
|
|
|
-int nf_flow_rule_route_ipv6(struct net *net, const struct flow_offload *flow,
|
|
+int nf_flow_rule_route_ipv6(struct net *net, struct flow_offload *flow,
|
|
enum flow_offload_tuple_dir dir,
|
|
struct nf_flow_rule *flow_rule)
|
|
{
|
|
@@ -735,7 +735,7 @@ nf_flow_offload_rule_alloc(struct net *net,
|
|
{
|
|
const struct nf_flowtable *flowtable = offload->flowtable;
|
|
const struct flow_offload_tuple *tuple, *other_tuple;
|
|
- const struct flow_offload *flow = offload->flow;
|
|
+ struct flow_offload *flow = offload->flow;
|
|
struct dst_entry *other_dst = NULL;
|
|
struct nf_flow_rule *flow_rule;
|
|
int err = -ENOMEM;
|
|
@@ -895,8 +895,9 @@ static int flow_offload_rule_add(struct flow_offload_work *offload,
|
|
|
|
ok_count += flow_offload_tuple_add(offload, flow_rule[0],
|
|
FLOW_OFFLOAD_DIR_ORIGINAL);
|
|
- ok_count += flow_offload_tuple_add(offload, flow_rule[1],
|
|
- FLOW_OFFLOAD_DIR_REPLY);
|
|
+ if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
|
|
+ ok_count += flow_offload_tuple_add(offload, flow_rule[1],
|
|
+ FLOW_OFFLOAD_DIR_REPLY);
|
|
if (ok_count == 0)
|
|
return -ENOENT;
|
|
|
|
@@ -926,7 +927,8 @@ static void flow_offload_work_del(struct flow_offload_work *offload)
|
|
{
|
|
clear_bit(IPS_HW_OFFLOAD_BIT, &offload->flow->ct->status);
|
|
flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_ORIGINAL);
|
|
- flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY);
|
|
+ if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
|
|
+ flow_offload_tuple_del(offload, FLOW_OFFLOAD_DIR_REPLY);
|
|
set_bit(NF_FLOW_HW_DEAD, &offload->flow->flags);
|
|
}
|
|
|
|
@@ -946,7 +948,9 @@ static void flow_offload_work_stats(struct flow_offload_work *offload)
|
|
u64 lastused;
|
|
|
|
flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_ORIGINAL, &stats[0]);
|
|
- flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_REPLY, &stats[1]);
|
|
+ if (test_bit(NF_FLOW_HW_BIDIRECTIONAL, &offload->flow->flags))
|
|
+ flow_offload_tuple_stats(offload, FLOW_OFFLOAD_DIR_REPLY,
|
|
+ &stats[1]);
|
|
|
|
lastused = max_t(u64, stats[0].lastused, stats[1].lastused);
|
|
offload->flow->timeout = max_t(u64, offload->flow->timeout,
|
|
diff --git a/net/netfilter/nf_log_syslog.c b/net/netfilter/nf_log_syslog.c
|
|
index cb894f0d63e9d..c66689ad2b491 100644
|
|
--- a/net/netfilter/nf_log_syslog.c
|
|
+++ b/net/netfilter/nf_log_syslog.c
|
|
@@ -322,7 +322,7 @@ dump_ipv4_packet(struct net *net, struct nf_log_buf *m,
|
|
|
|
/* Max length: 46 "LEN=65535 TOS=0xFF PREC=0xFF TTL=255 ID=65535 " */
|
|
nf_log_buf_add(m, "LEN=%u TOS=0x%02X PREC=0x%02X TTL=%u ID=%u ",
|
|
- ntohs(ih->tot_len), ih->tos & IPTOS_TOS_MASK,
|
|
+ iph_totlen(skb, ih), ih->tos & IPTOS_TOS_MASK,
|
|
ih->tos & IPTOS_PREC_MASK, ih->ttl, ntohs(ih->id));
|
|
|
|
/* Max length: 6 "CE DF MF " */
|
|
diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c
|
|
index cee3e4e905ec8..e0c117229ee9d 100644
|
|
--- a/net/netfilter/nf_tables_core.c
|
|
+++ b/net/netfilter/nf_tables_core.c
|
|
@@ -141,7 +141,7 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr,
|
|
else {
|
|
if (!(pkt->flags & NFT_PKTINFO_L4PROTO))
|
|
return false;
|
|
- ptr = skb_network_header(skb) + nft_thoff(pkt);
|
|
+ ptr = skb->data + nft_thoff(pkt);
|
|
}
|
|
|
|
ptr += priv->offset;
|
|
diff --git a/net/netfilter/nft_immediate.c b/net/netfilter/nft_immediate.c
|
|
index 5f59dbab3e933..55fcf0280c5c3 100644
|
|
--- a/net/netfilter/nft_immediate.c
|
|
+++ b/net/netfilter/nft_immediate.c
|
|
@@ -78,7 +78,7 @@ static int nft_immediate_init(const struct nft_ctx *ctx,
|
|
case NFT_GOTO:
|
|
err = nf_tables_bind_chain(ctx, chain);
|
|
if (err < 0)
|
|
- return err;
|
|
+ goto err1;
|
|
break;
|
|
default:
|
|
break;
|
|
diff --git a/net/netfilter/xt_length.c b/net/netfilter/xt_length.c
|
|
index 9fbfad13176f0..ca730cedb5d41 100644
|
|
--- a/net/netfilter/xt_length.c
|
|
+++ b/net/netfilter/xt_length.c
|
|
@@ -21,7 +21,7 @@ static bool
|
|
length_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
|
{
|
|
const struct xt_length_info *info = par->matchinfo;
|
|
- u_int16_t pktlen = ntohs(ip_hdr(skb)->tot_len);
|
|
+ u32 pktlen = skb_ip_totlen(skb);
|
|
|
|
return (pktlen >= info->min && pktlen <= info->max) ^ info->invert;
|
|
}
|
|
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c
|
|
index 1dac28136e6a3..18be13fb9b75a 100644
|
|
--- a/net/nfc/llcp_core.c
|
|
+++ b/net/nfc/llcp_core.c
|
|
@@ -145,6 +145,13 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool device,
|
|
|
|
static struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)
|
|
{
|
|
+ /* Since using nfc_llcp_local may result in usage of nfc_dev, whenever
|
|
+ * we hold a reference to local, we also need to hold a reference to
|
|
+ * the device to avoid UAF.
|
|
+ */
|
|
+ if (!nfc_get_device(local->dev->idx))
|
|
+ return NULL;
|
|
+
|
|
kref_get(&local->ref);
|
|
|
|
return local;
|
|
@@ -177,10 +184,18 @@ static void local_release(struct kref *ref)
|
|
|
|
int nfc_llcp_local_put(struct nfc_llcp_local *local)
|
|
{
|
|
+ struct nfc_dev *dev;
|
|
+ int ret;
|
|
+
|
|
if (local == NULL)
|
|
return 0;
|
|
|
|
- return kref_put(&local->ref, local_release);
|
|
+ dev = local->dev;
|
|
+
|
|
+ ret = kref_put(&local->ref, local_release);
|
|
+ nfc_put_device(dev);
|
|
+
|
|
+ return ret;
|
|
}
|
|
|
|
static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
|
|
@@ -959,8 +974,17 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,
|
|
}
|
|
|
|
new_sock = nfc_llcp_sock(new_sk);
|
|
- new_sock->dev = local->dev;
|
|
+
|
|
new_sock->local = nfc_llcp_local_get(local);
|
|
+ if (!new_sock->local) {
|
|
+ reason = LLCP_DM_REJ;
|
|
+ sock_put(&new_sock->sk);
|
|
+ release_sock(&sock->sk);
|
|
+ sock_put(&sock->sk);
|
|
+ goto fail;
|
|
+ }
|
|
+
|
|
+ new_sock->dev = local->dev;
|
|
new_sock->rw = sock->rw;
|
|
new_sock->miux = sock->miux;
|
|
new_sock->nfc_protocol = sock->nfc_protocol;
|
|
@@ -1597,7 +1621,16 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)
|
|
if (local == NULL)
|
|
return -ENOMEM;
|
|
|
|
- local->dev = ndev;
|
|
+ /* As we are going to initialize local's refcount, we need to get the
|
|
+ * nfc_dev to avoid UAF, otherwise there is no point in continuing.
|
|
+ * See nfc_llcp_local_get().
|
|
+ */
|
|
+ local->dev = nfc_get_device(ndev->idx);
|
|
+ if (!local->dev) {
|
|
+ kfree(local);
|
|
+ return -ENODEV;
|
|
+ }
|
|
+
|
|
INIT_LIST_HEAD(&local->list);
|
|
kref_init(&local->ref);
|
|
mutex_init(&local->sdp_lock);
|
|
diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c
|
|
index c8eaf4234b2e0..0591cfb289d50 100644
|
|
--- a/net/openvswitch/conntrack.c
|
|
+++ b/net/openvswitch/conntrack.c
|
|
@@ -1252,7 +1252,7 @@ static int ovs_ct_commit(struct net *net, struct sw_flow_key *key,
|
|
if (err)
|
|
return err;
|
|
|
|
- nf_conn_act_ct_ext_add(ct);
|
|
+ nf_conn_act_ct_ext_add(skb, ct, ctinfo);
|
|
} else if (IS_ENABLED(CONFIG_NF_CONNTRACK_LABELS) &&
|
|
labels_nonzero(&info->labels.mask)) {
|
|
err = ovs_ct_set_labels(ct, key, &info->labels.value,
|
|
diff --git a/net/sched/act_ct.c b/net/sched/act_ct.c
|
|
index 4c7f7861ea967..d6d33f854050a 100644
|
|
--- a/net/sched/act_ct.c
|
|
+++ b/net/sched/act_ct.c
|
|
@@ -168,11 +168,11 @@ tcf_ct_flow_table_add_action_nat_udp(const struct nf_conntrack_tuple *tuple,
|
|
|
|
static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct,
|
|
enum ip_conntrack_dir dir,
|
|
+ enum ip_conntrack_info ctinfo,
|
|
struct flow_action *action)
|
|
{
|
|
struct nf_conn_labels *ct_labels;
|
|
struct flow_action_entry *entry;
|
|
- enum ip_conntrack_info ctinfo;
|
|
u32 *act_ct_labels;
|
|
|
|
entry = tcf_ct_flow_table_flow_action_get_next(action);
|
|
@@ -180,8 +180,6 @@ static void tcf_ct_flow_table_add_action_meta(struct nf_conn *ct,
|
|
#if IS_ENABLED(CONFIG_NF_CONNTRACK_MARK)
|
|
entry->ct_metadata.mark = READ_ONCE(ct->mark);
|
|
#endif
|
|
- ctinfo = dir == IP_CT_DIR_ORIGINAL ? IP_CT_ESTABLISHED :
|
|
- IP_CT_ESTABLISHED_REPLY;
|
|
/* aligns with the CT reference on the SKB nf_ct_set */
|
|
entry->ct_metadata.cookie = (unsigned long)ct | ctinfo;
|
|
entry->ct_metadata.orig_dir = dir == IP_CT_DIR_ORIGINAL;
|
|
@@ -235,22 +233,26 @@ static int tcf_ct_flow_table_add_action_nat(struct net *net,
|
|
}
|
|
|
|
static int tcf_ct_flow_table_fill_actions(struct net *net,
|
|
- const struct flow_offload *flow,
|
|
+ struct flow_offload *flow,
|
|
enum flow_offload_tuple_dir tdir,
|
|
struct nf_flow_rule *flow_rule)
|
|
{
|
|
struct flow_action *action = &flow_rule->rule->action;
|
|
int num_entries = action->num_entries;
|
|
struct nf_conn *ct = flow->ct;
|
|
+ enum ip_conntrack_info ctinfo;
|
|
enum ip_conntrack_dir dir;
|
|
int i, err;
|
|
|
|
switch (tdir) {
|
|
case FLOW_OFFLOAD_DIR_ORIGINAL:
|
|
dir = IP_CT_DIR_ORIGINAL;
|
|
+ ctinfo = IP_CT_ESTABLISHED;
|
|
+ set_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags);
|
|
break;
|
|
case FLOW_OFFLOAD_DIR_REPLY:
|
|
dir = IP_CT_DIR_REPLY;
|
|
+ ctinfo = IP_CT_ESTABLISHED_REPLY;
|
|
break;
|
|
default:
|
|
return -EOPNOTSUPP;
|
|
@@ -260,7 +262,7 @@ static int tcf_ct_flow_table_fill_actions(struct net *net,
|
|
if (err)
|
|
goto err_nat;
|
|
|
|
- tcf_ct_flow_table_add_action_meta(ct, dir, action);
|
|
+ tcf_ct_flow_table_add_action_meta(ct, dir, ctinfo, action);
|
|
return 0;
|
|
|
|
err_nat:
|
|
@@ -272,8 +274,39 @@ err_nat:
|
|
return err;
|
|
}
|
|
|
|
+static bool tcf_ct_flow_is_outdated(const struct flow_offload *flow)
|
|
+{
|
|
+ return test_bit(IPS_SEEN_REPLY_BIT, &flow->ct->status) &&
|
|
+ test_bit(IPS_HW_OFFLOAD_BIT, &flow->ct->status) &&
|
|
+ !test_bit(NF_FLOW_HW_PENDING, &flow->flags) &&
|
|
+ !test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags);
|
|
+}
|
|
+
|
|
+static void tcf_ct_flow_table_get_ref(struct tcf_ct_flow_table *ct_ft);
|
|
+
|
|
+static void tcf_ct_nf_get(struct nf_flowtable *ft)
|
|
+{
|
|
+ struct tcf_ct_flow_table *ct_ft =
|
|
+ container_of(ft, struct tcf_ct_flow_table, nf_ft);
|
|
+
|
|
+ tcf_ct_flow_table_get_ref(ct_ft);
|
|
+}
|
|
+
|
|
+static void tcf_ct_flow_table_put(struct tcf_ct_flow_table *ct_ft);
|
|
+
|
|
+static void tcf_ct_nf_put(struct nf_flowtable *ft)
|
|
+{
|
|
+ struct tcf_ct_flow_table *ct_ft =
|
|
+ container_of(ft, struct tcf_ct_flow_table, nf_ft);
|
|
+
|
|
+ tcf_ct_flow_table_put(ct_ft);
|
|
+}
|
|
+
|
|
static struct nf_flowtable_type flowtable_ct = {
|
|
+ .gc = tcf_ct_flow_is_outdated,
|
|
.action = tcf_ct_flow_table_fill_actions,
|
|
+ .get = tcf_ct_nf_get,
|
|
+ .put = tcf_ct_nf_put,
|
|
.owner = THIS_MODULE,
|
|
};
|
|
|
|
@@ -322,9 +355,13 @@ err_alloc:
|
|
return err;
|
|
}
|
|
|
|
+static void tcf_ct_flow_table_get_ref(struct tcf_ct_flow_table *ct_ft)
|
|
+{
|
|
+ refcount_inc(&ct_ft->ref);
|
|
+}
|
|
+
|
|
static void tcf_ct_flow_table_cleanup_work(struct work_struct *work)
|
|
{
|
|
- struct flow_block_cb *block_cb, *tmp_cb;
|
|
struct tcf_ct_flow_table *ct_ft;
|
|
struct flow_block *block;
|
|
|
|
@@ -332,24 +369,18 @@ static void tcf_ct_flow_table_cleanup_work(struct work_struct *work)
|
|
rwork);
|
|
nf_flow_table_free(&ct_ft->nf_ft);
|
|
|
|
- /* Remove any remaining callbacks before cleanup */
|
|
block = &ct_ft->nf_ft.flow_block;
|
|
down_write(&ct_ft->nf_ft.flow_block_lock);
|
|
- list_for_each_entry_safe(block_cb, tmp_cb, &block->cb_list, list) {
|
|
- list_del(&block_cb->list);
|
|
- flow_block_cb_free(block_cb);
|
|
- }
|
|
+ WARN_ON(!list_empty(&block->cb_list));
|
|
up_write(&ct_ft->nf_ft.flow_block_lock);
|
|
kfree(ct_ft);
|
|
|
|
module_put(THIS_MODULE);
|
|
}
|
|
|
|
-static void tcf_ct_flow_table_put(struct tcf_ct_params *params)
|
|
+static void tcf_ct_flow_table_put(struct tcf_ct_flow_table *ct_ft)
|
|
{
|
|
- struct tcf_ct_flow_table *ct_ft = params->ct_ft;
|
|
-
|
|
- if (refcount_dec_and_test(¶ms->ct_ft->ref)) {
|
|
+ if (refcount_dec_and_test(&ct_ft->ref)) {
|
|
rhashtable_remove_fast(&zones_ht, &ct_ft->node, zones_params);
|
|
INIT_RCU_WORK(&ct_ft->rwork, tcf_ct_flow_table_cleanup_work);
|
|
queue_rcu_work(act_ct_wq, &ct_ft->rwork);
|
|
@@ -363,9 +394,20 @@ static void tcf_ct_flow_tc_ifidx(struct flow_offload *entry,
|
|
entry->tuplehash[dir].tuple.tc.iifidx = act_ct_ext->ifindex[dir];
|
|
}
|
|
|
|
+static void tcf_ct_flow_ct_ext_ifidx_update(struct flow_offload *entry)
|
|
+{
|
|
+ struct nf_conn_act_ct_ext *act_ct_ext;
|
|
+
|
|
+ act_ct_ext = nf_conn_act_ct_ext_find(entry->ct);
|
|
+ if (act_ct_ext) {
|
|
+ tcf_ct_flow_tc_ifidx(entry, act_ct_ext, FLOW_OFFLOAD_DIR_ORIGINAL);
|
|
+ tcf_ct_flow_tc_ifidx(entry, act_ct_ext, FLOW_OFFLOAD_DIR_REPLY);
|
|
+ }
|
|
+}
|
|
+
|
|
static void tcf_ct_flow_table_add(struct tcf_ct_flow_table *ct_ft,
|
|
struct nf_conn *ct,
|
|
- bool tcp)
|
|
+ bool tcp, bool bidirectional)
|
|
{
|
|
struct nf_conn_act_ct_ext *act_ct_ext;
|
|
struct flow_offload *entry;
|
|
@@ -384,6 +426,8 @@ static void tcf_ct_flow_table_add(struct tcf_ct_flow_table *ct_ft,
|
|
ct->proto.tcp.seen[0].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
|
|
ct->proto.tcp.seen[1].flags |= IP_CT_TCP_FLAG_BE_LIBERAL;
|
|
}
|
|
+ if (bidirectional)
|
|
+ __set_bit(NF_FLOW_HW_BIDIRECTIONAL, &entry->flags);
|
|
|
|
act_ct_ext = nf_conn_act_ct_ext_find(ct);
|
|
if (act_ct_ext) {
|
|
@@ -407,26 +451,34 @@ static void tcf_ct_flow_table_process_conn(struct tcf_ct_flow_table *ct_ft,
|
|
struct nf_conn *ct,
|
|
enum ip_conntrack_info ctinfo)
|
|
{
|
|
- bool tcp = false;
|
|
-
|
|
- if ((ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY) ||
|
|
- !test_bit(IPS_ASSURED_BIT, &ct->status))
|
|
- return;
|
|
+ bool tcp = false, bidirectional = true;
|
|
|
|
switch (nf_ct_protonum(ct)) {
|
|
case IPPROTO_TCP:
|
|
- tcp = true;
|
|
- if (ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
|
|
+ if ((ctinfo != IP_CT_ESTABLISHED &&
|
|
+ ctinfo != IP_CT_ESTABLISHED_REPLY) ||
|
|
+ !test_bit(IPS_ASSURED_BIT, &ct->status) ||
|
|
+ ct->proto.tcp.state != TCP_CONNTRACK_ESTABLISHED)
|
|
return;
|
|
+
|
|
+ tcp = true;
|
|
break;
|
|
case IPPROTO_UDP:
|
|
+ if (!nf_ct_is_confirmed(ct))
|
|
+ return;
|
|
+ if (!test_bit(IPS_ASSURED_BIT, &ct->status))
|
|
+ bidirectional = false;
|
|
break;
|
|
#ifdef CONFIG_NF_CT_PROTO_GRE
|
|
case IPPROTO_GRE: {
|
|
struct nf_conntrack_tuple *tuple;
|
|
|
|
- if (ct->status & IPS_NAT_MASK)
|
|
+ if ((ctinfo != IP_CT_ESTABLISHED &&
|
|
+ ctinfo != IP_CT_ESTABLISHED_REPLY) ||
|
|
+ !test_bit(IPS_ASSURED_BIT, &ct->status) ||
|
|
+ ct->status & IPS_NAT_MASK)
|
|
return;
|
|
+
|
|
tuple = &ct->tuplehash[IP_CT_DIR_ORIGINAL].tuple;
|
|
/* No support for GRE v1 */
|
|
if (tuple->src.u.gre.key || tuple->dst.u.gre.key)
|
|
@@ -442,7 +494,7 @@ static void tcf_ct_flow_table_process_conn(struct tcf_ct_flow_table *ct_ft,
|
|
ct->status & IPS_SEQ_ADJUST)
|
|
return;
|
|
|
|
- tcf_ct_flow_table_add(ct_ft, ct, tcp);
|
|
+ tcf_ct_flow_table_add(ct_ft, ct, tcp, bidirectional);
|
|
}
|
|
|
|
static bool
|
|
@@ -596,6 +648,7 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
|
|
struct flow_offload_tuple tuple = {};
|
|
enum ip_conntrack_info ctinfo;
|
|
struct tcphdr *tcph = NULL;
|
|
+ bool force_refresh = false;
|
|
struct flow_offload *flow;
|
|
struct nf_conn *ct;
|
|
u8 dir;
|
|
@@ -621,15 +674,40 @@ static bool tcf_ct_flow_table_lookup(struct tcf_ct_params *p,
|
|
flow = container_of(tuplehash, struct flow_offload, tuplehash[dir]);
|
|
ct = flow->ct;
|
|
|
|
+ if (dir == FLOW_OFFLOAD_DIR_REPLY &&
|
|
+ !test_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags)) {
|
|
+ /* Only offload reply direction after connection became
|
|
+ * assured.
|
|
+ */
|
|
+ if (test_bit(IPS_ASSURED_BIT, &ct->status))
|
|
+ set_bit(NF_FLOW_HW_BIDIRECTIONAL, &flow->flags);
|
|
+ else if (test_bit(NF_FLOW_HW_ESTABLISHED, &flow->flags))
|
|
+ /* If flow_table flow has already been updated to the
|
|
+ * established state, then don't refresh.
|
|
+ */
|
|
+ return false;
|
|
+ force_refresh = true;
|
|
+ }
|
|
+
|
|
if (tcph && (unlikely(tcph->fin || tcph->rst))) {
|
|
flow_offload_teardown(flow);
|
|
return false;
|
|
}
|
|
|
|
- ctinfo = dir == FLOW_OFFLOAD_DIR_ORIGINAL ? IP_CT_ESTABLISHED :
|
|
- IP_CT_ESTABLISHED_REPLY;
|
|
+ if (dir == FLOW_OFFLOAD_DIR_ORIGINAL)
|
|
+ ctinfo = test_bit(IPS_SEEN_REPLY_BIT, &ct->status) ?
|
|
+ IP_CT_ESTABLISHED : IP_CT_NEW;
|
|
+ else
|
|
+ ctinfo = IP_CT_ESTABLISHED_REPLY;
|
|
+
|
|
+ nf_conn_act_ct_ext_fill(skb, ct, ctinfo);
|
|
+ tcf_ct_flow_ct_ext_ifidx_update(flow);
|
|
+ flow_offload_refresh(nf_ft, flow, force_refresh);
|
|
+ if (!test_bit(IPS_ASSURED_BIT, &ct->status)) {
|
|
+ /* Process this flow in SW to allow promoting to ASSURED */
|
|
+ return false;
|
|
+ }
|
|
|
|
- flow_offload_refresh(nf_ft, flow);
|
|
nf_conntrack_get(&ct->ct_general);
|
|
nf_ct_set(skb, ct, ctinfo);
|
|
if (nf_ft->flags & NF_FLOWTABLE_COUNTER)
|
|
@@ -832,18 +910,23 @@ out_free:
|
|
return err;
|
|
}
|
|
|
|
-static void tcf_ct_params_free(struct rcu_head *head)
|
|
+static void tcf_ct_params_free(struct tcf_ct_params *params)
|
|
{
|
|
- struct tcf_ct_params *params = container_of(head,
|
|
- struct tcf_ct_params, rcu);
|
|
-
|
|
- tcf_ct_flow_table_put(params);
|
|
-
|
|
+ if (params->ct_ft)
|
|
+ tcf_ct_flow_table_put(params->ct_ft);
|
|
if (params->tmpl)
|
|
nf_ct_put(params->tmpl);
|
|
kfree(params);
|
|
}
|
|
|
|
+static void tcf_ct_params_free_rcu(struct rcu_head *head)
|
|
+{
|
|
+ struct tcf_ct_params *params;
|
|
+
|
|
+ params = container_of(head, struct tcf_ct_params, rcu);
|
|
+ tcf_ct_params_free(params);
|
|
+}
|
|
+
|
|
#if IS_ENABLED(CONFIG_NF_NAT)
|
|
/* Modelled after nf_nat_ipv[46]_fn().
|
|
* range is only used for new, uninitialized NAT state.
|
|
@@ -1121,7 +1204,7 @@ do_nat:
|
|
tcf_ct_act_set_labels(ct, p->labels, p->labels_mask);
|
|
|
|
if (!nf_ct_is_confirmed(ct))
|
|
- nf_conn_act_ct_ext_add(ct);
|
|
+ nf_conn_act_ct_ext_add(skb, ct, ctinfo);
|
|
|
|
/* This will take care of sending queued events
|
|
* even if the connection is already confirmed.
|
|
@@ -1390,7 +1473,7 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla,
|
|
|
|
err = tcf_ct_flow_table_get(net, params);
|
|
if (err)
|
|
- goto cleanup_params;
|
|
+ goto cleanup;
|
|
|
|
spin_lock_bh(&c->tcf_lock);
|
|
goto_ch = tcf_action_set_ctrlact(*a, parm->action, goto_ch);
|
|
@@ -1401,17 +1484,15 @@ static int tcf_ct_init(struct net *net, struct nlattr *nla,
|
|
if (goto_ch)
|
|
tcf_chain_put_by_act(goto_ch);
|
|
if (params)
|
|
- call_rcu(¶ms->rcu, tcf_ct_params_free);
|
|
+ call_rcu(¶ms->rcu, tcf_ct_params_free_rcu);
|
|
|
|
return res;
|
|
|
|
-cleanup_params:
|
|
- if (params->tmpl)
|
|
- nf_ct_put(params->tmpl);
|
|
cleanup:
|
|
if (goto_ch)
|
|
tcf_chain_put_by_act(goto_ch);
|
|
- kfree(params);
|
|
+ if (params)
|
|
+ tcf_ct_params_free(params);
|
|
tcf_idr_release(*a, bind);
|
|
return err;
|
|
}
|
|
@@ -1423,7 +1504,7 @@ static void tcf_ct_cleanup(struct tc_action *a)
|
|
|
|
params = rcu_dereference_protected(c->params, 1);
|
|
if (params)
|
|
- call_rcu(¶ms->rcu, tcf_ct_params_free);
|
|
+ call_rcu(¶ms->rcu, tcf_ct_params_free_rcu);
|
|
}
|
|
|
|
static int tcf_ct_dump_key_val(struct sk_buff *skb,
|
|
diff --git a/net/sched/em_text.c b/net/sched/em_text.c
|
|
index 6f3c1fb2fb44c..f176afb70559e 100644
|
|
--- a/net/sched/em_text.c
|
|
+++ b/net/sched/em_text.c
|
|
@@ -97,8 +97,10 @@ retry:
|
|
|
|
static void em_text_destroy(struct tcf_ematch *m)
|
|
{
|
|
- if (EM_TEXT_PRIV(m) && EM_TEXT_PRIV(m)->config)
|
|
+ if (EM_TEXT_PRIV(m) && EM_TEXT_PRIV(m)->config) {
|
|
textsearch_destroy(EM_TEXT_PRIV(m)->config);
|
|
+ kfree(EM_TEXT_PRIV(m));
|
|
+ }
|
|
}
|
|
|
|
static int em_text_dump(struct sk_buff *skb, struct tcf_ematch *m)
|
|
diff --git a/net/smc/smc_diag.c b/net/smc/smc_diag.c
|
|
index 80ea7d954eceb..801044e7d1949 100644
|
|
--- a/net/smc/smc_diag.c
|
|
+++ b/net/smc/smc_diag.c
|
|
@@ -153,8 +153,7 @@ static int __smc_diag_dump(struct sock *sk, struct sk_buff *skb,
|
|
.lnk[0].link_id = link->link_id,
|
|
};
|
|
|
|
- memcpy(linfo.lnk[0].ibname,
|
|
- smc->conn.lgr->lnk[0].smcibdev->ibdev->name,
|
|
+ memcpy(linfo.lnk[0].ibname, link->smcibdev->ibdev->name,
|
|
sizeof(link->smcibdev->ibdev->name));
|
|
smc_gid_be16_convert(linfo.lnk[0].gid, link->gid);
|
|
smc_gid_be16_convert(linfo.lnk[0].peer_gid, link->peer_gid);
|
|
diff --git a/net/socket.c b/net/socket.c
|
|
index 04cba91c7cbe5..639d76f20384e 100644
|
|
--- a/net/socket.c
|
|
+++ b/net/socket.c
|
|
@@ -130,6 +130,7 @@ static ssize_t sock_sendpage(struct file *file, struct page *page,
|
|
static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
|
|
struct pipe_inode_info *pipe, size_t len,
|
|
unsigned int flags);
|
|
+static void sock_splice_eof(struct file *file);
|
|
|
|
#ifdef CONFIG_PROC_FS
|
|
static void sock_show_fdinfo(struct seq_file *m, struct file *f)
|
|
@@ -164,6 +165,7 @@ static const struct file_operations socket_file_ops = {
|
|
.sendpage = sock_sendpage,
|
|
.splice_write = generic_splice_sendpage,
|
|
.splice_read = sock_splice_read,
|
|
+ .splice_eof = sock_splice_eof,
|
|
.show_fdinfo = sock_show_fdinfo,
|
|
};
|
|
|
|
@@ -740,6 +742,7 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg)
|
|
{
|
|
struct sockaddr_storage *save_addr = (struct sockaddr_storage *)msg->msg_name;
|
|
struct sockaddr_storage address;
|
|
+ int save_len = msg->msg_namelen;
|
|
int ret;
|
|
|
|
if (msg->msg_name) {
|
|
@@ -749,6 +752,7 @@ int sock_sendmsg(struct socket *sock, struct msghdr *msg)
|
|
|
|
ret = __sock_sendmsg(sock, msg);
|
|
msg->msg_name = save_addr;
|
|
+ msg->msg_namelen = save_len;
|
|
|
|
return ret;
|
|
}
|
|
@@ -826,7 +830,7 @@ static bool skb_is_swtx_tstamp(const struct sk_buff *skb, int false_tstamp)
|
|
|
|
static ktime_t get_timestamp(struct sock *sk, struct sk_buff *skb, int *if_index)
|
|
{
|
|
- bool cycles = sk->sk_tsflags & SOF_TIMESTAMPING_BIND_PHC;
|
|
+ bool cycles = READ_ONCE(sk->sk_tsflags) & SOF_TIMESTAMPING_BIND_PHC;
|
|
struct skb_shared_hwtstamps *shhwtstamps = skb_hwtstamps(skb);
|
|
struct net_device *orig_dev;
|
|
ktime_t hwtstamp;
|
|
@@ -878,12 +882,12 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
|
|
int need_software_tstamp = sock_flag(sk, SOCK_RCVTSTAMP);
|
|
int new_tstamp = sock_flag(sk, SOCK_TSTAMP_NEW);
|
|
struct scm_timestamping_internal tss;
|
|
-
|
|
int empty = 1, false_tstamp = 0;
|
|
struct skb_shared_hwtstamps *shhwtstamps =
|
|
skb_hwtstamps(skb);
|
|
int if_index;
|
|
ktime_t hwtstamp;
|
|
+ u32 tsflags;
|
|
|
|
/* Race occurred between timestamp enabling and packet
|
|
receiving. Fill in the current time for now. */
|
|
@@ -925,11 +929,12 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
|
|
}
|
|
|
|
memset(&tss, 0, sizeof(tss));
|
|
- if ((sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) &&
|
|
+ tsflags = READ_ONCE(sk->sk_tsflags);
|
|
+ if ((tsflags & SOF_TIMESTAMPING_SOFTWARE) &&
|
|
ktime_to_timespec64_cond(skb->tstamp, tss.ts + 0))
|
|
empty = 0;
|
|
if (shhwtstamps &&
|
|
- (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) &&
|
|
+ (tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) &&
|
|
!skb_is_swtx_tstamp(skb, false_tstamp)) {
|
|
if_index = 0;
|
|
if (skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP_NETDEV)
|
|
@@ -937,14 +942,14 @@ void __sock_recv_timestamp(struct msghdr *msg, struct sock *sk,
|
|
else
|
|
hwtstamp = shhwtstamps->hwtstamp;
|
|
|
|
- if (sk->sk_tsflags & SOF_TIMESTAMPING_BIND_PHC)
|
|
+ if (tsflags & SOF_TIMESTAMPING_BIND_PHC)
|
|
hwtstamp = ptp_convert_timestamp(&hwtstamp,
|
|
- sk->sk_bind_phc);
|
|
+ READ_ONCE(sk->sk_bind_phc));
|
|
|
|
if (ktime_to_timespec64_cond(hwtstamp, tss.ts + 2)) {
|
|
empty = 0;
|
|
|
|
- if ((sk->sk_tsflags & SOF_TIMESTAMPING_OPT_PKTINFO) &&
|
|
+ if ((tsflags & SOF_TIMESTAMPING_OPT_PKTINFO) &&
|
|
!skb_is_err_queue(skb))
|
|
put_ts_pktinfo(msg, skb, if_index);
|
|
}
|
|
@@ -1088,6 +1093,14 @@ static ssize_t sock_splice_read(struct file *file, loff_t *ppos,
|
|
return sock->ops->splice_read(sock, ppos, pipe, len, flags);
|
|
}
|
|
|
|
+static void sock_splice_eof(struct file *file)
|
|
+{
|
|
+ struct socket *sock = file->private_data;
|
|
+
|
|
+ if (sock->ops->splice_eof)
|
|
+ sock->ops->splice_eof(sock);
|
|
+}
|
|
+
|
|
static ssize_t sock_read_iter(struct kiocb *iocb, struct iov_iter *to)
|
|
{
|
|
struct file *file = iocb->ki_filp;
|
|
@@ -2128,6 +2141,7 @@ int __sys_sendto(int fd, void __user *buff, size_t len, unsigned int flags,
|
|
msg.msg_name = (struct sockaddr *)&address;
|
|
msg.msg_namelen = addr_len;
|
|
}
|
|
+ flags &= ~MSG_INTERNAL_SENDMSG_FLAGS;
|
|
if (sock->file->f_flags & O_NONBLOCK)
|
|
flags |= MSG_DONTWAIT;
|
|
msg.msg_flags = flags;
|
|
@@ -2479,6 +2493,7 @@ static int ____sys_sendmsg(struct socket *sock, struct msghdr *msg_sys,
|
|
msg_sys->msg_control = ctl_buf;
|
|
msg_sys->msg_control_is_user = false;
|
|
}
|
|
+ flags &= ~MSG_INTERNAL_SENDMSG_FLAGS;
|
|
msg_sys->msg_flags = flags;
|
|
|
|
if (sock->file->f_flags & O_NONBLOCK)
|
|
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
|
|
index 6dbeb80073338..be2ed7b0fe21c 100644
|
|
--- a/net/unix/af_unix.c
|
|
+++ b/net/unix/af_unix.c
|
|
@@ -211,8 +211,6 @@ static inline bool unix_secdata_eq(struct scm_cookie *scm, struct sk_buff *skb)
|
|
}
|
|
#endif /* CONFIG_SECURITY_NETWORK */
|
|
|
|
-#define unix_peer(sk) (unix_sk(sk)->peer)
|
|
-
|
|
static inline int unix_our_peer(struct sock *sk, struct sock *osk)
|
|
{
|
|
return unix_peer(osk) == sk;
|
|
diff --git a/net/unix/unix_bpf.c b/net/unix/unix_bpf.c
|
|
index 2f9d8271c6ec7..7ea7c3a0d0d06 100644
|
|
--- a/net/unix/unix_bpf.c
|
|
+++ b/net/unix/unix_bpf.c
|
|
@@ -159,12 +159,17 @@ int unix_dgram_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool re
|
|
|
|
int unix_stream_bpf_update_proto(struct sock *sk, struct sk_psock *psock, bool restore)
|
|
{
|
|
+ struct sock *sk_pair;
|
|
+
|
|
if (restore) {
|
|
sk->sk_write_space = psock->saved_write_space;
|
|
sock_replace_proto(sk, psock->sk_proto);
|
|
return 0;
|
|
}
|
|
|
|
+ sk_pair = unix_peer(sk);
|
|
+ sock_hold(sk_pair);
|
|
+ psock->sk_pair = sk_pair;
|
|
unix_stream_bpf_check_needs_rebuild(psock->sk_proto);
|
|
sock_replace_proto(sk, &unix_stream_bpf_prot);
|
|
return 0;
|
|
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
|
|
index a88ed60dcd96a..1c8ffc5cf97f6 100644
|
|
--- a/sound/pci/hda/patch_realtek.c
|
|
+++ b/sound/pci/hda/patch_realtek.c
|
|
@@ -9581,6 +9581,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|
SND_PCI_QUIRK(0x103c, 0x84da, "HP OMEN dc0019-ur", ALC295_FIXUP_HP_OMEN),
|
|
SND_PCI_QUIRK(0x103c, 0x84e7, "HP Pavilion 15", ALC269_FIXUP_HP_MUTE_LED_MIC3),
|
|
SND_PCI_QUIRK(0x103c, 0x8519, "HP Spectre x360 15-df0xxx", ALC285_FIXUP_HP_SPECTRE_X360),
|
|
+ SND_PCI_QUIRK(0x103c, 0x8537, "HP ProBook 440 G6", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
|
|
SND_PCI_QUIRK(0x103c, 0x860f, "HP ZBook 15 G6", ALC285_FIXUP_HP_GPIO_AMP_INIT),
|
|
SND_PCI_QUIRK(0x103c, 0x861f, "HP Elite Dragonfly G1", ALC285_FIXUP_HP_GPIO_AMP_INIT),
|
|
SND_PCI_QUIRK(0x103c, 0x869d, "HP", ALC236_FIXUP_HP_MUTE_LED),
|
|
@@ -9663,6 +9664,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|
SND_PCI_QUIRK(0x103c, 0x89c6, "Zbook Fury 17 G9", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
|
|
SND_PCI_QUIRK(0x103c, 0x89ca, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
|
|
SND_PCI_QUIRK(0x103c, 0x89d3, "HP EliteBook 645 G9 (MB 89D2)", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
|
|
+ SND_PCI_QUIRK(0x103c, 0x8a0f, "HP Pavilion 14-ec1xxx", ALC287_FIXUP_HP_GPIO_LED),
|
|
SND_PCI_QUIRK(0x103c, 0x8a20, "HP Laptop 15s-fq5xxx", ALC236_FIXUP_HP_MUTE_LED_COEFBIT2),
|
|
SND_PCI_QUIRK(0x103c, 0x8a25, "HP Victus 16-d1xxx (MB 8A25)", ALC245_FIXUP_HP_MUTE_LED_COEFBIT),
|
|
SND_PCI_QUIRK(0x103c, 0x8a78, "HP Dev One", ALC285_FIXUP_HP_LIMIT_INT_MIC_BOOST),
|
|
@@ -9707,6 +9709,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|
SND_PCI_QUIRK(0x103c, 0x8c70, "HP EliteBook 835 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
|
|
SND_PCI_QUIRK(0x103c, 0x8c71, "HP EliteBook 845 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
|
|
SND_PCI_QUIRK(0x103c, 0x8c72, "HP EliteBook 865 G11", ALC287_FIXUP_CS35L41_I2C_2_HP_GPIO_LED),
|
|
+ SND_PCI_QUIRK(0x103c, 0x8c96, "HP", ALC236_FIXUP_HP_MUTE_LED_MICMUTE_VREF),
|
|
SND_PCI_QUIRK(0x103c, 0x8ca4, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
|
|
SND_PCI_QUIRK(0x103c, 0x8ca7, "HP ZBook Fury", ALC245_FIXUP_CS35L41_SPI_2_HP_GPIO_LED),
|
|
SND_PCI_QUIRK(0x103c, 0x8cf5, "HP ZBook Studio 16", ALC245_FIXUP_CS35L41_SPI_4_HP_GPIO_LED),
|
|
@@ -9904,6 +9907,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
|
|
SND_PCI_QUIRK(0x1558, 0xc019, "Clevo NH77D[BE]Q", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
|
|
SND_PCI_QUIRK(0x1558, 0xc022, "Clevo NH77[DC][QW]", ALC293_FIXUP_SYSTEM76_MIC_NO_PRESENCE),
|
|
SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
|
|
+ SND_PCI_QUIRK(0x17aa, 0x3882, "Lenovo Yoga Pro 7 14APH8", ALC287_FIXUP_YOGA9_14IAP7_BASS_SPK_PIN),
|
|
SND_PCI_QUIRK(0x17aa, 0x1048, "ThinkCentre Station", ALC623_FIXUP_LENOVO_THINKSTATION_P340),
|
|
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
|
|
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
|
|
diff --git a/sound/soc/fsl/fsl_rpmsg.c b/sound/soc/fsl/fsl_rpmsg.c
|
|
index bf94838bdbefe..5c07a8ff0c9c0 100644
|
|
--- a/sound/soc/fsl/fsl_rpmsg.c
|
|
+++ b/sound/soc/fsl/fsl_rpmsg.c
|
|
@@ -231,7 +231,7 @@ static int fsl_rpmsg_probe(struct platform_device *pdev)
|
|
ret = devm_snd_soc_register_component(&pdev->dev, &fsl_component,
|
|
&fsl_rpmsg_dai, 1);
|
|
if (ret)
|
|
- return ret;
|
|
+ goto err_pm_disable;
|
|
|
|
rpmsg->card_pdev = platform_device_register_data(&pdev->dev,
|
|
"imx-audio-rpmsg",
|
|
@@ -241,16 +241,22 @@ static int fsl_rpmsg_probe(struct platform_device *pdev)
|
|
if (IS_ERR(rpmsg->card_pdev)) {
|
|
dev_err(&pdev->dev, "failed to register rpmsg card\n");
|
|
ret = PTR_ERR(rpmsg->card_pdev);
|
|
- return ret;
|
|
+ goto err_pm_disable;
|
|
}
|
|
|
|
return 0;
|
|
+
|
|
+err_pm_disable:
|
|
+ pm_runtime_disable(&pdev->dev);
|
|
+ return ret;
|
|
}
|
|
|
|
static int fsl_rpmsg_remove(struct platform_device *pdev)
|
|
{
|
|
struct fsl_rpmsg *rpmsg = platform_get_drvdata(pdev);
|
|
|
|
+ pm_runtime_disable(&pdev->dev);
|
|
+
|
|
if (rpmsg->card_pdev)
|
|
platform_device_unregister(rpmsg->card_pdev);
|
|
|
|
diff --git a/sound/soc/mediatek/mt8186/mt8186-dai-adda.c b/sound/soc/mediatek/mt8186/mt8186-dai-adda.c
|
|
index 094402470dc23..858b95b199dcb 100644
|
|
--- a/sound/soc/mediatek/mt8186/mt8186-dai-adda.c
|
|
+++ b/sound/soc/mediatek/mt8186/mt8186-dai-adda.c
|
|
@@ -499,7 +499,7 @@ static const struct snd_soc_dapm_widget mtk_dai_adda_widgets[] = {
|
|
SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
|
|
|
|
SND_SOC_DAPM_SUPPLY_S("AUD_PAD_TOP", SUPPLY_SEQ_ADDA_AUD_PAD_TOP,
|
|
- 0, 0, 0,
|
|
+ AFE_AUD_PAD_TOP, RG_RX_FIFO_ON_SFT, 0,
|
|
mtk_adda_pad_top_event,
|
|
SND_SOC_DAPM_PRE_PMU),
|
|
SND_SOC_DAPM_SUPPLY_S("ADDA_MTKAIF_CFG", SUPPLY_SEQ_ADDA_MTKAIF_CFG,
|
|
diff --git a/sound/soc/meson/g12a-toacodec.c b/sound/soc/meson/g12a-toacodec.c
|
|
index ddc667956cf5e..8d8d848ebd58b 100644
|
|
--- a/sound/soc/meson/g12a-toacodec.c
|
|
+++ b/sound/soc/meson/g12a-toacodec.c
|
|
@@ -71,6 +71,9 @@ static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol,
|
|
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
|
unsigned int mux, reg;
|
|
|
|
+ if (ucontrol->value.enumerated.item[0] >= e->items)
|
|
+ return -EINVAL;
|
|
+
|
|
mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
|
|
regmap_field_read(priv->field_dat_sel, ®);
|
|
|
|
@@ -101,7 +104,7 @@ static int g12a_toacodec_mux_put_enum(struct snd_kcontrol *kcontrol,
|
|
|
|
snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
|
|
|
|
- return 0;
|
|
+ return 1;
|
|
}
|
|
|
|
static SOC_ENUM_SINGLE_DECL(g12a_toacodec_mux_enum, TOACODEC_CTRL0,
|
|
diff --git a/sound/soc/meson/g12a-tohdmitx.c b/sound/soc/meson/g12a-tohdmitx.c
|
|
index 579a04ad4d197..154c324fdd42a 100644
|
|
--- a/sound/soc/meson/g12a-tohdmitx.c
|
|
+++ b/sound/soc/meson/g12a-tohdmitx.c
|
|
@@ -45,6 +45,9 @@ static int g12a_tohdmitx_i2s_mux_put_enum(struct snd_kcontrol *kcontrol,
|
|
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
|
unsigned int mux, changed;
|
|
|
|
+ if (ucontrol->value.enumerated.item[0] >= e->items)
|
|
+ return -EINVAL;
|
|
+
|
|
mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
|
|
changed = snd_soc_component_test_bits(component, e->reg,
|
|
CTRL0_I2S_DAT_SEL,
|
|
@@ -93,6 +96,9 @@ static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol,
|
|
struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
|
|
unsigned int mux, changed;
|
|
|
|
+ if (ucontrol->value.enumerated.item[0] >= e->items)
|
|
+ return -EINVAL;
|
|
+
|
|
mux = snd_soc_enum_item_to_val(e, ucontrol->value.enumerated.item[0]);
|
|
changed = snd_soc_component_test_bits(component, TOHDMITX_CTRL0,
|
|
CTRL0_SPDIF_SEL,
|
|
@@ -112,7 +118,7 @@ static int g12a_tohdmitx_spdif_mux_put_enum(struct snd_kcontrol *kcontrol,
|
|
|
|
snd_soc_dapm_mux_update_power(dapm, kcontrol, mux, e, NULL);
|
|
|
|
- return 0;
|
|
+ return 1;
|
|
}
|
|
|
|
static SOC_ENUM_SINGLE_DECL(g12a_tohdmitx_spdif_mux_enum, TOHDMITX_CTRL0,
|
|
diff --git a/tools/testing/selftests/bpf/verifier/ld_imm64.c b/tools/testing/selftests/bpf/verifier/ld_imm64.c
|
|
index f9297900cea6d..78f19c255f20b 100644
|
|
--- a/tools/testing/selftests/bpf/verifier/ld_imm64.c
|
|
+++ b/tools/testing/selftests/bpf/verifier/ld_imm64.c
|
|
@@ -9,8 +9,8 @@
|
|
BPF_MOV64_IMM(BPF_REG_0, 2),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
- .errstr = "invalid BPF_LD_IMM insn",
|
|
- .errstr_unpriv = "R1 pointer comparison",
|
|
+ .errstr = "jump into the middle of ldimm64 insn 1",
|
|
+ .errstr_unpriv = "jump into the middle of ldimm64 insn 1",
|
|
.result = REJECT,
|
|
},
|
|
{
|
|
@@ -23,8 +23,8 @@
|
|
BPF_LD_IMM64(BPF_REG_0, 1),
|
|
BPF_EXIT_INSN(),
|
|
},
|
|
- .errstr = "invalid BPF_LD_IMM insn",
|
|
- .errstr_unpriv = "R1 pointer comparison",
|
|
+ .errstr = "jump into the middle of ldimm64 insn 1",
|
|
+ .errstr_unpriv = "jump into the middle of ldimm64 insn 1",
|
|
.result = REJECT,
|
|
},
|
|
{
|
|
diff --git a/tools/testing/selftests/drivers/net/bonding/bond-arp-interval-causes-panic.sh b/tools/testing/selftests/drivers/net/bonding/bond-arp-interval-causes-panic.sh
|
|
index 71c00bfafbc99..2ff58fed76e28 100755
|
|
--- a/tools/testing/selftests/drivers/net/bonding/bond-arp-interval-causes-panic.sh
|
|
+++ b/tools/testing/selftests/drivers/net/bonding/bond-arp-interval-causes-panic.sh
|
|
@@ -33,16 +33,16 @@ ip netns add "client"
|
|
ip link set dev link1_1 netns client down name eth0
|
|
ip netns exec client ip link add dev bond0 down type bond mode 1 \
|
|
miimon 100 all_slaves_active 1
|
|
-ip netns exec client ip link set dev eth0 down master bond0
|
|
+ip netns exec client ip link set dev eth0 master bond0
|
|
ip netns exec client ip link set dev bond0 up
|
|
ip netns exec client ip addr add ${client_ip4}/24 dev bond0
|
|
ip netns exec client ping -c 5 $server_ip4 >/dev/null
|
|
|
|
-ip netns exec client ip link set dev eth0 down nomaster
|
|
+ip netns exec client ip link set dev eth0 nomaster
|
|
ip netns exec client ip link set dev bond0 down
|
|
ip netns exec client ip link set dev bond0 type bond mode 0 \
|
|
arp_interval 1000 arp_ip_target "+${server_ip4}"
|
|
-ip netns exec client ip link set dev eth0 down master bond0
|
|
+ip netns exec client ip link set dev eth0 master bond0
|
|
ip netns exec client ip link set dev bond0 up
|
|
ip netns exec client ping -c 5 $server_ip4 >/dev/null
|
|
|
|
diff --git a/tools/testing/selftests/net/mptcp/mptcp_join.sh b/tools/testing/selftests/net/mptcp/mptcp_join.sh
|
|
index e52d513009fb0..2107579e2939d 100755
|
|
--- a/tools/testing/selftests/net/mptcp/mptcp_join.sh
|
|
+++ b/tools/testing/selftests/net/mptcp/mptcp_join.sh
|
|
@@ -2167,9 +2167,9 @@ link_failure_tests()
|
|
pm_nl_set_limits $ns1 0 2
|
|
pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal
|
|
pm_nl_set_limits $ns2 1 2
|
|
- FAILING_LINKS="1"
|
|
pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup
|
|
- run_tests $ns1 $ns2 10.0.1.1 1
|
|
+ FAILING_LINKS="1" \
|
|
+ run_tests $ns1 $ns2 10.0.1.1 1
|
|
chk_join_nr 2 2 2
|
|
chk_add_nr 1 1
|
|
chk_link_usage $ns2 ns2eth3 $cinsent 0
|
|
@@ -2183,8 +2183,8 @@ link_failure_tests()
|
|
pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal
|
|
pm_nl_set_limits $ns2 1 2
|
|
pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup
|
|
- FAILING_LINKS="1 2"
|
|
- run_tests $ns1 $ns2 10.0.1.1 1
|
|
+ FAILING_LINKS="1 2" \
|
|
+ run_tests $ns1 $ns2 10.0.1.1 1
|
|
chk_join_nr 2 2 2
|
|
chk_add_nr 1 1
|
|
chk_stale_nr $ns2 2 4 2
|
|
@@ -2199,8 +2199,8 @@ link_failure_tests()
|
|
pm_nl_add_endpoint $ns1 10.0.2.1 dev ns1eth2 flags signal
|
|
pm_nl_set_limits $ns2 1 3
|
|
pm_nl_add_endpoint $ns2 10.0.3.2 dev ns2eth3 flags subflow,backup
|
|
- FAILING_LINKS="1 2"
|
|
- run_tests $ns1 $ns2 10.0.1.1 2
|
|
+ FAILING_LINKS="1 2" \
|
|
+ run_tests $ns1 $ns2 10.0.1.1 2
|
|
chk_join_nr 2 2 2
|
|
chk_add_nr 1 1
|
|
chk_stale_nr $ns2 1 -1 2
|
|
@@ -3041,7 +3041,7 @@ fastclose_tests()
|
|
|
|
if reset_check_counter "fastclose server test" "MPTcpExtMPFastcloseRx"; then
|
|
run_tests $ns1 $ns2 10.0.1.1 1024 0 fastclose_server
|
|
- chk_join_nr 0 0 0
|
|
+ chk_join_nr 0 0 0 0 0 0 1
|
|
chk_fclose_nr 1 1 invert
|
|
chk_rst_nr 1 1
|
|
fi
|
|
diff --git a/tools/testing/selftests/vm/memfd_secret.c b/tools/testing/selftests/vm/memfd_secret.c
|
|
index 957b9e18c7295..9b298f6a04b37 100644
|
|
--- a/tools/testing/selftests/vm/memfd_secret.c
|
|
+++ b/tools/testing/selftests/vm/memfd_secret.c
|
|
@@ -62,6 +62,9 @@ static void test_mlock_limit(int fd)
|
|
char *mem;
|
|
|
|
len = mlock_limit_cur;
|
|
+ if (len % page_size != 0)
|
|
+ len = (len/page_size) * page_size;
|
|
+
|
|
mem = mmap(NULL, len, prot, mode, fd, 0);
|
|
if (mem == MAP_FAILED) {
|
|
fail("unable to mmap secret memory\n");
|